12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414 |
- """
- @package datacatalog::tree
- @brief Data catalog tree classes
- Classes:
- - datacatalog::NameEntryDialog
- - datacatalog::DataCatalogNode
- - datacatalog::DataCatalogTree
- (C) 2014-2018 by Tereza Fiedlerova, and the GRASS Development Team
- This program is free software under the GNU General Public
- License (>=v2). Read the file COPYING that comes with GRASS
- for details.
- @author Tereza Fiedlerova
- @author Anna Petrasova (kratochanna gmail com)
- @author Linda Kladivova (l.kladivova@seznam.cz)
- """
- import os
- import re
- import copy
- from multiprocessing import Process, Queue, cpu_count
- watchdog_used = True
- try:
- from watchdog.observers import Observer
- from watchdog.events import PatternMatchingEventHandler, FileSystemEventHandler
- except ImportError:
- watchdog_used = False
- PatternMatchingEventHandler = object
- FileSystemEventHandler = object
- import wx
- from wx.lib.newevent import NewEvent
- from core.gcmd import RunCommand, GError, GMessage
- from core.utils import GetListOfLocations
- from core.debug import Debug
- from core.gthread import gThread
- from gui_core.dialogs import TextEntryDialog
- from core.giface import StandaloneGrassInterface
- from core.treemodel import TreeModel, DictNode
- from gui_core.treeview import TreeView
- from gui_core.wrap import Menu
- from datacatalog.dialogs import CatalogReprojectionDialog
- from icons.icon import MetaIcon
- from core.settings import UserSettings
- from startup.guiutils import (
- create_mapset_interactively,
- create_location_interactively,
- rename_mapset_interactively,
- rename_location_interactively,
- delete_mapsets_interactively,
- delete_locations_interactively,
- download_location_interactively,
- delete_grassdb_interactively,
- can_switch_mapset_interactive,
- switch_mapset_interactively,
- get_reason_mapset_not_removable,
- get_reasons_location_not_removable,
- get_mapset_name_invalid_reason,
- get_location_name_invalid_reason,
- )
- from grass.grassdb.manage import rename_mapset, rename_location
- from grass.pydispatch.signal import Signal
- import grass.script as gscript
- from grass.script import gisenv
- from grass.grassdb.data import map_exists
- from grass.grassdb.checks import (
- get_mapset_owner,
- is_mapset_locked,
- is_different_mapset_owner,
- is_first_time_user,
- )
- from grass.exceptions import CalledModuleError
- updateMapset, EVT_UPDATE_MAPSET = NewEvent()
- currentMapsetChanged, EVT_CURRENT_MAPSET_CHANGED = NewEvent()
- def getLocationTree(gisdbase, location, queue, mapsets=None, lazy=False):
- """Creates dictionary with mapsets, elements, layers for given location.
- Returns tuple with the dictionary and error (or None)"""
- tmp_gisrc_file, env = gscript.create_environment(gisdbase, location, "PERMANENT")
- env["GRASS_SKIP_MAPSET_OWNER_CHECK"] = "1"
- maps_dict = {}
- elements = ["raster", "raster_3d", "vector"]
- try:
- if not mapsets:
- mapsets = gscript.read_command(
- "g.mapsets", flags="l", separator="comma", quiet=True, env=env
- ).strip()
- except CalledModuleError:
- queue.put(
- (
- maps_dict,
- _("Failed to read mapsets from location <{l}>.").format(l=location),
- )
- )
- gscript.try_remove(tmp_gisrc_file)
- return
- else:
- mapsets = mapsets.split(",")
- Debug.msg(4, "Location <{0}>: {1} mapsets found".format(location, len(mapsets)))
- for each in mapsets:
- maps_dict[each] = []
- if lazy:
- queue.put((maps_dict, None))
- return
- try:
- maplist = gscript.read_command(
- "g.list",
- flags="mt",
- type=elements,
- mapset=",".join(mapsets),
- quiet=True,
- env=env,
- ).strip()
- except CalledModuleError:
- queue.put(
- (
- maps_dict,
- _("Failed to read maps from location <{l}>.").format(l=location),
- )
- )
- gscript.try_remove(tmp_gisrc_file)
- return
- else:
- # fill dictionary
- listOfMaps = maplist.splitlines()
- Debug.msg(4, "Location <{0}>: {1} maps found".format(location, len(listOfMaps)))
- for each in listOfMaps:
- ltype, wholename = each.split("/")
- name, mapset = wholename.split("@", maxsplit=1)
- maps_dict[mapset].append({"name": name, "type": ltype})
- queue.put((maps_dict, None))
- gscript.try_remove(tmp_gisrc_file)
- class CurrentMapsetWatch(FileSystemEventHandler):
- """Monitors rc file to check if mapset has been changed.
- In that case wx event is dispatched to event handler.
- Needs to check timestamp, because the modified event is sent twice.
- This assumes new instance of this class is started
- whenever mapset is changed."""
- def __init__(self, rcfile, mapset_path, event_handler):
- FileSystemEventHandler.__init__(self)
- self.event_handler = event_handler
- self.mapset_path = mapset_path
- self.rcfile_name = os.path.basename(rcfile)
- self.modified_time = 0
- def on_modified(self, event):
- if (
- not event.is_directory
- and os.path.basename(event.src_path) == self.rcfile_name
- ):
- timestamp = os.stat(event.src_path).st_mtime
- if timestamp - self.modified_time < 0.5:
- return
- self.modified_time = timestamp
- with open(event.src_path, "r") as f:
- gisrc = {}
- for line in f.readlines():
- key, val = line.split(":")
- gisrc[key.strip()] = val.strip()
- new = os.path.join(
- gisrc["GISDBASE"], gisrc["LOCATION_NAME"], gisrc["MAPSET"]
- )
- if new != self.mapset_path:
- evt = currentMapsetChanged()
- wx.PostEvent(self.event_handler, evt)
- class MapWatch(PatternMatchingEventHandler):
- """Monitors file events (create, delete, move files) using watchdog
- to inform about changes in current mapset. One instance monitors
- only one element (raster, vector, raster_3d).
- Patterns are not used/needed in this case, use just '*' for matching
- everything. When file/directory change is detected, wx event is dispatched
- to event handler (can't use Signals because this is different thread),
- containing info about the change."""
- def __init__(self, patterns, element, event_handler):
- PatternMatchingEventHandler.__init__(self, patterns=patterns)
- self.element = element
- self.event_handler = event_handler
- def on_created(self, event):
- if (
- self.element == "vector" or self.element == "raster_3d"
- ) and not event.is_directory:
- return
- evt = updateMapset(
- src_path=event.src_path,
- event_type=event.event_type,
- is_directory=event.is_directory,
- dest_path=None,
- )
- wx.PostEvent(self.event_handler, evt)
- def on_deleted(self, event):
- if (
- self.element == "vector" or self.element == "raster_3d"
- ) and not event.is_directory:
- return
- evt = updateMapset(
- src_path=event.src_path,
- event_type=event.event_type,
- is_directory=event.is_directory,
- dest_path=None,
- )
- wx.PostEvent(self.event_handler, evt)
- def on_moved(self, event):
- if (
- self.element == "vector" or self.element == "raster_3d"
- ) and not event.is_directory:
- return
- evt = updateMapset(
- src_path=event.src_path,
- event_type=event.event_type,
- is_directory=event.is_directory,
- dest_path=event.dest_path,
- )
- wx.PostEvent(self.event_handler, evt)
- class NameEntryDialog(TextEntryDialog):
- def __init__(self, element, mapset, env, **kwargs):
- TextEntryDialog.__init__(self, **kwargs)
- self._element = element
- self._mapset = mapset
- self._env = env
- id_OK = self.GetAffirmativeId()
- self.Bind(wx.EVT_BUTTON, self.OnOK, self.FindWindowById(id_OK))
- def OnOK(self, event):
- new = self.GetValue()
- if not new:
- return
- if map_exists(new, self._element, env=self._env, mapset=self._mapset):
- dlg = wx.MessageDialog(
- self,
- message=_(
- "Map of type {elem} <{name}> already exists in mapset <{mapset}>. "
- "Do you want to overwrite it?"
- ).format(elem=self._element, name=new, mapset=self._mapset),
- caption=_("Overwrite?"),
- style=wx.YES_NO,
- )
- if dlg.ShowModal() == wx.ID_YES:
- dlg.Destroy()
- self._env["GRASS_OVERWRITE"] = "1"
- self.EndModal(wx.ID_OK)
- else:
- dlg.Destroy()
- return
- else:
- self.EndModal(wx.ID_OK)
- class DataCatalogNode(DictNode):
- """Node representing item in datacatalog."""
- def __init__(self, data=None):
- super(DataCatalogNode, self).__init__(data=data)
- @property
- def label(self):
- data = self.data
- if data["type"] == "mapset":
- owner = data["owner"] if data["owner"] else _("name unknown")
- if data["current"]:
- return _("{name} (current)").format(**data)
- elif data["is_different_owner"] and data["lock"]:
- return _("{name} (in use, owner: {owner})").format(
- name=data["name"], owner=owner
- )
- elif data["lock"]:
- return _("{name} (in use)").format(**data)
- elif data["is_different_owner"]:
- return _("{name} (owner: {owner})").format(
- name=data["name"], owner=owner
- )
- return _("{name}").format(**data)
- def match(self, method="exact", **kwargs):
- """Method used for searching according to given parameters.
- :param method: 'exact' for exact match or 'filtering' for filtering by type/name
- :param kwargs key-value to be matched, filtering method uses 'type' and 'name'
- where 'name' is compiled regex
- """
- if not kwargs:
- return False
- if method == "exact":
- for key, value in kwargs.items():
- if not (key in self.data and self.data[key] == value):
- return False
- return True
- # for filtering
- if (
- "type" in kwargs
- and "type" in self.data
- and kwargs["type"] != self.data["type"]
- ):
- return False
- if (
- "name" in kwargs
- and "name" in self.data
- and not kwargs["name"].search(self.data["name"])
- ):
- return False
- return True
- class DataCatalogTree(TreeView):
- """Tree structure visualizing and managing grass database.
- Uses virtual tree and model defined in core/treemodel.py.
- When changes to data are initiated from inside, the model
- and the tree are not changed directly, rather a grassdbChanged
- signal needs to be emitted and the handler of the signal
- takes care of the refresh. At the same time, watchdog (if installed)
- monitors changes in current mapset and refreshes the tree.
- """
- def __init__(
- self,
- parent,
- model=None,
- giface=None,
- style=wx.TR_HIDE_ROOT
- | wx.TR_EDIT_LABELS
- | wx.TR_LINES_AT_ROOT
- | wx.TR_HAS_BUTTONS
- | wx.TR_FULL_ROW_HIGHLIGHT
- | wx.TR_MULTIPLE,
- ):
- """Location Map Tree constructor."""
- self._model = TreeModel(DataCatalogNode)
- self._orig_model = self._model
- super(DataCatalogTree, self).__init__(
- parent=parent, model=self._model, id=wx.ID_ANY, style=style
- )
- self._giface = giface
- self._restricted = True
- self.showNotification = Signal("Tree.showNotification")
- self.showImportDataInfo = Signal("Tree.showImportDataInfo")
- self.loadingDone = Signal("Tree.loadingDone")
- self.parent = parent
- self.contextMenu.connect(self.OnRightClick)
- self.itemActivated.connect(self.OnDoubleClick)
- self._giface.currentMapsetChanged.connect(self.UpdateAfterMapsetChanged)
- self._giface.grassdbChanged.connect(self._updateAfterGrassdbChanged)
- self._iconTypes = [
- "grassdb",
- "location",
- "mapset",
- "raster",
- "vector",
- "raster_3d",
- ]
- self._initImages()
- self.thread = gThread()
- self._resetSelectVariables()
- self._resetCopyVariables()
- self.current_grassdb_node = None
- self.current_location_node = None
- self.current_mapset_node = None
- self.UpdateCurrentDbLocationMapsetNode()
- self._lastWatchdogUpdate = gscript.clock()
- self._updateMapsetWhenIdle = None
- # Get databases from settings
- # add current to settings if it's not included
- self.grassdatabases = self._getValidSavedGrassDBs()
- currentDB = gisenv()["GISDBASE"]
- if currentDB not in self.grassdatabases:
- self.grassdatabases.append(currentDB)
- self._saveGrassDBs()
- self.beginDrag = Signal("DataCatalogTree.beginDrag")
- self.endDrag = Signal("DataCatalogTree.endDrag")
- self.startEdit = Signal("DataCatalogTree.startEdit")
- self.endEdit = Signal("DataCatalogTree.endEdit")
- self.Bind(
- wx.EVT_TREE_BEGIN_DRAG,
- lambda evt: self._emitSignal(evt.GetItem(), self.beginDrag, event=evt),
- )
- self.Bind(
- wx.EVT_TREE_END_DRAG,
- lambda evt: self._emitSignal(evt.GetItem(), self.endDrag, event=evt),
- )
- self.beginDrag.connect(self.OnBeginDrag)
- self.endDrag.connect(self.OnEndDrag)
- self.Bind(
- wx.EVT_TREE_BEGIN_LABEL_EDIT,
- lambda evt: self._emitSignal(evt.GetItem(), self.startEdit, event=evt),
- )
- self.Bind(
- wx.EVT_TREE_END_LABEL_EDIT,
- lambda evt: self._emitSignal(evt.GetItem(), self.endEdit, event=evt),
- )
- self.startEdit.connect(self.OnStartEditLabel)
- self.endEdit.connect(self.OnEditLabel)
- self.Bind(
- EVT_UPDATE_MAPSET, lambda evt: self._onWatchdogMapsetReload(evt.src_path)
- )
- self.Bind(wx.EVT_IDLE, self._onUpdateMapsetWhenIdle)
- self.Bind(
- EVT_CURRENT_MAPSET_CHANGED, lambda evt: self._updateAfterMapsetChanged()
- )
- self.observer = None
- def _resetSelectVariables(self):
- """Reset variables related to item selection."""
- self.selected_grassdb = []
- self.selected_layer = []
- self.selected_mapset = []
- self.selected_location = []
- self.mixed = False
- def _resetCopyVariables(self):
- """Reset copy related variables."""
- self.copy_mode = False
- self.copy_layer = None
- self.copy_mapset = None
- self.copy_location = None
- self.copy_grassdb = None
- def _useLazyLoading(self):
- settings = UserSettings.Get(group="datacatalog")
- # workaround defining new settings in datacatalog group
- # force writing new settings in the wx.json file during start
- # can be removed later on
- if "lazyLoading" not in settings:
- lazySettings = UserSettings.Get(
- group="datacatalog", key="lazyLoading", settings_type="default"
- )
- # update local settings
- for subkey, value in lazySettings.items():
- UserSettings.Append(
- UserSettings.userSettings,
- group="datacatalog",
- key="lazyLoading",
- subkey=subkey,
- value=value,
- overwrite=False,
- )
- # update settings file
- jsonSettings = {}
- UserSettings.ReadSettingsFile(settings=jsonSettings)
- jsonSettings["datacatalog"]["lazyLoading"] = lazySettings
- UserSettings.SaveToFile(jsonSettings)
- return UserSettings.Get(
- group="datacatalog", key="lazyLoading", subkey="enabled"
- )
- def _getValidSavedGrassDBs(self):
- """Returns list of GRASS databases from settings.
- Returns only existing directories."""
- dbs = UserSettings.Get(
- group="datacatalog", key="grassdbs", subkey="listAsString"
- )
- dbs = [db for db in dbs.split(",") if os.path.isdir(db)]
- return dbs
- def _saveGrassDBs(self):
- """Save current grass dbs in tree to settings"""
- UserSettings.Set(
- group="datacatalog",
- key="grassdbs",
- subkey="listAsString",
- value=",".join(self.grassdatabases),
- )
- grassdbSettings = {}
- UserSettings.ReadSettingsFile(settings=grassdbSettings)
- if "datacatalog" not in grassdbSettings:
- grassdbSettings["datacatalog"] = UserSettings.Get(group="datacatalog")
- # update only dbs
- grassdbSettings["datacatalog"]["grassdbs"] = UserSettings.Get(
- group="datacatalog", key="grassdbs"
- )
- UserSettings.SaveToFile(grassdbSettings)
- def _reloadMapsetNode(self, mapset_node):
- """Recursively reload the model of a specific mapset node"""
- if mapset_node.children:
- del mapset_node.children[:]
- q = Queue()
- p = Process(
- target=getLocationTree,
- args=(
- mapset_node.parent.parent.data["name"],
- mapset_node.parent.data["name"],
- q,
- mapset_node.data["name"],
- ),
- )
- p.start()
- maps, error = q.get()
- self._populateMapsetItem(mapset_node, maps[mapset_node.data["name"]])
- self._orig_model = copy.deepcopy(self._model)
- return error
- def _reloadLocationNode(self, location_node):
- """Recursively reload the model of a specific location node"""
- if location_node.children:
- del location_node.children[:]
- q = Queue()
- p = Process(
- target=getLocationTree,
- args=(
- location_node.parent.data["name"],
- location_node.data["name"],
- q,
- None,
- ),
- )
- p.start()
- maps, error = q.get()
- for mapset in maps:
- mapset_path = os.path.join(
- location_node.parent.data["name"], location_node.data["name"], mapset
- )
- mapset_node = self._model.AppendNode(
- parent=location_node,
- data=dict(
- type="mapset",
- name=mapset,
- current=False,
- lock=is_mapset_locked(mapset_path),
- is_different_owner=is_different_mapset_owner(mapset_path),
- owner=get_mapset_owner(mapset_path),
- ),
- )
- self._populateMapsetItem(mapset_node, maps[mapset])
- self._model.SortChildren(location_node)
- self._orig_model = copy.deepcopy(self._model)
- return error
- def _lazyReloadGrassDBNode(self, grassdb_node):
- genv = gisenv()
- if grassdb_node.children:
- del grassdb_node.children[:]
- all_location_nodes = []
- errors = []
- current_mapset_node = None
- locations = GetListOfLocations(grassdb_node.data["name"])
- for location in locations:
- loc_node = self._model.AppendNode(
- parent=grassdb_node, data=dict(type="location", name=location)
- )
- all_location_nodes.append(loc_node)
- q = Queue()
- getLocationTree(grassdb_node.data["name"], location, q, lazy=True)
- maps, error = q.get()
- if error:
- errors.append(error)
- for key in sorted(maps.keys()):
- mapset_path = os.path.join(
- loc_node.parent.data["name"], loc_node.data["name"], key
- )
- mapset_node = self._model.AppendNode(
- parent=loc_node,
- data=dict(
- type="mapset",
- name=key,
- lock=is_mapset_locked(mapset_path),
- current=False,
- is_different_owner=is_different_mapset_owner(mapset_path),
- owner=get_mapset_owner(mapset_path),
- ),
- )
- if (
- grassdb_node.data["name"] == genv["GISDBASE"]
- and location == genv["LOCATION_NAME"]
- and key == genv["MAPSET"]
- ):
- current_mapset_node = mapset_node
- if current_mapset_node:
- self._reloadMapsetNode(current_mapset_node)
- for node in all_location_nodes:
- self._model.SortChildren(node)
- self._model.SortChildren(grassdb_node)
- self._orig_model = copy.deepcopy(self._model)
- return errors
- def _reloadGrassDBNode(self, grassdb_node):
- """Recursively reload the model of a specific grassdb node.
- Runs reloading locations in parallel."""
- if grassdb_node.children:
- del grassdb_node.children[:]
- locations = GetListOfLocations(grassdb_node.data["name"])
- loc_count = proc_count = 0
- queue_list = []
- proc_list = []
- loc_list = []
- try:
- nprocs = max(1, cpu_count() - 1)
- except NotImplementedError:
- nprocs = 1
- results = dict()
- errors = []
- location_nodes = []
- all_location_nodes = []
- nlocations = len(locations)
- for location in locations:
- results[location] = dict()
- varloc = self._model.AppendNode(
- parent=grassdb_node, data=dict(type="location", name=location)
- )
- location_nodes.append(varloc)
- all_location_nodes.append(varloc)
- loc_count += 1
- Debug.msg(
- 3,
- "Scanning location <{0}> ({1}/{2})".format(
- location, loc_count, nlocations
- ),
- )
- q = Queue()
- p = Process(
- target=getLocationTree, args=(grassdb_node.data["name"], location, q)
- )
- p.start()
- queue_list.append(q)
- proc_list.append(p)
- loc_list.append(location)
- proc_count += 1
- # Wait for all running processes
- if proc_count == nprocs or loc_count == nlocations:
- Debug.msg(4, "Process subresults")
- for i in range(len(loc_list)):
- maps, error = queue_list[i].get()
- proc_list[i].join()
- if error:
- errors.append(error)
- for key in sorted(maps.keys()):
- mapset_path = os.path.join(
- location_nodes[i].parent.data["name"],
- location_nodes[i].data["name"],
- key,
- )
- mapset_node = self._model.AppendNode(
- parent=location_nodes[i],
- data=dict(
- type="mapset",
- name=key,
- lock=is_mapset_locked(mapset_path),
- current=False,
- is_different_owner=is_different_mapset_owner(
- mapset_path
- ),
- owner=get_mapset_owner(mapset_path),
- ),
- )
- self._populateMapsetItem(mapset_node, maps[key])
- proc_count = 0
- proc_list = []
- queue_list = []
- loc_list = []
- location_nodes = []
- for node in all_location_nodes:
- self._model.SortChildren(node)
- self._model.SortChildren(grassdb_node)
- self._orig_model = copy.deepcopy(self._model)
- return errors
- def _reloadTreeItems(self, full=False):
- """Updates grass databases, locations, mapsets and layers in the tree.
- It runs in thread, so it should not directly interact with GUI.
- In case of any errors it returns the errors as a list of strings, otherwise None.
- Option full=True forces full reload, full=False will behave based on user settings.
- """
- errors = []
- for grassdatabase in self.grassdatabases:
- grassdb_nodes = self._model.SearchNodes(name=grassdatabase, type="grassdb")
- if not grassdb_nodes:
- grassdb_node = self._model.AppendNode(
- parent=self._model.root,
- data=dict(type="grassdb", name=grassdatabase),
- )
- else:
- grassdb_node = grassdb_nodes[0]
- if full or not self._useLazyLoading():
- error = self._reloadGrassDBNode(grassdb_node)
- else:
- error = self._lazyReloadGrassDBNode(grassdb_node)
- if error:
- errors += error
- if errors:
- return errors
- return None
- def ScheduleWatchCurrentMapset(self):
- """Using watchdog library, sets up watching of current mapset folder
- to detect changes not captured by other means (e.g. from command line).
- Schedules 3 watches (raster, vector, 3D raster).
- If watchdog observers are active, it restarts the observers in current mapset.
- Also schedules monitoring of rc file to detect mapset change.
- """
- global watchdog_used
- if not watchdog_used:
- return
- if self.observer and self.observer.is_alive():
- self.observer.stop()
- self.observer.join()
- self.observer.unschedule_all()
- self.observer = Observer()
- gisenv = gscript.gisenv()
- mapset_path = os.path.join(
- gisenv["GISDBASE"], gisenv["LOCATION_NAME"], gisenv["MAPSET"]
- )
- rcfile = os.environ["GISRC"]
- self.observer.schedule(
- CurrentMapsetWatch(rcfile, mapset_path, self),
- os.path.dirname(rcfile),
- recursive=False,
- )
- for element, directory in (
- ("raster", "cell"),
- ("vector", "vector"),
- ("raster_3d", "grid3"),
- ):
- path = os.path.join(mapset_path, directory)
- if not os.path.exists(path):
- try:
- os.mkdir(path)
- except OSError:
- pass
- if os.path.exists(path):
- self.observer.schedule(
- MapWatch("*", element, self), path=path, recursive=False
- )
- try:
- self.observer.start()
- except OSError:
- # in case inotify on linux exceeds limits
- watchdog_used = False
- return
- def _onUpdateMapsetWhenIdle(self, event):
- """When idle, check if current mapset should be reloaded
- because there are skipped update events."""
- if self._updateMapsetWhenIdle:
- self._lastWatchdogUpdate = 0
- self._onWatchdogMapsetReload(self._updateMapsetWhenIdle)
- self._updateMapsetWhenIdle = None
- def _onWatchdogMapsetReload(self, event_path):
- """Reload mapset node associated with watchdog event.
- Check if events come to quickly and skip them."""
- time = gscript.clock()
- time_diff = time - self._lastWatchdogUpdate
- self._lastWatchdogUpdate = time
- if (time_diff) < 0.5:
- self._updateMapsetWhenIdle = event_path
- return
- mapset_path = os.path.dirname(os.path.dirname(os.path.abspath(event_path)))
- location_path = os.path.dirname(os.path.abspath(mapset_path))
- db = os.path.dirname(os.path.abspath(location_path))
- node = self.GetDbNode(
- grassdb=db,
- location=os.path.basename(location_path),
- mapset=os.path.basename(mapset_path),
- )
- if node:
- self._reloadMapsetNode(node)
- self.RefreshNode(node, recursive=True)
- def UpdateAfterMapsetChanged(self):
- """Wrapper around updating function called
- after mapset was changed, as a handler of signal.
- If watchdog is active, updating is skipped here
- to avoid double updating.
- """
- if not watchdog_used:
- self._updateAfterMapsetChanged()
- def GetDbNode(self, grassdb, location=None, mapset=None, map=None, map_type=None):
- """Returns node representing db/location/mapset/map or None if not found."""
- grassdb_nodes = self._model.SearchNodes(name=grassdb, type="grassdb")
- if grassdb_nodes:
- if not location:
- return grassdb_nodes[0]
- location_nodes = self._model.SearchNodes(
- parent=grassdb_nodes[0], name=location, type="location"
- )
- if location_nodes:
- if not mapset:
- return location_nodes[0]
- mapset_nodes = self._model.SearchNodes(
- parent=location_nodes[0], name=mapset, type="mapset"
- )
- if mapset_nodes:
- if not map:
- return mapset_nodes[0]
- map_nodes = self._model.SearchNodes(
- parent=mapset_nodes[0], name=map, type=map_type
- )
- if map_nodes:
- return map_nodes[0]
- return None
- def _renameNode(self, node, name):
- """Rename node (map, mapset, location), sort and refresh.
- Should be called after actual renaming of a map, mapset, location."""
- node.data["name"] = name
- self._model.SortChildren(node.parent)
- self.RefreshNode(node.parent, recursive=True)
- def UpdateCurrentDbLocationMapsetNode(self):
- """Update variables storing current mapset/location/grassdb node.
- Updates associated mapset node data ('lock' and 'current').
- """
- def is_current_mapset_node_locked():
- mapset_path = os.path.join(
- self.current_grassdb_node.data["name"],
- self.current_location_node.data["name"],
- self.current_mapset_node.data["name"],
- )
- return is_mapset_locked(mapset_path)
- if self.current_mapset_node:
- self.current_mapset_node.data["current"] = False
- self.current_mapset_node.data["lock"] = is_current_mapset_node_locked()
- (
- self.current_grassdb_node,
- self.current_location_node,
- self.current_mapset_node,
- ) = self.GetCurrentDbLocationMapsetNode()
- if self.current_mapset_node:
- self.current_mapset_node.data["current"] = True
- self.current_mapset_node.data["lock"] = is_current_mapset_node_locked()
- def ReloadTreeItems(self, full=False):
- """Reload dbs, locations, mapsets and layers in the tree."""
- self.busy = wx.BusyCursor()
- if full or not self._useLazyLoading():
- self._quickLoading()
- self.thread.Run(
- callable=self._reloadTreeItems, full=full, ondone=self._loadItemsDone
- )
- def _quickLoading(self):
- """Quick loading of locations to show
- something when loading for the first time"""
- if self._model.root.children:
- return
- gisenv = gscript.gisenv()
- for grassdatabase in self.grassdatabases:
- grassdb_node = self._model.AppendNode(
- parent=self._model.root, data=dict(type="grassdb", name=grassdatabase)
- )
- for location in GetListOfLocations(grassdatabase):
- self._model.AppendNode(
- parent=grassdb_node, data=dict(type="location", name=location)
- )
- self.RefreshItems()
- if grassdatabase == gisenv["GISDBASE"]:
- self.ExpandNode(grassdb_node, recursive=False)
- def _loadItemsDone(self, event):
- Debug.msg(1, "Tree filled")
- del self.busy
- if event.ret is not None:
- self._giface.WriteWarning("\n".join(event.ret))
- self.UpdateCurrentDbLocationMapsetNode()
- self.ScheduleWatchCurrentMapset()
- self.RefreshItems()
- self.ExpandCurrentMapset()
- self.loadingDone.emit()
- def ReloadCurrentMapset(self):
- """Reload current mapset tree only."""
- self.UpdateCurrentDbLocationMapsetNode()
- if (
- not self.current_grassdb_node
- or not self.current_location_node
- or not self.current_mapset_node
- ):
- return
- self._reloadMapsetNode(self.current_mapset_node)
- self.RefreshNode(self.current_mapset_node, recursive=True)
- def _populateMapsetItem(self, mapset_node, data):
- for item in data:
- self._model.AppendNode(parent=mapset_node, data=dict(**item))
- self._model.SortChildren(mapset_node)
- def _initImages(self):
- bmpsize = (16, 16)
- icons = {
- "grassdb": MetaIcon(img="grassdb").GetBitmap(bmpsize),
- "location": MetaIcon(img="location").GetBitmap(bmpsize),
- "mapset": MetaIcon(img="mapset").GetBitmap(bmpsize),
- "raster": MetaIcon(img="raster").GetBitmap(bmpsize),
- "vector": MetaIcon(img="vector").GetBitmap(bmpsize),
- "raster_3d": MetaIcon(img="raster3d").GetBitmap(bmpsize),
- }
- il = wx.ImageList(bmpsize[0], bmpsize[1], mask=False)
- for each in self._iconTypes:
- il.Add(icons[each])
- self.AssignImageList(il)
- def GetControl(self):
- """Returns control itself."""
- return self
- def DefineItems(self, selected):
- """Set selected items."""
- self._resetSelectVariables()
- mixed = []
- for item in selected:
- type = item.data["type"]
- if type in ("raster", "raster_3d", "vector"):
- self.selected_layer.append(item)
- self.selected_mapset.append(item.parent)
- self.selected_location.append(item.parent.parent)
- self.selected_grassdb.append(item.parent.parent.parent)
- mixed.append("layer")
- elif type == "mapset":
- self.selected_layer.append(None)
- self.selected_mapset.append(item)
- self.selected_location.append(item.parent)
- self.selected_grassdb.append(item.parent.parent)
- mixed.append("mapset")
- elif type == "location":
- self.selected_layer.append(None)
- self.selected_mapset.append(None)
- self.selected_location.append(item)
- self.selected_grassdb.append(item.parent)
- mixed.append("location")
- elif type == "grassdb":
- self.selected_layer.append(None)
- self.selected_mapset.append(None)
- self.selected_location.append(None)
- self.selected_grassdb.append(item)
- mixed.append("grassdb")
- self.mixed = False
- if len(set(mixed)) > 1:
- self.mixed = True
- def OnSelChanged(self, event):
- self.selected_layer = None
- def OnRightClick(self, node):
- """Display popup menu."""
- self.DefineItems(self.GetSelected())
- if self.mixed:
- self._popupMenuEmpty()
- return
- if not self.selected_layer:
- self._popupMenuEmpty()
- elif self.selected_layer[0]:
- self._popupMenuLayer()
- elif self.selected_mapset[0] and len(self.selected_mapset) == 1:
- self._popupMenuMapset()
- elif (
- self.selected_location[0]
- and not self.selected_mapset[0]
- and len(self.selected_location) == 1
- ):
- self._popupMenuLocation()
- elif (
- self.selected_grassdb[0]
- and not self.selected_location[0]
- and len(self.selected_grassdb) == 1
- ):
- self._popupMenuGrassDb()
- elif len(self.selected_grassdb) > 1 and not self.selected_location[0]:
- self._popupMenuEmpty()
- elif len(self.selected_location) > 1 and not self.selected_mapset[0]:
- self._popupMenuMultipleLocations()
- elif len(self.selected_mapset) > 1:
- self._popupMenuMultipleMapsets()
- else:
- self._popupMenuEmpty()
- def OnDoubleClick(self, node):
- """Double click on item/node.
- Display selected layer if node is a map layer otherwise
- expand/collapse node.
- """
- if not isinstance(self._giface, StandaloneGrassInterface):
- self.DefineItems([node])
- selected_layer = self.selected_layer[0]
- selected_mapset = self.selected_mapset[0]
- selected_loc = self.selected_location[0]
- selected_db = self.selected_grassdb[0]
- if selected_layer is not None:
- genv = gisenv()
- # Check if the layer is in different location
- if selected_loc.data["name"] != genv["LOCATION_NAME"]:
- dlg = wx.MessageDialog(
- parent=self,
- message=_(
- "Map <{map_name}@{map_mapset}> is not in the current location. "
- "To be able to display it you need to switch to <{map_location}> "
- "location. Note that if you switch there all current "
- "Map Displays will be closed.\n\n"
- "Do you want to switch anyway?"
- ).format(
- map_name=selected_layer.data["name"],
- map_mapset=selected_mapset.data["name"],
- map_location=selected_loc.data["name"],
- ),
- caption=_("Map in a different location"),
- style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION,
- )
- dlg.SetYesNoLabels("S&witch", "C&ancel")
- if dlg.ShowModal() == wx.ID_YES:
- if self.SwitchMapset(
- selected_db.data["name"],
- selected_loc.data["name"],
- selected_mapset.data["name"],
- ):
- self.DisplayLayer()
- dlg.Destroy()
- else:
- self.DisplayLayer()
- return
- if node.data["type"] == "mapset" and not node.children:
- self._reloadMapsetNode(node)
- self.RefreshNode(node, recursive=True)
- if node.data["type"] in ("mapset", "location", "grassdb"):
- # expand/collapse location/mapset...
- if self.IsNodeExpanded(node):
- self.CollapseNode(node, recursive=False)
- else:
- self.ExpandNode(node, recursive=False)
- def ExpandCurrentLocation(self):
- """Expand current location"""
- location = gscript.gisenv()["LOCATION_NAME"]
- item = self._model.SearchNodes(name=location, type="location")
- if item:
- self.Select(item[0], select=True)
- self.ExpandNode(item[0], recursive=False)
- else:
- Debug.msg(1, "Location <%s> not found" % location)
- def GetCurrentDbLocationMapsetNode(self):
- """Get current mapset node"""
- genv = gisenv()
- gisdbase = genv["GISDBASE"]
- location = genv["LOCATION_NAME"]
- mapset = genv["MAPSET"]
- grassdbItem = self._model.SearchNodes(name=gisdbase, type="grassdb")
- if not grassdbItem:
- return None, None, None
- locationItem = self._model.SearchNodes(
- parent=grassdbItem[0], name=location, type="location"
- )
- if not locationItem:
- return grassdbItem[0], None, None
- mapsetItem = self._model.SearchNodes(
- parent=locationItem[0], name=mapset, type="mapset"
- )
- if not mapsetItem:
- return grassdbItem[0], locationItem[0], None
- return grassdbItem[0], locationItem[0], mapsetItem[0]
- def OnGetItemImage(self, index, which=wx.TreeItemIcon_Normal, column=0):
- """Overridden method to return image for each item."""
- node = self._model.GetNodeByIndex(index)
- try:
- return self._iconTypes.index(node.data["type"])
- except ValueError:
- return 0
- def OnGetItemTextColour(self, index):
- """Overridden method to return colour for each item.
- Used to distinguish lock and ownership on mapsets."""
- node = self._model.GetNodeByIndex(index)
- if node.data["type"] == "mapset":
- if node.data["current"]:
- return wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
- elif node.data["lock"] or node.data["is_different_owner"]:
- return wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
- return wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
- def OnGetItemFont(self, index):
- """Overridden method to return font for each item.
- Used to highlight current db/loc/mapset."""
- node = self._model.GetNodeByIndex(index)
- font = self.GetFont()
- if node.data["type"] in ("grassdb", "location", "mapset"):
- if node in (
- self.current_grassdb_node,
- self.current_location_node,
- self.current_mapset_node,
- ):
- font.SetWeight(wx.FONTWEIGHT_BOLD)
- else:
- font.SetWeight(wx.FONTWEIGHT_NORMAL)
- return font
- def ExpandCurrentMapset(self):
- """Expand current mapset"""
- if self.current_mapset_node:
- self.Select(self.current_mapset_node, select=True)
- self.ExpandNode(self.current_mapset_node, recursive=True)
- def SetRestriction(self, restrict):
- self._restricted = restrict
- def _runCommand(self, prog, **kwargs):
- cmdString = " ".join(gscript.make_command(prog, **kwargs))
- ret = RunCommand(prog, parent=self, **kwargs)
- return ret, cmdString
- def OnMoveMap(self, event):
- """Move layer or mapset (just save it temporarily, copying is done by paste)"""
- self.copy_mode = False
- self.copy_layer = self.selected_layer[:]
- self.copy_mapset = self.selected_mapset[:]
- self.copy_location = self.selected_location[:]
- self.copy_grassdb = self.selected_grassdb[:]
- if len(self.copy_layer) > 1:
- label = _("{c} maps marked for moving.").format(c=len(self.selected_layer))
- else:
- label = _("Map <{layer}> marked for moving.").format(
- layer=self.copy_layer[0].data["name"]
- )
- self.showNotification.emit(message=label)
- def OnCopyMap(self, event):
- """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
- self.copy_mode = True
- self.copy_layer = self.selected_layer[:]
- self.copy_mapset = self.selected_mapset[:]
- self.copy_location = self.selected_location[:]
- self.copy_grassdb = self.selected_grassdb[:]
- if len(self.copy_layer) > 1:
- label = _("{c} maps marked for copying.").format(c=len(self.selected_layer))
- else:
- label = _("Map <{layer}> marked for copying.").format(
- layer=self.copy_layer[0].data["name"]
- )
- self.showNotification.emit(message=label)
- def OnRenameMap(self, event):
- """Rename layer with dialog"""
- old_name = self.selected_layer[0].data["name"]
- gisrc, env = gscript.create_environment(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- )
- new_name = self._getNewMapName(
- _("New name"),
- _("Rename map"),
- old_name,
- env=env,
- mapset=self.selected_mapset[0].data["name"],
- element=self.selected_layer[0].data["type"],
- )
- if new_name:
- self.Rename(old_name, new_name)
- def CreateMapset(self, grassdb_node, location_node):
- """Creates new mapset interactively and adds it to the tree."""
- mapset = create_mapset_interactively(
- self, grassdb_node.data["name"], location_node.data["name"]
- )
- if mapset:
- self._giface.grassdbChanged.emit(
- grassdb=grassdb_node.data["name"],
- location=location_node.data["name"],
- mapset=mapset,
- element="mapset",
- action="new",
- )
- self.SwitchMapset(
- grassdb_node.data["name"],
- location_node.data["name"],
- mapset,
- show_confirmation=True,
- )
- def OnCreateMapset(self, event):
- """Create new mapset"""
- self.CreateMapset(self.selected_grassdb[0], self.selected_location[0])
- def CreateLocation(self, grassdb_node):
- """
- Creates new location interactively and adds it to the tree and switch
- to its new PERMANENT mapset.
- If a user is a first-time user, it shows data import infobar.
- """
- grassdatabase, location, mapset = create_location_interactively(
- self, grassdb_node.data["name"]
- )
- if location:
- self._giface.grassdbChanged.emit(
- grassdb=grassdatabase,
- location=location,
- element="location",
- action="new",
- )
- # switch to PERMANENT mapset in newly created location
- self.SwitchMapset(grassdatabase, location, mapset, show_confirmation=True)
- # show data import infobar for first-time user with proper layout
- if is_first_time_user():
- self.showImportDataInfo.emit()
- def OnCreateLocation(self, event):
- """Create new location"""
- self.CreateLocation(self.selected_grassdb[0])
- def OnRenameMapset(self, event):
- """
- Rename selected mapset
- """
- newmapset = rename_mapset_interactively(
- self,
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- )
- if newmapset:
- self._giface.grassdbChanged.emit(
- grassdb=self.selected_grassdb[0].data["name"],
- location=self.selected_location[0].data["name"],
- mapset=self.selected_mapset[0].data["name"],
- element="mapset",
- action="rename",
- newname=newmapset,
- )
- def OnRenameLocation(self, event):
- """
- Rename selected location
- """
- newlocation = rename_location_interactively(
- self,
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- )
- if newlocation:
- self._giface.grassdbChanged.emit(
- grassdb=self.selected_grassdb[0].data["name"],
- location=self.selected_location[0].data["name"],
- element="location",
- action="rename",
- newname=newlocation,
- )
- def OnStartEditLabel(self, node, event):
- """Start label editing"""
- self.DefineItems([node])
- # Not allowed for grassdb node
- if node.data["type"] == "grassdb":
- event.Veto()
- # Check selected mapset
- elif node.data["type"] == "mapset":
- if self._restricted or get_reason_mapset_not_removable(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- check_permanent=True,
- ):
- event.Veto()
- # Check selected location
- elif node.data["type"] == "location":
- if self._restricted or get_reasons_location_not_removable(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- ):
- event.Veto()
- elif node.data["type"] in ("raster", "raster_3d", "vector"):
- currentGrassDb, currentLocation, currentMapset = self._isCurrent(gisenv())
- if not currentMapset:
- event.Veto()
- def OnEditLabel(self, node, event):
- """End label editing"""
- if event.IsEditCancelled():
- return
- old_name = node.data["name"]
- Debug.msg(1, "End label edit {name}".format(name=old_name))
- new_name = event.GetLabel()
- if node.data["type"] in ("raster", "raster_3d", "vector"):
- self.Rename(old_name, new_name)
- elif node.data["type"] == "mapset":
- message = get_mapset_name_invalid_reason(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- new_name,
- )
- if message:
- GError(
- parent=self,
- message=message,
- caption=_("Cannot rename mapset"),
- showTraceback=False,
- )
- event.Veto()
- return
- rename_mapset(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- new_name,
- )
- self._renameNode(self.selected_mapset[0], new_name)
- label = _(
- "Renaming mapset <{oldmapset}> to <{newmapset}> completed"
- ).format(oldmapset=old_name, newmapset=new_name)
- self.showNotification.emit(message=label)
- elif node.data["type"] == "location":
- message = get_location_name_invalid_reason(
- self.selected_grassdb[0].data["name"], new_name
- )
- if message:
- GError(
- parent=self,
- message=message,
- caption=_("Cannot rename location"),
- showTraceback=False,
- )
- event.Veto()
- return
- rename_location(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- new_name,
- )
- self._renameNode(self.selected_location[0], new_name)
- label = _(
- "Renaming location <{oldlocation}> to <{newlocation}> completed"
- ).format(oldlocation=old_name, newlocation=new_name)
- self.showNotification.emit(message=label)
- def Rename(self, old, new):
- """Rename layer"""
- string = old + "," + new
- gisrc, env = gscript.create_environment(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- )
- label = _("Renaming map <{name}>...").format(name=string)
- self.showNotification.emit(message=label)
- if self.selected_layer[0].data["type"] == "vector":
- renamed, cmd = self._runCommand("g.rename", vector=string, env=env)
- elif self.selected_layer[0].data["type"] == "raster":
- renamed, cmd = self._runCommand("g.rename", raster=string, env=env)
- else:
- renamed, cmd = self._runCommand("g.rename", raster3d=string, env=env)
- gscript.try_remove(gisrc)
- if renamed == 0:
- self.showNotification.emit(message=_("{cmd} -- completed").format(cmd=cmd))
- Debug.msg(1, "LAYER RENAMED TO: " + new)
- self._giface.grassdbChanged.emit(
- grassdb=self.selected_grassdb[0].data["name"],
- location=self.selected_location[0].data["name"],
- mapset=self.selected_mapset[0].data["name"],
- map=old,
- element=self.selected_layer[0].data["type"],
- newname=new,
- action="rename",
- )
- def OnPasteMap(self, event):
- # copying between mapsets of one location
- if not self.copy_layer:
- if self.copy_mode:
- GMessage(_("No map selected for copying."), parent=self)
- else:
- GMessage(_("No map selected for moving."), parent=self)
- return
- for i in range(len(self.copy_layer)):
- gisrc, env = gscript.create_environment(
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- )
- gisrc2, env2 = gscript.create_environment(
- self.copy_grassdb[i].data["name"],
- self.copy_location[i].data["name"],
- self.copy_mapset[i].data["name"],
- )
- new_name = self.copy_layer[i].data["name"]
- if self.selected_location[0] == self.copy_location[i]:
- # within one mapset
- if self.selected_mapset[0] == self.copy_mapset[i]:
- # ignore when just moves map
- if self.copy_mode is False:
- return
- new_name = self._getNewMapName(
- _("New name for <{n}>").format(
- n=self.copy_layer[i].data["name"]
- ),
- _("Select new name"),
- self.copy_layer[i].data["name"],
- env=env,
- mapset=self.selected_mapset[0].data["name"],
- element=self.copy_layer[i].data["type"],
- )
- if not new_name:
- return
- # within one location, different mapsets
- else:
- if map_exists(
- new_name,
- element=self.copy_layer[i].data["type"],
- env=env,
- mapset=self.selected_mapset[0].data["name"],
- ):
- new_name = self._getNewMapName(
- _("New name for <{n}>").format(
- n=self.copy_layer[i].data["name"]
- ),
- _("Select new name"),
- self.copy_layer[i].data["name"],
- env=env,
- mapset=self.selected_mapset[0].data["name"],
- element=self.copy_layer[i].data["type"],
- )
- if not new_name:
- return
- string = (
- self.copy_layer[i].data["name"]
- + "@"
- + self.copy_mapset[i].data["name"]
- + ","
- + new_name
- )
- pasted = 0
- if self.copy_mode:
- label = _("Copying <{name}>...").format(name=string)
- else:
- label = _("Moving <{name}>...").format(name=string)
- self.showNotification.emit(message=label)
- if self.copy_layer[i].data["type"] == "vector":
- pasted, cmd = self._runCommand("g.copy", vector=string, env=env)
- node = "vector"
- elif self.copy_layer[i].data["type"] == "raster":
- pasted, cmd = self._runCommand("g.copy", raster=string, env=env)
- node = "raster"
- else:
- pasted, cmd = self._runCommand("g.copy", raster_3d=string, env=env)
- node = "raster_3d"
- if pasted == 0:
- Debug.msg(1, "COPIED TO: " + new_name)
- if self.copy_mode:
- self.showNotification.emit(message=_("g.copy completed"))
- else:
- self.showNotification.emit(message=_("g.copy completed"))
- self._giface.grassdbChanged.emit(
- grassdb=self.selected_grassdb[0].data["name"],
- location=self.selected_location[0].data["name"],
- mapset=self.selected_mapset[0].data["name"],
- map=new_name,
- element=node,
- action="new",
- )
- # remove old
- if not self.copy_mode:
- self._removeMapAfterCopy(
- self.copy_layer[i], self.copy_mapset[i], env2
- )
- gscript.try_remove(gisrc)
- gscript.try_remove(gisrc2)
- # expand selected mapset
- else:
- if self.copy_layer[i].data["type"] == "raster_3d":
- GError(
- _("Reprojection is not implemented for 3D rasters"), parent=self
- )
- return
- if map_exists(
- new_name,
- element=self.copy_layer[i].data["type"],
- env=env,
- mapset=self.selected_mapset[0].data["name"],
- ):
- new_name = self._getNewMapName(
- _("New name"),
- _("Select new name"),
- self.copy_layer[i].data["name"],
- env=env,
- mapset=self.selected_mapset[0].data["name"],
- element=self.copy_layer[i].data["type"],
- )
- if not new_name:
- continue
- callback = lambda gisrc2=gisrc2, gisrc=gisrc, cLayer=self.copy_layer[
- i
- ], cMapset=self.copy_mapset[
- i
- ], cMode=self.copy_mode, sMapset=self.selected_mapset[
- 0
- ], name=new_name: self._onDoneReprojection(
- env2, gisrc2, gisrc, cLayer, cMapset, cMode, sMapset, name
- )
- dlg = CatalogReprojectionDialog(
- self,
- self._giface,
- self.copy_grassdb[i].data["name"],
- self.copy_location[i].data["name"],
- self.copy_mapset[i].data["name"],
- self.copy_layer[i].data["name"],
- env2,
- self.selected_grassdb[0].data["name"],
- self.selected_location[0].data["name"],
- self.selected_mapset[0].data["name"],
- new_name,
- self.copy_layer[i].data["type"],
- env,
- callback,
- )
- if dlg.ShowModal() == wx.ID_CANCEL:
- return
- self.ExpandNode(self.selected_mapset[0], recursive=True)
- self._resetCopyVariables()
- def _onDoneReprojection(
- self, iEnv, iGisrc, oGisrc, cLayer, cMapset, cMode, sMapset, name
- ):
- self._giface.grassdbChanged.emit(
- grassdb=sMapset.parent.parent.data["name"],
- location=sMapset.parent.data["name"],
- mapset=sMapset.data["name"],
- element=cLayer.data["type"],
- map=name,
- action="new",
- )
- if not cMode:
- self._removeMapAfterCopy(cLayer, cMapset, iEnv)
- gscript.try_remove(iGisrc)
- gscript.try_remove(oGisrc)
- self.ExpandNode(sMapset, recursive=True)
- def _removeMapAfterCopy(self, cLayer, cMapset, env):
- removed, cmd = self._runCommand(
- "g.remove",
- type=cLayer.data["type"],
- name=cLayer.data["name"],
- flags="f",
- env=env,
- )
- if removed == 0:
- Debug.msg(1, "LAYER " + cLayer.data["name"] + " DELETED")
- self.showNotification.emit(message=_("g.remove completed"))
- self._giface.grassdbChanged.emit(
- grassdb=cMapset.parent.parent.data["name"],
- location=cMapset.parent.data["name"],
- mapset=cMapset.data["name"],
- map=cLayer.data["name"],
- element=cLayer.data["type"],
- action="delete",
- )
- def InsertLayer(self, name, mapset_node, element_name):
- """Insert layer into model and refresh tree"""
- self._model.AppendNode(
- parent=mapset_node, data=dict(type=element_name, name=name)
- )
- self._model.SortChildren(mapset_node)
- self.RefreshNode(mapset_node, recursive=True)
- def InsertMapset(self, name, location_node):
- """Insert new mapset into model and refresh tree.
- Assumes mapset is empty."""
- mapset_path = os.path.join(
- location_node.parent.data["name"], location_node.data["name"], name
- )
- mapset_node = self._model.AppendNode(
- parent=location_node,
- data=dict(
- type="mapset",
- name=name,
- current=False,
- lock=is_mapset_locked(mapset_path),
- is_different_owner=is_different_mapset_owner(mapset_path),
- owner=get_mapset_owner(mapset_path),
- ),
- )
- self._model.SortChildren(location_node)
- self.RefreshNode(location_node, recursive=True)
- return mapset_node
- def InsertLocation(self, name, grassdb_node):
- """Insert new location into model and refresh tree"""
- location_node = self._model.AppendNode(
- parent=grassdb_node, data=dict(type="location", name=name)
- )
- # reload new location since it has a mapset
- self._reloadLocationNode(location_node)
- self._model.SortChildren(grassdb_node)
- self.RefreshNode(grassdb_node, recursive=True)
- return location_node
- def InsertGrassDb(self, name):
- """
- Insert new grass db into model, update user setting and refresh tree.
- Check if not already added.
- """
- grassdb_node = self._model.SearchNodes(name=name, type="grassdb")
- if not grassdb_node:
- grassdb_node = self._model.AppendNode(
- parent=self._model.root, data=dict(type="grassdb", name=name)
- )
- if self._useLazyLoading():
- self._lazyReloadGrassDBNode(grassdb_node)
- else:
- self._reloadGrassDBNode(grassdb_node)
- self.RefreshItems()
- # Update user's settings
- self.grassdatabases.append(name)
- self._saveGrassDBs()
- return grassdb_node
- def OnDeleteMap(self, event):
- """Delete layer or mapset"""
- names = [
- self.selected_layer[i].data["name"]
- + "@"
- + self.selected_mapset[i].data["name"]
- for i in range(len(self.selected_layer))
- ]
- if len(names) < 10:
- question = _("Do you really want to delete map(s) <{m}>?").format(
- m=", ".join(names)
- )
- else:
- question = _("Do you really want to delete {n} maps?").format(n=len(names))
- if self._confirmDialog(question, title=_("Delete map")) == wx.ID_YES:
- label = _("Deleting {name}...").format(name=names)
- self.showNotification.emit(message=label)
- for i in range(len(self.selected_layer)):
- gisrc, env = gscript.create_environment(
- self.selected_grassdb[i].data["name"],
- self.selected_location[i].data["name"],
- self.selected_mapset[i].data["name"],
- )
- removed, cmd = self._runCommand(
- "g.remove",
- flags="f",
- type=self.selected_layer[i].data["type"],
- name=self.selected_layer[i].data["name"],
- env=env,
- )
- gscript.try_remove(gisrc)
- if removed == 0:
- self._giface.grassdbChanged.emit(
- grassdb=self.selected_grassdb[i].data["name"],
- location=self.selected_location[i].data["name"],
- mapset=self.selected_mapset[i].data["name"],
- element=self.selected_layer[i].data["type"],
- map=self.selected_layer[i].data["name"],
- action="delete",
- )
- Debug.msg(
- 1, "LAYER " + self.selected_layer[i].data["name"] + " DELETED"
- )
- self.UnselectAll()
- self.showNotification.emit(message=_("g.remove completed"))
- def OnDeleteMapset(self, event):
- """
- Delete selected mapset or mapsets
- """
- mapsets = []
- changes = []
- for i in range(len(self.selected_mapset)):
- # Append to the list of tuples
- mapsets.append(
- (
- self.selected_grassdb[i].data["name"],
- self.selected_location[i].data["name"],
- self.selected_mapset[i].data["name"],
- )
- )
- changes.append(
- dict(
- grassdb=self.selected_grassdb[i].data["name"],
- location=self.selected_location[i].data["name"],
- mapset=self.selected_mapset[i].data["name"],
- action="delete",
- element="mapset",
- )
- )
- if delete_mapsets_interactively(self, mapsets):
- for change in changes:
- self._giface.grassdbChanged.emit(**change)
- def OnDeleteLocation(self, event):
- """
- Delete selected location or locations
- """
- locations = []
- changes = []
- for i in range(len(self.selected_location)):
- # Append to the list of tuples
- locations.append(
- (
- self.selected_grassdb[i].data["name"],
- self.selected_location[i].data["name"],
- )
- )
- changes.append(
- dict(
- grassdb=self.selected_grassdb[i].data["name"],
- location=self.selected_location[i].data["name"],
- action="delete",
- element="location",
- )
- )
- if delete_locations_interactively(self, locations):
- for change in changes:
- self._giface.grassdbChanged.emit(**change)
- def DownloadLocation(self, grassdb_node):
- """
- Download new location interactively.
- """
- grassdatabase, location, mapset = download_location_interactively(
- self, grassdb_node.data["name"]
- )
- if location:
- self._reloadGrassDBNode(grassdb_node)
- self.UpdateCurrentDbLocationMapsetNode()
- self.RefreshItems()
- def OnDownloadLocation(self, event):
- """
- Download location online
- """
- self.DownloadLocation(self.selected_grassdb[0])
- def DeleteGrassDb(self, grassdb_node):
- """
- Delete grassdb from disk.
- """
- grassdb = grassdb_node.data["name"]
- if delete_grassdb_interactively(self, grassdb):
- self.RemoveGrassDB(grassdb_node)
- def OnDeleteGrassDb(self, event):
- """
- Delete grassdb from disk.
- """
- self.DeleteGrassDb(self.selected_grassdb[0])
- def OnRemoveGrassDb(self, event):
- """
- Remove grassdb node from data catalogue.
- """
- self.RemoveGrassDB(self.selected_grassdb[0])
- def RemoveGrassDB(self, grassdb_node):
- """
- Remove grassdb node from tree
- and updates settings. Doesn't check if it's current db.
- """
- self.grassdatabases.remove(grassdb_node.data["name"])
- self._model.RemoveNode(grassdb_node)
- self.RefreshItems()
- # Update user's settings
- self._saveGrassDBs()
- def OnDisplayLayer(self, event):
- """
- Display layer in current graphics view
- """
- self.DisplayLayer()
- def DisplayLayer(self):
- """Display selected layer in current graphics view"""
- all_names = []
- names = {"raster": [], "vector": [], "raster_3d": []}
- for i in range(len(self.selected_layer)):
- name = (
- self.selected_layer[i].data["name"]
- + "@"
- + self.selected_mapset[i].data["name"]
- )
- names[self.selected_layer[i].data["type"]].append(name)
- all_names.append(name)
- # if self.selected_location[0].data['name'] == gisenv()['LOCATION_NAME'] and self.selected_mapset[0]:
- for ltype in names:
- if names[ltype]:
- self._giface.lmgr.AddMaps(list(reversed(names[ltype])), ltype, True)
- if len(self._giface.GetLayerList()) == 1:
- # zoom to map if there is only one map layer
- self._giface.GetMapWindow().ZoomToMap()
- Debug.msg(1, "Displayed layer(s): " + str(all_names))
- def OnBeginDrag(self, node, event):
- """Just copy necessary data"""
- self.DefineItems(self.GetSelected())
- if None not in self.selected_layer and not (
- self._restricted
- and gisenv()["LOCATION_NAME"] != self.selected_location[0].data["name"]
- ):
- event.Allow()
- self.OnCopyMap(event)
- Debug.msg(1, "DRAG")
- else:
- event.Veto()
- def OnEndDrag(self, node, event):
- """Copy layer into target"""
- self.copy_mode = wx.GetMouseState().ControlDown()
- if node:
- self.DefineItems([node])
- if None not in self.selected_mapset:
- if (
- self._restricted
- and gisenv()["MAPSET"] != self.selected_mapset[0].data["name"]
- ):
- GMessage(
- _(
- "To move or copy maps to other mapsets, unlock editing of other mapsets"
- ),
- parent=self,
- )
- event.Veto()
- return
- event.Allow()
- Debug.msg(1, "DROP DONE")
- self.OnPasteMap(event)
- else:
- GMessage(
- _(
- "To move or copy maps to other location, "
- "please drag them to a mapset in the "
- "destination location"
- ),
- parent=self,
- )
- event.Veto()
- return
- def SwitchMapset(self, grassdb, location, mapset, show_confirmation=False):
- """
- Switch to location and mapset interactively.
- Returns True if switching is successful.
- """
- if can_switch_mapset_interactive(self, grassdb, location, mapset):
- genv = gisenv()
- # Switch to mapset in the same location
- if grassdb == genv["GISDBASE"] and location == genv["LOCATION_NAME"]:
- switch_mapset_interactively(
- self, self._giface, None, None, mapset, show_confirmation
- )
- # Switch to mapset in the same grassdb
- elif grassdb == genv["GISDBASE"]:
- switch_mapset_interactively(
- self, self._giface, None, location, mapset, show_confirmation
- )
- # Switch to mapset in a different grassdb
- else:
- switch_mapset_interactively(
- self, self._giface, grassdb, location, mapset, show_confirmation
- )
- return True
- return False
- def OnSwitchMapset(self, event):
- """Switch to location and mapset"""
- grassdb = self.selected_grassdb[0].data["name"]
- location = self.selected_location[0].data["name"]
- mapset = self.selected_mapset[0].data["name"]
- self.SwitchMapset(grassdb, location, mapset)
- def _updateAfterGrassdbChanged(
- self, action, element, grassdb, location, mapset=None, map=None, newname=None
- ):
- """Update tree after grassdata changed"""
- if element == "mapset":
- if action == "new":
- node = self.GetDbNode(grassdb=grassdb, location=location)
- if node:
- self.InsertMapset(name=mapset, location_node=node)
- elif action == "delete":
- node = self.GetDbNode(grassdb=grassdb, location=location)
- if node:
- self._reloadLocationNode(node)
- self.UpdateCurrentDbLocationMapsetNode()
- self.RefreshNode(node, recursive=True)
- elif action == "rename":
- node = self.GetDbNode(grassdb=grassdb, location=location, mapset=mapset)
- if node:
- self._renameNode(node, newname)
- elif element == "location":
- if action == "new":
- node = self.GetDbNode(grassdb=grassdb)
- if not node:
- node = self.InsertGrassDb(name=grassdb)
- if node:
- self.InsertLocation(location, node)
- elif action == "delete":
- node = self.GetDbNode(grassdb=grassdb)
- if node:
- self._reloadGrassDBNode(node)
- self.UpdateCurrentDbLocationMapsetNode()
- self.RefreshNode(node, recursive=True)
- elif action == "rename":
- node = self.GetDbNode(grassdb=grassdb, location=location)
- if node:
- self._renameNode(node, newname)
- elif element == "grassdb":
- if action == "delete":
- node = self.GetDbNode(grassdb=grassdb)
- if node:
- self.RemoveGrassDB(node)
- elif element in ("raster", "vector", "raster_3d"):
- # when watchdog is used, it watches current mapset,
- # so we don't process any signals here,
- # instead the watchdog handler takes care of refreshing tree
- if (
- watchdog_used
- and grassdb == self.current_grassdb_node.data["name"]
- and location == self.current_location_node.data["name"]
- and mapset == self.current_mapset_node.data["name"]
- ):
- return
- if action == "new":
- node = self.GetDbNode(grassdb=grassdb, location=location, mapset=mapset)
- if node:
- if map:
- # reload entire mapset when using lazy loading
- if not node.children and self._useLazyLoading():
- self._reloadMapsetNode(node)
- self.RefreshNode(node.parent, recursive=True)
- # check if map already exists
- elif not self._model.SearchNodes(
- parent=node, name=map, type=element
- ):
- self.InsertLayer(
- name=map, mapset_node=node, element_name=element
- )
- else:
- # we know some maps created
- self._reloadMapsetNode(node)
- self.RefreshNode(node)
- elif action == "delete":
- node = self.GetDbNode(
- grassdb=grassdb,
- location=location,
- mapset=mapset,
- map=map,
- map_type=element,
- )
- if node:
- self._model.RemoveNode(node)
- self.RefreshNode(node.parent, recursive=True)
- elif action == "rename":
- node = self.GetDbNode(
- grassdb=grassdb,
- location=location,
- mapset=mapset,
- map=map,
- map_type=element,
- )
- if node:
- self._renameNode(node, newname)
- def _updateAfterMapsetChanged(self):
- """Update tree after current mapset has changed"""
- db_node, location_node, mapset_node = self.GetCurrentDbLocationMapsetNode()
- # mapset is not in tree, create its node
- if not mapset_node:
- genv = gisenv()
- if not location_node:
- if not db_node:
- self.InsertGrassDb(genv["GISDBASE"])
- else:
- self.InsertLocation(genv["LOCATION_NAME"], db_node)
- else:
- self.InsertMapset(genv["MAPSET"], location_node)
- self.UpdateCurrentDbLocationMapsetNode()
- self._reloadMapsetNode(self.current_mapset_node)
- self.RefreshNode(self.current_mapset_node, recursive=True)
- self.ExpandCurrentMapset()
- self.RefreshItems()
- self.ScheduleWatchCurrentMapset()
- def OnMetadata(self, event):
- """Show metadata of any raster/vector/3draster"""
- def done(event):
- gscript.try_remove(event.userData)
- for i in range(len(self.selected_layer)):
- if self.selected_layer[i].data["type"] == "raster":
- cmd = ["r.info"]
- elif self.selected_layer[i].data["type"] == "vector":
- cmd = ["v.info"]
- elif self.selected_layer[i].data["type"] == "raster_3d":
- cmd = ["r3.info"]
- cmd.append(
- "map=%s@%s"
- % (
- self.selected_layer[i].data["name"],
- self.selected_mapset[i].data["name"],
- )
- )
- gisrc, env = gscript.create_environment(
- self.selected_grassdb[i].data["name"],
- self.selected_location[i].data["name"],
- self.selected_mapset[i].data["name"],
- )
- # print output to command log area
- # temp gisrc file must be deleted onDone
- self._giface.RunCmd(cmd, env=env, onDone=done, userData=gisrc)
- def OnCopyName(self, event):
- """Copy layer name to clipboard"""
- if wx.TheClipboard.Open():
- do = wx.TextDataObject()
- text = []
- for i in range(len(self.selected_layer)):
- text.append(
- "%s@%s"
- % (
- self.selected_layer[i].data["name"],
- self.selected_mapset[i].data["name"],
- )
- )
- do.SetText(",".join(text))
- wx.TheClipboard.SetData(do)
- wx.TheClipboard.Close()
- def Filter(self, text, element=None):
- """Filter tree based on name and type."""
- name = text.strip()
- if not name:
- self._model = self._orig_model
- else:
- try:
- compiled = re.compile(name)
- except re.error:
- return
- if element:
- self._model = self._orig_model.Filtered(
- method="filtering", name=compiled, type=element
- )
- else:
- self._model = self._orig_model.Filtered(
- method="filtering", name=compiled
- )
- self.UpdateCurrentDbLocationMapsetNode()
- self.RefreshItems()
- self.ExpandCurrentMapset()
- if self._model.GetLeafCount(self._model.root) <= 50:
- self.ExpandAll()
- def OnReloadMapset(self, event):
- """Reload selected mapset"""
- node = self.selected_mapset[0]
- self._reloadMapsetNode(node)
- self.RefreshNode(node, recursive=True)
- self.ExpandNode(node, recursive=False)
- def OnReloadLocation(self, event):
- """Reload all mapsets in selected location"""
- node = self.selected_location[0]
- self._reloadLocationNode(node)
- self.UpdateCurrentDbLocationMapsetNode()
- self.RefreshNode(node, recursive=True)
- self.ExpandNode(node, recursive=False)
- def OnReloadGrassdb(self, event):
- """Reload all mapsets in selected grass db"""
- node = self.selected_grassdb[0]
- self.busy = wx.BusyCursor()
- self.thread.Run(
- callable=self._reloadGrassDBNode,
- grassdb_node=node,
- ondone=self._onDoneReloadingGrassdb,
- userdata={"node": node},
- )
- def _onDoneReloadingGrassdb(self, event):
- del self.busy
- self.UpdateCurrentDbLocationMapsetNode()
- self.RefreshNode(event.userdata["node"], recursive=True)
- self.ExpandNode(event.userdata["node"], recursive=False)
- def _getNewMapName(self, message, title, value, element, mapset, env):
- """Dialog for simple text entry"""
- dlg = NameEntryDialog(
- parent=self,
- message=message,
- caption=title,
- element=element,
- env=env,
- mapset=mapset,
- )
- dlg.SetValue(value)
- if dlg.ShowModal() == wx.ID_OK:
- name = dlg.GetValue()
- else:
- name = None
- dlg.Destroy()
- return name
- def _confirmDialog(self, question, title):
- """Confirm dialog"""
- dlg = wx.MessageDialog(self, question, title, wx.YES_NO)
- res = dlg.ShowModal()
- dlg.Destroy()
- return res
- def _isCurrent(self, genv):
- if self._restricted:
- currentMapset = currentLocation = currentGrassDb = True
- for i in range(len(self.selected_grassdb)):
- if self.selected_grassdb[i].data["name"] != genv["GISDBASE"]:
- currentGrassDb = False
- currentLocation = False
- currentMapset = False
- break
- if currentLocation and self.selected_location[0]:
- for i in range(len(self.selected_location)):
- if self.selected_location[i].data["name"] != genv["LOCATION_NAME"]:
- currentLocation = False
- currentMapset = False
- break
- if currentMapset and self.selected_mapset[0]:
- for i in range(len(self.selected_mapset)):
- if self.selected_mapset[i].data["name"] != genv["MAPSET"]:
- currentMapset = False
- break
- return currentGrassDb, currentLocation, currentMapset
- else:
- return True, True, True
- def _popupMenuLayer(self):
- """Create popup menu for layers"""
- menu = Menu()
- genv = gisenv()
- currentGrassDb, currentLocation, currentMapset = self._isCurrent(genv)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Cut"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnMoveMap, item)
- if not currentMapset:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Copy"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnCopyMap, item)
- item = wx.MenuItem(menu, wx.ID_ANY, _("Copy &name"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnCopyName, item)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Paste"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
- if not (currentMapset and self.copy_layer):
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDeleteMap, item)
- item.Enable(currentMapset)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnRenameMap, item)
- item.Enable(currentMapset and len(self.selected_layer) == 1)
- menu.AppendSeparator()
- if not isinstance(self._giface, StandaloneGrassInterface):
- if all(
- [
- each.data["name"] == genv["LOCATION_NAME"]
- for each in self.selected_location
- ]
- ):
- if len(self.selected_layer) > 1:
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Display layers"))
- else:
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Display layer"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item)
- item = wx.MenuItem(menu, wx.ID_ANY, _("Show &metadata"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnMetadata, item)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuMapset(self):
- """Create popup menu for mapsets"""
- menu = Menu()
- genv = gisenv()
- currentGrassDb, currentLocation, currentMapset = self._isCurrent(genv)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Paste"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
- if not (currentMapset and self.copy_layer):
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Switch mapset"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnSwitchMapset, item)
- if (
- self.selected_grassdb[0].data["name"] == genv["GISDBASE"]
- and self.selected_location[0].data["name"] == genv["LOCATION_NAME"]
- and self.selected_mapset[0].data["name"] == genv["MAPSET"]
- ):
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete mapset"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDeleteMapset, item)
- if self._restricted:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename mapset"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnRenameMapset, item)
- if self._restricted:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("Re&load maps"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnReloadMapset, item)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuLocation(self):
- """Create popup menu for locations"""
- menu = Menu()
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Create mapset"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnCreateMapset, item)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete location"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDeleteLocation, item)
- if self._restricted:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Rename location"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnRenameLocation, item)
- if self._restricted:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("Re&load maps"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnReloadLocation, item)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuGrassDb(self):
- """Create popup menu for grass db"""
- menu = Menu()
- genv = gisenv()
- currentGrassDb, currentLocation, currentMapset = self._isCurrent(genv)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Create new location"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnCreateLocation, item)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Download sample location"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDownloadLocation, item)
- item = wx.MenuItem(
- menu, wx.ID_ANY, _("&Remove GRASS database from data catalog")
- )
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnRemoveGrassDb, item)
- if self.selected_grassdb[0].data["name"] == genv["GISDBASE"]:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete GRASS database from disk"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDeleteGrassDb, item)
- if self._restricted:
- item.Enable(False)
- item = wx.MenuItem(menu, wx.ID_ANY, _("Re&load maps"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnReloadGrassdb, item)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuElement(self):
- """Create popup menu for elements"""
- menu = Menu()
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Paste"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
- genv = gisenv()
- currentGrassDb, currentLocation, currentMapset = self._isCurrent(genv)
- if not (currentMapset and self.copy_layer):
- item.Enable(False)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuMultipleLocations(self):
- """Create popup menu for multiple selected locations"""
- menu = Menu()
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete locations"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDeleteLocation, item)
- if self._restricted:
- item.Enable(False)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuMultipleMapsets(self):
- """Create popup menu for multiple selected mapsets"""
- menu = Menu()
- item = wx.MenuItem(menu, wx.ID_ANY, _("&Delete mapsets"))
- menu.AppendItem(item)
- self.Bind(wx.EVT_MENU, self.OnDeleteMapset, item)
- if self._restricted:
- item.Enable(False)
- self.PopupMenu(menu)
- menu.Destroy()
- def _popupMenuEmpty(self):
- """Create empty popup when multiple different types of items are selected"""
- menu = Menu()
- item = wx.MenuItem(menu, wx.ID_ANY, _("No available options"))
- menu.AppendItem(item)
- item.Enable(False)
- self.PopupMenu(menu)
- menu.Destroy()
|