[GRASS-SVN] r61032 - grass/trunk/gui/wxpython/lmgr
svn_grass at osgeo.org
svn_grass at osgeo.org
Sat Jun 28 09:46:15 PDT 2014
Author: martinl
Date: 2014-06-28 09:46:15 -0700 (Sat, 28 Jun 2014)
New Revision: 61032
Added:
grass/trunk/gui/wxpython/lmgr/datacatalog.py
Modified:
grass/trunk/gui/wxpython/lmgr/__init__.py
grass/trunk/gui/wxpython/lmgr/frame.py
Log:
wxGUI: first experimental prototype of Data Catalog in Layer Manager (author: Tereza Fiedlerova)
Modified: grass/trunk/gui/wxpython/lmgr/__init__.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/__init__.py 2014-06-28 16:26:15 UTC (rev 61031)
+++ grass/trunk/gui/wxpython/lmgr/__init__.py 2014-06-28 16:46:15 UTC (rev 61032)
@@ -5,4 +5,5 @@
'pyshell',
'frame',
'giface',
+ 'datacatalog'
]
Added: grass/trunk/gui/wxpython/lmgr/datacatalog.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/datacatalog.py (rev 0)
+++ grass/trunk/gui/wxpython/lmgr/datacatalog.py 2014-06-28 16:46:15 UTC (rev 61032)
@@ -0,0 +1,477 @@
+"""
+ at package lmgr::datacatalog
+
+ at brief Data catalog
+
+Classes:
+ - datacatalog::DataCatalog
+ - datacatalog::DataCatalogTree
+
+(C) 2014 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.
+
+ at author Tereza Fiedlerova
+"""
+
+import os
+import sys
+
+import wx
+
+from core.gcmd import RunCommand, GError, GMessage
+from core.utils import GetListOfLocations
+from core.gthread import gThread
+from core.debug import Debug
+from gui_core.dialogs import TextEntryDialog
+
+from grass.pydispatch.signal import Signal
+
+import grass.script as grass
+
+class DataCatalog(wx.Panel):
+ """Data catalog panel"""
+ def __init__(self, parent, giface=None, id = wx.ID_ANY, title=_("Data catalog"),
+ name='catalog', **kwargs):
+ """Panel constructor """
+ self.showNotification = Signal('DataCatalog.showNotification')
+ self.parent = parent
+ self.baseTitle = title
+ wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
+ self.SetName("DataCatalog")
+
+ Debug.msg(1, "DataCatalog.__init__()")
+
+ # tree with layers
+ self.tree = DataCatalogTree(self)
+ self.thread = gThread()
+ self._loaded = False
+ self.tree.showNotification.connect(self.showNotification)
+
+ # some layout
+ self._layout()
+
+ def _layout(self):
+ """Do layout"""
+ sizer = wx.BoxSizer(wx.VERTICAL)
+
+ sizer.Add(item = self.tree.GetControl(), proportion = 1,
+ flag = wx.EXPAND)
+
+ self.SetAutoLayout(True)
+ self.SetSizer(sizer)
+
+ self.Layout()
+
+ def LoadItems(self):
+ if self._loaded:
+ return
+
+ self.thread.Run(callable=self.tree.InitTreeItems,
+ ondone=lambda event: self.LoadItemsDone())
+
+ def LoadItemsDone(self):
+ self._loaded = True
+
+class DataCatalogTree(wx.TreeCtrl):
+ def __init__(self, parent):
+ """Tree constructor."""
+ super(DataCatalogTree, self).__init__(parent, id=wx.ID_ANY, style = wx.TR_HIDE_ROOT | wx.TR_EDIT_LABELS |
+ wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_COLUMN_LINES | wx.TR_SINGLE)
+ self.showNotification = Signal('Tree.showNotification')
+ self.parent = parent
+ self.root = self.AddRoot('Catalog') # will not be displayed when we use TR_HIDE_ROOT flag
+
+ self._initVariables()
+ self.MakeBackup()
+
+ wx.EVT_TREE_ITEM_RIGHT_CLICK(self, wx.ID_ANY, self.OnRightClick)
+ wx.EVT_TREE_BEGIN_DRAG(self, wx.ID_ANY, self.OnBeginDrag)
+ wx.EVT_TREE_END_DRAG(self, wx.ID_ANY, self.OnEndDrag)
+
+ self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
+ self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+ self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
+
+ wx.EVT_TREE_END_LABEL_EDIT(self, wx.ID_ANY, self.OnEditLabel)
+ wx.EVT_TREE_BEGIN_LABEL_EDIT(self, wx.ID_ANY, self.OnStartEditLabel)
+
+ def _initVariables(self):
+ """Init variables."""
+ self.selected_layer = None
+ self.selected_type = None
+ self.selected_mapset = None
+ self.selected_location = None
+ self.copy_layer = None
+ self.copy_type = None
+ self.copy_mapset = None
+ self.copy_location = None
+ self.gisdbase = grass.gisenv()['GISDBASE']
+ self.ctrldown = False
+
+ def GetControl(self):
+ """Returns control itself."""
+ return self
+
+ def DefineItems(self, item0):
+ """Set selected items."""
+ self.selected_layer = None
+ self.selected_type = None
+ self.selected_mapset = None
+ self.selected_location = None
+ items = []
+ item = item0
+ while (self.GetItemParent(item)):
+ items.insert(0,item)
+ item = self.GetItemParent(item)
+
+ self.selected_location = items[0]
+ length = len(items)
+ if (length > 1):
+ self.selected_mapset = items[1]
+ if (length > 2):
+ self.selected_type = items[2]
+ if (length > 3):
+ self.selected_layer = items[3]
+
+ def InitTreeItems(self):
+ """Add locations, mapsets and layers to the tree."""
+ locations = GetListOfLocations(self.gisdbase)
+ first = True
+ for loc in locations:
+ location = loc
+ if first:
+ self.ChangeEnvironment(location, 'PERMANENT')
+ first = False
+ else:
+ self.ChangeEnvironment(location)
+ varloc = self.AppendItem(self.root, loc)
+ #get list of all maps in location
+ maplist = RunCommand('g.mlist', flags='mt', type='rast,rast3d,vect', mapset='*', quiet=True, read=True)
+ maplist = maplist.splitlines()
+ for ml in maplist:
+ # parse
+ parts1 = ml.split('/')
+ parts2 = parts1[1].split('@')
+ mapset = parts2[1]
+ mlayer = parts2[0]
+ ltype = parts1[0]
+ if self.itemExists(mapset, varloc) == False:
+ varmapset = self.AppendItem(varloc, mapset)
+ if (self.GetItemText(varmapset) == mapset):
+ if (self.itemExists(ltype, varmapset) == False):
+ vartype = self.AppendItem(varmapset, ltype)
+ else:
+ varmapset = self.getItemByName(mapset, varloc)
+ if (self.itemExists(ltype, varmapset) == False):
+ vartype = self.AppendItem(varmapset, ltype)
+ self.AppendItem(vartype, mlayer)
+ self.RestoreBackup()
+ Debug.msg(1, "Tree filled")
+
+ def getItemByName(self, match, root):
+ """Return match item from the root."""
+ item, cookie = self.GetFirstChild(root)
+ while item.IsOk():
+ if self.GetItemText(item) == match:
+ return item
+ item, cookie = self.GetNextChild(root, cookie)
+ return None
+
+ def itemExists(self, match, root):
+ """Return true if match item exists in the root item."""
+ item, cookie = self.GetFirstChild(root)
+ while item.IsOk():
+ if self.GetItemText(item) == match:
+ return True
+ item, cookie = self.GetNextChild(root, cookie)
+ return False
+
+ def UpdateTree(self):
+ """Update whole tree."""
+ self.DeleteAllItems()
+ self.root = self.AddRoot('Tree')
+ self.AddTreeItems()
+ label = "Tree updated."
+ self.showNotification.emit(message=label)
+
+ def OnSelChanged(self, event):
+ self.selected_layer = None
+
+ def OnRightClick(self, event):
+ """Display popup menu."""
+ self.DefineItems(event.GetItem())
+ if(self.selected_layer):
+ self._popupMenuLayer()
+ elif(self.selected_mapset and self.selected_type==None):
+ self._popupMenuMapset()
+
+ def OnDoubleClick(self, event):
+ """Rename layer"""
+ Debug.msg(1, "Double CLICK")
+
+ def OnCopy(self, event):
+ """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
+ self.copy_layer = self.selected_layer
+ self.copy_type = self.selected_type
+ self.copy_mapset = self.selected_mapset
+ self.copy_location = self.selected_location
+ label = "Layer "+self.GetItemText(self.copy_layer)+" copied to clipboard. You can paste it to selected mapset."
+ self.showNotification.emit(message=label)
+
+ def OnRename(self, event):
+ """Rename levent with dialog"""
+ if (self.selected_layer):
+ self.old_name = self.GetItemText(self.selected_layer)
+ self._textDialog(_('New name'), _('Rename map'), self.old_name)
+ self.rename()
+
+ def OnStartEditLabel(self, event):
+ """Start label editing"""
+ item = event.GetItem()
+ self.DefineItems(item)
+ Debug.msg(1, "Start label edit "+self.GetItemText(item))
+ label = _("Editing") + " " + self.GetItemText(item)
+ self.showNotification.emit(message=label)
+ if (self.selected_layer == None):
+ event.Veto()
+
+ def OnEditLabel(self, event):
+ """End label editing"""
+ if (self.selected_layer):
+ item = event.GetItem()
+ self.old_name = self.GetItemText(item)
+ Debug.msg(1, "End label edit "+self.old_name)
+ wx.CallAfter(self.afterEdit, self, item)
+
+ def afterEdit(pro, self, item):
+ self.new_name = self.GetItemText(item)
+ self.rename()
+
+ def rename(self):
+ """Rename layer"""
+ if (self.selected_layer):
+ string = self.old_name+','+self.new_name
+ self.ChangeEnvironment(self.GetItemText(self.selected_location), self.GetItemText(self.selected_mapset))
+ renamed = 0
+ label = _("Renaming") + " " + string + " ..."
+ self.showNotification.emit(message=label)
+ if (self.GetItemText(self.selected_type)=='vect'):
+ renamed = RunCommand('g.rename', vect=string)
+ elif (self.GetItemText(self.selected_type)=='rast'):
+ renamed = RunCommand('g.rename', rast=string)
+ else:
+ renamed = RunCommand('g.rename', rast3d=string)
+ if (renamed==0):
+ self.SetItemText(self.selected_layer,self.new_name)
+ label = "g.rename "+self.GetItemText(self.selected_type)+"="+string+" -- completed"
+ self.showNotification.emit(message=label)
+ Debug.msg(1,"LAYER RENAMED TO: "+self.new_name)
+ self.RestoreBackup()
+
+ def OnPaste(self, event):
+ """Paste layer or mapset"""
+ # copying between mapsets of one location
+ if (self.copy_layer == None):
+ return
+ if (self.selected_location == self.copy_location and self.selected_mapset):
+ if (self.selected_type != None):
+ if (self.GetItemText(self.copy_type) != self.GetItemText(self.selected_type)): # copy raster to vector or vice versa
+ GError(_("Failed to copy layer: invalid type."), parent = self)
+ return
+ self._textDialog(_('New name'), _('Copy map'), self.GetItemText(self.copy_layer)+'_copy')
+ if (self.GetItemText(self.copy_layer) == self.new_name):
+ GMessage(_("Layer was not copied: new layer has the same name"), parent=self)
+ return
+ string = self.GetItemText(self.copy_layer)+'@'+self.GetItemText(self.copy_mapset)+','+self.new_name
+ self.ChangeEnvironment(self.GetItemText(self.selected_location), self.GetItemText(self.selected_mapset))
+ pasted = 0
+ type = None
+ label = _("Copying") + " " + string + " ..."
+ self.showNotification.emit(message=label)
+ if (self.GetItemText(self.copy_type)=='vect'):
+ pasted = RunCommand('g.copy', vect=string)
+ node = 'vect'
+ elif (self.GetItemText(self.copy_type)=='rast'):
+ pasted = RunCommand('g.copy', rast=string)
+ node = 'rast'
+ else:
+ pasted = RunCommand('g.copy', rast3d=string)
+ node = 'rast3d'
+ if (pasted==0):
+ if (self.selected_type == None):
+ self.selected_type = self.getItemByName(node, self.selected_mapset)
+ self.AppendItem(self.selected_type,self.new_name)
+ self.SortChildren(self.selected_type)
+ Debug.msg(1,"COPIED TO: "+self.new_name)
+ label = "g.copy "+self.GetItemText(self.copy_type)+"="+string+" -- completed" # generate this message (command) automatically?
+ self.showNotification.emit(message=label)
+ else:
+ GError(_("Failed to copy layer: action is allowed only within the same location."),
+ parent=self)
+
+ self.RestoreBackup()
+
+
+ def OnDelete(self, event):
+ """Delete layer or mapset"""
+ if (self.selected_layer):
+ string = self.GetItemText(self.selected_layer)
+ self.ChangeEnvironment(self.GetItemText(self.selected_location), self.GetItemText(self.selected_mapset))
+ removed = 0
+ if (self._confirmDialog(_('Do you really want to delete layer') +string+'?', _('Delete map')) == wx.ID_YES):
+ label = _("Deleting") + " " + string + " ..."
+ self.showNotification.emit(message=label)
+ if (self.GetItemText(self.selected_type)=='vect'):
+ removed = RunCommand('g.remove', vect=string)
+ elif (self.GetItemText(self.selected_type)=='rast'):
+ removed = RunCommand('g.remove', rast=string)
+ else:
+ removed = RunCommand('g.remove', rast3d=string)
+ if (removed==0):
+ self.Delete(self.selected_layer)
+ Debug.msg(1,"LAYER "+string+" DELETED")
+ label = "g.remove "+self.GetItemText(self.selected_type)+"="+string+" -- completed" # generate this message (command) automatically?
+ self.showNotification.emit(message=label)
+ self.RestoreBackup()
+
+ def OnDisplayLayer(self, event):
+ """Display layer in current graphics view"""
+ layerName = []
+ if (self.GetItemText(self.selected_location) == self.glocation and self.selected_mapset):
+ string = self.GetItemText(self.selected_layer)+'@'+self.GetItemText(self.selected_mapset)
+ layerName.append(string)
+ label = _("Displaying") + " " + string + " ..."
+ self.showNotification.emit(message=label)
+ label = "d."+self.GetItemText(self.selected_type)+" --q map="+string+" -- completed. Go to Map layers for further operations."
+ if (self.GetItemText(self.selected_type)=='vect'):
+ self.parent.parent.AddMaps(layerName, 'vect', True)
+ elif (self.GetItemText(self.selected_type)=='rast'):
+ self.parent.parent.AddMaps(layerName, 'rast', True)
+ else:
+ self.parent.parent.AddMaps(layerName, 'rast3d', True)
+ label = "d.rast --q map="+string+" -- completed. Go to 'Map layers' for further operations." # generate this message (command) automatically?
+ self.showNotification.emit(message=label)
+ Debug.msg(1,"LAYER "+self.GetItemText(self.selected_layer)+" DISPLAYED")
+ else:
+ GError(_("Failed to display layer: not in current mapset or invalid layer"),
+ parent = self)
+
+ def OnBeginDrag(self, event):
+ """Just copy necessary data"""
+ if (self.ctrldown):
+ #cursor = wx.StockCursor(wx.CURSOR_HAND)
+ #self.SetCursor(cursor)
+ event.Allow()
+ self.DefineItems(event.GetItem())
+ self.OnCopy(event)
+ Debug.msg(1,"DRAG")
+ else:
+ event.Veto()
+ Debug.msg(1,"DRAGGING without ctrl key does nothing")
+
+ def OnEndDrag(self, event):
+ """Copy layer into target"""
+ #cursor = wx.StockCursor(wx.CURSOR_ARROW)
+ #self.SetCursor(cursor)
+ if (event.GetItem()):
+ self.DefineItems(event.GetItem())
+ if (self.selected_location == self.copy_location and self.selected_mapset):
+ event.Allow()
+ self.OnPaste(event)
+ self.ctrldown = False
+ #cursor = wx.StockCursor(wx.CURSOR_DEFAULT)
+ #self.SetCursor(cursor) # TODO: change cursor while dragging and then back, this is not working
+ Debug.msg(1,"DROP DONE")
+ else:
+ event.Veto()
+
+ def OnKeyDown(self, event):
+ """Set key event and check if control key is down"""
+ keycode = event.GetKeyCode()
+ if keycode == wx.WXK_CONTROL:
+ self.ctrldown = True
+ Debug.msg(1,"CONTROL ON")
+
+ def OnKeyUp(self, event):
+ """Check if control key is up"""
+ keycode = event.GetKeyCode()
+ if keycode == wx.WXK_CONTROL:
+ self.ctrldown = False
+ Debug.msg(1,"CONTROL OFF")
+
+ def _textDialog(self, message, title, value):
+ """Dialog for simple text entry"""
+ dlg = TextEntryDialog(self, message, title)
+ dlg.SetValue(value)
+ res = dlg.ShowModal()
+ self.new_name = dlg.GetValue()
+ dlg.Destroy()
+
+ def _confirmDialog(self, question, title):
+ """Confirm dialog"""
+ dlg = wx.MessageDialog(self, question, title, wx.YES_NO)
+ res = dlg.ShowModal()
+ dlg.Destroy()
+ return res
+
+ def _popupMenuLayer(self):
+ """Create popup menu for layers"""
+ menu = wx.Menu()
+
+ item = wx.MenuItem(menu, wx.NewId(), _("&Copy"))
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnCopy, item)
+
+ item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnPaste, item)
+
+ item = wx.MenuItem(menu, wx.NewId(), _("&Delete"))
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnDelete, item)
+
+ item = wx.MenuItem(menu, wx.NewId(), _("&Rename"))
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnRename, item)
+
+ item = wx.MenuItem(menu, wx.NewId(), _("&Display layer"))
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item)
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def _popupMenuMapset(self):
+ """Create popup menu for mapsets"""
+ menu = wx.Menu()
+
+ item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnPaste, item)
+
+ self.PopupMenu(menu)
+ menu.Destroy()
+
+ def MakeBackup(self):
+ """Make backup for case of change"""
+ self.glocation = grass.gisenv()['LOCATION_NAME']
+ self.gmapset = grass.gisenv()['MAPSET']
+
+ def RestoreBackup(self):
+ """Restore backup"""
+ stringl = 'LOCATION_NAME='+self.glocation
+ RunCommand('g.gisenv', set=stringl)
+ stringm = 'MAPSET='+self.gmapset
+ RunCommand('g.gisenv', set=stringm)
+
+ def ChangeEnvironment(self, location, mapset=None):
+ """Change gisenv variables -> location, mapset"""
+ stringl = 'LOCATION_NAME='+location
+ RunCommand('g.gisenv', set=stringl)
+ if mapset:
+ stringm = 'MAPSET='+mapset
+ RunCommand('g.gisenv', set=stringm)
+
Property changes on: grass/trunk/gui/wxpython/lmgr/datacatalog.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Modified: grass/trunk/gui/wxpython/lmgr/frame.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/frame.py 2014-06-28 16:26:15 UTC (rev 61031)
+++ grass/trunk/gui/wxpython/lmgr/frame.py 2014-06-28 16:46:15 UTC (rev 61032)
@@ -70,6 +70,7 @@
from lmgr.toolbars import LMMiscToolbar, LMVectorToolbar, LMNvizToolbar
from lmgr.pyshell import PyShellWindow
from lmgr.giface import LayerManagerGrassInterface
+from lmgr.datacatalog import DataCatalog
from gui_core.forms import GUI
from gcp.manager import GCPWizard
from nviz.main import haveNviz
@@ -317,6 +318,11 @@
else:
self.search = None
+ # create 'data catalog' notebook page
+ self.datacatalog = DataCatalog(parent = self, giface = self._giface)
+ self.datacatalog.showNotification.connect(lambda message: self.SetStatusText(message))
+ self.notebook.AddPage(page = self.datacatalog, text = _("Data catalog"), name = 'catalog')
+
# create 'python shell' notebook page
if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'pyshell'):
self.pyshell = PyShellWindow(parent = self)
@@ -544,6 +550,8 @@
page = event.GetSelection()
if page == self.notebook.GetPageIndexByName('output'):
wx.CallAfter(self.goutput.ResetFocus)
+ elif page == self.notebook.GetPageIndexByName('catalog'):
+ wx.CallAfter(self.datacatalog.LoadItems)
self.SetStatusText('', 0)
event.Skip()
More information about the grass-commit
mailing list