frame.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. """
  2. @package iscatt.frame
  3. @brief Main scatter plot widgets.
  4. Classes:
  5. - frame::IClassIScattPanel
  6. - frame::IScattDialog
  7. - frame::MapDispIScattPanel
  8. - frame::ScatterPlotsPanel
  9. - frame::CategoryListCtrl
  10. (C) 2013 by the GRASS Development Team
  11. This program is free software under the GNU General Public License
  12. (>=v2). Read the file COPYING that comes with GRASS for details.
  13. @author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
  14. """
  15. from __future__ import print_function
  16. import os
  17. import six
  18. import wx
  19. import wx.lib.scrolledpanel as scrolled
  20. import wx.lib.mixins.listctrl as listmix
  21. from core import globalvar
  22. from core.gcmd import GError, GMessage
  23. from gui_core.dialogs import SetOpacityDialog
  24. from gui_core.wrap import StaticBox, Menu, ListCtrl
  25. from iscatt.controllers import ScattsManager
  26. from iscatt.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
  27. from iscatt.iscatt_core import idScattToidBands
  28. from iscatt.dialogs import ManageBusyCursorMixin, RenameClassDialog
  29. from iscatt.plots import ScatterPlotWidget
  30. from iclass.dialogs import ContrastColor
  31. try:
  32. from agw import aui
  33. except ImportError:
  34. import wx.lib.agw.aui as aui
  35. class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
  36. def __init__(self, parent, giface, iclass_mapwin=None, id=wx.ID_ANY):
  37. # wx.SplitterWindow.__init__(self, parent = parent, id = id,
  38. # style = wx.SP_LIVE_UPDATE)
  39. wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
  40. ManageBusyCursorMixin.__init__(self, window=self)
  41. self.scatt_mgr = self._createScattMgr(
  42. guiparent=parent, giface=giface, iclass_mapwin=iclass_mapwin
  43. )
  44. # toobars
  45. self.toolbars = {}
  46. self.toolbars["mainToolbar"] = self._createMainToolbar()
  47. self.toolbars["editingToolbar"] = EditingToolbar(
  48. parent=self, scatt_mgr=self.scatt_mgr
  49. )
  50. self._createCategoryPanel(self)
  51. self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
  52. self.mainsizer = wx.BoxSizer(wx.VERTICAL)
  53. self.mainsizer.Add(self.toolbars["mainToolbar"], proportion=0, flag=wx.EXPAND)
  54. self.mainsizer.Add(
  55. self.toolbars["editingToolbar"], proportion=0, flag=wx.EXPAND
  56. )
  57. self.mainsizer.Add(
  58. self.catsPanel, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5
  59. )
  60. self.mainsizer.Add(self.plot_panel, proportion=1, flag=wx.EXPAND)
  61. self.catsPanel.Hide()
  62. self.toolbars["editingToolbar"].Hide()
  63. self.SetSizer(self.mainsizer)
  64. self.scatt_mgr.computingStarted.connect(lambda: self.UpdateCur(busy=True))
  65. self.scatt_mgr.renderingStarted.connect(lambda: self.UpdateCur(busy=True))
  66. self.scatt_mgr.renderingFinished.connect(lambda: self.UpdateCur(busy=False))
  67. # self.SetSashGravity(0.5)
  68. # self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
  69. self.Layout()
  70. def CloseWindow(self):
  71. self.scatt_mgr.CleanUp()
  72. def UpdateCur(self, busy):
  73. self.plot_panel.SetBusy(busy)
  74. ManageBusyCursorMixin.UpdateCur(self, busy)
  75. def _selCatInIScatt(self):
  76. return False
  77. def _createMainToolbar(self):
  78. return MainToolbar(parent=self, scatt_mgr=self.scatt_mgr)
  79. def _createScattMgr(self, guiparent, giface, iclass_mapwin):
  80. return ScattsManager(guiparent=self, giface=giface, iclass_mapwin=iclass_mapwin)
  81. def NewScatterPlot(self, scatt_id, transpose):
  82. return self.plot_panel.NewScatterPlot(scatt_id, transpose)
  83. def ShowPlotEditingToolbar(self, show):
  84. self.toolbars["editingToolbar"].Show(show)
  85. self.Layout()
  86. def ShowCategoryPanel(self, show):
  87. self.catsPanel.Show(show)
  88. # if show:
  89. # self.SetSashSize(5)
  90. # else:
  91. # self.SetSashSize(0)
  92. self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
  93. self.Layout()
  94. def _createCategoryPanel(self, parent):
  95. self.catsPanel = wx.Panel(parent=parent)
  96. self.cats_list = CategoryListCtrl(
  97. parent=self.catsPanel,
  98. cats_mgr=self.scatt_mgr.GetCategoriesManager(),
  99. sel_cats_in_iscatt=self._selCatInIScatt(),
  100. )
  101. self.catsPanel.SetMinSize((-1, 100))
  102. self.catsPanel.SetInitialSize((-1, 150))
  103. box_capt = StaticBox(
  104. parent=self.catsPanel,
  105. id=wx.ID_ANY,
  106. label=" %s " % _("Classes"),
  107. )
  108. catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
  109. self.toolbars["categoryToolbar"] = self._createCategoryToolbar(self.catsPanel)
  110. catsSizer.Add(self.cats_list, proportion=1, flag=wx.EXPAND | wx.TOP, border=5)
  111. if self.toolbars["categoryToolbar"]:
  112. catsSizer.Add(self.toolbars["categoryToolbar"], proportion=0)
  113. self.catsPanel.SetSizer(catsSizer)
  114. def _createCategoryToolbar(self, parent):
  115. return CategoryToolbar(
  116. parent=parent, scatt_mgr=self.scatt_mgr, cats_list=self.cats_list
  117. )
  118. class IScattDialog(wx.Dialog):
  119. def __init__(
  120. self,
  121. parent,
  122. giface,
  123. title=_("Interactive Scatter Plot Tool"),
  124. id=wx.ID_ANY,
  125. style=wx.DEFAULT_FRAME_STYLE,
  126. **kwargs,
  127. ):
  128. wx.Dialog.__init__(self, parent, id, style=style, title=title, **kwargs)
  129. self.SetIcon(
  130. wx.Icon(os.path.join(globalvar.ICONDIR, "grass.ico"), wx.BITMAP_TYPE_ICO)
  131. )
  132. self.iscatt_panel = MapDispIScattPanel(self, giface)
  133. mainsizer = wx.BoxSizer(wx.VERTICAL)
  134. mainsizer.Add(self.iscatt_panel, proportion=1, flag=wx.EXPAND)
  135. self.SetSizer(mainsizer)
  136. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  137. self.SetMinSize((300, 300))
  138. def OnCloseWindow(self, event):
  139. event.Skip()
  140. # self.
  141. class MapDispIScattPanel(IClassIScattPanel):
  142. def __init__(self, parent, giface, id=wx.ID_ANY, **kwargs):
  143. IClassIScattPanel.__init__(self, parent=parent, giface=giface, id=id, **kwargs)
  144. def _createScattMgr(self, guiparent, giface, iclass_mapwin):
  145. return ScattsManager(guiparent=self, giface=giface)
  146. def _createMainToolbar(self):
  147. return MainToolbar(
  148. parent=self, scatt_mgr=self.scatt_mgr, opt_tools=["add_group"]
  149. )
  150. def _selCatInIScatt(self):
  151. return True
  152. class ScatterPlotsPanel(scrolled.ScrolledPanel):
  153. def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
  154. scrolled.ScrolledPanel.__init__(self, parent)
  155. self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
  156. self.scatt_mgr = scatt_mgr
  157. self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
  158. # self._createCategoryPanel()
  159. # Fancy gui
  160. self._mgr = aui.AuiManager(self.mainPanel)
  161. # self._mgr.SetManagedWindow(self)
  162. self._mgr.Update()
  163. self._doLayout()
  164. self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
  165. self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
  166. self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
  167. dlgSize = (-1, 400)
  168. # self.SetBestSize(dlgSize)
  169. # self.SetInitialSize(dlgSize)
  170. self.SetAutoLayout(1)
  171. # fix goutput's pane size (required for Mac OSX)
  172. # if self.gwindow:
  173. # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
  174. self.ignore_scroll = 0
  175. self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
  176. self.scatt_i = 1
  177. self.scatt_id_scatt_i = {}
  178. self.transpose = {}
  179. self.scatts = {}
  180. self.Bind(wx.EVT_CLOSE, self.OnClose)
  181. self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
  182. def SetBusy(self, busy):
  183. for scatt in six.itervalues(self.scatts):
  184. scatt.UpdateCur(busy)
  185. def CursorPlotMove(self, x, y, scatt_id):
  186. try:
  187. x = int(round(x))
  188. y = int(round(y))
  189. coords = True
  190. except:
  191. coords = False
  192. pane = self._getPane(scatt_id)
  193. caption = self._creteCaption(scatt_id)
  194. if coords:
  195. caption += " %d, %d" % (x, y)
  196. pane.Caption(caption)
  197. self._mgr.RefreshCaptions()
  198. def _getPane(self, scatt_id):
  199. scatt_i = self.scatt_id_scatt_i[scatt_id]
  200. name = self._getScatterPlotName(scatt_i)
  201. return self._mgr.GetPane(name)
  202. def ScatterPlotClosed(self, scatt_id):
  203. scatt_i = self.scatt_id_scatt_i[scatt_id]
  204. name = self._getScatterPlotName(scatt_i)
  205. pane = self._mgr.GetPane(name)
  206. del self.scatt_id_scatt_i[scatt_id]
  207. del self.scatts[scatt_id]
  208. if pane.IsOk():
  209. self._mgr.ClosePane(pane)
  210. self._mgr.Update()
  211. def OnMouseWheel(self, event):
  212. # TODO very ugly find some better solution
  213. self.ignore_scroll = 3
  214. event.Skip()
  215. def ScrollChildIntoView(self, child):
  216. # For aui manager it does not work and returns position always to the
  217. # top -> deactivated.
  218. pass
  219. def OnPlotPaneClosed(self, event):
  220. if isinstance(event.pane.window, ScatterPlotWidget):
  221. event.pane.window.CleanUp()
  222. def OnScrollChanged(self, event):
  223. wx.CallAfter(self.Layout)
  224. def OnScroll(self, event):
  225. if self.ignore_scroll > 0:
  226. self.ignore_scroll -= 1
  227. else:
  228. event.Skip()
  229. # wx.CallAfter(self._mgr.Update)
  230. # wx.CallAfter(self.Layout)
  231. def _doLayout(self):
  232. mainsizer = wx.BoxSizer(wx.VERTICAL)
  233. mainsizer.Add(self.mainPanel, proportion=1, flag=wx.EXPAND)
  234. self.SetSizer(mainsizer)
  235. self.Layout()
  236. self.SetupScrolling()
  237. def OnClose(self, event):
  238. """Close dialog"""
  239. # TODO: why todo here, why print? just delete?
  240. print("closed")
  241. self.scatt_mgr.CleanUp()
  242. self.Destroy()
  243. def OnSettings(self, event):
  244. pass
  245. def _newScatterPlotName(self, scatt_id):
  246. name = self._getScatterPlotName(self.scatt_i)
  247. self.scatt_id_scatt_i[scatt_id] = self.scatt_i
  248. self.scatt_i += 1
  249. return name
  250. def _getScatterPlotName(self, i):
  251. name = "scatter plot %d" % i
  252. return name
  253. def NewScatterPlot(self, scatt_id, transpose):
  254. # TODO needs to be resolved (should be in this class)
  255. scatt = ScatterPlotWidget(
  256. parent=self.mainPanel,
  257. scatt_mgr=self.scatt_mgr,
  258. scatt_id=scatt_id,
  259. transpose=transpose,
  260. )
  261. scatt.plotClosed.connect(self.ScatterPlotClosed)
  262. self.transpose[scatt_id] = transpose
  263. caption = self._creteCaption(scatt_id)
  264. self._mgr.AddPane(
  265. scatt,
  266. aui.AuiPaneInfo()
  267. .Dockable(True)
  268. .Floatable(True)
  269. .Name(self._newScatterPlotName(scatt_id))
  270. .MinSize((-1, 300))
  271. .Caption(caption)
  272. .Center()
  273. .Position(1)
  274. .MaximizeButton(True)
  275. .MinimizeButton(True)
  276. .CaptionVisible(True)
  277. .CloseButton(True)
  278. .Layer(0),
  279. )
  280. self._mgr.Update()
  281. self.SetVirtualSize(self.GetBestVirtualSize())
  282. self.Layout()
  283. self.scatts[scatt_id] = scatt
  284. return scatt
  285. def _creteCaption(self, scatt_id):
  286. transpose = self.transpose[scatt_id]
  287. bands = self.scatt_mgr.GetBands()
  288. # TODO too low level
  289. b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
  290. x_b = bands[b1_id].split("@")[0]
  291. y_b = bands[b2_id].split("@")[0]
  292. if transpose:
  293. tmp = x_b
  294. x_b = y_b
  295. y_b = tmp
  296. return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
  297. def GetScattMgr(self):
  298. return self.scatt_mgr
  299. class CategoryListCtrl(ListCtrl, listmix.ListCtrlAutoWidthMixin):
  300. def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id=wx.ID_ANY):
  301. ListCtrl.__init__(
  302. self,
  303. parent,
  304. id,
  305. style=wx.LC_REPORT
  306. | wx.LC_VIRTUAL
  307. | wx.LC_HRULES
  308. | wx.LC_VRULES
  309. | wx.LC_SINGLE_SEL
  310. | wx.LC_NO_HEADER,
  311. )
  312. self.columns = ((_("Class name"), "name"),)
  313. # (_('Color'), 'color'))
  314. self.sel_cats_in_iscatt = sel_cats_in_iscatt
  315. self.Populate(columns=self.columns)
  316. self.cats_mgr = cats_mgr
  317. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  318. self.rightClickedItemIdx = wx.NOT_FOUND
  319. listmix.ListCtrlAutoWidthMixin.__init__(self)
  320. # listmix.TextEditMixin.__init__(self)
  321. self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) # wxMSW
  322. self.Bind(wx.EVT_RIGHT_UP, self.OnCategoryRightUp) # wxGTK
  323. # self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
  324. self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
  325. self.cats_mgr.setCategoryAttrs.connect(self.Update)
  326. self.cats_mgr.deletedCategory.connect(self.Update)
  327. self.cats_mgr.addedCategory.connect(self.Update)
  328. def Update(self, **kwargs):
  329. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  330. if len(self.cats_mgr.GetCategories()):
  331. self.RefreshItems(0, len(self.cats_mgr.GetCategories()) - 1)
  332. def SetVirtualData(self, row, column, text):
  333. attr = self.columns[column][1]
  334. if attr == "name":
  335. try:
  336. text.encode("ascii")
  337. except UnicodeEncodeError:
  338. GMessage(parent=self, message=_("Please use only ASCII characters."))
  339. return
  340. cat_id = self.cats_mgr.GetCategories()[row]
  341. self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
  342. self.cats_mgr.SetCategoryAttrs(cat_id, {attr: text})
  343. self.cats_mgr.setCategoryAttrs.connect(self.Update)
  344. self.Select(row)
  345. def Populate(self, columns):
  346. for i, col in enumerate(columns):
  347. self.InsertColumn(i, col[0]) # wx.LIST_FORMAT_RIGHT
  348. # self.SetColumnWidth(0, 100)
  349. # self.SetColumnWidth(1, 100)
  350. def AddCategory(self):
  351. self.cats_mgr.addedCategory.disconnect(self.Update)
  352. cat_id = self.cats_mgr.AddCategory()
  353. self.cats_mgr.addedCategory.connect(self.Update)
  354. if cat_id < 0:
  355. GError(_("Maximum limit of categories number was reached."))
  356. return
  357. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  358. def DeleteCategory(self):
  359. indexList = sorted(self.GetSelectedIndices(), reverse=True)
  360. cats = []
  361. for i in indexList:
  362. # remove temporary raster
  363. cat_id = self.cats_mgr.GetCategories()[i]
  364. cats.append(cat_id)
  365. self.cats_mgr.deletedCategory.disconnect(self.Update)
  366. self.cats_mgr.DeleteCategory(cat_id)
  367. self.cats_mgr.deletedCategory.connect(self.Update)
  368. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  369. def OnSel(self, event):
  370. if self.sel_cats_in_iscatt:
  371. indexList = self.GetSelectedIndices()
  372. sel_cats = []
  373. cats = self.cats_mgr.GetCategories()
  374. for i in indexList:
  375. sel_cats.append(cats[i])
  376. if sel_cats:
  377. self.cats_mgr.SetSelectedCat(sel_cats[0])
  378. event.Skip()
  379. def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED):
  380. indices = []
  381. lastFound = -1
  382. while True:
  383. index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
  384. if index == -1:
  385. break
  386. else:
  387. lastFound = index
  388. indices.append(index)
  389. return indices
  390. def DeselectAll(self):
  391. """Deselect all items"""
  392. indexList = self.GetSelectedIndices()
  393. for i in indexList:
  394. self.Select(i, on=0)
  395. # no highlight
  396. self.OnCategorySelected(None)
  397. def OnGetItemText(self, item, col):
  398. attr = self.columns[col][1]
  399. cat_id = self.cats_mgr.GetCategories()[item]
  400. return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
  401. def OnGetItemImage(self, item):
  402. return -1
  403. def OnGetItemAttr(self, item):
  404. cat_id = self.cats_mgr.GetCategories()[item]
  405. cattr = self.cats_mgr.GetCategoryAttrs(cat_id)
  406. if cattr["show"]:
  407. c = cattr["color"]
  408. back_c = wx.Colour(*map(int, c.split(":")))
  409. text_c = wx.Colour(*ContrastColor(back_c))
  410. else:
  411. back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
  412. text_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
  413. # if it is in scope of the method, gui falls, using self solved it
  414. self.l = wx.ListItemAttr()
  415. self.l.SetBackgroundColour(back_c)
  416. self.l.SetTextColour(text_c)
  417. return self.l
  418. def OnCategoryRightUp(self, event):
  419. """Show context menu on right click"""
  420. item, flags = self.HitTest((event.GetX(), event.GetY()))
  421. if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
  422. self.rightClickedItemIdx = item
  423. # generate popup-menu
  424. cat_idx = self.rightClickedItemIdx
  425. cats = self.cats_mgr.GetCategories()
  426. cat_id = cats[cat_idx]
  427. showed = self.cats_mgr.GetCategoryAttrs(cat_id)["show"]
  428. menu = Menu()
  429. item = menu.Append(wx.ID_ANY, _("Rename class"))
  430. self.Bind(wx.EVT_MENU, self.OnRename, item)
  431. item = menu.Append(wx.ID_ANY, _("Set color"))
  432. self.Bind(wx.EVT_MENU, self.OnSetColor, item)
  433. item = menu.Append(wx.ID_ANY, _("Change opacity level"))
  434. self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, item)
  435. if showed:
  436. text = _("Hide")
  437. else:
  438. text = _("Show")
  439. item = menu.Append(wx.ID_ANY, text)
  440. self.Bind(
  441. wx.EVT_MENU,
  442. lambda event: self._setCatAttrs(cat_id=cat_id, attrs={"show": not showed}),
  443. item,
  444. )
  445. menu.AppendSeparator()
  446. item = menu.Append(wx.ID_ANY, _("Move to top"))
  447. self.Bind(wx.EVT_MENU, self.OnMoveTop, item)
  448. if cat_idx == 0:
  449. menu.Enable(item.GetId(), False)
  450. item = menu.Append(wx.ID_ANY, _("Move to bottom"))
  451. self.Bind(wx.EVT_MENU, self.OnMoveBottom, item)
  452. if cat_idx == len(cats) - 1:
  453. menu.Enable(item.GetId(), False)
  454. menu.AppendSeparator()
  455. item = menu.Append(wx.ID_ANY, _("Move category up"))
  456. self.Bind(wx.EVT_MENU, self.OnMoveUp, item)
  457. if cat_idx == 0:
  458. menu.Enable(item.GetId(), False)
  459. item = menu.Append(wx.ID_ANY, _("Move category down"))
  460. self.Bind(wx.EVT_MENU, self.OnMoveDown, item)
  461. if cat_idx == len(cats) - 1:
  462. menu.Enable(item.GetId(), False)
  463. menu.AppendSeparator()
  464. item = menu.Append(wx.ID_ANY, _("Export class raster"))
  465. self.Bind(wx.EVT_MENU, self.OnExportCatRast, item)
  466. self.PopupMenu(menu)
  467. menu.Destroy()
  468. def OnExportCatRast(self, event):
  469. """Export training areas"""
  470. # TODO
  471. # GMessage(parent=self, message=_("No class raster to export."))
  472. # return
  473. cat_idx = self.rightClickedItemIdx
  474. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  475. self.cats_mgr.ExportCatRast(cat_id)
  476. def OnMoveUp(self, event):
  477. cat_idx = self.rightClickedItemIdx
  478. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  479. self.cats_mgr.ChangePosition(cat_id, cat_idx - 1)
  480. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  481. def OnMoveDown(self, event):
  482. cat_idx = self.rightClickedItemIdx
  483. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  484. self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
  485. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  486. def OnMoveTop(self, event):
  487. cat_idx = self.rightClickedItemIdx
  488. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  489. self.cats_mgr.ChangePosition(cat_id, 0)
  490. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  491. def OnMoveBottom(self, event):
  492. cat_idx = self.rightClickedItemIdx
  493. cats = self.cats_mgr.GetCategories()
  494. cat_id = cats[cat_idx]
  495. self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
  496. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  497. def OnSetColor(self, event):
  498. """Popup opacity level indicator"""
  499. cat_idx = self.rightClickedItemIdx
  500. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  501. col = self.cats_mgr.GetCategoryAttrs(cat_id)["color"]
  502. col = map(int, col.split(":"))
  503. col_data = wx.ColourData()
  504. col_data.SetColour(wx.Colour(*col))
  505. dlg = wx.ColourDialog(self, col_data)
  506. dlg.GetColourData().SetChooseFull(True)
  507. if dlg.ShowModal() == wx.ID_OK:
  508. color = dlg.GetColourData().GetColour().Get()
  509. color = ":".join(map(str, color))
  510. self.cats_mgr.SetCategoryAttrs(cat_id, {"color": color})
  511. dlg.Destroy()
  512. def OnPopupOpacityLevel(self, event):
  513. """Popup opacity level indicator"""
  514. cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
  515. cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  516. value = cat_attrs["opacity"] * 100
  517. name = cat_attrs["name"]
  518. dlg = SetOpacityDialog(
  519. self, opacity=value, title=_("Change opacity of class <%s>" % name)
  520. )
  521. dlg.applyOpacity.connect(
  522. lambda value: self._setCatAttrs(cat_id=cat_id, attrs={"opacity": value})
  523. )
  524. dlg.CentreOnParent()
  525. if dlg.ShowModal() == wx.ID_OK:
  526. self._setCatAttrs(cat_id=cat_id, attrs={"opacity": value})
  527. dlg.Destroy()
  528. def OnRename(self, event):
  529. cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
  530. cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  531. dlg = RenameClassDialog(self, old_name=cat_attrs["name"])
  532. dlg.CentreOnParent()
  533. while True:
  534. if dlg.ShowModal() == wx.ID_OK:
  535. name = dlg.GetNewName().strip()
  536. if not name:
  537. GMessage(parent=self, message=_("Empty name was inserted."))
  538. else:
  539. self.cats_mgr.SetCategoryAttrs(cat_id, {"name": name})
  540. break
  541. else:
  542. break
  543. dlg.Destroy()
  544. def _setCatAttrs(self, cat_id, attrs):
  545. self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
  546. self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
  547. self.cats_mgr.setCategoryAttrs.connect(self.Update)