frame.py 23 KB

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