tree.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. """
  2. @package datacatalog::tree
  3. @brief Data catalog tree classes
  4. Classes:
  5. - datacatalog::LocationMapTree
  6. - datacatalog::DataCatalogTree
  7. (C) 2014-2015 by Tereza Fiedlerova, and the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. @author Tereza Fiedlerova
  12. @author Anna Petrasova (kratochanna gmail com)
  13. """
  14. import os
  15. from multiprocessing import Process, Queue, cpu_count
  16. import wx
  17. from core.gcmd import RunCommand, GError, GMessage, GWarning
  18. from core.utils import GetListOfLocations
  19. from core.debug import Debug
  20. from gui_core.dialogs import TextEntryDialog
  21. from core.giface import StandaloneGrassInterface
  22. from core.treemodel import TreeModel, DictNode
  23. from gui_core.treeview import TreeView
  24. from grass.pydispatch.signal import Signal
  25. import grass.script as gscript
  26. from grass.script import gisenv
  27. from grass.exceptions import CalledModuleError
  28. def getEnvironment(gisdbase, location, mapset):
  29. """Creates environment to be passed in run_command for example.
  30. Returns tuple with temporary file path and the environment. The user
  31. of this function is responsile for deleting the file."""
  32. tmp_gisrc_file = gscript.tempfile()
  33. with open(tmp_gisrc_file, 'w') as f:
  34. f.write('MAPSET: {mapset}\n'.format(mapset=mapset))
  35. f.write('GISDBASE: {g}\n'.format(g=gisdbase))
  36. f.write('LOCATION_NAME: {l}\n'.format(l=location))
  37. f.write('GUI: text\n')
  38. env = os.environ.copy()
  39. env['GISRC'] = tmp_gisrc_file
  40. return tmp_gisrc_file, env
  41. def getLocationTree(gisdbase, location, queue, mapsets=None):
  42. """Creates dictionary with mapsets, elements, layers for given location.
  43. Returns tuple with the dictionary and error (or None)"""
  44. tmp_gisrc_file, env = getEnvironment(gisdbase, location, 'PERMANENT')
  45. env['GRASS_SKIP_MAPSET_OWNER_CHECK'] = '1'
  46. maps_dict = {}
  47. elements = ['raster', 'raster_3d', 'vector']
  48. try:
  49. if not mapsets:
  50. mapsets = gscript.read_command('g.mapsets', flags='l', quiet=True, env=env).strip()
  51. except CalledModuleError:
  52. queue.put((maps_dict, _("Failed to read mapsets from location <{l}>.").format(l=location)))
  53. gscript.try_remove(tmp_gisrc_file)
  54. return
  55. else:
  56. listOfMapsets = mapsets.split()
  57. Debug.msg(4, "Location <{}>: {} mapsets found".format(location, len(listOfMapsets)))
  58. for each in listOfMapsets:
  59. maps_dict[each] = {}
  60. for elem in elements:
  61. maps_dict[each][elem] = []
  62. try:
  63. maplist = gscript.read_command('g.list', flags='mt', type=elements,
  64. mapset=','.join(listOfMapsets), quiet=True, env=env).strip()
  65. except CalledModuleError:
  66. queue.put((maps_dict, _("Failed to read maps from location <{l}>.").format(l=location)))
  67. gscript.try_remove(tmp_gisrc_file)
  68. return
  69. else:
  70. # fill dictionary
  71. listOfMaps = maplist.splitlines()
  72. Debug.msg(4, "Location <{}>: {} maps found".format(location, len(listOfMaps)))
  73. for each in listOfMaps:
  74. ltype, wholename = each.split('/')
  75. name, mapset = wholename.split('@')
  76. maps_dict[mapset][ltype].append(name)
  77. queue.put((maps_dict, None))
  78. gscript.try_remove(tmp_gisrc_file)
  79. def map_exists(name, element, env, mapset=None):
  80. """Check is map is present in the mapset given in the environment
  81. :param name: name of the map
  82. :param element: data type ('raster', 'raster_3d', and 'vector')
  83. :param env environment created by function getEnvironment
  84. """
  85. if not mapset:
  86. mapset = gscript.run_command('g.mapset', flags='p', env=env).strip()
  87. # change type to element used by find file
  88. if element == 'raster':
  89. element = 'cell'
  90. elif element == 'raster_3d':
  91. element = 'grid3'
  92. # g.findfile returns non-zero when file was not found
  93. # se we ignore return code and just focus on stdout
  94. process = gscript.start_command('g.findfile', flags='n',
  95. element=element, file=name, mapset=mapset,
  96. stdout=gscript.PIPE, stderr=gscript.PIPE, env=env)
  97. output, errors = process.communicate()
  98. info = gscript.parse_key_val(output, sep='=')
  99. # file is the key questioned in grass.script.core find_file()
  100. # return code should be equivalent to checking the output
  101. if info['file']:
  102. return True
  103. else:
  104. return False
  105. class NameEntryDialog(TextEntryDialog):
  106. def __init__(self, element, mapset, env, **kwargs):
  107. TextEntryDialog.__init__(self, **kwargs)
  108. self._element = element
  109. self._mapset = mapset
  110. self._env = env
  111. id_OK = self.GetAffirmativeId()
  112. self.Bind(wx.EVT_BUTTON, self.OnOK, self.FindWindowById(id_OK))
  113. def OnOK(self, event):
  114. new = self.GetValue()
  115. if not new:
  116. return
  117. if map_exists(new, self._element, self._env, self._mapset):
  118. dlg = wx.MessageDialog(self, message=_("Map of type {elem} <{name}> already exists in mapset <{mapset}>. "
  119. "Do you want to overwrite it?").format(elem=self._element,
  120. name=new, mapset=self._mapset),
  121. caption=_("Overwrite?"), style=wx.YES_NO)
  122. if dlg.ShowModal() == wx.ID_YES:
  123. dlg.Destroy()
  124. self._env['GRASS_OVERWRITE'] = '1'
  125. self.EndModal(wx.ID_OK)
  126. else:
  127. dlg.Destroy()
  128. return
  129. else:
  130. self.EndModal(wx.ID_OK)
  131. class DataCatalogNode(DictNode):
  132. """Node representing item in datacatalog."""
  133. def __init__(self, label, data=None):
  134. super(DataCatalogNode, self).__init__(label=label, data=data)
  135. def match(self, **kwargs):
  136. """Method used for searching according to given parameters.
  137. :param value: dictionary value to be matched
  138. :param key: data dictionary key
  139. """
  140. if not kwargs:
  141. return False
  142. for key in kwargs:
  143. if not (key in self.data and self.data[key] == kwargs[key]):
  144. return False
  145. return True
  146. class LocationMapTree(TreeView):
  147. def __init__(self, parent, model=None, style=wx.TR_HIDE_ROOT | wx.TR_EDIT_LABELS | wx.TR_LINES_AT_ROOT |
  148. wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_SINGLE):
  149. """Location Map Tree constructor."""
  150. self._model = TreeModel(DataCatalogNode)
  151. super(LocationMapTree, self).__init__(parent=parent, model=self._model, id=wx.ID_ANY, style=style)
  152. self.showNotification = Signal('Tree.showNotification')
  153. self.parent = parent
  154. self.contextMenu.connect(self.OnRightClick)
  155. self.itemActivated.connect(self.OnDoubleClick)
  156. self._initVariables()
  157. def _initTreeItems(self, locations=None, mapsets=None):
  158. """Add locations, mapsets and layers to the tree.
  159. Runs in multiple processes. Saves resulting data and error."""
  160. # mapsets param currently unused
  161. genv = gisenv()
  162. if not locations:
  163. locations = GetListOfLocations(genv['GISDBASE'])
  164. loc_count = proc_count = 0
  165. queue_list = []
  166. proc_list = []
  167. loc_list = []
  168. nprocs = 4
  169. try:
  170. nprocs = cpu_count()
  171. except NotImplementedError:
  172. nprocs = 4
  173. results = dict()
  174. errors = []
  175. location_nodes = []
  176. nlocations = len(locations)
  177. grassdata_node = self._model.AppendNode(parent=self._model.root,
  178. label=_('GRASS locations in {}').format(genv['GISDBASE']),
  179. data=dict(type='grassdata'))
  180. for location in locations:
  181. results[location] = dict()
  182. varloc = self._model.AppendNode(parent=grassdata_node, label=location,
  183. data=dict(type='location', name=location))
  184. location_nodes.append(varloc)
  185. loc_count += 1
  186. Debug.msg(3, "Scanning location <{}> ({}/{})".format(location, loc_count, nlocations))
  187. q = Queue()
  188. p = Process(target=getLocationTree,
  189. args=(genv['GISDBASE'], location, q))
  190. p.start()
  191. queue_list.append(q)
  192. proc_list.append(p)
  193. loc_list.append(location)
  194. proc_count += 1
  195. # Wait for all running processes
  196. if proc_count == nprocs or loc_count == nlocations:
  197. Debug.msg(4, "Process subresults")
  198. for i in range(len(loc_list)):
  199. maps, error = queue_list[i].get()
  200. proc_list[i].join()
  201. if error:
  202. errors.append(error)
  203. for key in sorted(maps.keys()):
  204. mapset_node = self._model.AppendNode(parent=location_nodes[i], label=key,
  205. data=dict(type='mapset', name=key))
  206. self._populateMapsetItem(mapset_node, maps[key])
  207. proc_count = 0
  208. proc_list = []
  209. queue_list = []
  210. loc_list = []
  211. location_nodes = []
  212. if errors:
  213. GWarning('\n'.join(errors))
  214. Debug.msg(1, "Tree filled")
  215. self.RefreshItems()
  216. def InitTreeItems(self):
  217. """Load locations, mapsets and layers in the tree."""
  218. raise NotImplementedError()
  219. def ReloadTreeItems(self):
  220. """Reload locations, mapsets and layers in the tree."""
  221. self._model.RemoveNode(self._model.root)
  222. self.RefreshNode(self._model.root)
  223. self.InitTreeItems()
  224. def ReloadCurrentMapset(self):
  225. """Reload current mapset tree only."""
  226. def get_first_child(node):
  227. try:
  228. child = mapsetItem.children[0]
  229. except IndexError:
  230. child = None
  231. return child
  232. genv = gisenv()
  233. locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
  234. if not locationItem or not mapsetItem:
  235. return
  236. if mapsetItem.children:
  237. node = get_first_child(mapsetItem)
  238. while node:
  239. self._model.RemoveNode(node)
  240. node = get_first_child(mapsetItem)
  241. q = Queue()
  242. p = Process(target=getLocationTree,
  243. args=(genv['GISDBASE'], locationItem.data['name'], q, mapsetItem.data['name']))
  244. p.start()
  245. maps, error = q.get()
  246. if error:
  247. raise CalledModuleError(error)
  248. self._populateMapsetItem(mapsetItem, maps[mapsetItem.data['name']])
  249. self.RefreshNode(mapsetItem)
  250. self.RefreshItems()
  251. def _populateMapsetItem(self, mapset_node, data):
  252. for elem in data:
  253. if data[elem]:
  254. element_node = self._model.AppendNode(parent=mapset_node, label=elem,
  255. data=dict(type='element', name=elem))
  256. for layer in data[elem]:
  257. self._model.AppendNode(parent=element_node, label=layer,
  258. data=dict(type=elem, name=layer))
  259. def _popupMenuLayer(self):
  260. """Create popup menu for layers"""
  261. raise NotImplementedError()
  262. def _popupMenuMapset(self):
  263. """Create popup menu for mapsets"""
  264. raise NotImplementedError()
  265. def _popupMenuElement(self):
  266. """Create popup menu for elements"""
  267. raise NotImplementedError()
  268. def _initVariables(self):
  269. """Init variables."""
  270. self.selected_layer = None
  271. self.selected_type = None
  272. self.selected_mapset = None
  273. self.selected_location = None
  274. def GetControl(self):
  275. """Returns control itself."""
  276. return self
  277. def DefineItems(self, item):
  278. """Set selected items."""
  279. self.selected_layer = None
  280. self.selected_type = None
  281. self.selected_mapset = None
  282. self.selected_location = None
  283. type = item.data['type']
  284. if type in ('raster', 'raster_3d', 'vector'):
  285. self.selected_layer = item
  286. type = 'element'
  287. item = item.parent
  288. if type == 'element':
  289. self.selected_type = item
  290. type = 'mapset'
  291. item = item.parent
  292. if type == 'mapset':
  293. self.selected_mapset = item
  294. type = 'location'
  295. item = item.parent
  296. if type == 'location':
  297. self.selected_location = item
  298. def OnSelChanged(self, event):
  299. self.selected_layer = None
  300. def OnRightClick(self, node):
  301. """Display popup menu."""
  302. self.DefineItems(node)
  303. if self.selected_layer:
  304. self._popupMenuLayer()
  305. elif self.selected_mapset and not self.selected_type:
  306. self._popupMenuMapset()
  307. elif self.selected_type:
  308. self._popupMenuElement()
  309. def OnDoubleClick(self, node):
  310. """Expand/Collapse node."""
  311. if self.IsNodeExpanded(node):
  312. self.CollapseNode(node, recursive=False)
  313. else:
  314. self.ExpandNode(node, recursive=False)
  315. def ExpandCurrentLocation(self):
  316. """Expand current location"""
  317. location = gscript.gisenv()['LOCATION_NAME']
  318. item = self._model.SearchNodes(name=location, type='location')
  319. if item:
  320. self.Select(item[0], select=True)
  321. self.ExpandNode(item[0], recursive=False)
  322. else:
  323. Debug.msg(1, "Location <%s> not found" % location)
  324. def GetCurrentLocationMapsetNode(self):
  325. """Get current mapset node"""
  326. genv = gisenv()
  327. location = genv['LOCATION_NAME']
  328. mapset = genv['MAPSET']
  329. locationItem = self._model.SearchNodes(name=location, type='location')
  330. if not locationItem:
  331. return None, None
  332. mapsetItem = self._model.SearchNodes(parent=locationItem[0], name=mapset, type='mapset')
  333. if not mapsetItem:
  334. return locationItem[0], None
  335. return locationItem[0], mapsetItem[0]
  336. def ExpandCurrentMapset(self):
  337. """Expand current mapset"""
  338. locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
  339. if mapsetItem:
  340. self.Select(mapsetItem, select=True)
  341. self.ExpandNode(mapsetItem, recursive=True)
  342. class DataCatalogTree(LocationMapTree):
  343. def __init__(self, parent, giface=None):
  344. """Data Catalog Tree constructor."""
  345. super(DataCatalogTree, self).__init__(parent)
  346. self._giface = giface
  347. self._restricted = True
  348. self._initVariablesCatalog()
  349. self.beginDrag = Signal('DataCatalogTree.beginDrag')
  350. self.endDrag = Signal('DataCatalogTree.endDrag')
  351. self.startEdit = Signal('DataCatalogTree.startEdit')
  352. self.endEdit = Signal('DataCatalogTree.endEdit')
  353. self.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda evt:
  354. self._emitSignal(evt.GetItem(), self.beginDrag, event=evt))
  355. self.Bind(wx.EVT_TREE_END_DRAG, lambda evt:
  356. self._emitSignal(evt.GetItem(), self.endDrag, event=evt))
  357. self.beginDrag.connect(self.OnBeginDrag)
  358. self.endDrag.connect(self.OnEndDrag)
  359. self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt:
  360. self._emitSignal(evt.GetItem(), self.startEdit, event=evt))
  361. self.Bind(wx.EVT_TREE_END_LABEL_EDIT, lambda evt:
  362. self._emitSignal(evt.GetItem(), self.endEdit, event=evt))
  363. self.startEdit.connect(self.OnStartEditLabel)
  364. self.endEdit.connect(self.OnEditLabel)
  365. def _initVariablesCatalog(self):
  366. """Init variables."""
  367. self.copy_layer = None
  368. self.copy_type = None
  369. self.copy_mapset = None
  370. self.copy_location = None
  371. def SetRestriction(self, restrict):
  372. self._restricted = restrict
  373. def _runCommand(self, prog, **kwargs):
  374. cmdString = ' '.join(gscript.make_command(prog, **kwargs))
  375. ret = RunCommand(prog, parent=self, **kwargs)
  376. return ret, cmdString
  377. def InitTreeItems(self):
  378. """Add locations, mapsets and layers to the tree."""
  379. self._initTreeItems()
  380. def OnCopyMap(self, event):
  381. """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
  382. self.copy_layer = self.selected_layer
  383. self.copy_type = self.selected_type
  384. self.copy_mapset = self.selected_mapset
  385. self.copy_location = self.selected_location
  386. label = _("Map <{layer}> marked for copying. "
  387. "You can paste it to the current mapset "
  388. "<{mapset}>.".format(layer=self.copy_layer.label, mapset=gisenv()['MAPSET']))
  389. self.showNotification.emit(message=label)
  390. def OnRenameMap(self, event):
  391. """Rename layer with dialog"""
  392. old_name = self.selected_layer.label
  393. gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label)
  394. new_name = self._getNewMapName(_('New name'), _('Rename map'),
  395. old_name, env=env, mapset=self.selected_mapset.label, element=self.selected_type.label)
  396. if new_name:
  397. self.Rename(old_name, new_name)
  398. def OnStartEditLabel(self, node, event):
  399. """Start label editing"""
  400. self.DefineItems(node)
  401. Debug.msg(1, "Start label edit {name}".format(name=node.label))
  402. label = _("Editing {name}").format(name=node.label)
  403. self.showNotification.emit(message=label)
  404. if not self.selected_layer:
  405. event.Veto()
  406. def OnEditLabel(self, node, event):
  407. """End label editing"""
  408. if self.selected_layer and not event.IsEditCancelled():
  409. old_name = node.label
  410. Debug.msg(1, "End label edit {name}".format(name=old_name))
  411. new_name = event.GetLabel()
  412. self.Rename(old_name, new_name)
  413. def Rename(self, old, new):
  414. """Rename layer"""
  415. string = old + ',' + new
  416. gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label)
  417. label = _("Renaming map <{name}>...").format(name=string)
  418. self.showNotification.emit(message=label)
  419. if self.selected_type.label == 'vector':
  420. renamed, cmd = self._runCommand('g.rename', vector=string, env=env)
  421. elif self.selected_type.label == 'raster':
  422. renamed, cmd = self._runCommand('g.rename', raster=string, env=env)
  423. else:
  424. renamed, cmd = self._runCommand('g.rename', raster3d=string, env=env)
  425. if renamed == 0:
  426. self.selected_layer.label = new
  427. self.selected_layer.data['name'] = new
  428. self.RefreshNode(self.selected_layer)
  429. self.showNotification.emit(message=_("{cmd} -- completed").format(cmd=cmd))
  430. Debug.msg(1, "LAYER RENAMED TO: " + new)
  431. gscript.try_remove(gisrc)
  432. def OnPasteMap(self, event):
  433. """Paste layer"""
  434. # copying between mapsets of one location
  435. if not self.copy_layer:
  436. GMessage(_("No map selected for copying."), parent=self)
  437. return
  438. if self.selected_location == self.copy_location:
  439. gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label)
  440. new_name = self._getNewMapName(_('New name'), _('Copy map'),
  441. self.copy_layer.label, env=env, mapset=self.selected_mapset.label, element=self.copy_type.label)
  442. if not new_name:
  443. return
  444. if map_exists(new_name, element=self.copy_type.label, env=env, mapset=self.selected_mapset.label):
  445. GMessage(_("Failed to copy map: new map has the same name"), parent=self)
  446. return
  447. if not self.selected_type:
  448. found = self._model.SearchNodes(parent=self.selected_mapset, type='element', name=self.copy_type.label)
  449. self.selected_type = found[0] if found else None
  450. overwrite = False
  451. if self.selected_type:
  452. found = self._model.SearchNodes(parent=self.selected_type, type=self.copy_type.label, name=new_name)
  453. if found and found[0]:
  454. dlg = wx.MessageDialog(parent=self,
  455. message=_("Map <{map}> already exists "
  456. "in the current mapset. "
  457. "Do you want to overwrite it?").format(map=new_name),
  458. caption=_("Overwrite?"),
  459. style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
  460. ret = dlg.ShowModal()
  461. dlg.Destroy()
  462. if ret == wx.ID_YES:
  463. overwrite = True
  464. string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + new_name
  465. pasted = 0
  466. label = _("Copying <{name}>...").format(name=string)
  467. self.showNotification.emit(message=label)
  468. if self.copy_type.label == 'vector':
  469. pasted, cmd = self._runCommand('g.copy', vector=string, overwrite=overwrite, env=env)
  470. node = 'vector'
  471. elif self.copy_type.label == 'raster':
  472. pasted, cmd = self._runCommand('g.copy', raster=string, overwrite=overwrite, env=env)
  473. node = 'raster'
  474. else:
  475. pasted, cmd = self._runCommand('g.copy', raster_3d=string, overwrite=overwrite, env=env)
  476. node = 'raster_3d'
  477. if pasted == 0:
  478. self.InsertLayer(name=new_name, mapset_node=self.selected_mapset, element_name=node)
  479. Debug.msg(1, "COPIED TO: " + new_name)
  480. self.showNotification.emit(message=_("g.copy completed").format(cmd=cmd))
  481. gscript.try_remove(gisrc)
  482. else:
  483. GError(_("Failed to copy map: action is allowed only within the same location."),
  484. parent=self)
  485. # expand selected mapset
  486. self.ExpandNode(self.selected_mapset, recursive=True)
  487. def InsertLayer(self, name, mapset_node, element_name):
  488. """Insert layer into model and refresh tree"""
  489. found_element = self._model.SearchNodes(parent=mapset_node, type='element', name=element_name)
  490. found_element = found_element[0] if found_element else None
  491. if not found_element:
  492. # add type node if not exists
  493. found_element = self._model.AppendNode(parent=mapset_node, label=element_name,
  494. data=dict(type='element', name=element_name))
  495. found = self._model.SearchNodes(parent=found_element, name=name)
  496. if len(found) == 0:
  497. self._model.AppendNode(parent=found_element, label=name,
  498. data=dict(type=element_name, name=name))
  499. self._model.SortChildren(found_element)
  500. self.RefreshNode(mapset_node, recursive=True)
  501. def OnDeleteMap(self, event):
  502. """Delete layer or mapset"""
  503. name = self.selected_layer.label
  504. gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label)
  505. if self._confirmDialog(question=_("Do you really want to delete map <{m}> of type <{etype}> from mapset "
  506. "<{mapset}> in location <{loc}>?").format(m=name, mapset=self.selected_mapset.label,
  507. etype=self.selected_type.label,
  508. loc=self.selected_location.label),
  509. title=_('Delete map')) == wx.ID_YES:
  510. label = _("Deleting {name}...").format(name=name)
  511. self.showNotification.emit(message=label)
  512. if self.selected_type.label == 'vector':
  513. removed, cmd = self._runCommand('g.remove', flags='f', type='vector',
  514. name=name, env=env)
  515. elif self.selected_type.label == 'raster':
  516. removed, cmd = self._runCommand('g.remove', flags='f', type='raster',
  517. name=name, env=env)
  518. else:
  519. removed, cmd = self._runCommand('g.remove', flags='f', type='raster_3d',
  520. name=name, env=env)
  521. if removed == 0:
  522. self._model.RemoveNode(self.selected_layer)
  523. self.RefreshNode(self.selected_type, recursive=True)
  524. Debug.msg(1, "LAYER " + name + " DELETED")
  525. self.showNotification.emit(message=_("g.remove completed").format(cmd=cmd))
  526. gscript.try_remove(gisrc)
  527. def OnDisplayLayer(self, event):
  528. """Display layer in current graphics view"""
  529. layerName = []
  530. if self.selected_location.label == gisenv()['LOCATION_NAME'] and self.selected_mapset:
  531. string = self.selected_layer.label + '@' + self.selected_mapset.label
  532. layerName.append(string)
  533. label = _("Displaying {name}...").format(name=string)
  534. self.showNotification.emit(message=label)
  535. label = "d." + self.selected_type.label[:4] + " --q map=" + string + \
  536. _(" -- completed. Go to Layers tab for further operations.")
  537. if self.selected_type.label == 'vector':
  538. self._giface.lmgr.AddMaps(layerName, 'vector', True)
  539. elif self.selected_type.label == 'raster':
  540. self._giface.lmgr.AddMaps(layerName, 'raster', True)
  541. else:
  542. self._giface.lmgr.AddMaps(layerName, 'raster_3d', True)
  543. label = "d.rast --q map=" + string + _(" -- completed. Go to Layers tab for further operations.") # generate this message (command) automatically?
  544. self.showNotification.emit(message=label)
  545. Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED")
  546. else:
  547. GError(_("Failed to display layer: not in current mapset or invalid layer"),
  548. parent=self)
  549. def OnBeginDrag(self, node, event):
  550. """Just copy necessary data"""
  551. self.DefineItems(node)
  552. if self.selected_layer and \
  553. not (self._restricted and gisenv()['LOCATION_NAME'] != self.selected_location.label):
  554. event.Allow()
  555. self.OnCopyMap(event)
  556. Debug.msg(1, "DRAG")
  557. else:
  558. event.Veto()
  559. def OnEndDrag(self, node, event):
  560. """Copy layer into target"""
  561. if not wx.GetMouseState().ControlDown():
  562. GMessage(_("Moving maps not implemented"), parent=self)
  563. event.Veto()
  564. return
  565. if node:
  566. self.DefineItems(node)
  567. if self._restricted and gisenv()['MAPSET'] != self.selected_mapset.label:
  568. GMessage(_("Maps can be copied only to current mapset"), parent=self)
  569. event.Veto()
  570. return
  571. if self.selected_location == self.copy_location and self.selected_mapset:
  572. event.Allow()
  573. self.OnPasteMap(event)
  574. Debug.msg(1, "DROP DONE")
  575. else:
  576. event.Veto()
  577. def _getNewMapName(self, message, title, value, element, mapset, env):
  578. """Dialog for simple text entry"""
  579. dlg = NameEntryDialog(parent=self, message=message, caption=title,
  580. element=element, env=env, mapset=mapset)
  581. dlg.SetValue(value)
  582. if dlg.ShowModal() == wx.ID_OK:
  583. name = dlg.GetValue()
  584. else:
  585. name = None
  586. dlg.Destroy()
  587. return name
  588. def _confirmDialog(self, question, title):
  589. """Confirm dialog"""
  590. dlg = wx.MessageDialog(self, question, title, wx.YES_NO)
  591. res = dlg.ShowModal()
  592. dlg.Destroy()
  593. return res
  594. def _popupMenuLayer(self):
  595. """Create popup menu for layers"""
  596. menu = wx.Menu()
  597. if self._restricted:
  598. genv = gisenv()
  599. currentMapset = currentLocation = False
  600. if self.selected_location.label == genv['LOCATION_NAME']:
  601. currentLocation = True
  602. if self.selected_mapset.label == genv['MAPSET']:
  603. currentMapset = True
  604. else:
  605. currentMapset = currentLocation = True
  606. item = wx.MenuItem(menu, wx.NewId(), _("&Copy"))
  607. menu.AppendItem(item)
  608. self.Bind(wx.EVT_MENU, self.OnCopyMap, item)
  609. item.Enable(currentLocation)
  610. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  611. menu.AppendItem(item)
  612. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  613. if not(currentLocation and self.copy_layer and self.selected_location == self.copy_location):
  614. item.Enable(False)
  615. item = wx.MenuItem(menu, wx.NewId(), _("&Delete"))
  616. menu.AppendItem(item)
  617. self.Bind(wx.EVT_MENU, self.OnDeleteMap, item)
  618. item.Enable(currentMapset)
  619. item = wx.MenuItem(menu, wx.NewId(), _("&Rename"))
  620. menu.AppendItem(item)
  621. self.Bind(wx.EVT_MENU, self.OnRenameMap, item)
  622. item.Enable(currentMapset)
  623. if not isinstance(self._giface, StandaloneGrassInterface) and \
  624. self.selected_location.label == genv['LOCATION_NAME']:
  625. item = wx.MenuItem(menu, wx.NewId(), _("&Display layer"))
  626. menu.AppendItem(item)
  627. self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item)
  628. self.PopupMenu(menu)
  629. menu.Destroy()
  630. def _popupMenuMapset(self):
  631. """Create popup menu for mapsets"""
  632. menu = wx.Menu()
  633. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  634. menu.AppendItem(item)
  635. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  636. if not (self.copy_layer and self.selected_location == self.copy_location):
  637. item.Enable(False)
  638. self.PopupMenu(menu)
  639. menu.Destroy()
  640. def _popupMenuElement(self):
  641. """Create popup menu for elements"""
  642. menu = wx.Menu()
  643. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  644. menu.AppendItem(item)
  645. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  646. if not (self.copy_layer and self.selected_location == self.copy_location):
  647. item.Enable(False)
  648. self.PopupMenu(menu)
  649. menu.Destroy()