gmodeler.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. """!
  2. @package gmodeler.py
  3. @brief Graphical modeler to create edit, and manage models
  4. Classes:
  5. - ModelFrame
  6. - ModelCanvas
  7. - ModelAction
  8. - ModelSearchDialog
  9. (C) 2010 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Martin Landa <landa.martin gmail.com>
  13. """
  14. import os
  15. import shlex
  16. import time
  17. import globalvar
  18. if not os.getenv("GRASS_WXBUNDLED"):
  19. globalvar.CheckForWx()
  20. import wx
  21. import wx.lib.ogl as ogl
  22. import menu
  23. import menudata
  24. import toolbars
  25. import menuform
  26. import prompt
  27. from grass.script import core as grass
  28. class ModelFrame(wx.Frame):
  29. def __init__(self, parent, id = wx.ID_ANY, title = _("Graphical modeler (under development)"), **kwargs):
  30. """!Graphical modeler main window
  31. @param parent parent window
  32. @param id window id
  33. @param title window title
  34. @param kwargs wx.Frames' arguments
  35. """
  36. self.parent = parent
  37. self.searchDialog = None # module search dialog
  38. self.actions = list() # list of recoreded actions
  39. wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
  40. self.SetName("Modeler")
  41. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  42. self.menubar = menu.Menu(parent = self, data = menudata.ModelerData())
  43. self.SetMenuBar(self.menubar)
  44. self.toolbar = toolbars.ModelToolbar(parent = self)
  45. self.SetToolBar(self.toolbar)
  46. self.statusbar = self.CreateStatusBar(number = 1)
  47. self.canvas = ModelCanvas(self)
  48. self.canvas.SetBackgroundColour(wx.WHITE)
  49. self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
  50. self._layout()
  51. self.SetMinSize((640, 480))
  52. def _layout(self):
  53. """!Do layout"""
  54. sizer = wx.BoxSizer(wx.VERTICAL)
  55. sizer.Add(item = self.canvas, proportion = 1,
  56. flag = wx.EXPAND)
  57. self.SetAutoLayout(True)
  58. self.SetSizer(sizer)
  59. sizer.Fit(self)
  60. self.Layout()
  61. def OnCloseWindow(self, event):
  62. """!Close window"""
  63. self.Destroy()
  64. def OnModelNew(self, event):
  65. """!Create new model"""
  66. pass
  67. def OnModelOpen(self, event):
  68. """!Load model from file"""
  69. pass
  70. def OnModelSave(self, event):
  71. """!Save model to file"""
  72. pass
  73. def OnModelSaveAs(self, event):
  74. """!Create model to file as"""
  75. pass
  76. def OnAddAction(self, event):
  77. """!Add action to model"""
  78. debug = False
  79. if debug == False:
  80. if self.searchDialog is None:
  81. self.searchDialog = ModelSearchDialog(self)
  82. self.searchDialog.CentreOnParent()
  83. else:
  84. self.searchDialog.Reset()
  85. if self.searchDialog.ShowModal() == wx.ID_CANCEL:
  86. self.searchDialog.Hide()
  87. return
  88. cmd = self.searchDialog.GetCmd()
  89. self.searchDialog.Hide()
  90. else:
  91. cmd = ['r.buffer']
  92. # add action to canvas
  93. width, height = self.canvas.GetSize()
  94. action = ModelAction(self, cmd = cmd, x = width/2, y = height/2)
  95. self.canvas.diagram.AddShape(action)
  96. action.Show(True)
  97. evthandler = ModelEvtHandler(self.statusbar,
  98. self)
  99. evthandler.SetShape(action)
  100. evthandler.SetPreviousHandler(action.GetEventHandler())
  101. action.SetEventHandler(evthandler)
  102. self.actions.append(action)
  103. self.canvas.Refresh()
  104. time.sleep(.1)
  105. # show properties dialog
  106. win = action.GetPropDialog()
  107. if not win:
  108. module = menuform.GUI().ParseCommand(action.GetCmd(string = False),
  109. completed = (self.GetOptData, action, None),
  110. parentframe = self, show = True)
  111. elif not win.IsShown():
  112. win.Show()
  113. if win:
  114. win.Raise()
  115. def OnAddData(self, event):
  116. """!Add data item to model"""
  117. def OnHelp(self, event):
  118. """!Display manual page"""
  119. grass.run_command('g.manual',
  120. entry = 'wxGUI.Modeler')
  121. def GetOptData(self, dcmd, layer, params, propwin):
  122. """!Process action data"""
  123. layer.SetProperties(dcmd, params, propwin)
  124. self.SetStatusText(layer.GetCmd(), 0)
  125. class ModelCanvas(ogl.ShapeCanvas):
  126. """!Canvas where model is drawn"""
  127. def __init__(self, parent):
  128. ogl.OGLInitialize()
  129. ogl.ShapeCanvas.__init__(self, parent)
  130. self.diagram = ogl.Diagram()
  131. self.SetDiagram(self.diagram)
  132. self.diagram.SetCanvas(self)
  133. self.SetScrollbars(20, 20, 1000/20, 1000/20)
  134. class ModelAction(ogl.RectangleShape):
  135. """!Action class (GRASS module)"""
  136. def __init__(self, parent, x, y, cmd = None, width = 100, height = 50):
  137. self.parent = parent
  138. self.cmd = cmd
  139. self.params = None
  140. self.propWin = None
  141. ogl.RectangleShape.__init__(self, width, height)
  142. # self.Draggable(True)
  143. self.SetCanvas(self.parent)
  144. self.SetX(x)
  145. self.SetY(y)
  146. self.SetPen(wx.BLACK_PEN)
  147. self.SetBrush(wx.LIGHT_GREY_BRUSH)
  148. if self.cmd and len(self.cmd) > 0:
  149. self.AddText(self.cmd[0])
  150. else:
  151. self.AddText('<<module>>')
  152. def SetProperties(self, dcmd, params, propwin):
  153. """!Record properties dialog"""
  154. self.cmd = dcmd
  155. self.params = params
  156. self.propWin = propwin
  157. def GetPropDialog(self):
  158. """!Get properties dialog"""
  159. return self.propWin
  160. def GetCmd(self, string = True):
  161. """!Get command"""
  162. if string:
  163. if self.cmd is None:
  164. return ''
  165. else:
  166. return ' '.join(self.cmd)
  167. return self.cmd
  168. class ModelEvtHandler(ogl.ShapeEvtHandler):
  169. """!Model event handler class"""
  170. def __init__(self, log, frame):
  171. ogl.ShapeEvtHandler.__init__(self)
  172. self.log = log
  173. self.frame = frame
  174. def OnLeftClick(self, x, y, keys = 0, attachment = 0):
  175. """!Left mouse button pressed -> update statusbar"""
  176. shape = self.GetShape()
  177. self.log.SetStatusText(shape.GetCmd(), 0)
  178. def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
  179. """!Left mouse button pressed (double-click) -> show properties"""
  180. shape = self.GetShape()
  181. win = shape.GetPropDialog()
  182. if not win:
  183. module = menuform.GUI().ParseCommand(shape.cmd,
  184. completed = (self.frame.GetOptData, shape, None),
  185. parentframe = self.frame, show = True)
  186. elif not win.IsShown():
  187. win.Show()
  188. if win:
  189. win.Raise()
  190. class ModelSearchDialog(wx.Dialog):
  191. def __init__(self, parent, id = wx.ID_ANY, title = _("Find GRASS module"),
  192. style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
  193. """!Graphical modeler module search window
  194. @param parent parent window
  195. @param id window id
  196. @param title window title
  197. @param kwargs wx.Dialogs' arguments
  198. """
  199. self.parent = parent
  200. wx.Dialog.__init__(self, parent = parent, id = id, title = title, **kwargs)
  201. self.SetName("ModelerDialog")
  202. self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO))
  203. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  204. self.searchBy = wx.Choice(parent = self.panel, id = wx.ID_ANY,
  205. choices = [_("description"),
  206. _("keywords")])
  207. self.search = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY,
  208. value = "", size = (-1, 25))
  209. self.cmd_prompt = prompt.GPromptSTC(parent = self)
  210. self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
  211. self.btnOk = wx.Button(self.panel, wx.ID_OK)
  212. self.btnOk.SetDefault()
  213. self._layout()
  214. def _layout(self):
  215. btnSizer = wx.StdDialogButtonSizer()
  216. btnSizer.AddButton(self.btnCancel)
  217. btnSizer.AddButton(self.btnOk)
  218. btnSizer.Realize()
  219. bodyBox = wx.StaticBox(parent=self.panel, id=wx.ID_ANY,
  220. label=" %s " % _("Find GRASS module"))
  221. bodySizer = wx.StaticBoxSizer(bodyBox, wx.VERTICAL)
  222. searchSizer = wx.BoxSizer(wx.HORIZONTAL)
  223. searchSizer.Add(item = self.searchBy,
  224. proportion = 0, flag = wx.LEFT, border = 3)
  225. searchSizer.Add(item = self.search,
  226. proportion = 1, flag = wx.LEFT | wx.EXPAND, border = 3)
  227. bodySizer.Add(item=searchSizer, proportion=0,
  228. flag=wx.EXPAND | wx.ALL, border=1)
  229. bodySizer.Add(item=self.cmd_prompt, proportion=1,
  230. flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3)
  231. mainSizer = wx.BoxSizer(wx.VERTICAL)
  232. mainSizer.Add(item=bodySizer, proportion=1,
  233. flag=wx.EXPAND | wx.ALL, border=5)
  234. mainSizer.Add(item=btnSizer, proportion=0,
  235. flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
  236. self.panel.SetSizer(mainSizer)
  237. mainSizer.Fit(self.panel)
  238. def GetPanel(self):
  239. """!Get dialog panel"""
  240. return self.panel
  241. def GetCmd(self):
  242. """!Get command"""
  243. line = self.cmd_prompt.GetCurLine()[0].strip()
  244. if len(line) == 0:
  245. list()
  246. try:
  247. cmd = shlex.split(str(line))
  248. except UnicodeError:
  249. cmd = shlex.split(utils.EncodeString((line)))
  250. return cmd
  251. def OnOk(self, event):
  252. self.btnOk.SetFocus()
  253. def Reset(self):
  254. """!Reset dialog"""
  255. self.searchBy.SetSelection(0)
  256. self.search.SetValue('')
  257. self.cmd_prompt.OnCmdErase(None)
  258. def main():
  259. app = wx.PySimpleApp()
  260. frame = ModelFrame(parent = None)
  261. # frame.CentreOnScreen()
  262. frame.Show()
  263. app.MainLoop()
  264. if __name__ == "__main__":
  265. main()