frame.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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. # listmix.TextEditMixin):
  303. def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id=wx.ID_ANY):
  304. ListCtrl.__init__(
  305. self, parent, id, style=wx.LC_REPORT | wx.LC_VIRTUAL | wx.LC_HRULES |
  306. wx.LC_VRULES | wx.LC_SINGLE_SEL | wx.LC_NO_HEADER)
  307. self.columns = ((_('Class name'), 'name'), )
  308. #(_('Color'), 'color'))
  309. self.sel_cats_in_iscatt = sel_cats_in_iscatt
  310. self.Populate(columns=self.columns)
  311. self.cats_mgr = cats_mgr
  312. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  313. self.rightClickedItemIdx = wx.NOT_FOUND
  314. listmix.ListCtrlAutoWidthMixin.__init__(self)
  315. # listmix.TextEditMixin.__init__(self)
  316. self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) # wxMSW
  317. self.Bind(wx.EVT_RIGHT_UP, self.OnCategoryRightUp) # wxGTK
  318. #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
  319. self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
  320. self.cats_mgr.setCategoryAttrs.connect(self.Update)
  321. self.cats_mgr.deletedCategory.connect(self.Update)
  322. self.cats_mgr.addedCategory.connect(self.Update)
  323. def Update(self, **kwargs):
  324. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  325. if len(self.cats_mgr.GetCategories()):
  326. self.RefreshItems(0, len(self.cats_mgr.GetCategories()) - 1)
  327. def SetVirtualData(self, row, column, text):
  328. attr = self.columns[column][1]
  329. if attr == 'name':
  330. try:
  331. text.encode('ascii')
  332. except UnicodeEncodeError:
  333. GMessage(parent=self, message=_(
  334. "Please use only ASCII characters."))
  335. return
  336. cat_id = self.cats_mgr.GetCategories()[row]
  337. self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
  338. self.cats_mgr.SetCategoryAttrs(cat_id, {attr: text})
  339. self.cats_mgr.setCategoryAttrs.connect(self.Update)
  340. self.Select(row)
  341. def Populate(self, columns):
  342. for i, col in enumerate(columns):
  343. self.InsertColumn(i, col[0]) # wx.LIST_FORMAT_RIGHT
  344. #self.SetColumnWidth(0, 100)
  345. #self.SetColumnWidth(1, 100)
  346. def AddCategory(self):
  347. self.cats_mgr.addedCategory.disconnect(self.Update)
  348. cat_id = self.cats_mgr.AddCategory()
  349. self.cats_mgr.addedCategory.connect(self.Update)
  350. if cat_id < 0:
  351. GError(_("Maximum limit of categories number was reached."))
  352. return
  353. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  354. def DeleteCategory(self):
  355. indexList = sorted(self.GetSelectedIndices(), reverse=True)
  356. cats = []
  357. for i in indexList:
  358. # remove temporary raster
  359. cat_id = self.cats_mgr.GetCategories()[i]
  360. cats.append(cat_id)
  361. self.cats_mgr.deletedCategory.disconnect(self.Update)
  362. self.cats_mgr.DeleteCategory(cat_id)
  363. self.cats_mgr.deletedCategory.connect(self.Update)
  364. self.SetItemCount(len(self.cats_mgr.GetCategories()))
  365. def OnSel(self, event):
  366. if self.sel_cats_in_iscatt:
  367. indexList = self.GetSelectedIndices()
  368. sel_cats = []
  369. cats = self.cats_mgr.GetCategories()
  370. for i in indexList:
  371. sel_cats.append(cats[i])
  372. if sel_cats:
  373. self.cats_mgr.SetSelectedCat(sel_cats[0])
  374. event.Skip()
  375. def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED):
  376. indices = []
  377. lastFound = -1
  378. while True:
  379. index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
  380. if index == -1:
  381. break
  382. else:
  383. lastFound = index
  384. indices.append(index)
  385. return indices
  386. def DeselectAll(self):
  387. """Deselect all items"""
  388. indexList = self.GetSelectedIndices()
  389. for i in indexList:
  390. self.Select(i, on=0)
  391. # no highlight
  392. self.OnCategorySelected(None)
  393. def OnGetItemText(self, item, col):
  394. attr = self.columns[col][1]
  395. cat_id = self.cats_mgr.GetCategories()[item]
  396. return self.cats_mgr.GetCategoryAttrs(cat_id)[attr]
  397. def OnGetItemImage(self, item):
  398. return -1
  399. def OnGetItemAttr(self, item):
  400. cat_id = self.cats_mgr.GetCategories()[item]
  401. cattr = self.cats_mgr.GetCategoryAttrs(cat_id)
  402. if cattr['show']:
  403. c = cattr['color']
  404. back_c = wx.Colour(*map(int, c.split(':')))
  405. text_c = wx.Colour(*ContrastColor(back_c))
  406. else:
  407. back_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
  408. text_c = wx.SystemSettings.GetColour(
  409. wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
  410. # if it is in scope of the method, gui falls, using self solved it
  411. self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
  412. return self.l
  413. def OnCategoryRightUp(self, event):
  414. """Show context menu on right click"""
  415. item, flags = self.HitTest((event.GetX(), event.GetY()))
  416. if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
  417. self.rightClickedItemIdx = item
  418. # generate popup-menu
  419. cat_idx = self.rightClickedItemIdx
  420. cats = self.cats_mgr.GetCategories()
  421. cat_id = cats[cat_idx]
  422. showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
  423. menu = Menu()
  424. item = menu.Append(wx.ID_ANY, text=_("Rename class"))
  425. self.Bind(wx.EVT_MENU, self.OnRename, item)
  426. item = menu.Append(wx.ID_ANY, text=_("Set color"))
  427. self.Bind(wx.EVT_MENU, self.OnSetColor, item)
  428. item = menu.Append(item_id, text=_("Change opacity level"))
  429. self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, item)
  430. if showed:
  431. text = _("Hide")
  432. else:
  433. text = _("Show")
  434. item = menu.Append(wx.ID_ANY, text=text)
  435. self.Bind(
  436. wx.EVT_MENU,
  437. lambda event: self._setCatAttrs(
  438. cat_id=cat_id,
  439. attrs={
  440. 'show': not showed}),
  441. item)
  442. menu.AppendSeparator()
  443. item = menu.Append(wx.ID_ANY, text=_("Move to top"))
  444. self.Bind(wx.EVT_MENU, self.OnMoveTop, item)
  445. if cat_idx == 0:
  446. menu.Enable(item.GetId(), False)
  447. item = menu.Append(wx.ID_ANY, text=_("Move to bottom"))
  448. self.Bind(wx.EVT_MENU, self.OnMoveBottom, item)
  449. if cat_idx == len(cats) - 1:
  450. menu.Enable(item.GetId(), False)
  451. menu.AppendSeparator()
  452. item = menu.Append(wx.ID_ANY, text=_("Move category up"))
  453. self.Bind(wx.EVT_MENU, self.OnMoveUp, item)
  454. if cat_idx == 0:
  455. menu.Enable(item.GetId(), False)
  456. item = menu.Append(wx.ID_ANY, text=_("Move category down"))
  457. self.Bind(wx.EVT_MENU, self.OnMoveDown, item)
  458. if cat_idx == len(cats) - 1:
  459. menu.Enable(item.GetId(), False)
  460. menu.AppendSeparator()
  461. item = menu.Append(wx.ID_ANY, text=_("Export class raster"))
  462. self.Bind(wx.EVT_MENU, self.OnExportCatRast, item)
  463. self.PopupMenu(menu)
  464. menu.Destroy()
  465. def OnExportCatRast(self, event):
  466. """Export training areas"""
  467. # TODO
  468. # GMessage(parent=self, message=_("No class raster to export."))
  469. # return
  470. cat_idx = self.rightClickedItemIdx
  471. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  472. self.cats_mgr.ExportCatRast(cat_id)
  473. def OnMoveUp(self, event):
  474. cat_idx = self.rightClickedItemIdx
  475. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  476. self.cats_mgr.ChangePosition(cat_id, cat_idx - 1)
  477. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  478. def OnMoveDown(self, event):
  479. cat_idx = self.rightClickedItemIdx
  480. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  481. self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
  482. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  483. def OnMoveTop(self, event):
  484. cat_idx = self.rightClickedItemIdx
  485. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  486. self.cats_mgr.ChangePosition(cat_id, 0)
  487. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  488. def OnMoveBottom(self, event):
  489. cat_idx = self.rightClickedItemIdx
  490. cats = self.cats_mgr.GetCategories()
  491. cat_id = cats[cat_idx]
  492. self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
  493. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  494. def OnSetColor(self, event):
  495. """Popup opacity level indicator"""
  496. cat_idx = self.rightClickedItemIdx
  497. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  498. col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
  499. col = map(int, col.split(':'))
  500. col_data = wx.ColourData()
  501. col_data.SetColour(wx.Colour(*col))
  502. dlg = wx.ColourDialog(self, col_data)
  503. dlg.GetColourData().SetChooseFull(True)
  504. if dlg.ShowModal() == wx.ID_OK:
  505. color = dlg.GetColourData().GetColour().Get()
  506. color = ':'.join(map(str, color))
  507. self.cats_mgr.SetCategoryAttrs(cat_id, {"color": color})
  508. dlg.Destroy()
  509. def OnPopupOpacityLevel(self, event):
  510. """Popup opacity level indicator"""
  511. cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
  512. cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  513. value = cat_attrs['opacity'] * 100
  514. name = cat_attrs['name']
  515. dlg = SetOpacityDialog(self, opacity=value,
  516. title=_("Change opacity of class <%s>" % name))
  517. dlg.applyOpacity.connect(
  518. lambda value: self._setCatAttrs(
  519. cat_id=cat_id, attrs={
  520. 'opacity': value}))
  521. dlg.CentreOnParent()
  522. if dlg.ShowModal() == wx.ID_OK:
  523. self._setCatAttrs(cat_id=cat_id, attrs={'opacity': value})
  524. dlg.Destroy()
  525. def OnRename(self, event):
  526. cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
  527. cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  528. dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
  529. dlg.CentreOnParent()
  530. while True:
  531. if dlg.ShowModal() == wx.ID_OK:
  532. name = dlg.GetNewName().strip()
  533. if not name:
  534. GMessage(parent=self, message=_(
  535. "Empty name was inserted."))
  536. else:
  537. self.cats_mgr.SetCategoryAttrs(cat_id, {"name": name})
  538. break
  539. else:
  540. break
  541. dlg.Destroy()
  542. def _setCatAttrs(self, cat_id, attrs):
  543. self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
  544. self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
  545. self.cats_mgr.setCategoryAttrs.connect(self.Update)