frame.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  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. import os
  16. import sys
  17. import wx
  18. import wx.lib.scrolledpanel as scrolled
  19. import wx.lib.mixins.listctrl as listmix
  20. from core import globalvar
  21. from core.gcmd import GException, GError, RunCommand
  22. from gui_core.gselect import Select
  23. from gui_core.dialogs import SetOpacityDialog
  24. from iscatt.controllers import ScattsManager
  25. from iscatt.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
  26. from iscatt.iscatt_core import idScattToidBands
  27. from iscatt.dialogs import ManageBusyCursorMixin, RenameClassDialog
  28. from iscatt.plots import ScatterPlotWidget
  29. from iclass.dialogs import ContrastColor
  30. try:
  31. from agw import aui
  32. except ImportError:
  33. import wx.lib.agw.aui as aui
  34. class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin):
  35. def __init__(self, parent, giface, iclass_mapwin=None,
  36. id=wx.ID_ANY):
  37. # wx.SplitterWindow.__init__(self, parent = parent, id = id,
  38. # style = wx.SP_LIVE_UPDATE)
  39. wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)
  40. ManageBusyCursorMixin.__init__(self, window=self)
  41. self.scatt_mgr = self._createScattMgr(guiparent=parent, giface=giface,
  42. iclass_mapwin=iclass_mapwin)
  43. # toobars
  44. self.toolbars = {}
  45. self.toolbars['mainToolbar'] = self._createMainToolbar()
  46. self.toolbars['editingToolbar'] = EditingToolbar(
  47. parent=self, scatt_mgr=self.scatt_mgr)
  48. self._createCategoryPanel(self)
  49. self.plot_panel = ScatterPlotsPanel(self, self.scatt_mgr)
  50. self.mainsizer = wx.BoxSizer(wx.VERTICAL)
  51. self.mainsizer.Add(
  52. self.toolbars['mainToolbar'],
  53. proportion=0, flag=wx.EXPAND)
  54. self.mainsizer.Add(
  55. self.toolbars['editingToolbar'],
  56. proportion=0, flag=wx.EXPAND)
  57. self.mainsizer.Add(self.catsPanel, proportion=0,
  58. flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)
  59. self.mainsizer.Add(self.plot_panel, proportion=1, flag=wx.EXPAND)
  60. self.catsPanel.Hide()
  61. self.toolbars['editingToolbar'].Hide()
  62. self.SetSizer(self.mainsizer)
  63. self.scatt_mgr.computingStarted.connect(
  64. lambda: self.UpdateCur(busy=True))
  65. self.scatt_mgr.renderingStarted.connect(
  66. lambda: self.UpdateCur(busy=True))
  67. self.scatt_mgr.renderingFinished.connect(
  68. lambda: self.UpdateCur(busy=False))
  69. # self.SetSashGravity(0.5)
  70. #self.SplitHorizontally(self.head_panel, self.plot_panel, -50)
  71. self.Layout()
  72. def CloseWindow(self):
  73. self.scatt_mgr.CleanUp()
  74. def UpdateCur(self, busy):
  75. self.plot_panel.SetBusy(busy)
  76. ManageBusyCursorMixin.UpdateCur(self, busy)
  77. def _selCatInIScatt(self):
  78. return False
  79. def _createMainToolbar(self):
  80. return MainToolbar(parent=self, scatt_mgr=self.scatt_mgr)
  81. def _createScattMgr(self, guiparent, giface, iclass_mapwin):
  82. return ScattsManager(guiparent=self, giface=giface,
  83. iclass_mapwin=iclass_mapwin)
  84. def NewScatterPlot(self, scatt_id, transpose):
  85. return self.plot_panel.NewScatterPlot(scatt_id, transpose)
  86. def ShowPlotEditingToolbar(self, show):
  87. self.toolbars["editingToolbar"].Show(show)
  88. self.Layout()
  89. def ShowCategoryPanel(self, show):
  90. self.catsPanel.Show(show)
  91. # if show:
  92. # self.SetSashSize(5)
  93. # else:
  94. # self.SetSashSize(0)
  95. self.plot_panel.SetVirtualSize(self.plot_panel.GetBestVirtualSize())
  96. self.Layout()
  97. def _createCategoryPanel(self, parent):
  98. self.catsPanel = wx.Panel(parent=parent)
  99. self.cats_list = CategoryListCtrl(
  100. parent=self.catsPanel,
  101. cats_mgr=self.scatt_mgr.GetCategoriesManager(),
  102. sel_cats_in_iscatt=self._selCatInIScatt())
  103. self.catsPanel.SetMinSize((-1, 100))
  104. self.catsPanel.SetInitialSize((-1, 150))
  105. box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
  106. label=' %s ' % _("Classes"),)
  107. catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
  108. self.toolbars['categoryToolbar'] = self._createCategoryToolbar(
  109. self.catsPanel)
  110. catsSizer.Add(
  111. self.cats_list,
  112. proportion=1,
  113. flag=wx.EXPAND | wx.TOP,
  114. border=5)
  115. if self.toolbars['categoryToolbar']:
  116. catsSizer.Add(self.toolbars['categoryToolbar'], proportion=0)
  117. self.catsPanel.SetSizer(catsSizer)
  118. def _createCategoryToolbar(self, parent):
  119. return CategoryToolbar(parent=parent,
  120. scatt_mgr=self.scatt_mgr,
  121. cats_list=self.cats_list)
  122. class IScattDialog(wx.Dialog):
  123. def __init__(
  124. self, parent, giface,
  125. title=_("GRASS GIS Interactive Scatter Plot Tool"),
  126. id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, **kwargs):
  127. wx.Dialog.__init__(
  128. self,
  129. parent,
  130. id,
  131. style=style,
  132. title=title,
  133. **kwargs)
  134. self.SetIcon(
  135. wx.Icon(
  136. os.path.join(
  137. globalvar.ICONDIR,
  138. 'grass.ico'),
  139. wx.BITMAP_TYPE_ICO))
  140. self.iscatt_panel = MapDispIScattPanel(self, giface)
  141. mainsizer = wx.BoxSizer(wx.VERTICAL)
  142. mainsizer.Add(self.iscatt_panel, proportion=1, flag=wx.EXPAND)
  143. self.SetSizer(mainsizer)
  144. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  145. self.SetMinSize((300, 300))
  146. def OnCloseWindow(self, event):
  147. event.Skip()
  148. # self.
  149. class MapDispIScattPanel(IClassIScattPanel):
  150. def __init__(self, parent, giface,
  151. id=wx.ID_ANY, **kwargs):
  152. IClassIScattPanel.__init__(self, parent=parent, giface=giface,
  153. id=id, **kwargs)
  154. def _createScattMgr(self, guiparent, giface, iclass_mapwin):
  155. return ScattsManager(guiparent=self, giface=giface)
  156. def _createMainToolbar(self):
  157. return MainToolbar(
  158. parent=self, scatt_mgr=self.scatt_mgr, opt_tools=['add_group'])
  159. def _selCatInIScatt(self):
  160. return True
  161. class ScatterPlotsPanel(scrolled.ScrolledPanel):
  162. def __init__(self, parent, scatt_mgr, id=wx.ID_ANY):
  163. scrolled.ScrolledPanel.__init__(self, parent)
  164. self.SetupScrolling(scroll_x=False, scroll_y=True, scrollToTop=False)
  165. self.scatt_mgr = scatt_mgr
  166. self.mainPanel = wx.Panel(parent=self, id=wx.ID_ANY)
  167. # self._createCategoryPanel()
  168. # Fancy gui
  169. self._mgr = aui.AuiManager(self.mainPanel)
  170. # self._mgr.SetManagedWindow(self)
  171. self._mgr.Update()
  172. self._doLayout()
  173. self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
  174. self.Bind(wx.EVT_SCROLL_CHANGED, self.OnScrollChanged)
  175. self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPlotPaneClosed)
  176. dlgSize = (-1, 400)
  177. # self.SetBestSize(dlgSize)
  178. # self.SetInitialSize(dlgSize)
  179. self.SetAutoLayout(1)
  180. # fix goutput's pane size (required for Mac OSX)
  181. # if self.gwindow:
  182. # self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
  183. self.ignore_scroll = 0
  184. self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
  185. self.scatt_i = 1
  186. self.scatt_id_scatt_i = {}
  187. self.transpose = {}
  188. self.scatts = {}
  189. self.Bind(wx.EVT_CLOSE, self.OnClose)
  190. self.scatt_mgr.cursorPlotMove.connect(self.CursorPlotMove)
  191. def SetBusy(self, busy):
  192. for scatt in self.scatts.itervalues():
  193. scatt.UpdateCur(busy)
  194. def CursorPlotMove(self, x, y, scatt_id):
  195. try:
  196. x = int(round(x))
  197. y = int(round(y))
  198. coords = True
  199. except:
  200. coords = False
  201. pane = self._getPane(scatt_id)
  202. caption = self._creteCaption(scatt_id)
  203. if coords:
  204. caption += " %d, %d" % (x, y)
  205. pane.Caption(caption)
  206. self._mgr.RefreshCaptions()
  207. def _getPane(self, scatt_id):
  208. scatt_i = self.scatt_id_scatt_i[scatt_id]
  209. name = self._getScatterPlotName(scatt_i)
  210. return self._mgr.GetPane(name)
  211. def ScatterPlotClosed(self, scatt_id):
  212. scatt_i = self.scatt_id_scatt_i[scatt_id]
  213. name = self._getScatterPlotName(scatt_i)
  214. pane = self._mgr.GetPane(name)
  215. del self.scatt_id_scatt_i[scatt_id]
  216. del self.scatts[scatt_id]
  217. if pane.IsOk():
  218. self._mgr.ClosePane(pane)
  219. self._mgr.Update()
  220. def OnMouseWheel(self, event):
  221. # TODO very ugly find some better solution
  222. self.ignore_scroll = 3
  223. event.Skip()
  224. def ScrollChildIntoView(self, child):
  225. # For aui manager it does not work and returns position always to the
  226. # top -> deactivated.
  227. pass
  228. def OnPlotPaneClosed(self, event):
  229. if isinstance(event.pane.window, ScatterPlotWidget):
  230. event.pane.window.CleanUp()
  231. def OnScrollChanged(self, event):
  232. wx.CallAfter(self.Layout)
  233. def OnScroll(self, event):
  234. if self.ignore_scroll > 0:
  235. self.ignore_scroll -= 1
  236. else:
  237. event.Skip()
  238. # wx.CallAfter(self._mgr.Update)
  239. # wx.CallAfter(self.Layout)
  240. def _doLayout(self):
  241. mainsizer = wx.BoxSizer(wx.VERTICAL)
  242. mainsizer.Add(self.mainPanel, proportion=1, flag=wx.EXPAND)
  243. self.SetSizer(mainsizer)
  244. self.Layout()
  245. self.SetupScrolling()
  246. def OnClose(self, event):
  247. """Close dialog"""
  248. # TODO
  249. print "closed"
  250. self.scatt_mgr.CleanUp()
  251. self.Destroy()
  252. def OnSettings(self, event):
  253. pass
  254. def _newScatterPlotName(self, scatt_id):
  255. name = self._getScatterPlotName(self.scatt_i)
  256. self.scatt_id_scatt_i[scatt_id] = self.scatt_i
  257. self.scatt_i += 1
  258. return name
  259. def _getScatterPlotName(self, i):
  260. name = "scatter plot %d" % i
  261. return name
  262. def NewScatterPlot(self, scatt_id, transpose):
  263. # TODO needs to be resolved (should be in this class)
  264. scatt = ScatterPlotWidget(parent=self.mainPanel,
  265. scatt_mgr=self.scatt_mgr,
  266. scatt_id=scatt_id,
  267. transpose=transpose)
  268. scatt.plotClosed.connect(self.ScatterPlotClosed)
  269. self.transpose[scatt_id] = transpose
  270. caption = self._creteCaption(scatt_id)
  271. self._mgr.AddPane(scatt,
  272. aui.AuiPaneInfo().Dockable(True).Floatable(True).
  273. Name(self._newScatterPlotName(scatt_id)).MinSize((-1, 300)).
  274. Caption(caption).
  275. Center().Position(1).MaximizeButton(True).
  276. MinimizeButton(True).CaptionVisible(True).
  277. CloseButton(True).Layer(0))
  278. self._mgr.Update()
  279. self.SetVirtualSize(self.GetBestVirtualSize())
  280. self.Layout()
  281. self.scatts[scatt_id] = scatt
  282. return scatt
  283. def _creteCaption(self, scatt_id):
  284. transpose = self.transpose[scatt_id]
  285. bands = self.scatt_mgr.GetBands()
  286. # TODO too low level
  287. b1_id, b2_id = idScattToidBands(scatt_id, len(bands))
  288. x_b = bands[b1_id].split('@')[0]
  289. y_b = bands[b2_id].split('@')[0]
  290. if transpose:
  291. tmp = x_b
  292. x_b = y_b
  293. y_b = tmp
  294. return "%s x: %s y: %s" % (_("scatter plot"), x_b, y_b)
  295. def GetScattMgr(self):
  296. return self.scatt_mgr
  297. class CategoryListCtrl(wx.ListCtrl,
  298. listmix.ListCtrlAutoWidthMixin):
  299. # listmix.TextEditMixin):
  300. def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id=wx.ID_ANY):
  301. wx.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(colText=text_c, colBack=back_c)
  409. return self.l
  410. def OnCategoryRightUp(self, event):
  411. """Show context menu on right click"""
  412. item, flags = self.HitTest((event.GetX(), event.GetY()))
  413. if item != wx.NOT_FOUND and flags & wx.LIST_HITTEST_ONITEM:
  414. self.rightClickedItemIdx = item
  415. # generate popup-menu
  416. cat_idx = self.rightClickedItemIdx
  417. cats = self.cats_mgr.GetCategories()
  418. cat_id = cats[cat_idx]
  419. showed = self.cats_mgr.GetCategoryAttrs(cat_id)['show']
  420. menu = wx.Menu()
  421. item_id = wx.NewId()
  422. menu.Append(item_id, text=_("Rename class"))
  423. self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
  424. item_id = wx.NewId()
  425. menu.Append(item_id, text=_("Set color"))
  426. self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
  427. item_id = wx.NewId()
  428. menu.Append(item_id, text=_("Change opacity level"))
  429. self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
  430. if showed:
  431. text = _("Hide")
  432. else:
  433. text = _("Show")
  434. item_id = wx.NewId()
  435. menu.Append(item_id, text=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. id=item_id)
  443. menu.AppendSeparator()
  444. item_id = wx.NewId()
  445. menu.Append(item_id, text=_("Move to top"))
  446. self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
  447. if cat_idx == 0:
  448. menu.Enable(item_id, False)
  449. item_id = wx.NewId()
  450. menu.Append(item_id, text=_("Move to bottom"))
  451. self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
  452. if cat_idx == len(cats) - 1:
  453. menu.Enable(item_id, False)
  454. menu.AppendSeparator()
  455. item_id = wx.NewId()
  456. menu.Append(item_id, text=_("Move category up"))
  457. self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
  458. if cat_idx == 0:
  459. menu.Enable(item_id, False)
  460. item_id = wx.NewId()
  461. menu.Append(item_id, text=_("Move category down"))
  462. self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
  463. if cat_idx == len(cats) - 1:
  464. menu.Enable(item_id, False)
  465. menu.AppendSeparator()
  466. item_id = wx.NewId()
  467. menu.Append(item_id, text=_("Export class raster"))
  468. self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
  469. self.PopupMenu(menu)
  470. menu.Destroy()
  471. def OnExportCatRast(self, event):
  472. """Export training areas"""
  473. # TODO
  474. # GMessage(parent=self, message=_("No class raster to export."))
  475. # return
  476. cat_idx = self.rightClickedItemIdx
  477. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  478. self.cats_mgr.ExportCatRast(cat_id)
  479. def OnMoveUp(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 OnMoveDown(self, event):
  485. cat_idx = self.rightClickedItemIdx
  486. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  487. self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
  488. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  489. def OnMoveTop(self, event):
  490. cat_idx = self.rightClickedItemIdx
  491. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  492. self.cats_mgr.ChangePosition(cat_id, 0)
  493. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  494. def OnMoveBottom(self, event):
  495. cat_idx = self.rightClickedItemIdx
  496. cats = self.cats_mgr.GetCategories()
  497. cat_id = cats[cat_idx]
  498. self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
  499. self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
  500. def OnSetColor(self, event):
  501. """Popup opacity level indicator"""
  502. cat_idx = self.rightClickedItemIdx
  503. cat_id = self.cats_mgr.GetCategories()[cat_idx]
  504. col = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
  505. col = map(int, col.split(':'))
  506. col_data = wx.ColourData()
  507. col_data.SetColour(wx.Colour(*col))
  508. dlg = wx.ColourDialog(self, col_data)
  509. dlg.GetColourData().SetChooseFull(True)
  510. if dlg.ShowModal() == wx.ID_OK:
  511. color = dlg.GetColourData().GetColour().Get()
  512. color = ':'.join(map(str, color))
  513. self.cats_mgr.SetCategoryAttrs(cat_id, {"color": color})
  514. dlg.Destroy()
  515. def OnPopupOpacityLevel(self, event):
  516. """Popup opacity level indicator"""
  517. cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
  518. cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  519. value = cat_attrs['opacity'] * 100
  520. name = cat_attrs['name']
  521. dlg = SetOpacityDialog(self, opacity=value,
  522. title=_("Change opacity of class <%s>" % name))
  523. dlg.applyOpacity.connect(
  524. lambda value: self._setCatAttrs(
  525. cat_id=cat_id, attrs={
  526. 'opacity': value}))
  527. dlg.CentreOnParent()
  528. if dlg.ShowModal() == wx.ID_OK:
  529. self._setCatAttrs(cat_id=cat_id, attrs={'opacity': value})
  530. dlg.Destroy()
  531. def OnRename(self, event):
  532. cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
  533. cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
  534. dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
  535. dlg.CentreOnParent()
  536. while True:
  537. if dlg.ShowModal() == wx.ID_OK:
  538. name = dlg.GetNewName().strip()
  539. if not name:
  540. GMessage(parent=self, message=_(
  541. "Empty name was inserted."))
  542. else:
  543. self.cats_mgr.SetCategoryAttrs(cat_id, {"name": name})
  544. break
  545. else:
  546. break
  547. dlg.Destroy()
  548. def _setCatAttrs(self, cat_id, attrs):
  549. self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
  550. self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
  551. self.cats_mgr.setCategoryAttrs.connect(self.Update)