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.") % 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)
1052 canvas.PrepareDC(dc)
1054 if hasattr(self.
frame,
'defineRelation'):
1055 drel = self.frame.defineRelation
1056 if drel[
'from']
is None:
1057 drel[
'from'] = shape
1058 elif drel[
'to']
is None:
1060 rel = ModelRelation(parent = self.
frame, fromShape = drel[
'from'],
1061 toShape = drel[
'to'])
1062 dlg = ModelRelationDialog(parent = self.
frame,
1065 ret = dlg.ShowModal()
1067 option = dlg.GetOption()
1069 drel[
'from'].AddRelation(rel)
1070 drel[
'to'].AddRelation(rel)
1071 drel[
'from'].Update()
1072 params = {
'params' : [{
'name' : option,
1073 'value' : drel[
'from'].
GetValue()}] }
1074 drel[
'to'].MergeParams(params)
1075 self.frame.AddLine(rel)
1078 del self.frame.defineRelation
1083 if hasattr(shape,
"GetLog"):
1084 self.log.SetStatusText(shape.GetLog(), 0)
1086 self.log.SetStatusText(
'', 0)
1089 """!Left mouse button pressed (double-click) -> show properties"""
1093 """!Show properties dialog"""
1094 self.frame.ModelChanged()
1095 shape = self.GetShape()
1096 if isinstance(shape, ModelAction):
1097 module = GUI(parent = self.
frame, show =
True).ParseCommand(shape.GetLog(string =
False),
1098 completed = (self.frame.GetOptData, shape, shape.GetParams()))
1100 elif isinstance(shape, ModelData):
1101 dlg = ModelDataDialog(parent = self.
frame, shape = shape)
1102 shape.SetPropDialog(dlg)
1103 dlg.CentreOnParent()
1106 elif isinstance(shape, ModelLoop):
1107 dlg = ModelLoopDialog(parent = self.
frame, shape = shape)
1108 dlg.CentreOnParent()
1109 if dlg.ShowModal() == wx.ID_OK:
1110 shape.SetText(dlg.GetCondition())
1112 ids = dlg.GetItems()
1113 for aId
in ids[
'unchecked']:
1114 action = self.frame.GetModel().GetItem(aId)
1115 action.UnSetBlock(shape)
1116 for aId
in ids[
'checked']:
1117 action = self.frame.GetModel().GetItem(aId)
1118 action.SetBlock(shape)
1120 alist.append(action)
1121 shape.SetItems(alist)
1122 self.frame.DefineLoop(shape)
1123 self.frame.SetStatusText(shape.GetLog(), 0)
1124 self.frame.GetCanvas().Refresh()
1128 elif isinstance(shape, ModelCondition):
1129 dlg = ModelConditionDialog(parent = self.
frame, shape = shape)
1130 dlg.CentreOnParent()
1131 if dlg.ShowModal() == wx.ID_OK:
1132 shape.SetText(dlg.GetCondition())
1133 ids = dlg.GetItems()
1134 for b
in ids.keys():
1136 for aId
in ids[b][
'unchecked']:
1137 action = self.frame.GetModel().GetItem(aId)
1138 action.UnSetBlock(shape)
1139 for aId
in ids[b][
'checked']:
1140 action = self.frame.GetModel().GetItem(aId)
1141 action.SetBlock(shape)
1143 alist.append(action)
1144 shape.SetItems(alist, branch = b)
1145 self.frame.DefineCondition(shape)
1146 self.frame.GetCanvas().Refresh()
1151 """!Drag shape (begining)"""
1152 self.frame.ModelChanged()
1153 if self._previousHandler:
1154 self._previousHandler.OnBeginDragLeft(x, y, keys, attachment)
1157 """!Drag shape (end)"""
1158 if self._previousHandler:
1159 self._previousHandler.OnEndDragLeft(x, y, keys, attachment)
1161 shape = self.GetShape()
1162 if isinstance(shape, ModelLoop):
1163 self.frame.DefineLoop(shape)
1164 elif isinstance(shape, ModelCondition):
1165 self.frame.DefineCondition(shape)
1167 for mo
in shape.GetBlock():
1168 if isinstance(mo, ModelLoop):
1169 self.frame.DefineLoop(mo)
1170 elif isinstance(mo, ModelCondition):
1171 self.frame.DefineCondition(mo)
1175 self.frame.ModelChanged()
1176 if self._previousHandler:
1177 self._previousHandler.OnEndSize(x, y)
1180 """!Right click -> pop-up menu"""
1181 if not hasattr (self,
"popupID"):
1183 for key
in (
'remove',
'enable',
'addPoint',
1184 'delPoint',
'intermediate',
'props',
'id'):
1185 self.
popupID[key] = wx.NewId()
1192 shape = self.GetShape()
1195 popupMenu = wx.Menu()
1196 popupMenu.Append(self.
popupID[
'remove'], text=_(
'Remove'))
1197 self.frame.Bind(wx.EVT_MENU, self.
OnRemove, id = self.
popupID[
'remove'])
1198 if isinstance(shape, ModelAction)
or isinstance(shape, ModelLoop):
1199 if shape.IsEnabled():
1200 popupMenu.Append(self.
popupID[
'enable'], text=_(
'Disable'))
1203 popupMenu.Append(self.
popupID[
'enable'], text=_(
'Enable'))
1204 self.frame.Bind(wx.EVT_MENU, self.
OnEnable, id = self.
popupID[
'enable'])
1206 if isinstance(shape, ModelRelation):
1207 popupMenu.AppendSeparator()
1208 popupMenu.Append(self.
popupID[
'addPoint'], text=_(
'Add control point'))
1210 popupMenu.Append(self.
popupID[
'delPoint'], text=_(
'Remove control point'))
1212 if len(shape.GetLineControlPoints()) == 2:
1213 popupMenu.Enable(self.
popupID[
'delPoint'],
False)
1215 if isinstance(shape, ModelData)
and '@' not in shape.GetValue():
1216 popupMenu.AppendSeparator()
1217 popupMenu.Append(self.
popupID[
'intermediate'], text=_(
'Intermediate'),
1218 kind = wx.ITEM_CHECK)
1219 if self.GetShape().IsIntermediate():
1220 popupMenu.Check(self.
popupID[
'intermediate'],
True)
1224 if isinstance(shape, ModelData)
or \
1225 isinstance(shape, ModelAction)
or \
1226 isinstance(shape, ModelLoop):
1227 popupMenu.AppendSeparator()
1228 popupMenu.Append(self.
popupID[
'props'], text=_(
'Properties'))
1231 self.frame.PopupMenu(popupMenu)
1235 """!Disable action"""
1239 """!Disable action"""
1242 def _onEnable(self, enable):
1243 shape = self.GetShape()
1244 shape.Enable(enable)
1245 self.frame.ModelChanged()
1246 self.frame.canvas.Refresh()
1248 def _onSelectShape(self, shape):
1249 canvas = shape.GetCanvas()
1250 dc = wx.ClientDC(canvas)
1252 if shape.Selected():
1253 shape.Select(
False, dc)
1256 shapeList = canvas.GetDiagram().GetShapeList()
1261 toUnselect.append(s)
1263 shape.Select(
True, dc)
1265 for s
in toUnselect:
1268 canvas.Refresh(
False)
1271 """!Add control point"""
1272 shape = self.GetShape()
1273 shape.InsertLineControlPoint(point = wx.RealPoint(self.
x, self.
y))
1276 self.frame.ModelChanged()
1277 self.frame.canvas.Refresh()
1280 """!Remove control point"""
1281 shape = self.GetShape()
1282 shape.DeleteLineControlPoint()
1285 self.frame.ModelChanged()
1286 self.frame.canvas.Refresh()
1289 """!Mark data as intermediate"""
1290 self.frame.ModelChanged()
1291 shape = self.GetShape()
1292 shape.SetIntermediate(event.IsChecked())
1293 self.frame.canvas.Refresh()
1298 self.frame.GetCanvas().RemoveShapes([self.GetShape()])
1299 self.frame.itemPanel.Update()
1302 def __init__(self, parent, id = wx.ID_ANY,
1304 """!Manage model variables panel
1308 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1310 self.
listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1311 label=
" %s " % _(
"List of variables - right-click to delete"))
1313 self.
list = VariableListCtrl(parent = self,
1314 columns = [_(
"Name"), _(
"Data type"),
1315 _(
"Default value"), _(
"Description")])
1318 self.
addBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1319 label =
" %s " % _(
"Add new variable"))
1320 self.
name = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1321 wx.CallAfter(self.name.SetFocus)
1322 self.
type = wx.Choice(parent = self, id = wx.ID_ANY,
1323 choices = [_(
"integer"),
1330 self.type.SetSelection(2)
1331 self.
value = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1332 self.
desc = wx.TextCtrl(parent = self, id = wx.ID_ANY)
1335 self.
btnAdd = wx.Button(parent = self, id = wx.ID_ADD)
1336 self.btnAdd.SetToolTipString(_(
"Add new variable to the model"))
1337 self.btnAdd.Enable(
False)
1340 self.name.Bind(wx.EVT_TEXT, self.
OnText)
1341 self.value.Bind(wx.EVT_TEXT, self.
OnText)
1342 self.desc.Bind(wx.EVT_TEXT, self.
OnText)
1343 self.btnAdd.Bind(wx.EVT_BUTTON, self.
OnAdd)
1348 """!Layout dialog"""
1349 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
1350 listSizer.Add(item = self.
list, proportion = 1,
1353 addSizer = wx.StaticBoxSizer(self.
addBox, wx.VERTICAL)
1354 gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5)
1355 gridSizer.AddGrowableCol(1)
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 addSizer.Add(item = gridSizer,
1385 addSizer.Add(item = self.
btnAdd, proportion = 0,
1386 flag = wx.TOP | wx.ALIGN_RIGHT, border = 5)
1388 mainSizer = wx.BoxSizer(wx.VERTICAL)
1389 mainSizer.Add(item = listSizer, proportion = 1,
1390 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1391 mainSizer.Add(item = addSizer, proportion = 0,
1392 flag = wx.EXPAND | wx.ALIGN_CENTER |
1393 wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5)
1395 self.SetSizer(mainSizer)
1400 if self.name.GetValue():
1401 self.btnAdd.Enable()
1403 self.btnAdd.Enable(
False)
1406 """!Add new variable to the list"""
1407 msg = self.list.Append(self.name.GetValue(),
1408 self.type.GetStringSelection(),
1409 self.value.GetValue(),
1410 self.desc.GetValue())
1411 self.name.SetValue(
'')
1412 self.name.SetFocus()
1415 GError(parent = self,
1418 self.type.SetSelection(2)
1419 self.value.SetValue(
'')
1420 self.desc.SetValue(
'')
1424 """!Update model variables"""
1426 for values
in self.list.GetData().itervalues():
1428 variables[name] = {
'type' : str(values[1]) }
1430 variables[name][
'value'] = values[2]
1432 variables[name][
'description'] = values[3]
1434 self.parent.GetModel().SetVariables(variables)
1435 self.parent.ModelChanged()
1438 """!Reload list of variables"""
1439 self.list.OnReload(
None)
1442 """!Remove all variables"""
1443 self.list.DeleteAllItems()
1444 self.parent.GetModel().SetVariables([])
1447 def __init__(self, parent, id = wx.ID_ANY,
1449 """!Manage model items
1453 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1455 self.
listBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1456 label=
" %s " % _(
"List of items - right-click to delete"))
1458 self.
list = ItemListCtrl(parent = self,
1459 columns = [_(
"ID"), _(
"Name"), _(
"In block"),
1460 _(
"Command / Condition")])
1465 """!Layout dialog"""
1466 listSizer = wx.StaticBoxSizer(self.
listBox, wx.VERTICAL)
1467 listSizer.Add(item = self.
list, proportion = 1,
1470 mainSizer = wx.BoxSizer(wx.VERTICAL)
1471 mainSizer.Add(item = listSizer, proportion = 1,
1472 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5)
1474 self.SetSizer(mainSizer)
1478 """!Reload list of variables"""
1479 self.list.OnReload(
None)
1482 def __init__(self, parent, id = wx.ID_ANY,
1484 """!Model as python script
1488 wx.Panel.__init__(self, parent = parent, id = id, **kwargs)
1492 self.
bodyBox = wx.StaticBox(parent = self, id = wx.ID_ANY,
1493 label =
" %s " % _(
"Python script"))
1494 self.
body = PyStc(parent = self, statusbar = self.parent.GetStatusBar())
1496 self.
btnRun = wx.Button(parent = self, id = wx.ID_ANY, label = _(
"&Run"))
1497 self.btnRun.SetToolTipString(_(
"Run python script"))
1499 self.
btnSaveAs = wx.Button(parent = self, id = wx.ID_SAVEAS)
1500 self.btnSaveAs.SetToolTipString(_(
"Save python script to file"))
1503 self.btnRefresh.SetToolTipString(_(
"Refresh python script based on the model.\n"
1504 "It will discards all local changes."))
1510 sizer = wx.BoxSizer(wx.VERTICAL)
1511 bodySizer = wx.StaticBoxSizer(self.
bodyBox, wx.HORIZONTAL)
1512 btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1514 bodySizer.Add(item = self.
body, proportion = 1,
1515 flag = wx.EXPAND | wx.ALL, border = 3)
1517 btnSizer.Add(item = self.
btnRefresh, proportion = 0,
1518 flag = wx.LEFT | wx.RIGHT, border = 5)
1519 btnSizer.AddStretchSpacer()
1520 btnSizer.Add(item = self.
btnSaveAs, proportion = 0,
1521 flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
1522 btnSizer.Add(item = self.
btnRun, proportion = 0,
1523 flag = wx.RIGHT | wx.ALIGN_RIGHT, border = 5)
1525 sizer.Add(item = bodySizer, proportion = 1,
1526 flag = wx.EXPAND | wx.ALL, border = 3)
1527 sizer.Add(item = btnSizer, proportion = 0,
1528 flag = wx.EXPAND | wx.ALL, border = 3)
1531 sizer.SetSizeHints(self)
1532 self.SetSizer(sizer)
1535 """!Run Python script"""
1539 fd.write(self.body.GetText())
1541 GError(_(
"Unable to launch Python script. %s") % e,
1546 mode = stat.S_IMODE(os.lstat(self.
filename)[stat.ST_MODE])
1547 os.chmod(self.
filename, mode | stat.S_IXUSR)
1549 self.parent.goutput.RunCmd([fd.name], switchPage =
True,
1550 skipInterface =
True, onDone = self.
OnDone)
1555 """!Python script finished"""
1560 """!Save python script to file
1565 dlg = wx.FileDialog(parent = self,
1566 message = _(
"Choose file to save"),
1567 defaultDir = os.getcwd(),
1568 wildcard = _(
"Python script (*.py)|*.py"),
1571 if dlg.ShowModal() == wx.ID_OK:
1572 filename = dlg.GetPath()
1578 if filename[-3:] !=
".py":
1581 if os.path.exists(filename):
1582 dlg = wx.MessageDialog(self, message=_(
"File <%s> already exists. "
1583 "Do you want to overwrite this file?") % filename,
1584 caption=_(
"Save file"),
1585 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
1586 if dlg.ShowModal() == wx.ID_NO:
1592 fd = open(filename,
"w")
1595 WritePythonFile(fd, self.parent.GetModel())
1597 fd.write(self.body.GetText())
1602 os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR)
1607 """!Save python script to file"""
1608 self.
SaveAs(force =
False)
1612 """!Refresh Python script
1614 @return True on refresh
1615 @return False script hasn't been updated
1617 if self.body.modified:
1618 dlg = wx.MessageDialog(self,
1619 message = _(
"Python script is locally modificated. "
1620 "Refresh will discard all changes. "
1621 "Do you really want to continue?"),
1622 caption=_(
"Update"),
1623 style = wx.YES_NO | wx.NO_DEFAULT |
1624 wx.ICON_QUESTION | wx.CENTRE)
1625 ret = dlg.ShowModal()
1630 fd = tempfile.TemporaryFile()
1631 WritePythonFile(fd, self.parent.GetModel())
1633 self.body.SetText(fd.read())
1636 self.body.modified =
False
1641 """!Refresh Python script"""
1643 self.parent.SetStatusText(_(
'Python script is up-to-date'), 0)
1647 """!Check if python script has been modified"""
1648 return self.body.modified
1651 """!Check if python script is empty"""
1652 return len(self.body.GetText()) == 0
1656 gettext.install(
'grasswxpy', os.path.join(os.getenv(
"GISBASE"),
'locale'), unicode =
True)
1658 app = wx.PySimpleApp()
1659 wx.InitAllImageHandlers()
1661 if len(sys.argv) > 1:
1662 frame.LoadModelFile(sys.argv[1])
1667 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 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.