menutree.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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. 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(
  51. group="appearance", key="menustyle", subkey="selection"
  52. )
  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(
  76. label="",
  77. description="",
  78. handler="",
  79. command="",
  80. keywords="",
  81. shortcut="",
  82. wxId="",
  83. icon="",
  84. )
  85. self.model.AppendNode(parent=node, label="", data=data)
  86. elif item.tag == "menuitem":
  87. origLabel = _(item.find("label").text)
  88. handler = item.find("handler").text
  89. desc = item.find("help") # optional
  90. gcmd = item.find("command") # optional
  91. keywords = item.find("keywords") # optional
  92. shortcut = item.find("shortcut") # optional
  93. wxId = item.find("id") # optional
  94. icon = item.find("icon") # optional
  95. if gcmd is not None:
  96. gcmd = gcmd.text
  97. else:
  98. gcmd = ""
  99. if desc.text:
  100. desc = _(desc.text)
  101. else:
  102. desc = ""
  103. if keywords is None or keywords.text is None:
  104. keywords = ""
  105. else:
  106. keywords = keywords.text
  107. if shortcut is not None:
  108. shortcut = shortcut.text
  109. else:
  110. shortcut = ""
  111. if wxId is not None:
  112. wxId = eval("wx." + wxId.text)
  113. else:
  114. wxId = wx.ID_ANY
  115. if icon is not None:
  116. icon = icon.text
  117. else:
  118. icon = ""
  119. label = origLabel
  120. if gcmd:
  121. if self.menustyle == 1:
  122. label += " [" + gcmd + "]"
  123. elif self.menustyle == 2:
  124. label = " [" + gcmd + "]"
  125. data = dict(
  126. label=origLabel,
  127. description=desc,
  128. handler=handler,
  129. command=gcmd,
  130. keywords=keywords,
  131. shortcut=shortcut,
  132. wxId=wxId,
  133. icon=icon,
  134. )
  135. self.model.AppendNode(parent=node, label=label, data=data)
  136. elif item.tag == "menu":
  137. self._createMenu(item, node)
  138. else:
  139. raise ValueError(_("Unknow tag %s") % item.tag)
  140. def GetModel(self, separators=False):
  141. """Returns copy of model with or without separators
  142. (for menu or for search tree).
  143. """
  144. if separators:
  145. return copy.deepcopy(self.model)
  146. else:
  147. model = copy.deepcopy(self.model)
  148. removeSeparators(model)
  149. return model
  150. def PrintTree(self, fh):
  151. for child in self.model.root.children:
  152. printTree(node=child, fh=fh)
  153. def PrintStrings(self, fh):
  154. """Print menu strings to file (used for localization)
  155. :param fh: file descriptor
  156. """
  157. className = self.__class__.__name__
  158. fh.write("menustrings_%s = [\n" % className)
  159. for child in self.model.root.children:
  160. printStrings(child, fh)
  161. fh.write(" '']\n")
  162. def PrintCommands(self, fh):
  163. printCommands(self.model.root, fh, itemSep=" | ", menuSep=" > ")
  164. def removeSeparators(model, node=None):
  165. if not node:
  166. node = model.root
  167. if node.label or node is model.root:
  168. for child in reversed(node.children):
  169. removeSeparators(model, child)
  170. else:
  171. model.RemoveNode(node)
  172. def printTree(node, fh, indent=0):
  173. if not node.label:
  174. return
  175. text = "%s- %s\n" % (" " * indent, node.label.replace("&", ""))
  176. fh.write(text)
  177. for child in node.children:
  178. printTree(node=child, fh=fh, indent=indent + 2)
  179. def printStrings(node, fh):
  180. # node.label - with module in brackets
  181. # node.data['label'] - without module in brackets
  182. if node.label and not node.data:
  183. fh.write(" _(%r),\n" % str(node.label))
  184. if node.data:
  185. if "label" in node.data and node.data["label"]:
  186. fh.write(" _(%r),\n" % str(node.data["label"]))
  187. if "description" in node.data and node.data["description"]:
  188. fh.write(" _(%r),\n" % str(node.data["description"]))
  189. for child in node.children:
  190. printStrings(node=child, fh=fh)
  191. def printCommands(node, fh, itemSep, menuSep):
  192. def collectParents(node, parents):
  193. parent = node.parent
  194. if parent.parent:
  195. parents.insert(0, node.parent)
  196. collectParents(node.parent, parents)
  197. data = node.data
  198. if data and "command" in data and data["command"]:
  199. fh.write("%s%s" % (data["command"], itemSep))
  200. parents = [node]
  201. collectParents(node, parents)
  202. labels = [parent.label.replace("&", "") for parent in parents]
  203. fh.write(menuSep.join(labels))
  204. fh.write("\n")
  205. for child in node.children:
  206. printCommands(child, fh, itemSep, menuSep)
  207. if __name__ == "__main__":
  208. action = "strings"
  209. menu = "manager"
  210. for arg in sys.argv:
  211. if arg in ("strings", "tree", "commands", "dump"):
  212. action = arg
  213. elif arg in ("manager", "module_tree", "modeler", "psmap"):
  214. menu = arg
  215. # FIXME: cross-dependencies
  216. if menu == "manager":
  217. from lmgr.menudata import LayerManagerMenuData
  218. from core.globalvar import WXGUIDIR
  219. filename = os.path.join(WXGUIDIR, "xml", "menudata.xml")
  220. menudata = LayerManagerMenuData(filename)
  221. # FIXME: since module descriptions are used again we have now the third
  222. # copy of the same string (one is in modules)
  223. elif menu == "module_tree":
  224. from lmgr.menudata import LayerManagerModuleTree
  225. from core.globalvar import WXGUIDIR
  226. filename = os.path.join(WXGUIDIR, "xml", "module_tree_menudata.xml")
  227. menudata = LayerManagerModuleTree(filename)
  228. elif menu == "modeler":
  229. from gmodeler.menudata import ModelerMenuData
  230. menudata = ModelerMenuData()
  231. elif menu == "psmap":
  232. from psmap.menudata import PsMapMenuData
  233. menudata = PsMapMenuData()
  234. else:
  235. import grass.script.core as gscore
  236. gscore.fatal("Unknown value for parameter menu: " % menu)
  237. if action == "strings":
  238. menudata.PrintStrings(sys.stdout)
  239. elif action == "tree":
  240. menudata.PrintTree(sys.stdout)
  241. elif action == "commands":
  242. menudata.PrintCommands(sys.stdout)
  243. elif action == "dump":
  244. print(menudata.model)
  245. else:
  246. import grass.script.core as gscore
  247. gscore.fatal("Unknown value for parameter action: " % action)
  248. sys.exit(0)