frame.py 23 KB

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