frame.py 23 KB

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