menutree.py 9.1 KB

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