[GRASS-SVN] r57187 - in grass/trunk/gui/wxpython: . core lmgr xml

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Jul 17 04:17:55 PDT 2013


Author: wenzeslaus
Date: 2013-07-17 04:17:54 -0700 (Wed, 17 Jul 2013)
New Revision: 57187

Added:
   grass/trunk/gui/wxpython/xml/module_tree.xml
Modified:
   grass/trunk/gui/wxpython/Makefile
   grass/trunk/gui/wxpython/core/menutree.py
   grass/trunk/gui/wxpython/core/toolboxes.py
   grass/trunk/gui/wxpython/lmgr/frame.py
   grass/trunk/gui/wxpython/lmgr/menudata.py
   grass/trunk/gui/wxpython/xml/main_menu.xml
Log:
wxGUI/toolboxes: different tree for menu and for search module (missing some formal things)

Modified: grass/trunk/gui/wxpython/Makefile
===================================================================
--- grass/trunk/gui/wxpython/Makefile	2013-07-17 10:08:51 UTC (rev 57186)
+++ grass/trunk/gui/wxpython/Makefile	2013-07-17 11:17:54 UTC (rev 57187)
@@ -1,7 +1,7 @@
 MODULE_TOPDIR = ../..
 
 SUBDIRS = docs animation mapswipe gmodeler rlisetup psmap dbmgr vdigit iclass
-EXTRA_CLEAN_FILES = menustrings.py build_ext.pyc xml/menudata.xml
+EXTRA_CLEAN_FILES = menustrings.py build_ext.pyc xml/menudata.xml xml/module_tree_menudata.xml
 
 include $(MODULE_TOPDIR)/include/Make/Dir.make
 include $(MODULE_TOPDIR)/include/Make/Doxygen.make
@@ -28,6 +28,7 @@
 default: $(DSTFILES)
 	-$(MAKE) $(ETCDIR)/xml/module_items.xml
 	-$(MAKE) xml/menudata.xml
+	-$(MAKE) xml/module_tree_menudata.xml
 	-$(MAKE) menustrings.py
 	$(MAKE) parsubdirs
 
@@ -38,6 +39,9 @@
 xml/menudata.xml: core/toolboxes.py
 	$(call run_grass,$(PYTHON) $< > $@)
 
+xml/module_tree_menudata.xml: core/toolboxes.py
+	$(call run_grass,$(PYTHON) $< dummy_parameter > $@)
+
 menustrings.py: core/menutree.py $(ETCDIR)/xml/menudata.xml $(ETCDIR)/xml/menudata_modeler.xml $(ETCDIR)/xml/menudata_psmap.xml
 	@echo "# This is a generated file.\n" > $@
 	$(call run_grass,$(PYTHON) $< >> $@)

Modified: grass/trunk/gui/wxpython/core/menutree.py
===================================================================
--- grass/trunk/gui/wxpython/core/menutree.py	2013-07-17 10:08:51 UTC (rev 57186)
+++ grass/trunk/gui/wxpython/core/menutree.py	2013-07-17 11:17:54 UTC (rev 57187)
@@ -49,10 +49,13 @@
 
 from core.treemodel import TreeModel, ModuleNode
 from core.settings import UserSettings
+from core.toolboxes import expandAddons
 
 if not os.getenv("GISBASE"):
     sys.exit("GRASS is not running. Exiting...")
 
+
+# TODO: change the system to remove strange derived classes
 class MenuTreeModelBuilder:
     """!Abstract menu data class"""
     def __init__(self, filename):
@@ -62,6 +65,7 @@
                                           subkey = 'selection')
 
         xmlTree = etree.parse(filename)
+        expandAddons(xmlTree)
         self.model = TreeModel(ModuleNode)
         self._createModel(xmlTree)
 

Modified: grass/trunk/gui/wxpython/core/toolboxes.py
===================================================================
--- grass/trunk/gui/wxpython/core/toolboxes.py	2013-07-17 10:08:51 UTC (rev 57186)
+++ grass/trunk/gui/wxpython/core/toolboxes.py	2013-07-17 11:17:54 UTC (rev 57187)
@@ -43,6 +43,7 @@
 import grass.script.task as gtask
 import grass.script.core as gcore
 from grass.script.core import ScriptError
+from core.debug import Debug
 
 
 # this could be placed to functions
@@ -67,6 +68,89 @@
         gcore.try_remove(path)
 
 
+# TODO: merge the function with getMenuFile
+def getMenudataFile(userRootFile, newFile, fallback):
+    """!Returns path to XML file for building menu.
+
+    Creates toolbox directory where user defined toolboxes should be located.
+    Checks whether it is needed to create new XML file (user changed toolboxes)
+    or the already generated file could be used.
+    If something goes wrong during building or user doesn't modify menu,
+    default file (from distribution) is returned.
+    """
+    Debug.msg(1, "toolboxes.getMenudataFile: {userRootFile}, {newFile}, {fallback}".format(**locals()))
+
+    distributionRootFile = os.path.join(ETCWXDIR, 'xml', userRootFile)
+    userRootFile = os.path.join(GetSettingsPath(), 'toolboxes', userRootFile)
+    if not os.path.exists(userRootFile):
+        userRootFile = None
+
+    ##fallback = os.path.join(ETCWXDIR, 'xml', 'menudata.xml')
+    # always create toolboxes directory if does not exist yet
+    tbDir = _setupToolboxes()
+
+    if tbDir:
+        menudataFile = os.path.join(tbDir, newFile)
+        generateNew = False
+        # when any of main_menu.xml or toolboxes.xml are changed,
+        # generate new menudata.xml
+
+        if os.path.exists(menudataFile):
+            # remove menu file when there is no main_menu and toolboxes
+            if not userToolboxesFile and not userRootFile:
+                os.remove(menudataFile)
+                Debug.msg(2, "toolboxes.getMenudataFile: no user defined files, menudata deleted")
+                return fallback
+
+            if bool(userToolboxesFile) != bool(userRootFile):
+                # always generate new because we don't know if there has been any change
+                generateNew = True
+                Debug.msg(2, "toolboxes.getMenudataFile: only one of the user defined files")
+            else:
+                # if newer files -> generate new
+                menudataTime = os.path.getmtime(menudataFile)
+                if userToolboxesFile:
+                    if os.path.getmtime(userToolboxesFile) > menudataTime:
+                        Debug.msg(2, "toolboxes.getMenudataFile: user toolboxes is newer than menudata")
+                        generateNew = True
+                if userRootFile:
+                    if os.path.getmtime(userRootFile) > menudataTime:
+                        Debug.msg(2, "toolboxes.getMenudataFile: user root file is newer than menudata")
+                        generateNew = True
+        elif userToolboxesFile or userRootFile:
+            Debug.msg(2, "toolboxes.getMenudataFile: no menudata")
+            generateNew = True
+        else:
+            Debug.msg(2, "toolboxes.getMenudataFile: no user defined files")
+            return fallback
+
+        if generateNew:
+            try:
+                # The case when user does not have custom root
+                # file but has toolboxes requieres regeneration.
+                # Unfortunately, this is the case can be often: defined
+                # toolboxes but undefined module tree file.
+                Debug.msg(2, "toolboxes.getMenudataFile: creating a tree")
+                tree = createTree(distributionRootFile=distributionRootFile, userRootFile=userRootFile)
+            except ETREE_EXCEPTIONS:
+                GError(_("Unable to parse user toolboxes XML files. "
+                         "Default toolboxes will be loaded."))
+                return fallback
+
+            try:
+                xml = _getXMLString(tree.getroot())
+                fh = open(menudataFile, 'w')
+                fh.write(xml)
+                fh.close()
+                return menudataFile
+            except:
+                return fallback
+        else:
+            return menudataFile
+    else:
+        return fallback
+
+
 def getMenuFile():
     """!Returns path to XML file for building menu.
 
@@ -76,6 +160,7 @@
     If something goes wrong during building or user doesn't modify menu,
     default file (from distribution) is returned.
     """
+    Debug.msg(1, "toolboxes.getMenuFile")
     fallback = os.path.join(ETCWXDIR, 'xml', 'menudata.xml')
     # always create toolboxes directory if does not exist yet
     tbDir = _setupToolboxes()
@@ -156,7 +241,40 @@
             return False
     return True
 
+# TODO: merge with toolboxes2menudata
+def createTree(distributionRootFile, userRootFile, userDefined=True):
+    """!Creates XML file with data for menu.
 
+    Parses toolboxes files from distribution and from users,
+    puts them together, adds metadata to modules and convert
+    tree to previous format used for loading menu.
+
+    @param userDefined use toolboxes defined by user or not (during compilation)
+
+    @return ElementTree instance
+    """
+    if userDefined and userRootFile:
+        mainMenu = etree.parse(userRootFile)
+    else:
+        mainMenu = etree.parse(distributionRootFile)
+
+    toolboxes = etree.parse(toolboxesFile)
+
+    if userDefined and userToolboxesFile:
+        userToolboxes = etree.parse(userToolboxesFile)
+    else:
+        userToolboxes = None
+
+    wxguiItems = etree.parse(wxguiItemsFile)
+    moduleItems = etree.parse(moduleItemsFile)
+
+    return toolboxes2menudataInternal(mainMenu=mainMenu,
+                                      toolboxes=toolboxes,
+                                      userToolboxes=userToolboxes,
+                                      wxguiItems=wxguiItems,
+                                      moduleItems=moduleItems)
+
+
 def toolboxes2menudata(userDefined=True):
     """!Creates XML file with data for menu.
 
@@ -190,6 +308,7 @@
                                       moduleItems=moduleItems)
 
 
+# TODO: rename
 def toolboxes2menudataInternal(mainMenu, toolboxes, userToolboxes,
                                wxguiItems, moduleItems):
     """!Creates XML file with data for menu.
@@ -215,10 +334,10 @@
     if not userHasToolboxes:
         _removeUserToolboxesItem(root)
 
-    _expandAddonsItem(root)
-
     _expandToolboxes(root, toolboxes)
 
+    # we do not expand addons here since they need to be expanded in runtime
+
     _expandItems(root, moduleItems, 'module-item')
     _expandItems(root, wxguiItems, 'wxgui-item')
 
@@ -250,6 +369,17 @@
             elem.tail = i
 
 
+def expandAddons(tree):
+    """!Expands addons element.
+    """
+    root = tree.getroot()
+    _expandAddonsItem(root)
+    # expanding and converting is done twice, so there is some overhead
+    _expandRuntimeModules(root)
+    _addHandlers(root)
+    _convertTree(root)
+
+
 def _expandToolboxes(node, toolboxes):
     """!Expands tree with toolboxes.
 
@@ -344,7 +474,7 @@
     >>> toolboxes = etree.fromstring('<toolboxes><toolbox name="UserToolbox"><items><module-item name="g.region"/></items></toolbox></toolboxes>')
     >>> _expandUserToolboxesItem(tree, toolboxes)
     >>> etree.tostring(tree)
-    '<toolbox><items><toolbox name="GeneratedUserToolboxesList"><label>Toolboxes</label><items><toolbox name="UserToolbox"><items><module-item name="g.region" /></items></toolbox></items></toolbox></items></toolbox>'
+    '<toolbox><items><toolbox name="GeneratedUserToolboxesList"><label>Custom toolboxes</label><items><toolbox name="UserToolbox"><items><module-item name="g.region" /></items></toolbox></items></toolbox></items></toolbox>'
     """
     tboxes = toolboxes.findall('.//toolbox')
 
@@ -354,7 +484,7 @@
         el = etree.Element('toolbox', attrib={'name': 'GeneratedUserToolboxesList'})
         items.insert(idx, el)
         label = etree.SubElement(el, tag='label')
-        label.text = _("Toolboxes")
+        label.text = _("Custom toolboxes")
         it = etree.SubElement(el, tag='items')
         for toolbox in tboxes:
             it.append(copy.deepcopy(toolbox))
@@ -374,34 +504,50 @@
         items.remove(n)
 
 
+def _getAddons():
+    return sorted(RunCommand('g.extension', quiet=True, read=True,
+                             flags='a').splitlines())
+
+
+def _removeAddonsItem(node, addonsNodes):
+    # TODO: change impl to be similar with the remove toolboxes
+    for n in addonsNodes:
+        items = node.find('./items')
+        if items is not None:
+            items.remove(n)
+        # because of inconsistent menudata file
+        items = node.find('./menubar')
+        if items is not None:
+            items.remove(n)
+
+
 def _expandAddonsItem(node):
-    """!Expands tag addons (in main_menu.xml) with currently installed addons.append
+    """!Expands addons element with currently installed addons.
 
     Note: there is no mechanism yet to tell the gui to rebuild the menudata.xml
     file when new addons are added/removed.
     """
     # no addonsTag -> do nothing
-    addonsTags = node.findall('./items/addons')
+    addonsTags = node.findall('.//addons')
     if not addonsTags:
         return
     # fetch addons
-    addons = sorted(RunCommand('g.extension', quiet=True, read=True,
-                               flags = 'a').splitlines())
+    addons = _getAddons()
 
     # no addons -> remove addons tag
     if not addons:
-        for n in addonsTags:    
-            items = node.find('./items')
-            idx = items.getchildren().index(n)
-            items.remove(n)
+        _removeAddonsItem(node, addonsTags)
         return
 
     # create addons toolbox
     # keywords and desc are handled later automatically
-    for n in addonsTags:    
-        items = node.find('./items')
+    for n in addonsTags:
+        # find parent is not possible with implementation of etree (in 2.7)
+        items = node.find('./menubar')
         idx = items.getchildren().index(n)
-        el = etree.Element('toolbox', attrib={'name': 'AddonsToolboxesList'})
+        # do not set name since it is already in menudata file
+        # attib={'name': 'AddonsList'}
+        el = etree.Element('menu')
         items.insert(idx, el)
         label = etree.SubElement(el, tag='label')
         label.text = _("Addons")
@@ -548,7 +694,9 @@
     """
     root.attrib = {}
     label = root.find('label')
-    root.remove(label)
+    # must check because of inconsistent XML menudata file
+    if label is not None:
+        root.remove(label)
     _convertTag(root, 'description', 'help')
     _convertTag(root, 'wx-id', 'id')
     _convertTag(root, 'module', 'command')
@@ -558,7 +706,9 @@
 
     root.tag = 'menudata'
     i1 = root.find('./items')
-    i1.tag = 'menubar'
+    # must check because of inconsistent XML menudata file
+    if i1 is not None:
+        i1.tag = 'menubar'
     _convertTagAndRemoveAttrib(root, 'toolbox', 'menu')
 
 
@@ -669,14 +819,22 @@
 
     File is written to the standard output.
     """
-    tree = toolboxes2menudata(userDefined=False)
-    root = tree.getroot()
-    sys.stdout.write(_getXMLString(root))
+    # TODO: fix parameter handling
+    if len(sys.argv) > 1:
+        mainFile = os.path.join(ETCWXDIR, 'xml', 'module_tree.xml')
+        tree = createTree(distributionRootFile=mainFile, userRootFile=None, userDefined=False)
+        root = tree.getroot()
+        sys.stdout.write(_getXMLString(root))
+    else:
+        tree = toolboxes2menudata(userDefined=False)
+        root = tree.getroot()
+        sys.stdout.write(_getXMLString(root))
 
     return 0
 
 
 if __name__ == '__main__':
+    # TODO: fix parameter handling
     if len(sys.argv) > 1:
         if sys.argv[1] == 'doctest':
             sys.exit(doc_test())

Modified: grass/trunk/gui/wxpython/lmgr/frame.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/frame.py	2013-07-17 10:08:51 UTC (rev 57186)
+++ grass/trunk/gui/wxpython/lmgr/frame.py	2013-07-17 11:17:54 UTC (rev 57187)
@@ -47,7 +47,7 @@
 from core.utils            import SetAddOnPath, GetLayerNameFromCmd, command2ltype
 from gui_core.preferences  import MapsetAccess, PreferencesDialog
 from lmgr.layertree        import LayerTree, LMIcons
-from lmgr.menudata         import LayerManagerMenuData
+from lmgr.menudata         import LayerManagerMenuData, LayerManagerModuleTree
 from gui_core.widgets      import GNotebook
 from modules.mcalc_builder import MapCalcFrame
 from dbmgr.manager         import AttributeManager
@@ -105,7 +105,10 @@
         
         self._giface = LayerManagerGrassInterface(self)
         
+        # the main menu bar
         self._menuTreeBuilder = LayerManagerMenuData()
+        # the search tree and command console
+        self._moduleTreeBuilder = LayerManagerModuleTree()
         self._auimgr = wx.aui.AuiManager(self)
         
         
@@ -279,7 +282,7 @@
         self._gconsole = GConsole(guiparent = self, giface = self._giface,
                                   ignoredCmdPattern = '^d\..*|^r[3]?\.mapcalc$|^i.group')
         self.goutput = GConsoleWindow(parent = self, gconsole = self._gconsole,
-                                      menuModel=self._menuTreeBuilder.GetModel(),
+                                      menuModel=self._moduleTreeBuilder.GetModel(),
                                       gcstyle = GC_SEARCH | GC_PROMPT)
         self.notebook.AddPage(page = self.goutput, text = _("Command console"), name = 'output')
 
@@ -317,7 +320,7 @@
         
         # create 'search module' notebook page
         if not UserSettings.Get(group = 'manager', key = 'hideTabs', subkey = 'search'):
-            self.search = SearchModuleWindow(parent = self, model=self._menuTreeBuilder.GetModel())
+            self.search = SearchModuleWindow(parent = self, model=self._moduleTreeBuilder.GetModel())
             self.search.showNotification.connect(lambda message: self.SetStatusText(message))
             self.notebook.AddPage(page = self.search, text = _("Search modules"), name = 'search')
         else:

Modified: grass/trunk/gui/wxpython/lmgr/menudata.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/menudata.py	2013-07-17 10:08:51 UTC (rev 57186)
+++ grass/trunk/gui/wxpython/lmgr/menudata.py	2013-07-17 11:17:54 UTC (rev 57187)
@@ -18,7 +18,7 @@
 import os
 
 from core.menutree  import MenuTreeModelBuilder
-from core.toolboxes import getMenuFile
+from core.toolboxes import getMenuFile, getMenudataFile
 from core.globalvar import ETCWXDIR
 from core.gcmd import GError
 
@@ -34,3 +34,19 @@
                      "Default toolboxes will be loaded."))
             fallback = os.path.join(ETCWXDIR, 'xml', 'menudata.xml')
             MenuTreeModelBuilder.__init__(self, fallback)
+
+
+class LayerManagerModuleTree(MenuTreeModelBuilder):
+    def __init__(self, filename=None):
+        fallback = os.path.join(ETCWXDIR, 'xml', 'module_tree_menudata.xml')
+        if not filename:
+            filename = getMenudataFile(userRootFile='module_tree.xml',
+                                       newFile='module_tree_menudata.xml',
+                                       fallback=fallback)
+        # TODO: try-except useless?
+        try:
+            MenuTreeModelBuilder.__init__(self, filename)
+        except (ValueError, AttributeError, TypeError):
+            GError(_("Unable to parse user toolboxes XML files. "
+                     "Default toolboxes will be loaded."))
+            MenuTreeModelBuilder.__init__(self, fallback)

Modified: grass/trunk/gui/wxpython/xml/main_menu.xml
===================================================================
--- grass/trunk/gui/wxpython/xml/main_menu.xml	2013-07-17 10:08:51 UTC (rev 57186)
+++ grass/trunk/gui/wxpython/xml/main_menu.xml	2013-07-17 11:17:54 UTC (rev 57187)
@@ -10,7 +10,6 @@
     <subtoolbox name="Imagery"/>
     <subtoolbox name="Volumes"/>
     <subtoolbox name="Database"/>
-    <user-toolboxes-list />
     <subtoolbox name="Help"/>
   </items>
 </toolbox>

Added: grass/trunk/gui/wxpython/xml/module_tree.xml
===================================================================
--- grass/trunk/gui/wxpython/xml/module_tree.xml	                        (rev 0)
+++ grass/trunk/gui/wxpython/xml/module_tree.xml	2013-07-17 11:17:54 UTC (rev 57187)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE toolbox SYSTEM "main_menu.dtd">
+<toolbox name="DefaultModuleTree">
+  <label>Default GRASS GIS module tree</label>
+  <items>
+    <subtoolbox name="Raster"/>
+    <subtoolbox name="Vector"/>
+    <subtoolbox name="Imagery"/>
+    <subtoolbox name="Volumes"/>
+    <subtoolbox name="Database"/>
+    <subtoolbox name="Temporal"/>
+    <user-toolboxes-list/>
+    <addons/>
+  </items>
+</toolbox>
+


Property changes on: grass/trunk/gui/wxpython/xml/module_tree.xml
___________________________________________________________________
Added: svn:mime-type
   + text/xml
Added: svn:eol-style
   + native



More information about the grass-commit mailing list