menutree.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. """
  2. @package core.menutree
  3. @brief Creates tree structure for wxGUI menus (former menudata.py)
  4. Classes:
  5. - menutree::MenuTreeModelBuilder
  6. Usage:
  7. @code
  8. python menutree.py [action] [menu]
  9. @endcode
  10. where <i>action</i>:
  11. - strings (default, used for translations)
  12. - tree (simple tree structure)
  13. - commands (command names and their place in tree)
  14. - dump (tree structure with stored data)
  15. and <i>menu</i>:
  16. - manager (Main menu in Layer Manager)
  17. - module_tree (Module tree in Layer Manager)
  18. - modeler (Graphical Modeler)
  19. - psmap (Cartographic Composer)
  20. (C) 2013 by the GRASS Development Team
  21. This program is free software under the GNU General Public License
  22. (>=v2). Read the file COPYING that comes with GRASS for details.
  23. @author Glynn Clements (menudata.py)
  24. @author Martin Landa <landa.martin gmail.com> (menudata.py)
  25. @author Anna Petrasova <kratochanna gmail.com>
  26. """
  27. from __future__ import print_function
  28. import os
  29. import sys
  30. import copy
  31. try:
  32. import xml.etree.ElementTree as etree
  33. except ImportError:
  34. import elementtree.ElementTree as etree # Python <= 2.4
  35. import wx
  36. from core.treemodel import TreeModel, ModuleNode
  37. from core.settings import UserSettings
  38. from core.toolboxes import expandAddons as expAddons
  39. from core.toolboxes import getMessages as getToolboxMessages
  40. from core.toolboxes import clearMessages as clearToolboxMessages
  41. from core.gcmd import GError
  42. from core.utils import _
  43. if not os.getenv("GISBASE"):
  44. sys.exit("GRASS is not running. Exiting...")
  45. # TODO: change the system to remove strange derived classes
  46. class MenuTreeModelBuilder:
  47. """Abstract menu data class"""
  48. # TODO: message_handler=GError is just for backwards compatibility
  49. # message_handler=GError should be replaced by None
  50. def __init__(self, filename, expandAddons=True, message_handler=GError):
  51. self.menustyle = UserSettings.Get(group='appearance',
  52. key='menustyle',
  53. subkey='selection')
  54. xmlTree = etree.parse(filename)
  55. if expandAddons:
  56. expAddons(xmlTree)
  57. for message in getToolboxMessages():
  58. message_handler(message)
  59. clearToolboxMessages()
  60. self.model = TreeModel(ModuleNode)
  61. self._createModel(xmlTree)
  62. def _createModel(self, xmlTree):
  63. root = xmlTree.getroot()
  64. menubar = root.findall('menubar')[0]
  65. menus = menubar.findall('menu')
  66. for m in menus:
  67. self._createMenu(m, self.model.root)
  68. def _createMenu(self, menu, node):
  69. label = _(menu.find('label').text)
  70. items = menu.find('items')
  71. node = self.model.AppendNode(parent=node, label=label)
  72. for item in items:
  73. self._createItem(item, node)
  74. def _createItem(self, item, node):
  75. if item.tag == 'separator':
  76. data = dict(label='', description='', handler='',
  77. command='', keywords='', shortcut='', wxId='', icon='')
  78. self.model.AppendNode(parent=node, label='', data=data)
  79. elif item.tag == 'menuitem':
  80. origLabel = _(item.find('label').text)
  81. handler = item.find('handler').text
  82. desc = item.find('help') # optional
  83. gcmd = item.find('command') # optional
  84. keywords = item.find('keywords') # optional
  85. shortcut = item.find('shortcut') # optional
  86. wxId = item.find('id') # optional
  87. icon = item.find('icon') # optional
  88. if gcmd is not None:
  89. gcmd = gcmd.text
  90. else:
  91. gcmd = ""
  92. if desc.text:
  93. desc = _(desc.text)
  94. else:
  95. desc = ""
  96. if keywords is None or keywords.text is None:
  97. keywords = ""
  98. else:
  99. keywords = keywords.text
  100. if shortcut is not None:
  101. shortcut = shortcut.text
  102. else:
  103. shortcut = ""
  104. if wxId is not None:
  105. wxId = eval('wx.' + wxId.text)
  106. else:
  107. wxId = wx.ID_ANY
  108. if icon is not None:
  109. icon = icon.text
  110. else:
  111. icon = ''
  112. label = origLabel
  113. if gcmd:
  114. if self.menustyle == 1:
  115. label += ' [' + gcmd + ']'
  116. elif self.menustyle == 2:
  117. label = ' [' + gcmd + ']'
  118. data = dict(
  119. label=origLabel,
  120. description=desc,
  121. handler=handler,
  122. command=gcmd,
  123. keywords=keywords,
  124. shortcut=shortcut,
  125. wxId=wxId,
  126. icon=icon)
  127. self.model.AppendNode(parent=node, label=label, data=data)
  128. elif item.tag == 'menu':
  129. self._createMenu(item, node)
  130. else:
  131. raise ValueError(_("Unknow tag %s") % item.tag)
  132. def GetModel(self, separators=False):
  133. """Returns copy of model with or without separators
  134. (for menu or for search tree).
  135. """
  136. if separators:
  137. return copy.deepcopy(self.model)
  138. else:
  139. model = copy.deepcopy(self.model)
  140. removeSeparators(model)
  141. return model
  142. def PrintTree(self, fh):
  143. for child in self.model.root.children:
  144. printTree(node=child, fh=fh)
  145. def PrintStrings(self, fh):
  146. """Print menu strings to file (used for localization)
  147. :param fh: file descriptor
  148. """
  149. className = str(self.__class__).split('.', 1)[1]
  150. fh.write('menustrings_%s = [\n' % className)
  151. for child in self.model.root.children:
  152. printStrings(child, fh)
  153. fh.write(' \'\']\n')
  154. def PrintCommands(self, fh):
  155. printCommands(self.model.root, fh, itemSep=' | ', menuSep=' > ')
  156. def removeSeparators(model, node=None):
  157. if not node:
  158. node = model.root
  159. if node.label:
  160. for child in reversed(node.children):
  161. removeSeparators(model, child)
  162. else:
  163. model.RemoveNode(node)
  164. def printTree(node, fh, indent=0):
  165. if not node.label:
  166. return
  167. text = '%s- %s\n' % (' ' * indent, node.label.replace('&', ''))
  168. fh.write(text)
  169. for child in node.children:
  170. printTree(node=child, fh=fh, indent=indent + 2)
  171. def printStrings(node, fh):
  172. # node.label - with module in brackets
  173. # node.data['label'] - without module in brackets
  174. if node.label and not node.data:
  175. fh.write(' _(%r),\n' % str(node.label))
  176. if node.data:
  177. if 'label' in node.data and node.data['label']:
  178. fh.write(' _(%r),\n' % str(node.data['label']))
  179. if 'description' in node.data and node.data['description']:
  180. fh.write(' _(%r),\n' % str(node.data['description']))
  181. for child in node.children:
  182. printStrings(node=child, fh=fh)
  183. def printCommands(node, fh, itemSep, menuSep):
  184. def collectParents(node, parents):
  185. parent = node.parent
  186. if parent.parent:
  187. parents.insert(0, node.parent)
  188. collectParents(node.parent, parents)
  189. data = node.data
  190. if data and 'command' in data and data['command']:
  191. fh.write('%s%s' % (data['command'], itemSep))
  192. parents = [node]
  193. collectParents(node, parents)
  194. labels = [parent.label.replace('&', '') for parent in parents]
  195. fh.write(menuSep.join(labels))
  196. fh.write('\n')
  197. for child in node.children:
  198. printCommands(child, fh, itemSep, menuSep)
  199. if __name__ == "__main__":
  200. action = 'strings'
  201. menu = 'manager'
  202. for arg in sys.argv:
  203. if arg in ('strings', 'tree', 'commands', 'dump'):
  204. action = arg
  205. elif arg in ('manager', 'module_tree', 'modeler', 'psmap'):
  206. menu = arg
  207. # FIXME: cross-dependencies
  208. if menu == 'manager':
  209. from lmgr.menudata import LayerManagerMenuData
  210. from core.globalvar import WXGUIDIR
  211. filename = os.path.join(WXGUIDIR, 'xml', 'menudata.xml')
  212. menudata = LayerManagerMenuData(filename)
  213. # FIXME: since module descriptions are used again we have now the third
  214. # copy of the same string (one is in modules)
  215. elif menu == 'module_tree':
  216. from lmgr.menudata import LayerManagerModuleTree
  217. from core.globalvar import WXGUIDIR
  218. filename = os.path.join(WXGUIDIR, 'xml', 'module_tree_menudata.xml')
  219. menudata = LayerManagerModuleTree(filename)
  220. elif menu == 'modeler':
  221. from gmodeler.menudata import ModelerMenuData
  222. menudata = ModelerMenuData()
  223. elif menu == 'psmap':
  224. from psmap.menudata import PsMapMenuData
  225. menudata = PsMapMenuData()
  226. else:
  227. import grass.script.core as gscore
  228. gscore.fatal("Unknown value for parameter menu: " % menu)
  229. if action == 'strings':
  230. menudata.PrintStrings(sys.stdout)
  231. elif action == 'tree':
  232. menudata.PrintTree(sys.stdout)
  233. elif action == 'commands':
  234. menudata.PrintCommands(sys.stdout)
  235. elif action == 'dump':
  236. print(menudata.model)
  237. else:
  238. import grass.script.core as gscore
  239. gscore.fatal("Unknown value for parameter action: " % action)
  240. sys.exit(0)