simplelmgr.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. # -*- coding: utf-8 -*-
  2. """!
  3. @package gui_core.simplelmgr
  4. @brief GUI class for simple layer management.
  5. Classes:
  6. - simplelmgr::SimpleLayerManager
  7. - simplelmgr::SimpleLmgrToolbar
  8. (C) 2013 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Anna Petrasova (kratochanna gmail.com)
  12. """
  13. import os
  14. import sys
  15. # adding a path to wxGUI modules
  16. if __name__ == '__main__':
  17. WXGUIBASE = os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')
  18. if WXGUIBASE not in sys.path:
  19. sys.path.append(WXGUIBASE)
  20. import wx
  21. import wx.aui
  22. from grass.pydispatch.signal import Signal
  23. from gui_core.toolbars import BaseToolbar, BaseIcons
  24. from icons.icon import MetaIcon
  25. from gui_core.forms import GUI
  26. from gui_core.dialogs import SetOpacityDialog
  27. from core.utils import GetLayerNameFromCmd
  28. from core.gcmd import GError
  29. from core.layerlist import LayerList
  30. SIMPLE_LMGR_RASTER = 1
  31. SIMPLE_LMGR_VECTOR = 2
  32. SIMPLE_LMGR_RASTER3D = 4
  33. SIMPLE_LMGR_TB_TOP = 8
  34. SIMPLE_LMGR_TB_BOTTOM = 16
  35. SIMPLE_LMGR_TB_LEFT = 32
  36. SIMPLE_LMGR_TB_RIGHT = 64
  37. class SimpleLayerManager(wx.Panel):
  38. """!Simple layer manager class provides similar functionality to
  39. Layertree, but it's just list, not tree."""
  40. def __init__(self, parent, layerList,
  41. lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT):
  42. wx.Panel.__init__(self, parent=parent, name='SimpleLayerManager')
  43. self._style = lmgrStyle
  44. self._layerList = layerList
  45. self._checkList = wx.CheckListBox(self, style=wx.LB_EXTENDED)
  46. # dialog windows held separately
  47. self._dialogs = {}
  48. self._toolbar = SimpleLmgrToolbar(self, lmgrStyle=self._style)
  49. self._auimgr = wx.aui.AuiManager(self)
  50. # needed in order not to change selection when moving layers
  51. self._blockSelectionChanged = False
  52. self._checkList.Bind(wx.EVT_LISTBOX, lambda evt: self._selectionChanged())
  53. self._checkList.Bind(wx.EVT_LISTBOX_DCLICK, self.OnLayerChangeProperties)
  54. self._checkList.Bind(wx.EVT_CHECKLISTBOX, self.OnLayerChecked)
  55. # signal emitted when somethin in layer list changes
  56. self.opacityChanged = Signal('SimpleLayerManager.opacityChanged')
  57. self.cmdChanged = Signal('SimpleLayerManager.cmdChanged')
  58. self.layerAdded = Signal('SimpleLayerManager.layerAdded')
  59. self.layerRemoved = Signal('SimpleLayerManager.layerRemoved')
  60. self.layerActivated = Signal('SimpleLayerManager.layerActivated')
  61. self.layerMovedUp = Signal('SimpleLayerManager.layerMovedUp')
  62. self.layerMovedDown = Signal('SimpleLayerManager.layerMovedDown')
  63. # emitted by any change (e.g. for rerendering)
  64. self.anyChange = Signal('SimpleLayerManager.layerChange')
  65. self._layout()
  66. self.SetMinSize((200, -1))
  67. def _layout(self):
  68. self._auimgr.AddPane(self._checkList,
  69. wx.aui.AuiPaneInfo().
  70. Name("checklist").
  71. CenterPane().
  72. CloseButton(False).
  73. BestSize((self._checkList.GetBestSize())))
  74. paneInfo = wx.aui.AuiPaneInfo(). \
  75. Name("toolbar").Caption(_("Toolbar")).ToolbarPane(). \
  76. CloseButton(False).Layer(1).Gripper(False). \
  77. BestSize((self._toolbar.GetBestSize()))
  78. if self._style & SIMPLE_LMGR_TB_LEFT:
  79. paneInfo.Left()
  80. elif self._style & SIMPLE_LMGR_TB_RIGHT:
  81. paneInfo.Right()
  82. elif self._style & SIMPLE_LMGR_TB_TOP:
  83. paneInfo.Top()
  84. else:
  85. paneInfo.Bottom()
  86. self._auimgr.AddPane(self._toolbar, paneInfo)
  87. self._auimgr.Update()
  88. def _selectionChanged(self):
  89. """!Selection was changed externally,
  90. updates selection info in layers."""
  91. if self._blockSelectionChanged:
  92. return
  93. selected = self._checkList.GetSelections()
  94. for i, layer in enumerate(self._layerList):
  95. layer.Select(i in selected)
  96. def OnLayerChecked(self, event):
  97. """!Layer was (un)checked, update layer's info."""
  98. checkedIdxs = self._checkList.GetChecked()
  99. for i, layer in enumerate(self._layerList):
  100. if i in checkedIdxs and not layer.IsActive():
  101. layer.Activate()
  102. self.layerActivated.emit(index=i, layer=layer)
  103. elif i not in checkedIdxs and layer.IsActive():
  104. layer.Activate(False)
  105. self.layerActivated.emit(index=i, layer=layer)
  106. self.anyChange.emit()
  107. event.Skip()
  108. def OnAddRaster(self, event):
  109. """!Opens d.rast dialog and adds layer.
  110. Dummy layer is added first."""
  111. cmd = ['d.rast']
  112. layer = self.AddRaster(name='', cmd=cmd, hidden=True, dialog=None)
  113. GUI(parent=self, giface=None, modal=True).ParseCommand(cmd=cmd,
  114. completed=(self.GetOptData, layer, ''))
  115. event.Skip()
  116. def OnAddVector(self, event):
  117. """!Opens d.vect dialog and adds layer.
  118. Dummy layer is added first."""
  119. cmd = ['d.vect']
  120. layer = self.AddVector(name='', cmd=cmd, hidden=True, dialog=None)
  121. GUI(parent=self, giface=None, modal=True).ParseCommand(cmd=cmd,
  122. completed=(self.GetOptData, layer, ''))
  123. event.Skip()
  124. def OnAddRast3d(self, event):
  125. """!Opens d.rast3d dialog and adds layer.
  126. Dummy layer is added first."""
  127. cmd = ['d.rast3d']
  128. layer = self.AddRast3d(name='', cmd=cmd, hidden=True, dialog=None)
  129. GUI(parent=self, giface=None, modal=True).ParseCommand(cmd=cmd,
  130. completed=(self.GetOptData, layer, ''))
  131. event.Skip()
  132. def OnRemove(self, event):
  133. """!Removes selected layers from list."""
  134. layers = self._layerList.GetSelectedLayers(activeOnly=False)
  135. for layer in layers:
  136. self.layerRemoved.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
  137. self._layerList.RemoveLayer(layer)
  138. if self._dialogs[layer]:
  139. self._dialogs[layer].Destroy()
  140. self._update()
  141. self.anyChange.emit()
  142. event.Skip()
  143. def OnLayerUp(self, event):
  144. """!Moves selected layers one step up.
  145. Note: not completely correct for multiple layers."""
  146. layers = self._layerList.GetSelectedLayers()
  147. self._blockSelectionChanged = True
  148. for layer in layers:
  149. idx = self._layerList.GetLayerIndex(layer)
  150. if idx > 0:
  151. self.layerMovedUp.emit(index=idx, layer=layer)
  152. self._layerList.MoveLayerUp(layer)
  153. self._update()
  154. self._blockSelectionChanged = False
  155. self.anyChange.emit()
  156. event.Skip()
  157. def OnLayerDown(self, event):
  158. """!Moves selected layers one step down.
  159. Note: not completely correct for multiple layers."""
  160. layers = self._layerList.GetSelectedLayers()
  161. self._blockSelectionChanged = True
  162. for layer in layers:
  163. idx = self._layerList.GetLayerIndex(layer)
  164. if idx < len(self._layerList) - 1:
  165. self.layerMovedDown.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
  166. self._layerList.MoveLayerDown(layer)
  167. self._update()
  168. self._blockSelectionChanged = False
  169. self.anyChange.emit()
  170. event.Skip()
  171. def OnLayerChangeProperties(self, event):
  172. """Opens module dialog to edit layer command."""
  173. layers = self._layerList.GetSelectedLayers()
  174. if not layers or len(layers) > 1:
  175. return
  176. self._layerChangeProperties(layers[0])
  177. event.Skip()
  178. def _layerChangeProperties(self, layer):
  179. """!Opens new module dialog or recycles it."""
  180. dlg = self._dialogs[layer]
  181. if dlg is not None:
  182. if dlg.IsShown():
  183. dlg.Raise()
  184. dlg.SetFocus()
  185. else:
  186. dlg.Show()
  187. else:
  188. GUI(parent=self, giface=None,
  189. modal=False).ParseCommand(cmd=layer.cmd,
  190. completed=(self.GetOptData, layer, ''))
  191. def OnLayerChangeOpacity(self, event):
  192. """!Opacity of a layer is changing."""
  193. layers = self._layerList.GetSelectedLayers()
  194. if not layers or len(layers) > 1:
  195. return
  196. layer = layers[0]
  197. dlg = SetOpacityDialog(self, opacity=layer.opacity,
  198. title=_("Set opacity of <%s>") % layer.name)
  199. dlg.applyOpacity.connect(lambda value:
  200. self._setLayerOpacity(layer, value))
  201. dlg.CentreOnParent()
  202. if dlg.ShowModal() == wx.ID_OK:
  203. self._setLayerOpacity(layer, dlg.GetOpacity())
  204. dlg.Destroy()
  205. event.Skip()
  206. def _setLayerOpacity(self, layer, value):
  207. """!Sets layer's opacity.'"""
  208. # * 100 to be compatible, should be changes everywhere to 0-100 range
  209. layer.opacity = int(value * 100)
  210. self._update()
  211. self.opacityChanged.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
  212. self.anyChange.emit()
  213. def _update(self):
  214. """!Updates checklistbox according to layerList structure."""
  215. items = []
  216. active = []
  217. selected = []
  218. for layer in self._layerList:
  219. if layer.opacity < 100:
  220. items.append("{name} (opacity {opacity}%)".format(name=layer.name,
  221. opacity=layer.opacity))
  222. else:
  223. items.append(layer.name)
  224. active.append(layer.IsActive())
  225. selected.append(layer.IsSelected())
  226. self._checkList.SetItems(items)
  227. for i, check in enumerate(active):
  228. self._checkList.Check(i, check)
  229. for i, layer in enumerate(self._layerList):
  230. if selected[i]:
  231. self._checkList.Select(i)
  232. else:
  233. self._checkList.Deselect(i)
  234. def GetOptData(self, dcmd, layer, params, propwin):
  235. """!Handler for module dialogs."""
  236. if dcmd:
  237. layer.cmd = dcmd
  238. self._dialogs[layer] = propwin
  239. layer.selected = True
  240. mapName, found = GetLayerNameFromCmd(dcmd)
  241. if found:
  242. try:
  243. if layer.hidden:
  244. layer.hidden = False
  245. signal = self.layerAdded
  246. else:
  247. signal = self.cmdChanged
  248. layer.name = mapName
  249. signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
  250. except ValueError, e:
  251. self._layerList.RemoveLayer(layer)
  252. GError(parent=self,
  253. message=str(e),
  254. showTraceback=False)
  255. self._update()
  256. self.anyChange.emit()
  257. def AddRaster(self, name, cmd, hidden, dialog):
  258. """!Ads new raster layer."""
  259. layer = self._layerList.AddNewLayer(name=name, mapType='rast',
  260. active=True,
  261. cmd=cmd, hidden=hidden)
  262. self._dialogs[layer] = dialog
  263. return layer
  264. def AddRast3d(self, name, cmd, hidden, dialog):
  265. """!Ads new raster3d layer."""
  266. layer = self._layerList.AddNewLayer(name=name, mapType='rast3d',
  267. active=True,
  268. cmd=cmd, hidden=hidden)
  269. self._dialogs[layer] = dialog
  270. return layer
  271. def AddVector(self, name, cmd, hidden, dialog):
  272. """!Ads new vector layer."""
  273. layer = self._layerList.AddNewLayer(name=name, mapType='vect',
  274. active=True,
  275. cmd=cmd, hidden=hidden)
  276. self._dialogs[layer] = dialog
  277. return layer
  278. def GetLayerInfo(self, layer, key):
  279. """!Just for compatibility, should be removed in the future"""
  280. value = getattr(layer, key)
  281. # hack to return empty list, required in OnCancel in forms
  282. # not sure why it should be empty
  283. if key == 'cmd' and len(value) == 1:
  284. return []
  285. return value
  286. def Delete(self, layer):
  287. """!Just for compatibility, should be removed in the future"""
  288. self._layerList.RemoveLayer(layer)
  289. if self._dialogs[layer]:
  290. self._dialogs[layer].Destroy()
  291. class SimpleLmgrToolbar(BaseToolbar):
  292. """!Toolbar of simple layer manager.
  293. Style of the toolbar can be changed (horizontal,
  294. vertical, which map types to include).
  295. """
  296. def __init__(self, parent, lmgrStyle):
  297. """!Toolbar constructor
  298. """
  299. self._style = lmgrStyle
  300. if lmgrStyle & (SIMPLE_LMGR_TB_LEFT | SIMPLE_LMGR_TB_RIGHT):
  301. direction = wx.TB_VERTICAL
  302. else:
  303. direction = wx.TB_HORIZONTAL
  304. BaseToolbar.__init__(self, parent, style=wx.NO_BORDER | direction)
  305. self.InitToolbar(self._toolbarData())
  306. # realize the toolbar
  307. self.Realize()
  308. def _toolbarData(self):
  309. """!Toolbar data"""
  310. data = [('edit', icons['edit'],
  311. self.parent.OnLayerChangeProperties),
  312. ('remove', icons['remove'],
  313. self.parent.OnRemove),
  314. (None, ),
  315. ('up', icons['up'],
  316. self.parent.OnLayerUp),
  317. ('down', icons['down'],
  318. self.parent.OnLayerDown),
  319. (None, ),
  320. ('opacity', icons['opacity'],
  321. self.parent.OnLayerChangeOpacity),
  322. ]
  323. if self._style & SIMPLE_LMGR_RASTER3D:
  324. data.insert(0, ('addRaster3d', icons['addRast3d'],
  325. self.parent.OnAddRast3d))
  326. if self._style & SIMPLE_LMGR_VECTOR:
  327. data.insert(0, ('addVector', BaseIcons['addVect'],
  328. self.parent.OnAddVector))
  329. if self._style & SIMPLE_LMGR_RASTER:
  330. data.insert(0, ('addRaster', BaseIcons['addRast'],
  331. self.parent.OnAddRaster))
  332. return self._getToolbarData(data)
  333. icons = {
  334. 'remove': MetaIcon(img='layer-remove',
  335. label=_("Remove"),
  336. desc=_("Remove selected map(s) from list")),
  337. 'up': MetaIcon(img='layer-up',
  338. label=_("Layer up"),
  339. desc=_("Move selected layer(s) up")),
  340. 'down': MetaIcon(img='layer-down',
  341. label=_("Layer down"),
  342. desc=_("Move selected layer(s) down")),
  343. 'edit': MetaIcon(img='layer-edit',
  344. label=_("Edit layer properties"),
  345. desc=_("Edit layer properties")),
  346. 'opacity': MetaIcon(img='layer-opacity',
  347. label=_("Change opacity"),
  348. desc=_("Change layer opacity")),
  349. 'addRast3d': MetaIcon(img='layer-raster3d-add',
  350. label=_("Add 3D raster map layer"),
  351. desc=_("Add 3D raster map layer")),
  352. }
  353. class TestFrame(wx.Frame):
  354. def __init__(self, parent):
  355. wx.Frame.__init__(self, parent=parent)
  356. SimpleLayerManager(parent=self, layerList=LayerList())
  357. def test():
  358. app = wx.App()
  359. frame = TestFrame(None)
  360. frame.Show()
  361. app.MainLoop()
  362. if __name__ == '__main__':
  363. test()