2 @package gmodeler.frame
4 @brief wxGUI Graphical Modeler for creating, editing, and managing models
9 - frame::ModelEvtHandler
10 - frame::VariablePanel
14 (C) 2010-2012 by the GRASS Development Team
16 This program is free software under the GNU General Public License
17 (>=v2). Read the file COPYING that comes with GRASS for details.
19 @author Martin Landa <landa.martin gmail.com>
32 if __name__ ==
"__main__":
33 sys.path.append(os.path.join(os.getenv(
'GISBASE'),
'etc',
'wxpython'))
36 from wx.lib
import ogl
37 import wx.lib.flatnotebook
as FN
39 from core
import globalvar
43 from core.gcmd import GMessage, GException, GWarning, GError, RunCommand
50 from gui_core.forms
import GUI
60 def __init__(self, parent, id = wx.ID_ANY,
61 title = _(
"GRASS GIS Graphical Modeler (experimental prototype)"), **kwargs):
62 """!Graphical modeler main window
64 @param parent parent window
66 @param title window title
68 @param kwargs wx.Frames' arguments
78 "default" : wx.StockCursor(wx.CURSOR_ARROW),
79 "cross" : wx.StockCursor(wx.CURSOR_CROSS),
82 wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs)
83 self.SetName(
"Modeler")
84 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR,
'grass.ico'), wx.BITMAP_TYPE_ICO))
86 self.
menubar = Menu(parent = self, data = ModelerData())
90 self.
toolbar = ModelerToolbar(parent = self)
96 style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
97 FN.FNB_NO_NAV_BUTTONS | FN.FNB_NO_X_BUTTON)
100 self.canvas.SetBackgroundColour(wx.WHITE)
101 self.canvas.SetCursor(self.
cursors[
"default"])
113 self.notebook.AddPage(page = self.
canvas, text=_(
'Model'), name =
'model')
114 self.notebook.AddPage(page = self.
itemPanel, text=_(
'Items'), name =
'items')
115 self.notebook.AddPage(page = self.
variablePanel, text=_(
'Variables'), name =
'variables')
116 self.notebook.AddPage(page = self.
pythonPanel, text=_(
'Python editor'), name =
'python')
117 self.notebook.AddPage(page = self.
goutput, text=_(
'Command output'), name =
'output')
118 wx.CallAfter(self.notebook.SetSelectionByName,
'model')
122 self.Bind(wx.EVT_SIZE, self.
OnSize)
123 self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.
OnPageChanged)
126 self.SetMinSize((640, 300))
127 self.SetSize((800, 600))
131 self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
135 sizer = wx.BoxSizer(wx.VERTICAL)
137 sizer.Add(item = self.
notebook, proportion = 1,
140 self.SetAutoLayout(
True)
146 def _addEvent(self, item):
147 """!Add event to item"""
150 evthandler.SetShape(item)
151 evthandler.SetPreviousHandler(item.GetEventHandler())
152 item.SetEventHandler(evthandler)
154 def _randomShift(self):
155 """!Returns random value to shift layout"""
167 """!Update window title"""
179 """!Page in notebook changed"""
180 page = event.GetSelection()
181 if page == self.notebook.GetPageIndexByName(
'python'):
182 if self.pythonPanel.IsEmpty():
183 self.pythonPanel.RefreshScript()
185 if self.pythonPanel.IsModified():
186 self.SetStatusText(_(
'Python script contains local modifications'), 0)
188 self.SetStatusText(_(
'Python script is up-to-date'), 0)
193 """!Switch to variables page"""
194 self.notebook.SetSelectionByName(
'variables')
202 """!Refresh canvas"""
203 self.SetStatusText(_(
"Redrawing model..."), 0)
205 self.SetStatusText(
"", 0)
210 action = self.
GetModel().GetItems()[event.pid]
211 if hasattr(action,
"task"):
212 action.Update(running =
True)
217 """!Prepare for running command"""
218 if not event.userData:
221 event.onPrepare(item = event.userData[
'item'],
222 params = event.userData[
'params'])
225 """!Command done (or aborted)"""
227 action = self.
GetModel().GetItems()[event.pid]
228 if hasattr(action,
"task"):
229 action.Update(running =
True)
236 UserSettings.Get(group=
'manager', key=
'askOnQuit', subkey=
'enabled'):
238 message = _(
"Do you want to save changes in the model?")
240 message = _(
"Do you want to store current model settings "
244 dlg = wx.MessageDialog(self,
246 caption=_(
"Quit Graphical Modeler"),
247 style = wx.YES_NO | wx.YES_DEFAULT |
248 wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE)
249 ret = dlg.ShowModal()
252 self.OnWorkspaceSaveAs()
255 elif ret == wx.ID_CANCEL:
263 """Window resized, save to the model"""
268 """!Open preferences dialog"""
269 dlg = PreferencesDialog(parent = self)
273 self.canvas.Refresh()
277 if self.
parent and self.parent.GetName() ==
'LayerManager':
278 log = self.parent.GetLogWindow()
279 log.RunCmd([
'g.manual',
280 'entry=wxGUI.Modeler'])
284 entry =
'wxGUI.Modeler')
287 """!Model properties dialog"""
288 dlg = PropertiesDialog(parent = self)
290 properties = self.model.GetProperties()
292 if dlg.ShowModal() == wx.ID_OK:
294 for key, value
in dlg.GetValues().iteritems():
295 properties[key] = value
296 for action
in self.model.GetItems(objType = ModelAction):
297 action.GetTask().set_flag(
'overwrite', properties[
'overwrite'])
302 """!Delete intermediate data"""
303 rast, vect, rast3d, msg = self.model.GetIntermediateData()
305 if not rast
and not vect
and not rast3d:
306 GMessage(parent = self,
307 message = _(
'No intermediate data to delete.'))
310 dlg = wx.MessageDialog(parent = self,
311 message= _(
"Do you want to permanently delete data?%s" % msg),
312 caption=_(
"Delete intermediate data?"),
313 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
315 ret = dlg.ShowModal()
320 self.goutput.RunCmd([
'g.remove',
'rast=%s' %
','.join(rast)])
322 self.goutput.RunCmd([
'g.remove',
'rast3d=%s' %
','.join(rast3d)])
324 self.goutput.RunCmd([
'g.remove',
'vect=%s' %
','.join(vect)])
326 self.SetStatusText(_(
"%d maps deleted from current mapset") % \
327 int(len(rast) + len(rast3d) + len(vect)))
333 """!Create new model"""
334 Debug.msg(4,
"ModelFrame.OnModelNew():")
340 (self.model.GetNumItems() > 0
or len(self.model.GetData()) > 0):
341 dlg = wx.MessageDialog(self, message=_(
"Current model is not empty. "
342 "Do you want to store current settings "
344 caption=_(
"Create new model?"),
345 style=wx.YES_NO | wx.YES_DEFAULT |
346 wx.CANCEL | wx.ICON_QUESTION)
347 ret = dlg.ShowModal()
350 elif ret == wx.ID_CANCEL:
357 self.canvas.GetDiagram().DeleteAllShapes()
359 self.canvas.Refresh()
360 self.itemPanel.Update()
361 self.variablePanel.Reset()
369 """!Load model from file"""
371 dlg = wx.FileDialog(parent = self, message=_(
"Choose model file"),
372 defaultDir = os.getcwd(),
373 wildcard=_(
"GRASS Model File (*.gxm)|*.gxm"))
374 if dlg.ShowModal() == wx.ID_OK:
375 filename = dlg.GetPath()
380 Debug.msg(4,
"ModelFrame.OnModelOpen(): filename=%s" % filename)
389 self.SetStatusText(_(
'%(items)d items (%(actions)d actions) loaded into model') % \
390 {
'items' : self.model.GetNumItems(),
391 'actions' : self.model.GetNumItems(actionOnly =
True) }, 0)
394 """!Save model to file"""
396 dlg = wx.MessageDialog(self, message=_(
"Model file <%s> already exists. "
397 "Do you want to overwrite this file?") % \
399 caption=_(
"Save model"),
400 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
401 if dlg.ShowModal() == wx.ID_NO:
404 Debug.msg(4,
"ModelFrame.OnModelSave(): filename=%s" % self.
modelFile)
406 self.SetStatusText(_(
'File <%s> saved') % self.
modelFile, 0)
412 """!Create model to file as"""
414 dlg = wx.FileDialog(parent = self,
415 message = _(
"Choose file to save current model"),
416 defaultDir = os.getcwd(),
417 wildcard=_(
"GRASS Model File (*.gxm)|*.gxm"),
421 if dlg.ShowModal() == wx.ID_OK:
422 filename = dlg.GetPath()
428 if filename[-4:] !=
".gxm":
431 if os.path.exists(filename):
432 dlg = wx.MessageDialog(parent = self,
433 message=_(
"Model file <%s> already exists. "
434 "Do you want to overwrite this file?") % filename,
435 caption=_(
"File already exists"),
436 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
437 if dlg.ShowModal() != wx.ID_YES:
441 Debug.msg(4,
"GMFrame.OnModelSaveAs(): filename=%s" % filename)
446 self.SetStatusText(_(
'File <%s> saved') % self.
modelFile, 0)
449 """!Close model file"""
450 Debug.msg(4,
"ModelFrame.OnModelClose(): file=%s" % self.
modelFile)
455 (self.model.GetNumItems() > 0
or len(self.model.GetData()) > 0):
456 dlg = wx.MessageDialog(self, message=_(
"Current model is not empty. "
457 "Do you want to store current settings "
459 caption=_(
"Create new model?"),
460 style=wx.YES_NO | wx.YES_DEFAULT |
461 wx.CANCEL | wx.ICON_QUESTION)
462 ret = dlg.ShowModal()
465 elif ret == wx.ID_CANCEL:
474 self.canvas.GetDiagram().DeleteAllShapes()
477 self.canvas.Refresh()
480 """!Run entire model"""
484 """!Computation finished"""
485 self.SetStatusText(
'', 0)
487 if hasattr(self.
model,
"fileInput"):
488 for finput
in self.model.fileInput:
489 data = self.model.fileInput[finput]
493 fd = open(finput,
"w")
498 del self.model.fileInput
501 """!Validate entire model"""
502 if self.model.GetNumItems() < 1:
503 GMessage(parent = self,
504 message = _(
'Model is empty. Nothing to validate.'))
508 self.SetStatusText(_(
'Validating model...'), 0)
509 errList = self.model.Validate()
510 self.SetStatusText(
'', 0)
513 GWarning(parent = self,
514 message = _(
'Model is not valid.\n\n%s') %
'\n'.join(errList))
516 GMessage(parent = self,
517 message = _(
'Model is valid.'))
520 """!Export model to image (default image)
527 for shape
in self.canvas.GetDiagram().GetShapeList():
528 w, h = shape.GetBoundingBoxMax()
543 size = wx.Size(int(xmaxImg - xminImg) + 50,
544 int(ymaxImg - yminImg) + 50)
545 bitmap = wx.EmptyBitmap(width = size.width, height = size.height)
549 dlg = wx.FileDialog(parent = self,
550 message = _(
"Choose a file name to save the image (no need to add extension)"),
554 style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
556 if dlg.ShowModal() == wx.ID_OK:
562 base, ext = os.path.splitext(path)
563 fileType = ltype[dlg.GetFilterIndex()][
'type']
564 extType = ltype[dlg.GetFilterIndex()][
'ext']
566 path = base +
'.' + extType
568 dc = wx.MemoryDC(bitmap)
569 dc.SetBackground(wx.WHITE_BRUSH)
570 dc.SetBackgroundMode(wx.SOLID)
573 self.canvas.GetDiagram().Clear(dc)
574 self.canvas.GetDiagram().Redraw(dc)
577 bitmap.SaveFile(path, fileType)
578 self.SetStatusText(_(
"Model exported to <%s>") % path)
583 """!Export model to Python script"""
584 filename = self.pythonPanel.SaveAs(force =
True)
585 self.SetStatusText(_(
"Model exported to <%s>") % filename)
588 """!Define relation between data and action items"""
589 self.canvas.SetCursor(self.
cursors[
"cross"])
594 """!Define new loop in the model"""
597 width, height = self.canvas.GetSize()
598 loop = ModelLoop(self, x = width/2, y = height/2,
599 id = self.model.GetNumItems() + 1)
600 self.canvas.diagram.AddShape(loop)
604 self.model.AddItem(loop)
606 self.canvas.Refresh()
609 """!Define new condition in the model"""
612 width, height = self.canvas.GetSize()
613 cond = ModelCondition(self, x = width/2, y = height/2,
614 id = self.model.GetNumItems() + 1)
615 self.canvas.diagram.AddShape(cond)
619 self.model.AddItem(cond)
621 self.canvas.Refresh()
624 """!Add action to model"""
627 self.searchDialog.CentreOnParent()
629 self.searchDialog.Reset()
631 if self.searchDialog.ShowModal() == wx.ID_CANCEL:
632 self.searchDialog.Hide()
635 cmd = self.searchDialog.GetCmd()
636 self.searchDialog.Hide()
641 x, y = self.canvas.GetNewShapePos()
642 action = ModelAction(self.
model, cmd = cmd,
645 id = self.model.GetNextId())
646 overwrite = self.model.GetProperties().get(
'overwrite',
None)
647 if overwrite
is not None:
648 action.GetTask().set_flag(
'overwrite', overwrite)
650 self.canvas.diagram.AddShape(action)
654 self.model.AddItem(action)
656 self.itemPanel.Update()
657 self.canvas.Refresh()
661 win = action.GetPropDialog()
664 self.
GetOptData(dcmd = action.GetLog(string =
False), layer = action,
665 params = action.GetParams(), propwin =
None)
667 GUI(parent = self, show =
True).ParseCommand(action.GetLog(string =
False),
668 completed = (self.
GetOptData, action, action.GetParams()))
669 elif win
and not win.IsShown():
676 """!Add data item to model
679 width, height = self.canvas.GetSize()
680 data = ModelData(self, x = width/2 + self.
_randomShift(),
683 dlg = ModelDataDialog(parent = self, shape = data)
684 data.SetPropDialog(dlg)
686 ret = dlg.ShowModal()
692 self.canvas.diagram.AddShape(data)
698 self.model.AddItem(data)
700 self.canvas.Refresh()
704 """!Display manual page"""
705 grass.run_command(
'g.manual',
706 entry =
'wxGUI.Modeler')
709 """!Display About window"""
710 info = wx.AboutDialogInfo()
712 info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR,
'grass.ico'), wx.BITMAP_TYPE_ICO))
713 info.SetName(_(
'wxGUI Graphical Modeler'))
714 info.SetWebSite(
'http://grass.osgeo.org')
715 year = grass.version()[
'date']
716 info.SetDescription(_(
'(C) 2010-%s by the GRASS Development Team\n\n') % year +
717 '\n'.join(textwrap.wrap(_(
'This program is free software under the GNU General Public License'
718 '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75)))
723 """!Process action data"""
725 width, height = self.canvas.GetSize()
728 for p
in params[
'params']:
729 if p.get(
'prompt',
'')
in (
'raster',
'vector',
'raster3d')
and \
730 (p.get(
'value',
None)
or \
731 (p.get(
'age',
'old') !=
'old' and p.get(
'required',
'no') ==
'yes')):
732 data = layer.FindData(p.get(
'name',
''))
734 data.SetValue(p.get(
'value',
''))
738 data = self.model.FindData(p.get(
'value',
''),
741 if p.get(
'age',
'old') ==
'old':
742 rel = ModelRelation(parent = self, fromShape = data,
743 toShape = layer, param = p.get(
'name',
''))
745 rel = ModelRelation(parent = self, fromShape = layer,
746 toShape = data, param = p.get(
'name',
''))
747 layer.AddRelation(rel)
748 data.AddRelation(rel)
753 data = ModelData(self, value = p.get(
'value',
''),
754 prompt = p.get(
'prompt',
''),
757 self.canvas.diagram.AddShape(data)
760 if p.get(
'age',
'old') ==
'old':
761 rel = ModelRelation(parent = self, fromShape = data,
762 toShape = layer, param = p.get(
'name',
''))
764 rel = ModelRelation(parent = self, fromShape = layer,
765 toShape = data, param = p.get(
'name',
''))
766 layer.AddRelation(rel)
767 data.AddRelation(rel)
772 layer.SetValid(params)
774 self.canvas.Refresh()
777 layer.SetProperties(params, propwin)
779 self.SetStatusText(layer.GetLog(), 0)
782 """!Add connection between model objects
786 fromShape = rel.GetFrom()
787 toShape = rel.GetTo()
790 rel.SetPen(wx.BLACK_PEN)
791 rel.SetBrush(wx.BLACK_BRUSH)
792 rel.AddArrow(ogl.ARROW_ARROW)
793 points = rel.GetControlPoints()
794 rel.MakeLineControlPoints(2)
797 rel.InsertLineControlPoint(point = wx.RealPoint(x, y))
801 fromShape.AddLine(rel, toShape)
805 self.canvas.diagram.AddShape(rel)
809 """!Load model definition stored in GRASS Model XML file (gxm)
812 self.model.LoadModel(filename)
813 except GException, e:
814 GError(parent = self,
815 message = _(
"Reading model file <%s> failed.\n"
816 "Invalid file, unable to parse XML document.") % filename)
821 self.SetStatusText(_(
"Please wait, loading model..."), 0)
824 for item
in self.model.GetItems(objType = ModelAction):
826 self.canvas.diagram.AddShape(item)
829 for rel
in item.GetRelations():
830 if rel.GetFrom() == item:
831 dataItem = rel.GetTo()
833 dataItem = rel.GetFrom()
835 self.canvas.diagram.AddShape(dataItem)
840 for item
in self.model.GetItems(objType = ModelLoop):
842 self.canvas.diagram.AddShape(item)
849 for item
in self.model.GetItems(objType = ModelCondition):
851 self.canvas.diagram.AddShape(item)
858 self.variablePanel.Update()
859 self.itemPanel.Update()
860 self.SetStatusText(
'', 0)
863 for action
in self.model.GetItems(objType = ModelAction):
864 action.SetValid(action.GetParams())
867 self.canvas.Refresh(
True)
870 """!Save model to model file, recover original file on error.
872 @return True on success
873 @return False on failure
876 tmpfile = tempfile.TemporaryFile(mode=
'w+b')
879 except StandardError:
880 GError(parent = self,
881 message = _(
"Writing current settings to model file failed."))
885 mfile = open(filename,
"w")
887 for line
in tmpfile.readlines():
890 wx.MessageBox(parent = self,
891 message = _(
"Unable to open file <%s> for writing.") % filename,
892 caption = _(
"Error"),
893 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
901 """!Define loop with given list of items"""
903 items = loop.GetItems()
908 for rel
in loop.GetRelations():
909 self.canvas.GetDiagram().RemoveShape(rel)
913 rel = ModelRelation(parent = self, fromShape = parent, toShape = item)
914 dx = item.GetX() - parent.GetX()
915 dy = item.GetY() - parent.GetY()
916 loop.AddRelation(rel)
918 rel.SetControlPoints(((parent.GetX(), parent.GetY() + dy / 2),
919 (parent.GetX() + dx, parent.GetY() + dy / 2)))
924 item = loop.GetItems()[-1]
925 rel = ModelRelation(parent = self, fromShape = item, toShape = loop)
926 loop.AddRelation(rel)
928 dx = (item.GetX() - loop.GetX()) + loop.GetWidth() / 2 + 50
929 dy = item.GetHeight() / 2 + 50
930 rel.MakeLineControlPoints(0)
931 rel.InsertLineControlPoint(point = wx.RealPoint(loop.GetX() - loop.GetWidth() / 2 ,
933 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
934 item.GetY() + item.GetHeight() / 2))
935 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
937 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
939 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
942 self.canvas.Refresh()
945 """!Define if-else statement with given list of items"""
947 items = condition.GetItems()
948 if not items[
'if']
and not items[
'else']:
952 for rel
in condition.GetRelations():
953 self.canvas.GetDiagram().RemoveShape(rel)
955 dxIf = condition.GetX() + condition.GetWidth() / 2
956 dxElse = condition.GetX() - condition.GetWidth() / 2
957 dy = condition.GetY()
958 for branch
in items.keys():
959 for item
in items[branch]:
960 rel = ModelRelation(parent = self, fromShape = parent,
962 condition.AddRelation(rel)
964 rel.MakeLineControlPoints(0)
966 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
967 rel.InsertLineControlPoint(point = wx.RealPoint(dxIf, dy))
969 rel.InsertLineControlPoint(point = wx.RealPoint(dxElse, dy))
970 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
973 self.canvas.Refresh()
976 """!Canvas where model is drawn"""
980 ogl.ShapeCanvas.__init__(self, parent)
984 self.diagram.SetCanvas(self)
986 self.SetScrollbars(20, 20, 2000/20, 2000/20)
988 self.Bind(wx.EVT_CHAR, self.
OnChar)
992 kc = event.GetKeyCode()
993 diagram = self.GetDiagram()
994 if kc == wx.WXK_DELETE:
998 """!Remove selected shapes"""
999 self.parent.ModelChanged()
1001 diagram = self.GetDiagram()
1002 shapes = [shape
for shape
in diagram.GetShapeList()
if shape.Selected()]
1006 """!Removes shapes"""
1007 self.parent.ModelChanged()
1008 diagram = self.GetDiagram()
1009 for shape
in shapes:
1010 remList, upList = self.parent.GetModel().RemoveItem(shape)
1012 diagram.RemoveShape(shape)
1014 for item
in remList:
1015 diagram.RemoveShape(item)
1024 """!Determine optimal position for newly added object
1028 xNew, yNew = map(
lambda x: x / 2, self.GetSize())
1029 diagram = self.GetDiagram()
1031 for shape
in diagram.GetShapeList():
1033 yBox = shape.GetBoundingBoxMin()[1] / 2
1034 if yBox > 0
and y < yNew + yBox
and y > yNew - yBox:
1040 """!Model event handler class"""
1042 ogl.ShapeEvtHandler.__init__(self)
1048 """!Left mouse button pressed -> select item & update statusbar"""
1049 shape = self.GetShape()
1050 canvas = shape.GetCanvas()
1051 dc = wx.ClientDC(canvas)
1055 if hasattr(self.
frame,
'defineRelation'):
1056 drel = self.frame.defineRelation
1057 if drel[
'from']
is None:
1058 drel[
'from'] = shape
1059 elif drel[
'to']
is None:
1061 rel = ModelRelation(parent = self.
frame, fromShape = drel[
'from'],
1062 toShape = drel[
'to'])
1063 dlg = ModelRelationDialog(parent = self.
frame,
1066 ret = dlg.ShowModal()
1068 option = dlg.GetOption()
1070 drel[
'from'].AddRelation(rel)
1071 drel[
'to'].AddRelation(rel)
1072 drel[
'from'].Update()
1073 params = {
'params' : [{
'name' : option,
1074 'value' : drel[
'from'].
GetValue()}] }
1075 drel[
'to'].MergeParams(params)
1076 self.frame.AddLine(rel)
1079 del self.frame.defineRelation
1084 if hasattr(shape,
"GetLog"):
1085 self.log.SetStatusText(shape.GetLog(), 0)
1087 self.log.SetStatusText(
'', 0)
1090 """!Left mouse button pressed (double-click) -> show properties"""
1094 """!Show properties dialog"""
1095 self.frame.ModelChanged()
1096 shape = self.GetShape()
1097 if isinstance(shape, ModelAction):
1098 module = GUI(parent = self.
frame, show =
True).ParseCommand(shape.GetLog(string =
False),
1099 completed = (self.frame.GetOptData, shape, shape.GetParams()))
1101 elif isinstance(shape, ModelData):
1102 dlg = ModelDataDialog(parent = self.
frame, shape = shape)
1103 shape.SetPropDialog(dlg)
1104 dlg.CentreOnParent()
1107 elif isinstance(shape, ModelLoop):
1108 dlg = ModelLoopDialog(parent = self.
frame, shape = shape)
1109 dlg.CentreOnParent()
1110 if dlg.ShowModal() == wx.ID_OK:
1111 shape.SetText(dlg.GetCondition())
1113 ids = dlg.GetItems()
1114 for aId
in ids[
'unchecked']:
1115 action = self.frame.GetModel().GetItem(aId)
1116 action.UnSetBlock(shape)
1117 for aId
in ids[
'checked']:
1118 action = self.frame.GetModel().GetItem(aId)
1119 action.SetBlock(shape)
1121 alist.append(action)
1122 shape.SetItems(alist)
1123 self.frame.DefineLoop(shape)
1124 self.frame.SetStatusText(shape.GetLog(), 0)
1125 self.frame.GetCanvas().Refresh()
1129 elif isinstance(shape, ModelCondition):
1130 dlg = ModelConditionDialog(parent = self.
frame, shape = shape)
1131 dlg.CentreOnParent()
1132 if dlg.ShowModal() == wx.ID_OK:
1133 shape.SetText(dlg.GetCondition())
1134 ids = dlg.GetItems()
1135 for b
in ids.keys():
1137 for aId
in ids[b][
'unchecked']:
1138 action = self.frame.GetModel().GetItem(aId)
1139 action.UnSetBlock(shape)
1140 for aId
in ids[b][
'checked']:
1141 action = self.frame.GetModel().GetItem(aId)
1142 action.SetBlock(shape)
1144 alist.append(action)
1145 shape.SetItems(alist, branch = b)
1146 self.frame.DefineCondition(shape)
1147 self.frame.GetCanvas().Refresh()
1152 """!Drag shape (begining)"""
1153 self.frame.ModelChanged()
1154 if self._previousHandler:
1155 self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
1158 """!Drag shape (end)"""
1159 if self._previousHandler:
1160 self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
1162 shape = self.GetShape()
1163 if isinstance(shape, ModelLoop):
1164 self.frame.DefineLoop(shape)
1165 elif isinstance(shape, ModelCondition):
1166 self.frame.DefineCondition(shape)
1168 for mo
in shape.GetBlock():
1169 if isinstance(mo, ModelLoop):
1170 self.frame.DefineLoop(mo)
1171 elif isinstance(mo, ModelCondition):
1172 self.frame.DefineCondition(mo)
1176 self.frame.ModelChanged()
1177 if self._previousHandler:
1178 self._previousHandler.OnEndSize(x, y)
1181 """!Right click -> pop-up menu"""
1182 if not hasattr (self,
"popupID"):
1184 for key
in (
'remove',
'enable',
'addPoint',
1185 'delPoint',
'intermediate',
'props',
'id'):
1186 self.
popupID[key] = wx.NewId()
1193 shape = self.GetShape()
1196 popupMenu = wx.Menu()
1197 popupMenu.Append(self.
popupID[
'remove'], text=_(
'Remove'))
1198 self.frame.Bind(wx.EVT_MENU, self.
OnRemove, id = self.
popupID[
'remove'])
1199 if isinstance(shape, ModelAction)
or isinstance(shape, ModelLoop):
1200 if shape.IsEnabled():
1201 popupMenu.Append(self.
popupID[
'enable'], text=_(
'Disable'))
1204 popupMenu.Append(self.
popupID[
'enable'], text=_(
'Enable'))
1205 self.frame.Bind(wx.EVT_MENU, self.
OnEnable, id = self.
popupID[
'enable'])
1207 if isinstance(shape, ModelRelation):
1208 popupMenu.AppendSeparator()
1209 popupMenu.Append(self.
popupID[
'addPoint'], text=_(
'Add control point'))
1211 popupMenu.Append(self.
popupID[
'delPoint'], text=_(
'Remove control point'))
1213 if len(shape.GetLineControlPoints()) == 2:
1214 popupMenu.Enable(self.
popupID[
'delPoint'],
False)
1216 if isinstance(shape, ModelData)
and '@' not in shape.GetValue():
1217 popupMenu.AppendSeparator()
1218 popupMenu.Append(self.
popupID[
'intermediate'], text=_(
'Intermediate'),
1219 kind = wx.ITEM_CHECK)
1220 if self.GetShape().IsIntermediate():
1221 popupMenu.Check(self.
popupID[
'intermediate'],
True)
1225 if isinstance(shape, ModelData)
or \
1226 isinstance(shape, ModelAction)
or \
1227 isinstance(shape, ModelLoop):
1228 popupMenu.AppendSeparator()
1229 popupMenu.Append(self.
popupID[
'props'], text=_(
'Properties'))
1232 self.frame.PopupMenu(popupMenu)
1236 """!Disable action"""
1240 """!Disable action"""
1243 def _onEnable(self, enable):
1244 shape = self.GetShape()
1245 shape.Enable(enable)
1246 self.frame.ModelChanged()
1247 self.frame.canvas.Refresh()
1249 def _onSelectShape(self, shape):
1250 canvas = shape.GetCanvas()
1251 dc = wx.ClientDC(canvas)
1253 if shape.Selected():
1254 shape.Select(
False, dc)
1257 shapeList = canvas.GetDiagram().GetShapeList()
1262 toUnselect.append(s)
1264 shape.Select(
True, dc)
1266 for s
in toUnselect:
1269 canvas.Refresh(
False)
1272 """!Add control point"""
1273 shape = self.GetShape()
1274 shape.InsertLineControlPoint(point = wx.RealPoint(self.
x, self.
y))
1277 self.frame.ModelChanged()
1278 self.frame.canvas.Refresh()
1281 """!Remove control point"""
1282 shape = self.GetShape()
1283 shape.DeleteLineControlPoint()
1286 self.frame.ModelChanged()
1287 self.frame.canvas.Refresh()
1290 """!Mark data as intermediate"""
1291 self.frame.ModelChanged()
1292 shape = self.GetShape()
1293 shape.SetIntermediate(event.IsChecked())
1294 self.frame.canvas.Refresh()
1299 self.frame.GetCanvas().RemoveShapes([self.GetShape()])
1300 self.frame.itemPanel.Update()
1303 def __init__(self, parent, id = wx.ID_ANY,
1305 """!Manage model variables panel
1309 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1311 self.
listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1312 label=
" %s " % _(
"List of variables - right-click to delete"))
1314 self.
list = VariableListCtrl(parent = self,
1315 columns = [_(
"Name"), _(
"Data type"),
1316 _(
"Default value"), _(
"Description")])
1319 self.
addBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1320 label =
" %s " % _(
"Add new variable"))
1321 self.
name = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1322 wx.CallAfter(self.name.SetFocus)
1323 self.
type = wx.Choice(parent = self, id = wx.ID_ANY,
1324 choices = [_(
"integer"),
1331 self.type.SetSelection(2)
1332 self.
value = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1333 self.
desc = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1336 self.
btnAdd = wx.Button(parent = self, id = wx.ID_ADD)
1337 self.btnAdd.SetToolTipString(_(
"Add new variable to the model"))
1338 self.btnAdd.Enable(
False)
1341 self.name.Bind(wx.EVT_TEXT, self.
OnText)
1342 self.value.Bind(wx.EVT_TEXT, self.
OnText)
1343 self.desc.Bind(wx.EVT_TEXT, self.
OnText)
1344 self.btnAdd.Bind(wx.EVT_BUTTON, self.
OnAdd)
1349 """!Layout dialog"""
1350 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
1351 listSizer.Add(item = self.
list, proportion = 1,
1354 addSizer = wx.StaticBoxSizer(self.
addBox, wx.VERTICAL)
1355 gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
1356 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1357 label =
"%s:" % _(
"Name")),
1358 flag = wx.ALIGN_CENTER_VERTICAL,
1360 gridSizer.Add(item = self.
name,
1363 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1364 label =
"%s:" % _(
"Data type")),
1365 flag = wx.ALIGN_CENTER_VERTICAL,
1367 gridSizer.Add(item = self.
type,
1369 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1370 label =
"%s:" % _(
"Default value")),
1371 flag = wx.ALIGN_CENTER_VERTICAL,
1373 gridSizer.Add(item = self.
value,
1374 pos = (1, 1), span = (1, 3),
1376 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1377 label =
"%s:" % _(
"Description")),
1378 flag = wx.ALIGN_CENTER_VERTICAL,
1380 gridSizer.Add(item = self.
desc,
1381 pos = (2, 1), span = (1, 3),
1383 gridSizer.AddGrowableCol(1)
1384 addSizer.Add(item = gridSizer,
1386 addSizer.Add(item = self.
btnAdd, proportion = 0,
1387 flag = wx.TOP | wx.ALIGN_RIGHT, border = 5)
1389 mainSizer = wx.BoxSizer(wx.VERTICAL)
1390 mainSizer.Add(item = listSizer, proportion = 1,
1391 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1392 mainSizer.Add(item = addSizer, proportion = 0,
1393 flag = wx.EXPAND | wx.ALIGN_CENTER |
1394 wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
1396 self.SetSizer(mainSizer)
1401 if self.name.GetValue():
1402 self.btnAdd.Enable()
1404 self.btnAdd.Enable(
False)
1407 """!Add new variable to the list"""
1408 msg = self.list.Append(self.name.GetValue(),
1409 self.type.GetStringSelection(),
1410 self.value.GetValue(),
1411 self.desc.GetValue())
1412 self.name.SetValue(
'')
1413 self.name.SetFocus()
1416 GError(parent = self,
1419 self.type.SetSelection(2)
1420 self.value.SetValue(
'')
1421 self.desc.SetValue(
'')
1425 """!Update model variables"""
1427 for values
in self.list.GetData().itervalues():
1429 variables[name] = {
'type' : str(values[1]) }
1431 variables[name][
'value'] = values[2]
1433 variables[name][
'description'] = values[3]
1435 self.parent.GetModel().SetVariables(variables)
1436 self.parent.ModelChanged()
1439 """!Reload list of variables"""
1440 self.list.OnReload(
None)
1443 """!Remove all variables"""
1444 self.list.DeleteAllItems()
1445 self.parent.GetModel().SetVariables([])
1448 def __init__(self, parent, id = wx.ID_ANY,
1450 """!Manage model items
1454 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1456 self.
listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1457 label=
" %s " % _(
"List of items - right-click to delete"))
1459 self.
list = ItemListCtrl(parent = self,
1460 columns = [_(
"ID"), _(
"Name"), _(
"In block"),
1461 _(
"Command / Condition")])
1466 """!Layout dialog"""
1467 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
1468 listSizer.Add(item = self.
list, proportion = 1,
1471 mainSizer = wx.BoxSizer(wx.VERTICAL)
1472 mainSizer.Add(item = listSizer, proportion = 1,
1473 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1475 self.SetSizer(mainSizer)
1479 """!Reload list of variables"""
1480 self.list.OnReload(
None)
1483 def __init__(self, parent, id = wx.ID_ANY,
1485 """!Model as python script
1489 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1493 self.
bodyBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1494 label =
" %s " % _(
"Python script"))
1495 self.
body = PyStc(parent = self, statusbar = self.parent.GetStatusBar())
1497 self.
btnRun = wx.Button(parent = self, id = wx.ID_ANY, label = _(
"&Run"))
1498 self.btnRun.SetToolTipString(_(
"Run python script"))
1500 self.
btnSaveAs = wx.Button(parent = self, id = wx.ID_SAVEAS)
1501 self.btnSaveAs.SetToolTipString(_(
"Save python script to file"))
1504 self.btnRefresh.SetToolTipString(_(
"Refresh python script based on the model.\n"
1505 "It will discards all local changes."))
1511 sizer = wx.BoxSizer(wx.VERTICAL)
1512 bodySizer = wx.StaticBoxSizer(self.
bodyBox, wx.HORIZONTAL)
1513 btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1515 bodySizer.Add(item = self.
body, proportion = 1,
1516 flag = wx.EXPAND | wx.ALL, border = 3)
1518 btnSizer.Add(item = self.
btnRefresh, proportion = 0,
1519 flag = wx.LEFT | wx.RIGHT, border = 5)
1520 btnSizer.AddStretchSpacer()
1521 btnSizer.Add(item = self.
btnSaveAs, proportion = 0,
1522 flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
1523 btnSizer.Add(item = self.
btnRun, proportion = 0,
1524 flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
1526 sizer.Add(item = bodySizer, proportion = 1,
1527 flag = wx.EXPAND | wx.ALL, border = 3)
1528 sizer.Add(item = btnSizer, proportion = 0,
1529 flag = wx.EXPAND | wx.ALL, border = 3)
1532 sizer.SetSizeHints(self)
1533 self.SetSizer(sizer)
1536 """!Run Python script"""
1540 fd.write(self.body.GetText())
1542 GError(_(
"Unable to launch Python script. %s") % e,
1547 mode = stat.S_IMODE(os.lstat(self.
filename)[stat.ST_MODE])
1548 os.chmod(self.
filename, mode | stat.S_IXUSR)
1550 self.parent.goutput.RunCmd([fd.name], switchPage =
True,
1551 skipInterface =
True, onDone = self.
OnDone)
1556 """!Python script finished"""
1561 """!Save python script to file
1566 dlg = wx.FileDialog(parent = self,
1567 message = _(
"Choose file to save"),
1568 defaultDir = os.getcwd(),
1569 wildcard = _(
"Python script (*.py)|*.py"),
1572 if dlg.ShowModal() == wx.ID_OK:
1573 filename = dlg.GetPath()
1579 if filename[-3:] !=
".py":
1582 if os.path.exists(filename):
1583 dlg = wx.MessageDialog(self, message=_(
"File <%s> already exists. "
1584 "Do you want to overwrite this file?") % filename,
1585 caption=_(
"Save file"),
1586 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
1587 if dlg.ShowModal() == wx.ID_NO:
1593 fd = open(filename,
"w")
1596 WritePythonFile(fd, self.parent.GetModel())
1598 fd.write(self.body.GetText())
1603 os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
1608 """!Save python script to file"""
1609 self.
SaveAs(force =
False)
1613 """!Refresh Python script
1615 @return True on refresh
1616 @return False script hasn't been updated
1618 if self.body.modified:
1619 dlg = wx.MessageDialog(self,
1620 message = _(
"Python script is locally modificated. "
1621 "Refresh will discard all changes. "
1622 "Do you really want to continue?"),
1623 caption=_(
"Update"),
1624 style = wx.YES_NO | wx.NO_DEFAULT |
1625 wx.ICON_QUESTION | wx.CENTRE)
1626 ret = dlg.ShowModal()
1631 fd = tempfile.TemporaryFile()
1632 WritePythonFile(fd, self.parent.GetModel())
1634 self.body.SetText(fd.read())
1637 self.body.modified =
False
1642 """!Refresh Python script"""
1644 self.parent.SetStatusText(_(
'Python script is up-to-date'), 0)
1648 """!Check if python script has been modified"""
1649 return self.body.modified
1652 """!Check if python script is empty"""
1653 return len(self.body.GetText()) == 0
1657 gettext.install(
'grasswxpy', os.path.join(os.getenv(
"GISBASE"),
'locale'), unicode =
True)
1659 app = wx.PySimpleApp()
1661 wx.InitAllImageHandlers()
1663 if len(sys.argv) > 1:
1664 frame.LoadModelFile(sys.argv[1])
1669 if __name__ ==
"__main__":
def Reset(self)
Remove all variables.
def __init__(self, parent, id=wx.ID_ANY, kwargs)
Manage model variables panel.
def OnLeftDoubleClick
Left mouse button pressed (double-click) -> show properties.
wxGUI Graphical Modeler - dialogs
def OnRemovePoint(self, event)
Remove control point.
def OnIntermediate(self, event)
Mark data as intermediate.
def GetCanvas(self)
Get canvas.
def OnSaveAs(self, event)
Save python script to file.
def OnBeginDragLeft
Drag shape (begining)
def OnAdd(self, event)
Add new variable to the list.
def OnCloseWindow(self, event)
Close window.
def _onEnable(self, enable)
def _layout(self)
Layout dialog.
def GetNewShapePos(self)
Determine optimal position for newly added object.
wxGUI Graphical Modeler (base classes & read/write)
def OnCanvasRefresh(self, event)
Refresh canvas.
def Update(self)
Reload list of variables.
def OnProperties
Show properties dialog.
def OnChar(self, event)
Key pressed.
def GetImageHandlers(image)
Get list of supported image handlers.
def IsEmpty(self)
Check if python script is empty.
def OnAddAction(self, event)
Add action to model.
def __init__(self, parent, id=wx.ID_ANY, kwargs)
Manage model items.
def OnRefresh(self, event)
Refresh Python script.
def LoadModelFile(self, filename)
Load model definition stored in GRASS Model XML file (gxm)
wxGUI Graphical Modeler - preferences
def DefineLoop(self, loop)
Define loop with given list of items.
Various dialogs used in wxGUI.
def OnModelSaveAs(self, event)
Create model to file as.
def OnDone(self, cmd, returncode)
Computation finished.
def ModelChanged
Update window title.
def OnEndSize(self, x, y)
Resize shape.
def _addEvent(self, item)
Add event to item.
def OnText(self, event)
Text entered.
def __init__(self, parent)
def RemoveSelected(self)
Remove selected shapes.
Canvas where model is drawn.
def WriteModelFile(self, filename)
Save model to model file, recover original file on error.
def OnHelp(self, event)
Show help.
def OnModelOpen(self, event)
Load model from file.
def OnModelClose
Close model file.
def OnValidateModel
Validate entire model.
def OnAddPoint(self, event)
Add control point.
def OnEnable(self, event)
Disable action.
def OnRun(self, event)
Run Python script.
def UpdateModelVariables(self)
Update model variables.
def RemoveShapes(self, shapes)
Removes shapes.
def OnModelSave
Save model to file.
def CheckWxVersion(version=[2)
Check wx version.
def OnModelProperties(self, event)
Model properties dialog.
def _layout(self)
Layout dialog.
def GetOptData(self, dcmd, layer, params, propwin)
Process action data.
Model event handler class.
def AddLine(self, rel)
Add connection between model objects.
def OnDefineCondition(self, event)
Define new condition in the model.
def __init__(self, parent, id=wx.ID_ANY, title=_("GRASS GIS Graphical Modeler (experimental prototype)"), kwargs)
Graphical modeler main window.
def OnDefineRelation(self, event)
Define relation between data and action items.
def OnExportImage(self, event)
Export model to image (default image)
def OnDone(self, cmd, returncode)
Python script finished.
def OnDefineLoop(self, event)
Define new loop in the model.
def OnRightClick
Right click -> pop-up menu.
def Update(self)
Reload list of variables.
def OnRemoveItem(self, event)
Remove shape.
def OnPageChanged(self, event)
Page in notebook changed.
def OnLeftClick
Left mouse button pressed -> select item & update statusbar.
def SaveAs
Save python script to file.
def OnDeleteData(self, event)
Delete intermediate data.
def RunCommand(prog, flags="", overwrite=False, quiet=False, verbose=False, parent=None, read=False, stdin=None, getErrorMsg=False, kwargs)
Run GRASS command.
def OnRunModel(self, event)
Run entire model.
def RefreshScript(self)
Refresh Python script.
def GetModel(self)
Get model.
def __init__(self, log, frame)
def OnModelNew(self, event)
Create new model.
def OnDisable(self, event)
Disable action.
def OnPreferences(self, event)
Open preferences dialog.
def OnCmdPrepare(self, event)
Prepare for running command.
def OnEndDragLeft
Drag shape (end)
def OnVariables(self, event)
Switch to variables page.
def _randomShift(self)
Returns random value to shift layout.
def OnAddData(self, event)
Add data item to model.
def OnRemove(self, event)
Remove shape.
def OnCmdRun(self, event)
Run command.
def OnCmdDone(self, event)
Command done (or aborted)
def DefineCondition(self, condition)
Define if-else statement with given list of items.
def _layout(self)
Do layout.
def _onSelectShape(self, shape)
def OnExportPython
Export model to Python script.
def IsModified(self)
Check if python script has been modified.
def __init__(self, parent, id=wx.ID_ANY, kwargs)
Model as python script.
def OnAbout(self, event)
Display About window.