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.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.\n\n%s") % \
818 showTraceback =
False)
824 self.SetStatusText(_(
"Please wait, loading model..."), 0)
827 for item
in self.model.GetItems(objType = ModelAction):
829 self.canvas.diagram.AddShape(item)
832 for rel
in item.GetRelations():
833 if rel.GetFrom() == item:
834 dataItem = rel.GetTo()
836 dataItem = rel.GetFrom()
838 self.canvas.diagram.AddShape(dataItem)
843 for item
in self.model.GetItems(objType = ModelLoop):
845 self.canvas.diagram.AddShape(item)
852 for item
in self.model.GetItems(objType = ModelCondition):
854 self.canvas.diagram.AddShape(item)
861 self.variablePanel.Update()
862 self.itemPanel.Update()
863 self.SetStatusText(
'', 0)
866 for action
in self.model.GetItems(objType = ModelAction):
867 action.SetValid(action.GetParams())
870 self.canvas.Refresh(
True)
873 """!Save model to model file, recover original file on error.
875 @return True on success
876 @return False on failure
879 tmpfile = tempfile.TemporaryFile(mode=
'w+b')
882 except StandardError:
883 GError(parent = self,
884 message = _(
"Writing current settings to model file failed."))
888 mfile = open(filename,
"w")
890 for line
in tmpfile.readlines():
893 wx.MessageBox(parent = self,
894 message = _(
"Unable to open file <%s> for writing.") % filename,
895 caption = _(
"Error"),
896 style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
904 """!Define loop with given list of items"""
906 items = loop.GetItems()
911 for rel
in loop.GetRelations():
912 self.canvas.GetDiagram().RemoveShape(rel)
916 rel = ModelRelation(parent = self, fromShape = parent, toShape = item)
917 dx = item.GetX() - parent.GetX()
918 dy = item.GetY() - parent.GetY()
919 loop.AddRelation(rel)
921 rel.SetControlPoints(((parent.GetX(), parent.GetY() + dy / 2),
922 (parent.GetX() + dx, parent.GetY() + dy / 2)))
927 item = loop.GetItems()[-1]
928 rel = ModelRelation(parent = self, fromShape = item, toShape = loop)
929 loop.AddRelation(rel)
931 dx = (item.GetX() - loop.GetX()) + loop.GetWidth() / 2 + 50
932 dy = item.GetHeight() / 2 + 50
933 rel.MakeLineControlPoints(0)
934 rel.InsertLineControlPoint(point = wx.RealPoint(loop.GetX() - loop.GetWidth() / 2 ,
936 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
937 item.GetY() + item.GetHeight() / 2))
938 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(),
940 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
942 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx,
945 self.canvas.Refresh()
948 """!Define if-else statement with given list of items"""
950 items = condition.GetItems()
951 if not items[
'if']
and not items[
'else']:
955 for rel
in condition.GetRelations():
956 self.canvas.GetDiagram().RemoveShape(rel)
958 dxIf = condition.GetX() + condition.GetWidth() / 2
959 dxElse = condition.GetX() - condition.GetWidth() / 2
960 dy = condition.GetY()
961 for branch
in items.keys():
962 for item
in items[branch]:
963 rel = ModelRelation(parent = self, fromShape = parent,
965 condition.AddRelation(rel)
967 rel.MakeLineControlPoints(0)
969 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
970 rel.InsertLineControlPoint(point = wx.RealPoint(dxIf, dy))
972 rel.InsertLineControlPoint(point = wx.RealPoint(dxElse, dy))
973 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY()))
976 self.canvas.Refresh()
979 """!Canvas where model is drawn"""
983 ogl.ShapeCanvas.__init__(self, parent)
987 self.diagram.SetCanvas(self)
989 self.SetScrollbars(20, 20, 2000/20, 2000/20)
991 self.Bind(wx.EVT_CHAR, self.
OnChar)
995 kc = event.GetKeyCode()
996 diagram = self.GetDiagram()
997 if kc == wx.WXK_DELETE:
1001 """!Remove selected shapes"""
1002 self.parent.ModelChanged()
1004 diagram = self.GetDiagram()
1005 shapes = [shape
for shape
in diagram.GetShapeList()
if shape.Selected()]
1009 """!Removes shapes"""
1010 self.parent.ModelChanged()
1011 diagram = self.GetDiagram()
1012 for shape
in shapes:
1013 remList, upList = self.parent.GetModel().RemoveItem(shape)
1015 diagram.RemoveShape(shape)
1017 for item
in remList:
1018 diagram.RemoveShape(item)
1027 """!Determine optimal position for newly added object
1031 xNew, yNew = map(
lambda x: x / 2, self.GetSize())
1032 diagram = self.GetDiagram()
1034 for shape
in diagram.GetShapeList():
1036 yBox = shape.GetBoundingBoxMin()[1] / 2
1037 if yBox > 0
and y < yNew + yBox
and y > yNew - yBox:
1043 """!Model event handler class"""
1045 ogl.ShapeEvtHandler.__init__(self)
1051 """!Left mouse button pressed -> select item & update statusbar"""
1052 shape = self.GetShape()
1053 canvas = shape.GetCanvas()
1054 dc = wx.ClientDC(canvas)
1058 if hasattr(self.
frame,
'defineRelation'):
1059 drel = self.frame.defineRelation
1060 if drel[
'from']
is None:
1061 drel[
'from'] = shape
1062 elif drel[
'to']
is None:
1064 rel = ModelRelation(parent = self.
frame, fromShape = drel[
'from'],
1065 toShape = drel[
'to'])
1066 dlg = ModelRelationDialog(parent = self.
frame,
1069 ret = dlg.ShowModal()
1071 option = dlg.GetOption()
1073 drel[
'from'].AddRelation(rel)
1074 drel[
'to'].AddRelation(rel)
1075 drel[
'from'].Update()
1076 params = {
'params' : [{
'name' : option,
1077 'value' : drel[
'from'].
GetValue()}] }
1078 drel[
'to'].MergeParams(params)
1079 self.frame.AddLine(rel)
1082 del self.frame.defineRelation
1087 if hasattr(shape,
"GetLog"):
1088 self.log.SetStatusText(shape.GetLog(), 0)
1090 self.log.SetStatusText(
'', 0)
1093 """!Left mouse button pressed (double-click) -> show properties"""
1097 """!Show properties dialog"""
1098 self.frame.ModelChanged()
1099 shape = self.GetShape()
1100 if isinstance(shape, ModelAction):
1101 module = GUI(parent = self.
frame, show =
True).ParseCommand(shape.GetLog(string =
False),
1102 completed = (self.frame.GetOptData, shape, shape.GetParams()))
1104 elif isinstance(shape, ModelData):
1105 dlg = ModelDataDialog(parent = self.
frame, shape = shape)
1106 shape.SetPropDialog(dlg)
1107 dlg.CentreOnParent()
1110 elif isinstance(shape, ModelLoop):
1111 dlg = ModelLoopDialog(parent = self.
frame, shape = shape)
1112 dlg.CentreOnParent()
1113 if dlg.ShowModal() == wx.ID_OK:
1114 shape.SetText(dlg.GetCondition())
1116 ids = dlg.GetItems()
1117 for aId
in ids[
'unchecked']:
1118 action = self.frame.GetModel().GetItem(aId)
1119 action.UnSetBlock(shape)
1120 for aId
in ids[
'checked']:
1121 action = self.frame.GetModel().GetItem(aId)
1122 action.SetBlock(shape)
1124 alist.append(action)
1125 shape.SetItems(alist)
1126 self.frame.DefineLoop(shape)
1127 self.frame.SetStatusText(shape.GetLog(), 0)
1128 self.frame.GetCanvas().Refresh()
1132 elif isinstance(shape, ModelCondition):
1133 dlg = ModelConditionDialog(parent = self.
frame, shape = shape)
1134 dlg.CentreOnParent()
1135 if dlg.ShowModal() == wx.ID_OK:
1136 shape.SetText(dlg.GetCondition())
1137 ids = dlg.GetItems()
1138 for b
in ids.keys():
1140 for aId
in ids[b][
'unchecked']:
1141 action = self.frame.GetModel().GetItem(aId)
1142 action.UnSetBlock(shape)
1143 for aId
in ids[b][
'checked']:
1144 action = self.frame.GetModel().GetItem(aId)
1145 action.SetBlock(shape)
1147 alist.append(action)
1148 shape.SetItems(alist, branch = b)
1149 self.frame.DefineCondition(shape)
1150 self.frame.GetCanvas().Refresh()
1155 """!Drag shape (begining)"""
1156 self.frame.ModelChanged()
1157 if self._previousHandler:
1158 self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
1161 """!Drag shape (end)"""
1162 if self._previousHandler:
1163 self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
1165 shape = self.GetShape()
1166 if isinstance(shape, ModelLoop):
1167 self.frame.DefineLoop(shape)
1168 elif isinstance(shape, ModelCondition):
1169 self.frame.DefineCondition(shape)
1171 for mo
in shape.GetBlock():
1172 if isinstance(mo, ModelLoop):
1173 self.frame.DefineLoop(mo)
1174 elif isinstance(mo, ModelCondition):
1175 self.frame.DefineCondition(mo)
1179 self.frame.ModelChanged()
1180 if self._previousHandler:
1181 self._previousHandler.OnEndSize(x, y)
1184 """!Right click -> pop-up menu"""
1185 if not hasattr (self,
"popupID"):
1187 for key
in (
'remove',
'enable',
'addPoint',
1188 'delPoint',
'intermediate',
'props',
'id'):
1189 self.
popupID[key] = wx.NewId()
1196 shape = self.GetShape()
1199 popupMenu = wx.Menu()
1200 popupMenu.Append(self.
popupID[
'remove'], text=_(
'Remove'))
1201 self.frame.Bind(wx.EVT_MENU, self.
OnRemove, id = self.
popupID[
'remove'])
1202 if isinstance(shape, ModelAction)
or isinstance(shape, ModelLoop):
1203 if shape.IsEnabled():
1204 popupMenu.Append(self.
popupID[
'enable'], text=_(
'Disable'))
1207 popupMenu.Append(self.
popupID[
'enable'], text=_(
'Enable'))
1208 self.frame.Bind(wx.EVT_MENU, self.
OnEnable, id = self.
popupID[
'enable'])
1210 if isinstance(shape, ModelRelation):
1211 popupMenu.AppendSeparator()
1212 popupMenu.Append(self.
popupID[
'addPoint'], text=_(
'Add control point'))
1214 popupMenu.Append(self.
popupID[
'delPoint'], text=_(
'Remove control point'))
1216 if len(shape.GetLineControlPoints()) == 2:
1217 popupMenu.Enable(self.
popupID[
'delPoint'],
False)
1219 if isinstance(shape, ModelData)
and '@' not in shape.GetValue():
1220 popupMenu.AppendSeparator()
1221 popupMenu.Append(self.
popupID[
'intermediate'], text=_(
'Intermediate'),
1222 kind = wx.ITEM_CHECK)
1223 if self.GetShape().IsIntermediate():
1224 popupMenu.Check(self.
popupID[
'intermediate'],
True)
1228 if isinstance(shape, ModelData)
or \
1229 isinstance(shape, ModelAction)
or \
1230 isinstance(shape, ModelLoop):
1231 popupMenu.AppendSeparator()
1232 popupMenu.Append(self.
popupID[
'props'], text=_(
'Properties'))
1235 self.frame.PopupMenu(popupMenu)
1239 """!Disable action"""
1243 """!Disable action"""
1246 def _onEnable(self, enable):
1247 shape = self.GetShape()
1248 shape.Enable(enable)
1249 self.frame.ModelChanged()
1250 self.frame.canvas.Refresh()
1252 def _onSelectShape(self, shape):
1253 canvas = shape.GetCanvas()
1254 dc = wx.ClientDC(canvas)
1256 if shape.Selected():
1257 shape.Select(
False, dc)
1260 shapeList = canvas.GetDiagram().GetShapeList()
1265 toUnselect.append(s)
1267 shape.Select(
True, dc)
1269 for s
in toUnselect:
1272 canvas.Refresh(
False)
1275 """!Add control point"""
1276 shape = self.GetShape()
1277 shape.InsertLineControlPoint(point = wx.RealPoint(self.
x, self.
y))
1280 self.frame.ModelChanged()
1281 self.frame.canvas.Refresh()
1284 """!Remove control point"""
1285 shape = self.GetShape()
1286 shape.DeleteLineControlPoint()
1289 self.frame.ModelChanged()
1290 self.frame.canvas.Refresh()
1293 """!Mark data as intermediate"""
1294 self.frame.ModelChanged()
1295 shape = self.GetShape()
1296 shape.SetIntermediate(event.IsChecked())
1297 self.frame.canvas.Refresh()
1302 self.frame.GetCanvas().RemoveShapes([self.GetShape()])
1303 self.frame.itemPanel.Update()
1306 def __init__(self, parent, id = wx.ID_ANY,
1308 """!Manage model variables panel
1312 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1314 self.
listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1315 label=
" %s " % _(
"List of variables - right-click to delete"))
1317 self.
list = VariableListCtrl(parent = self,
1318 columns = [_(
"Name"), _(
"Data type"),
1319 _(
"Default value"), _(
"Description")])
1322 self.
addBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1323 label =
" %s " % _(
"Add new variable"))
1324 self.
name = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1325 wx.CallAfter(self.name.SetFocus)
1326 self.
type = wx.Choice(parent = self, id = wx.ID_ANY,
1327 choices = [_(
"integer"),
1334 self.type.SetSelection(2)
1335 self.
value = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1336 self.
desc = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1339 self.
btnAdd = wx.Button(parent = self, id = wx.ID_ADD)
1340 self.btnAdd.SetToolTipString(_(
"Add new variable to the model"))
1341 self.btnAdd.Enable(
False)
1344 self.name.Bind(wx.EVT_TEXT, self.
OnText)
1345 self.value.Bind(wx.EVT_TEXT, self.
OnText)
1346 self.desc.Bind(wx.EVT_TEXT, self.
OnText)
1347 self.btnAdd.Bind(wx.EVT_BUTTON, self.
OnAdd)
1352 """!Layout dialog"""
1353 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
1354 listSizer.Add(item = self.
list, proportion = 1,
1357 addSizer = wx.StaticBoxSizer(self.
addBox, wx.VERTICAL)
1358 gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
1359 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1360 label =
"%s:" % _(
"Name")),
1361 flag = wx.ALIGN_CENTER_VERTICAL,
1363 gridSizer.Add(item = self.
name,
1366 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1367 label =
"%s:" % _(
"Data type")),
1368 flag = wx.ALIGN_CENTER_VERTICAL,
1370 gridSizer.Add(item = self.
type,
1372 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1373 label =
"%s:" % _(
"Default value")),
1374 flag = wx.ALIGN_CENTER_VERTICAL,
1376 gridSizer.Add(item = self.
value,
1377 pos = (1, 1), span = (1, 3),
1379 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY,
1380 label =
"%s:" % _(
"Description")),
1381 flag = wx.ALIGN_CENTER_VERTICAL,
1383 gridSizer.Add(item = self.
desc,
1384 pos = (2, 1), span = (1, 3),
1386 gridSizer.AddGrowableCol(1)
1387 addSizer.Add(item = gridSizer,
1389 addSizer.Add(item = self.
btnAdd, proportion = 0,
1390 flag = wx.TOP | wx.ALIGN_RIGHT, border = 5)
1392 mainSizer = wx.BoxSizer(wx.VERTICAL)
1393 mainSizer.Add(item = listSizer, proportion = 1,
1394 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1395 mainSizer.Add(item = addSizer, proportion = 0,
1396 flag = wx.EXPAND | wx.ALIGN_CENTER |
1397 wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
1399 self.SetSizer(mainSizer)
1404 if self.name.GetValue():
1405 self.btnAdd.Enable()
1407 self.btnAdd.Enable(
False)
1410 """!Add new variable to the list"""
1411 msg = self.list.Append(self.name.GetValue(),
1412 self.type.GetStringSelection(),
1413 self.value.GetValue(),
1414 self.desc.GetValue())
1415 self.name.SetValue(
'')
1416 self.name.SetFocus()
1419 GError(parent = self,
1422 self.type.SetSelection(2)
1423 self.value.SetValue(
'')
1424 self.desc.SetValue(
'')
1428 """!Update model variables"""
1430 for values
in self.list.GetData().itervalues():
1432 variables[name] = {
'type' : str(values[1]) }
1434 variables[name][
'value'] = values[2]
1436 variables[name][
'description'] = values[3]
1438 self.parent.GetModel().SetVariables(variables)
1439 self.parent.ModelChanged()
1442 """!Reload list of variables"""
1443 self.list.OnReload(
None)
1446 """!Remove all variables"""
1447 self.list.DeleteAllItems()
1448 self.parent.GetModel().SetVariables([])
1451 def __init__(self, parent, id = wx.ID_ANY,
1453 """!Manage model items
1457 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1459 self.
listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1460 label=
" %s " % _(
"List of items - right-click to delete"))
1462 self.
list = ItemListCtrl(parent = self,
1463 columns = [_(
"ID"), _(
"Name"), _(
"In block"),
1464 _(
"Command / Condition")])
1469 """!Layout dialog"""
1470 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
1471 listSizer.Add(item = self.
list, proportion = 1,
1474 mainSizer = wx.BoxSizer(wx.VERTICAL)
1475 mainSizer.Add(item = listSizer, proportion = 1,
1476 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1478 self.SetSizer(mainSizer)
1482 """!Reload list of variables"""
1483 self.list.OnReload(
None)
1486 def __init__(self, parent, id = wx.ID_ANY,
1488 """!Model as python script
1492 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1496 self.
bodyBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1497 label =
" %s " % _(
"Python script"))
1498 self.
body = PyStc(parent = self, statusbar = self.parent.GetStatusBar())
1500 self.
btnRun = wx.Button(parent = self, id = wx.ID_ANY, label = _(
"&Run"))
1501 self.btnRun.SetToolTipString(_(
"Run python script"))
1503 self.
btnSaveAs = wx.Button(parent = self, id = wx.ID_SAVEAS)
1504 self.btnSaveAs.SetToolTipString(_(
"Save python script to file"))
1507 self.btnRefresh.SetToolTipString(_(
"Refresh python script based on the model.\n"
1508 "It will discards all local changes."))
1514 sizer = wx.BoxSizer(wx.VERTICAL)
1515 bodySizer = wx.StaticBoxSizer(self.
bodyBox, wx.HORIZONTAL)
1516 btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1518 bodySizer.Add(item = self.
body, proportion = 1,
1519 flag = wx.EXPAND | wx.ALL, border = 3)
1521 btnSizer.Add(item = self.
btnRefresh, proportion = 0,
1522 flag = wx.LEFT | wx.RIGHT, border = 5)
1523 btnSizer.AddStretchSpacer()
1524 btnSizer.Add(item = self.
btnSaveAs, proportion = 0,
1525 flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
1526 btnSizer.Add(item = self.
btnRun, proportion = 0,
1527 flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
1529 sizer.Add(item = bodySizer, proportion = 1,
1530 flag = wx.EXPAND | wx.ALL, border = 3)
1531 sizer.Add(item = btnSizer, proportion = 0,
1532 flag = wx.EXPAND | wx.ALL, border = 3)
1535 sizer.SetSizeHints(self)
1536 self.SetSizer(sizer)
1539 """!Run Python script"""
1543 fd.write(self.body.GetText())
1545 GError(_(
"Unable to launch Python script. %s") % e,
1550 mode = stat.S_IMODE(os.lstat(self.
filename)[stat.ST_MODE])
1551 os.chmod(self.
filename, mode | stat.S_IXUSR)
1553 self.parent.goutput.RunCmd([fd.name], switchPage =
True,
1554 skipInterface =
True, onDone = self.
OnDone)
1559 """!Python script finished"""
1564 """!Save python script to file
1569 dlg = wx.FileDialog(parent = self,
1570 message = _(
"Choose file to save"),
1571 defaultDir = os.getcwd(),
1572 wildcard = _(
"Python script (*.py)|*.py"),
1575 if dlg.ShowModal() == wx.ID_OK:
1576 filename = dlg.GetPath()
1582 if filename[-3:] !=
".py":
1585 if os.path.exists(filename):
1586 dlg = wx.MessageDialog(self, message=_(
"File <%s> already exists. "
1587 "Do you want to overwrite this file?") % filename,
1588 caption=_(
"Save file"),
1589 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
1590 if dlg.ShowModal() == wx.ID_NO:
1596 fd = open(filename,
"w")
1599 WritePythonFile(fd, self.parent.GetModel())
1601 fd.write(self.body.GetText())
1606 os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
1611 """!Save python script to file"""
1612 self.
SaveAs(force =
False)
1616 """!Refresh Python script
1618 @return True on refresh
1619 @return False script hasn't been updated
1621 if self.body.modified:
1622 dlg = wx.MessageDialog(self,
1623 message = _(
"Python script is locally modificated. "
1624 "Refresh will discard all changes. "
1625 "Do you really want to continue?"),
1626 caption=_(
"Update"),
1627 style = wx.YES_NO | wx.NO_DEFAULT |
1628 wx.ICON_QUESTION | wx.CENTRE)
1629 ret = dlg.ShowModal()
1634 fd = tempfile.TemporaryFile()
1635 WritePythonFile(fd, self.parent.GetModel())
1637 self.body.SetText(fd.read())
1640 self.body.modified =
False
1645 """!Refresh Python script"""
1647 self.parent.SetStatusText(_(
'Python script is up-to-date'), 0)
1651 """!Check if python script has been modified"""
1652 return self.body.modified
1655 """!Check if python script is empty"""
1656 return len(self.body.GetText()) == 0
1660 gettext.install(
'grasswxpy', os.path.join(os.getenv(
"GISBASE"),
'locale'), unicode =
True)
1662 app = wx.PySimpleApp()
1663 wx.InitAllImageHandlers()
1665 if len(sys.argv) > 1:
1666 frame.LoadModelFile(sys.argv[1])
1671 if __name__ ==
"__main__":
def LoadModelFile
Load model definition stored in GRASS Model XML file (gxm)
def OnLeftDoubleClick
Left mouse button pressed (double-click) -> show properties.
def OnRefresh
Refresh Python script.
def WriteModelFile
Save model to model file, recover original file on error.
wxGUI Graphical Modeler - dialogs
def OnSize
Window resized, save to the model.
def OnCanvasRefresh
Refresh canvas.
def OnRemoveItem
Remove shape.
def OnBeginDragLeft
Drag shape (begining)
def OnAbout
Display About window.
def OnAdd
Add new variable to the list.
def OnRunModel
Run entire model.
def OnVariables
Switch to variables page.
def OnModelNew
Create new model.
def IsEmpty
Check if python script is empty.
wxGUI Graphical Modeler (base classes & read/write)
def RemoveSelected
Remove selected shapes.
def OnProperties
Show properties dialog.
def OnDeleteData
Delete intermediate data.
def OnDisable
Disable action.
def AddLine
Add connection between model objects.
def __init__
Manage model variables panel.
def DefineLoop
Define loop with given list of items.
def _layout
Layout dialog.
wxGUI Graphical Modeler - preferences
Various dialogs used in wxGUI.
def OnRemovePoint
Remove control point.
def OnCloseWindow
Close window.
def OnEndSize
Resize shape.
def _layout
Layout dialog.
def OnPageChanged
Page in notebook changed.
def DefineCondition
Define if-else statement with given list of items.
def ModelChanged
Update window title.
Canvas where model is drawn.
def GetOptData
Process action data.
def Update
Reload list of variables.
def UpdateModelVariables
Update model variables.
def OnExportImage
Export model to image (default image)
def OnPreferences
Open preferences dialog.
def OnCmdDone
Command done (or aborted)
def OnModelClose
Close model file.
def GetNewShapePos
Determine optimal position for newly added object.
def OnDone
Computation finished.
def OnValidateModel
Validate entire model.
def OnModelProperties
Model properties dialog.
def OnAddAction
Add action to model.
def OnRemove
Remove shape.
def OnEnable
Disable action.
def __init__
Manage model items.
def RefreshScript
Refresh Python script.
def GetImageHandlers
Get list of supported image handlers.
def OnModelSave
Save model to file.
def OnDefineCondition
Define new condition in the model.
def OnAddPoint
Add control point.
Model event handler class.
def OnModelOpen
Load model from file.
def _randomShift
Returns random value to shift layout.
def OnRun
Run Python script.
def OnRightClick
Right click -> pop-up menu.
def _addEvent
Add event to item.
def RemoveShapes
Removes shapes.
def OnDefineLoop
Define new loop in the model.
def OnLeftClick
Left mouse button pressed -> select item & update statusbar.
def SaveAs
Save python script to file.
def __init__
Graphical modeler main window.
def Reset
Remove all variables.
def __init__
Model as python script.
def OnDone
Python script finished.
def OnSaveAs
Save python script to file.
def Update
Reload list of variables.
def OnCmdPrepare
Prepare for running command.
def OnEndDragLeft
Drag shape (end)
def IsModified
Check if python script has been modified.
def OnDefineRelation
Define relation between data and action items.
def OnExportPython
Export model to Python script.
def OnAddData
Add data item to model.
def OnModelSaveAs
Create model to file as.
def RunCommand
Run GRASS command.
def OnIntermediate
Mark data as intermediate.