Browse Source

wxGUI/toolboxes: doctests, deleting of special element and documentation

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@56086 15284696-431f-4ddb-bdfa-cd5b030d7da7
Vaclav Petras 12 years ago
parent
commit
a89efca39f
2 changed files with 191 additions and 11 deletions
  1. 159 9
      gui/wxpython/core/toolboxes.py
  2. 32 2
      gui/wxpython/wxguitoolboxes.dox

+ 159 - 9
gui/wxpython/core/toolboxes.py

@@ -42,6 +42,7 @@ import grass.script.task as gtask
 from grass.script.core import ScriptError
 from grass.script.core import ScriptError
 
 
 
 
+# this could be placed to functions
 mainMenuFile = os.path.join(ETCWXDIR, 'xml', 'main_menu.xml')
 mainMenuFile = os.path.join(ETCWXDIR, 'xml', 'main_menu.xml')
 toolboxesFile = os.path.join(ETCWXDIR, 'xml', 'toolboxes.xml')
 toolboxesFile = os.path.join(ETCWXDIR, 'xml', 'toolboxes.xml')
 wxguiItemsFile = os.path.join(ETCWXDIR, 'xml', 'wxgui_items.xml')
 wxguiItemsFile = os.path.join(ETCWXDIR, 'xml', 'wxgui_items.xml')
@@ -200,6 +201,55 @@ def _expandToolboxes(node, toolboxes):
 
 
     @param node tree node where to look for subtoolboxes to be expanded
     @param node tree node where to look for subtoolboxes to be expanded
     @param toolboxes tree of toolboxes to be used for expansion
     @param toolboxes tree of toolboxes to be used for expansion
+    
+    >>> menu = etree.fromstring('''
+    ... <toolbox name="Raster">
+    ...   <label>&amp;Raster</label>
+    ...   <items>
+    ...     <module-item name="r.mask"/>
+    ...     <wxgui-item name="RasterMapCalculator"/>
+    ...     <subtoolbox name="NeighborhoodAnalysis"/>
+    ...     <subtoolbox name="OverlayRasters"/>
+    ...   </items>
+    ... </toolbox>''')
+    >>> toolboxes = etree.fromstring('''
+    ... <toolboxes>
+    ...   <toolbox name="NeighborhoodAnalysis">
+    ...     <label>Neighborhood analysis</label>
+    ...     <items>
+    ...       <module-item name="r.neighbors"/>
+    ...       <module-item name="v.neighbors"/>
+    ...     </items>
+    ...   </toolbox>
+    ...   <toolbox name="OverlayRasters">
+    ...     <label>Overlay rasters</label>
+    ...     <items>
+    ...       <module-item name="r.cross"/>
+    ...     </items>
+    ...   </toolbox>
+    ... </toolboxes>''')
+    >>> _expandToolboxes(menu, toolboxes)
+    >>> print etree.tostring(menu)
+    <toolbox name="Raster">
+      <label>&amp;Raster</label>
+      <items>
+        <module-item name="r.mask" />
+        <wxgui-item name="RasterMapCalculator" />
+        <toolbox name="NeighborhoodAnalysis">
+        <label>Neighborhood analysis</label>
+        <items>
+          <module-item name="r.neighbors" />
+          <module-item name="v.neighbors" />
+        </items>
+      </toolbox>
+      <toolbox name="OverlayRasters">
+        <label>Overlay rasters</label>
+        <items>
+          <module-item name="r.cross" />
+        </items>
+      </toolbox>
+    </items>
+    </toolbox>
     """
     """
     nodes = node.findall('.//toolbox')
     nodes = node.findall('.//toolbox')
     if node.tag == 'toolbox':  # root
     if node.tag == 'toolbox':  # root
@@ -233,30 +283,50 @@ def _expandUserToolboxesItem(node, toolboxes):
     """!Expand tag 'user-toolboxes-list'.
     """!Expand tag 'user-toolboxes-list'.
 
 
     Include all user toolboxes.
     Include all user toolboxes.
+
+    >>> tree = etree.fromstring('<toolbox><items><user-toolboxes-list/></items></toolbox>')
+    >>> 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>'
     """
     """
     tboxes = toolboxes.findall('.//toolbox')
     tboxes = toolboxes.findall('.//toolbox')
 
 
     for n in node.findall('./items/user-toolboxes-list'):
     for n in node.findall('./items/user-toolboxes-list'):
         items = node.find('./items')
         items = node.find('./items')
         idx = items.getchildren().index(n)
         idx = items.getchildren().index(n)
-        el = etree.Element('toolbox', attrib={'name': 'dummy'})
+        el = etree.Element('toolbox', attrib={'name': 'GeneratedUserToolboxesList'})
         items.insert(idx, el)
         items.insert(idx, el)
         label = etree.SubElement(el, tag='label')
         label = etree.SubElement(el, tag='label')
         label.text = _("Toolboxes")
         label.text = _("Toolboxes")
         it = etree.SubElement(el, tag='items')
         it = etree.SubElement(el, tag='items')
         for toolbox in tboxes:
         for toolbox in tboxes:
             it.append(copy.deepcopy(toolbox))
             it.append(copy.deepcopy(toolbox))
+        items.remove(n)
 
 
 
 
 def _removeUserToolboxesItem(root):
 def _removeUserToolboxesItem(root):
-    """!Removes tag 'user-toolboxes-list' if there are no user toolboxes."""
+    """!Removes tag 'user-toolboxes-list' if there are no user toolboxes.
+
+    >>> tree = etree.fromstring('<toolbox><items><user-toolboxes-list/></items></toolbox>')
+    >>> _removeUserToolboxesItem(tree)
+    >>> etree.tostring(tree)
+    '<toolbox><items /></toolbox>'
+    """
     for n in root.findall('./items/user-toolboxes-list'):
     for n in root.findall('./items/user-toolboxes-list'):
         items = root.find('./items')
         items = root.find('./items')
         items.remove(n)
         items.remove(n)
 
 
 
 
 def _expandItems(node, items, itemTag):
 def _expandItems(node, items, itemTag):
-    """!Expand items from file"""
+    """!Expand items from file
+
+    >>> tree = etree.fromstring('<items><module-item name="g.region"></module-item></items>')
+    >>> items = etree.fromstring('<module-items><module-item name="g.region"><module>g.region</module><description>GRASS region management</description></module-item></module-items>')
+    >>> _expandItems(tree, items, 'module-item')
+    >>> etree.tostring(tree)
+    '<items><module-item name="g.region"><module>g.region</module><description>GRASS region management</description></module-item></items>'
+    """
     for moduleItem in node.findall('.//' + itemTag):
     for moduleItem in node.findall('.//' + itemTag):
         itemName = moduleItem.get('name')
         itemName = moduleItem.get('name')
         if has_xpath:
         if has_xpath:
@@ -280,7 +350,15 @@ def _expandItems(node, items, itemTag):
 
 
 def _expandRuntimeModules(node):
 def _expandRuntimeModules(node):
     """!Add information to modules (desc, keywords)
     """!Add information to modules (desc, keywords)
-    by running them with --interface-description."""
+    by running them with --interface-description.
+
+    >>> tree = etree.fromstring('<items>'
+    ...                         '<module-item name="g.region"></module-item>'
+    ...                         '</items>')
+    >>> _expandRuntimeModules(tree)
+    >>> etree.tostring(tree)
+    '<items><module-item name="g.region"><module>g.region</module><description>Manages the boundary definitions for the geographic region.</description><keywords>general,settings</keywords></module-item></items>'
+    """
     modules = node.findall('.//module-item')
     modules = node.findall('.//module-item')
     for module in modules:
     for module in modules:
         name = module.get('name')
         name = module.get('name')
@@ -299,7 +377,11 @@ def _expandRuntimeModules(node):
 def _escapeXML(text):
 def _escapeXML(text):
     """!Helper function for correct escaping characters for XML.
     """!Helper function for correct escaping characters for XML.
 
 
-    Duplicate function in core/toolboxes.
+    Duplicate function in core/toolboxes and probably also in man compilation
+    and some existing Python package.
+
+    >>> _escapeXML('<>&')
+    '&amp;lt;&gt;&amp;'
     """
     """
     return text.replace('<', '&lt;').replace("&", '&amp;').replace(">", '&gt;')
     return text.replace('<', '&lt;').replace("&", '&amp;').replace(">", '&gt;')
 
 
@@ -334,20 +416,40 @@ def _addHandlers(node):
 
 
 
 
 def _convertTag(node, old, new):
 def _convertTag(node, old, new):
-    """!Converts tag name."""
+    """!Converts tag name.
+    
+    >>> tree = etree.fromstring('<toolboxes><toolbox><items><module-item/></items></toolbox></toolboxes>')
+    >>> _convertTag(tree, 'toolbox', 'menu')
+    >>> _convertTag(tree, 'module-item', 'menuitem')
+    >>> etree.tostring(tree)
+    '<toolboxes><menu><items><menuitem /></items></menu></toolboxes>'
+    """
     for n in node.findall('.//%s' % old):
     for n in node.findall('.//%s' % old):
         n.tag = new
         n.tag = new
 
 
 
 
 def _convertTagAndRemoveAttrib(node, old, new):
 def _convertTagAndRemoveAttrib(node, old, new):
-    "Converts tag name and removes attributes."
+    """Converts tag name and removes attributes.
+
+    >>> tree = etree.fromstring('<toolboxes><toolbox name="Raster"><items><module-item name="g.region"/></items></toolbox></toolboxes>')
+    >>> _convertTagAndRemoveAttrib(tree, 'toolbox', 'menu')
+    >>> _convertTagAndRemoveAttrib(tree, 'module-item', 'menuitem')
+    >>> etree.tostring(tree)
+    '<toolboxes><menu><items><menuitem /></items></menu></toolboxes>'
+    """
     for n in node.findall('.//%s' % old):
     for n in node.findall('.//%s' % old):
         n.tag = new
         n.tag = new
         n.attrib = {}
         n.attrib = {}
 
 
 
 
 def _convertTree(root):
 def _convertTree(root):
-    """!Converts tree to be the form readable by core/menutree.py."""
+    """!Converts tree to be the form readable by core/menutree.py.
+
+    >>> tree = etree.fromstring('<toolbox name="MainMenu"><label>Main menu</label><items><toolbox><label>Raster</label><items><module-item name="g.region"><module>g.region</module></module-item></items></toolbox></items></toolbox>')
+    >>> _convertTree(tree)
+    >>> etree.tostring(tree)
+    '<menudata><menubar><menu><label>Raster</label><items><menuitem><command>g.region</command></menuitem></items></menu></menubar></menudata>'
+    """
     root.attrib = {}
     root.attrib = {}
     label = root.find('label')
     label = root.find('label')
     root.remove(label)
     root.remove(label)
@@ -365,7 +467,10 @@ def _convertTree(root):
 
 
 
 
 def _getXMLString(root):
 def _getXMLString(root):
-    """!Adds comment (about aotogenerated file) to XML.
+    """!Converts XML tree to string
+
+    Since it is usually requier, this function adds a comment (about
+    autogenerated file) to XML file.
 
 
     @return XML as string
     @return XML as string
     """
     """
@@ -375,7 +480,49 @@ def _getXMLString(root):
                        "<!--This is an auto-generated file-->\n")
                        "<!--This is an auto-generated file-->\n")
 
 
 
 
+def do_doctest_gettext_workaround():
+    """Setups environment for doing a doctest with gettext usage.
+
+    When using gettext with dynamically defined underscore function
+    (`_("For translation")`), doctest does not work properly. One option is to
+    use `import as` instead of dynamically defined underscore function but this
+    would require change all modules which are used by tested module. This
+    should be considered for the future. The second option is to define dummy
+    underscore function and one other function which creates the right
+    environment to satisfy all. This is done by this function.
+    """
+    def new_displayhook(string):
+        """A replacement for default `sys.displayhook`"""
+        if string is not None:
+            sys.stdout.write("%r\n" % (string,))
+
+    def new_translator(string):
+        """A fake gettext underscore function."""
+        return string
+
+    sys.displayhook = new_displayhook
+
+    import __builtin__
+    __builtin__._ = new_translator
+
+
+def test():
+    """Tests the module using doctest
+
+    @return a number of failed tests
+    """
+    import doctest
+
+    do_doctest_gettext_workaround()
+
+    return doctest.testmod().failed
+
+
 def main():
 def main():
+    """Converts the toolboxes files on standard paths to the menudata file
+
+    File is written to the standard output.
+    """
     tree = toolboxes2menudata(userDefined=False)
     tree = toolboxes2menudata(userDefined=False)
     root = tree.getroot()
     root = tree.getroot()
     sys.stdout.write(_getXMLString(root))
     sys.stdout.write(_getXMLString(root))
@@ -384,4 +531,7 @@ def main():
 
 
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
+    if len(sys.argv) > 1:
+        if sys.argv[1] == 'doctest':
+            sys.exit(test())
     sys.exit(main())
     sys.exit(main())

+ 32 - 2
gui/wxpython/wxguitoolboxes.dox

@@ -23,7 +23,7 @@ Note that in XML context, the term <i>tag</i> partially overlaps with the term
 content of these objects. Tag emphasizes the markup and the name of the element.
 content of these objects. Tag emphasizes the markup and the name of the element.
 
 
 
 
-\section filesOverview Files overview
+\section toolboxesFilesOverview Files overview
 
 
 \subsection toolboxesFiles Files related to toolboxes
 \subsection toolboxesFiles Files related to toolboxes
 
 
@@ -90,7 +90,7 @@ menudata -> menustrings;
 \enddot
 \enddot
 
 
 
 
-\subsection otherFiles Other files
+\subsection toolboxesOtherFiles Other files
 
 
 GRASS source code contains these XML files:
 GRASS source code contains these XML files:
 <ul>
 <ul>
@@ -101,6 +101,36 @@ In GRASS distribution these XML files are in the \c etc/gui/wxpython/xml
 directory.
 directory.
 
 
 
 
+\section toolboxesGeneration Generation of files and menu
+
+As noted in the section \ref toolboxesFilesOverview, there are files in the
+GRASS distribution and in the user home directory (particularly in
+\c ".grass7/tooboxes" subdirectory).
+
+When user doesn't have any \c toolboxes.xml or \c main_menu.xml files in the
+home directory, file \c menudata.xml included in the distribution is used to
+build a menu.
+
+When \c toolboxes.xml or \c main_menu.xml file (in user home directory) is newer
+than \c menudata.xml in user home directory or \c menudata.xml does not exists
+in user home directory, the \c menudata.xml is generated when GUI starts.
+
+When \c menudata.xml in user home directory is fresh enough,
+it is used to create a menu.
+
+When \c toolboxes.xml or \c main_menu.xml file is not in user home directory
+but \c menudata.xml is, the file is re-generated (each time the GUI starts).
+So, if you just have your own \c main_menu.xml, it is better to create also
+a \c toolboxes.xml file with no toolboxes (you may want to remove
+\c "<user-toolboxes-list>" tag from your \c main_menu.xml file because it will
+be no longer ignored). Similarly, if you have only the \c toolboxes.xml file it
+is better to copy the \c main_menu.xml file from distribution into your home
+directory.
+
+When reading the main_menu file, user toolboxes are expanded first and then
+toolboxes from distribution are expanded.
+
+
 \section toolboxesFile Toolboxes file
 \section toolboxesFile Toolboxes file
 
 
 This file contains definition of toolboxes. A toolbox contains references
 This file contains definition of toolboxes. A toolbox contains references