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__":