GRASS Programmer's Manual
6.4.2(2012)
|
00001 """! 00002 @package gmodeler.py 00003 00004 @brief wxGUI Graphical Modeler for creating, editing, and managing models 00005 00006 Classes: 00007 - Model 00008 - ModelFrame 00009 - ModelCanvas 00010 - ModelObject 00011 - ModelAction 00012 - ModelSearchDialog 00013 - ModelData 00014 - ModelDataDialog 00015 - ModelRelation 00016 - ModelRelationDialog 00017 - ProcessModelFile 00018 - WriteModelFile 00019 - PreferencesDialog 00020 - PropertiesDialog 00021 - ModelParamDialog 00022 - ModelListCtrl 00023 - VariablePanel 00024 - ValiableListCtrl 00025 - ModelItem 00026 - ModelItemDialog 00027 - ModelLoop 00028 - ModelLoopDialog 00029 - ItemPanel 00030 - ItemListCtrl 00031 - ItemCheckListCtrl 00032 - ModelCondition 00033 - ModelConditionDialog 00034 - WritePythonFile 00035 00036 (C) 2010-2011 by the GRASS Development Team 00037 This program is free software under the GNU General Public License 00038 (>=v2). Read the file COPYING that comes with GRASS for details. 00039 00040 @author Martin Landa <landa.martin gmail.com> 00041 """ 00042 00043 import os 00044 import sys 00045 import time 00046 import traceback 00047 import getpass 00048 import stat 00049 import textwrap 00050 import tempfile 00051 import copy 00052 import re 00053 00054 try: 00055 import xml.etree.ElementTree as etree 00056 except ImportError: 00057 import elementtree.ElementTree as etree # Python <= 2.4 00058 00059 import globalvar 00060 import wx 00061 import wx.lib.ogl as ogl 00062 import wx.lib.flatnotebook as FN 00063 import wx.lib.colourselect as csel 00064 import wx.lib.mixins.listctrl as listmix 00065 00066 import menu 00067 import menudata 00068 import toolbars 00069 import menuform 00070 import prompt 00071 import utils 00072 import goutput 00073 import gselect 00074 from debug import Debug 00075 from gcmd import GMessage, GException, GWarning, GError, RunCommand 00076 from gdialogs import ElementDialog, GetImageHandlers 00077 from preferences import PreferencesBaseDialog, globalSettings as UserSettings 00078 from ghelp import SearchModuleWindow 00079 00080 from grass.script import core as grass 00081 from grass.script import task as gtask 00082 00083 class Model(object): 00084 """!Class representing the model""" 00085 def __init__(self, canvas = None): 00086 self.items = list() # list of actions/loops/... 00087 00088 # model properties 00089 self.properties = { 'name' : _("model"), 00090 'description' : _("Script generated by wxGUI Graphical Modeler."), 00091 'author' : getpass.getuser() } 00092 # model variables 00093 self.variables = dict() 00094 self.variablesParams = dict() 00095 00096 self.canvas = canvas 00097 00098 def GetCanvas(self): 00099 """!Get canvas or None""" 00100 return self.canvas 00101 00102 def GetItems(self, objType = None): 00103 """!Get list of model items 00104 00105 @param objType Object type to filter model objects 00106 """ 00107 if not objType: 00108 return self.items 00109 00110 result = list() 00111 for item in self.items: 00112 if isinstance(item, objType): 00113 result.append(item) 00114 00115 return result 00116 00117 def GetItem(self, aId): 00118 """!Get item of given id 00119 00120 @param aId item id 00121 00122 @return Model* instance 00123 @return None if no item found 00124 """ 00125 ilist = self.GetItems() 00126 for item in ilist: 00127 if item.GetId() == aId: 00128 return item 00129 00130 return None 00131 00132 def GetNumItems(self, actionOnly = False): 00133 """!Get number of items""" 00134 if actionOnly: 00135 return len(self.GetItems(objType = ModelAction)) 00136 00137 return len(self.GetItems()) 00138 00139 def GetNextId(self): 00140 """!Get next id (data ignored) 00141 00142 @return next id to be used (default: 1) 00143 """ 00144 if len(self.items) < 1: 00145 return 1 00146 00147 currId = self.items[-1].GetId() 00148 if currId > 0: 00149 return currId + 1 00150 00151 return 1 00152 00153 def GetProperties(self): 00154 """!Get model properties""" 00155 return self.properties 00156 00157 def GetVariables(self, params = False): 00158 """!Get model variables""" 00159 if params: 00160 return self.variablesParams 00161 00162 return self.variables 00163 00164 def SetVariables(self, data): 00165 """!Set model variables""" 00166 self.variables = data 00167 00168 def Reset(self): 00169 """!Reset model""" 00170 self.items = list() 00171 00172 def RemoveItem(self, item): 00173 """!Remove item from model 00174 00175 @return list of related items to remove/update 00176 """ 00177 relList = list() 00178 upList = list() 00179 00180 if not isinstance(item, ModelData): 00181 self.items.remove(item) 00182 00183 if isinstance(item, ModelAction): 00184 for rel in item.GetRelations(): 00185 relList.append(rel) 00186 data = rel.GetData() 00187 if len(data.GetRelations()) < 2: 00188 relList.append(data) 00189 else: 00190 upList.append(data) 00191 00192 elif isinstance(item, ModelData): 00193 for rel in item.GetRelations(): 00194 relList.append(rel) 00195 if rel.GetFrom() == self: 00196 relList.append(rel.GetTo()) 00197 else: 00198 relList.append(rel.GetFrom()) 00199 00200 elif isinstance(item, ModelLoop): 00201 for rel in item.GetRelations(): 00202 relList.append(rel) 00203 for action in self.GetItems(): 00204 action.UnSetBlock(item) 00205 00206 return relList, upList 00207 00208 def FindAction(self, aId): 00209 """!Find action by id""" 00210 alist = self.GetItems(objType = ModelAction) 00211 for action in alist: 00212 if action.GetId() == aId: 00213 return action 00214 00215 return None 00216 00217 def GetData(self): 00218 """!Get list of data items""" 00219 result = list() 00220 dataItems = self.GetItems(objType = ModelData) 00221 00222 for action in self.GetItems(objType = ModelAction): 00223 for rel in action.GetRelations(): 00224 dataItem = rel.GetData() 00225 if dataItem not in result: 00226 result.append(dataItem) 00227 if dataItem in dataItems: 00228 dataItems.remove(dataItem) 00229 00230 # standalone data 00231 if dataItems: 00232 result += dataItems 00233 00234 return result 00235 00236 def FindData(self, value, prompt): 00237 """!Find data item in the model 00238 00239 @param value value 00240 @param prompt prompt 00241 00242 @return ModelData instance 00243 @return None if not found 00244 """ 00245 for data in self.GetData(): 00246 if data.GetValue() == value and \ 00247 data.GetPrompt() == prompt: 00248 return data 00249 00250 return None 00251 00252 def LoadModel(self, filename): 00253 """!Load model definition stored in GRASS Model XML file (gxm) 00254 00255 @todo Validate against DTD 00256 00257 Raise exception on error. 00258 """ 00259 dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxm.dtd") 00260 00261 # parse workspace file 00262 try: 00263 gxmXml = ProcessModelFile(etree.parse(filename)) 00264 except StandardError, e: 00265 raise GException(e) 00266 00267 if self.canvas: 00268 win = self.canvas.parent 00269 if gxmXml.pos: 00270 win.SetPosition(gxmXml.pos) 00271 if gxmXml.size: 00272 win.SetSize(gxmXml.size) 00273 00274 # load properties 00275 self.properties = gxmXml.properties 00276 self.variables = gxmXml.variables 00277 00278 # load model.GetActions() 00279 for action in gxmXml.actions: 00280 actionItem = ModelAction(parent = self, 00281 x = action['pos'][0], 00282 y = action['pos'][1], 00283 width = action['size'][0], 00284 height = action['size'][1], 00285 task = action['task'], 00286 id = action['id']) 00287 00288 if action['disabled']: 00289 actionItem.Enable(False) 00290 00291 self.AddItem(actionItem) 00292 00293 task = actionItem.GetTask() 00294 parameterized = False 00295 valid = True 00296 for f in task.get_options()['flags']: 00297 if f.get('parameterized', False): 00298 parameterized = True 00299 break 00300 for p in task.get_options()['params']: 00301 if p.get('required', 'no') != 'no' and \ 00302 p.get('value', '') == '' and \ 00303 p.get('default', '') == '': 00304 valid = False 00305 if p.get('parameterized', False): 00306 parameterized = True 00307 00308 actionItem.SetValid(valid) 00309 actionItem.SetParameterized(parameterized) 00310 actionItem.GetLog() # substitute variables (-> valid/invalid) 00311 00312 # load data & relations 00313 for data in gxmXml.data: 00314 dataItem = ModelData(parent = self, 00315 x = data['pos'][0], 00316 y = data['pos'][1], 00317 width = data['size'][0], 00318 height = data['size'][1], 00319 prompt = data['prompt'], 00320 value = data['value']) 00321 dataItem.SetIntermediate(data['intermediate']) 00322 00323 for rel in data['rels']: 00324 actionItem = self.FindAction(rel['id']) 00325 if rel['dir'] == 'from': 00326 relation = ModelRelation(parent = self, fromShape = dataItem, 00327 toShape = actionItem, param = rel['name']) 00328 else: 00329 relation = ModelRelation(parent = self, fromShape = actionItem, 00330 toShape = dataItem, param = rel['name']) 00331 relation.SetControlPoints(rel['points']) 00332 actionItem.AddRelation(relation) 00333 dataItem.AddRelation(relation) 00334 00335 if self.canvas: 00336 dataItem.Update() 00337 00338 # load loops 00339 for loop in gxmXml.loops: 00340 loopItem = ModelLoop(parent = self, 00341 x = loop['pos'][0], 00342 y = loop['pos'][1], 00343 width = loop['size'][0], 00344 height = loop['size'][1], 00345 text = loop['text'], 00346 id = loop['id']) 00347 self.AddItem(loopItem) 00348 00349 # load conditions 00350 for condition in gxmXml.conditions: 00351 conditionItem = ModelCondition(parent = self, 00352 x = condition['pos'][0], 00353 y = condition['pos'][1], 00354 width = condition['size'][0], 00355 height = condition['size'][1], 00356 text = condition['text'], 00357 id = condition['id']) 00358 self.AddItem(conditionItem) 00359 00360 # define loops & if/else items 00361 for loop in gxmXml.loops: 00362 alist = list() 00363 for aId in loop['items']: 00364 action = self.GetItem(aId) 00365 alist.append(action) 00366 00367 loopItem = self.GetItem(loop['id']) 00368 loopItem.SetItems(alist) 00369 00370 for action in loopItem.GetItems(): 00371 action.SetBlock(loopItem) 00372 00373 for condition in gxmXml.conditions: 00374 conditionItem = self.GetItem(condition['id']) 00375 for b in condition['items'].keys(): 00376 alist = list() 00377 for aId in condition['items'][b]: 00378 action = self.GetItem(aId) 00379 alist.append(action) 00380 conditionItem.SetItems(alist, branch = b) 00381 00382 items = conditionItem.GetItems() 00383 for b in items.keys(): 00384 for action in items[b]: 00385 action.SetBlock(conditionItem) 00386 00387 def AddItem(self, newItem): 00388 """!Add item to the list""" 00389 iId = newItem.GetId() 00390 00391 i = 0 00392 for item in self.items: 00393 if item.GetId() > iId: 00394 self.items.insert(i, newItem) 00395 return 00396 i += 1 00397 00398 self.items.append(newItem) 00399 00400 def IsValid(self): 00401 """Return True if model is valid""" 00402 if self.Validate(): 00403 return False 00404 00405 return True 00406 00407 def Validate(self): 00408 """!Validate model, return None if model is valid otherwise 00409 error string""" 00410 errList = list() 00411 for action in self.GetItems(objType = ModelAction): 00412 task = menuform.GUI(show = None).ParseCommand(cmd = action.GetLog(string = False)) 00413 errList += task.getCmdError() 00414 00415 return errList 00416 00417 def RunAction(self, item, params, log, onDone, statusbar = None): 00418 """!Run given action 00419 00420 @param item action item 00421 @param params parameters dict 00422 @param log logging window 00423 @param onDone on-done method 00424 @param statusbar wx.StatusBar instance or None 00425 """ 00426 name = item.GetName() 00427 if name in params: 00428 paramsOrig = item.GetParams(dcopy = True) 00429 item.MergeParams(params[name]) 00430 00431 if statusbar: 00432 statusbar.SetStatusText(_('Running model...'), 0) 00433 log.RunCmd(command = item.GetLog(string = False), 00434 onDone = onDone) 00435 00436 if name in params: 00437 item.SetParams(paramsOrig) 00438 00439 def Run(self, log, onDone, parent = None): 00440 """!Run model 00441 00442 @param log logging window (see goutput.GMConsole) 00443 @param onDone on-done method 00444 @param parent window for messages or None 00445 """ 00446 if self.GetNumItems() < 1: 00447 GMessage(parent = parent, 00448 message = _('Model is empty. Nothing to run.')) 00449 return 00450 00451 statusbar = None 00452 if isinstance(parent, wx.Frame): 00453 statusbar = parent.GetStatusBar() 00454 00455 # validation 00456 if statusbar: 00457 statusbar.SetStatusText(_('Validating model...'), 0) 00458 errList = self.Validate() 00459 if statusbar: 00460 statusbar.SetStatusText('', 0) 00461 if errList: 00462 dlg = wx.MessageDialog(parent = parent, 00463 message = _('Model is not valid. Do you want to ' 00464 'run the model anyway?\n\n%s') % '\n'.join(errList), 00465 caption = _("Run model?"), 00466 style = wx.YES_NO | wx.NO_DEFAULT | 00467 wx.ICON_QUESTION | wx.CENTRE) 00468 ret = dlg.ShowModal() 00469 if ret != wx.ID_YES: 00470 return 00471 00472 # parametrization 00473 params = self.Parameterize() 00474 if params: 00475 dlg = ModelParamDialog(parent = parent, 00476 params = params) 00477 dlg.CenterOnParent() 00478 00479 ret = dlg.ShowModal() 00480 if ret != wx.ID_OK: 00481 dlg.Destroy() 00482 return 00483 00484 err = dlg.GetErrors() 00485 if err: 00486 GError(parent = self, message = unicode('\n'.join(err))) 00487 return 00488 00489 log.cmdThread.SetId(-1) 00490 for item in self.GetItems(): 00491 if not item.IsEnabled(): 00492 continue 00493 if isinstance(item, ModelAction): 00494 if item.GetBlockId(): 00495 continue 00496 self.RunAction(item, params, log, onDone) 00497 elif isinstance(item, ModelLoop): 00498 cond = item.GetText() 00499 # substitute variables in condition 00500 variables = self.GetVariables() 00501 for variable in variables: 00502 pattern = re.compile('%' + variable) 00503 if pattern.search(cond): 00504 value = variables[variable].get('value', '') 00505 vtype = variables[variable].get('type', 'string') 00506 if vtype == 'string': 00507 value = '"' + value + '"' 00508 cond = pattern.sub(value, cond) 00509 # split condition 00510 condVar, condText = re.split('\s*in\s*', cond) 00511 00512 for action in item.GetItems(): 00513 for vars()[condVar] in eval(condText): 00514 if not isinstance(action, ModelAction) or \ 00515 not action.IsEnabled(): 00516 continue 00517 00518 self.RunAction(action, params, log, onDone) 00519 00520 if params: 00521 dlg.Destroy() 00522 00523 def DeleteIntermediateData(self, log): 00524 """!Detele intermediate data""" 00525 rast, vect, rast3d, msg = self.GetIntermediateData() 00526 00527 if rast: 00528 log.RunCmd(['g.remove', 'rast=%s' %','.join(rast)]) 00529 if rast3d: 00530 log.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)]) 00531 if vect: 00532 log.RunCmd(['g.remove', 'vect=%s' %','.join(vect)]) 00533 00534 def GetIntermediateData(self): 00535 """!Get info about intermediate data""" 00536 rast = list() 00537 rast3d = list() 00538 vect = list() 00539 for data in self.GetData(): 00540 if not data.IsIntermediate(): 00541 continue 00542 name = data.GetValue() 00543 prompt = data.GetPrompt() 00544 if prompt == 'raster': 00545 rast.append(name) 00546 elif prompt == 'vector': 00547 vect.append(name) 00548 elif prompt == 'rast3d': 00549 rast3d.append(name) 00550 00551 msg = '' 00552 if rast: 00553 msg += '\n\n%s: ' % _('Raster maps') 00554 msg += ', '.join(rast) 00555 if rast3d: 00556 msg += '\n\n%s: ' % _('3D raster maps') 00557 msg += ', '.join(rast3d) 00558 if vect: 00559 msg += '\n\n%s: ' % _('Vector maps') 00560 msg += ', '.join(vect) 00561 00562 return rast, vect, rast3d, msg 00563 00564 def Update(self): 00565 """!Update model""" 00566 for item in self.items: 00567 item.Update() 00568 00569 def IsParameterized(self): 00570 """!Return True if model is parameterized""" 00571 if self.Parameterize(): 00572 return True 00573 00574 return False 00575 00576 def Parameterize(self): 00577 """!Return parameterized options""" 00578 result = dict() 00579 idx = 0 00580 if self.variables: 00581 params = list() 00582 result[_("Variables")] = { 'flags' : list(), 00583 'params' : params, 00584 'idx' : idx } 00585 for name, values in self.variables.iteritems(): 00586 gtype = values.get('type', 'string') 00587 if gtype in ('raster', 'vector'): 00588 gisprompt = True 00589 prompt = gtype 00590 if gtype == 'raster': 00591 element = 'cell' 00592 else: 00593 element = 'vector' 00594 ptype = 'string' 00595 else: 00596 gisprompt = False 00597 prompt = None 00598 element = None 00599 ptype = gtype 00600 params.append({ 'gisprompt' : gisprompt, 00601 'multiple' : 'no', 00602 'description' : values.get('description', ''), 00603 'guidependency' : '', 00604 'default' : '', 00605 'age' : None, 00606 'required' : 'yes', 00607 'value' : values.get('value', ''), 00608 'label' : '', 00609 'guisection' : '', 00610 'key_desc' : '', 00611 'values' : list(), 00612 'parameterized' : False, 00613 'values_desc' : list(), 00614 'prompt' : prompt, 00615 'element' : element, 00616 'type' : ptype, 00617 'name' : name }) 00618 00619 idx += 1 00620 00621 for action in self.GetItems(objType = ModelAction): 00622 if not action.IsEnabled(): 00623 continue 00624 name = action.GetName() 00625 params = action.GetParams() 00626 for f in params['flags']: 00627 if f.get('parameterized', False): 00628 if name not in result: 00629 result[name] = { 'flags' : list(), 00630 'params': list(), 00631 'idx' : idx } 00632 result[name]['flags'].append(f) 00633 for p in params['params']: 00634 if p.get('parameterized', False): 00635 if name not in result: 00636 result[name] = { 'flags' : list(), 00637 'params': list(), 00638 'idx' : idx } 00639 result[name]['params'].append(p) 00640 idx += 1 00641 00642 self.variablesParams = result # record parameters 00643 00644 return result 00645 00646 class ModelFrame(wx.Frame): 00647 def __init__(self, parent, id = wx.ID_ANY, 00648 title = _("GRASS GIS Graphical Modeler (experimental prototype)"), **kwargs): 00649 """!Graphical modeler main window 00650 00651 @param parent parent window 00652 @param id window id 00653 @param title window title 00654 00655 @param kwargs wx.Frames' arguments 00656 """ 00657 self.parent = parent 00658 self.searchDialog = None # module search dialog 00659 self.baseTitle = title 00660 self.modelFile = None # loaded model 00661 self.modelChanged = False 00662 00663 self.cursors = { 00664 "default" : wx.StockCursor(wx.CURSOR_ARROW), 00665 "cross" : wx.StockCursor(wx.CURSOR_CROSS), 00666 } 00667 00668 wx.Frame.__init__(self, parent = parent, id = id, title = title, **kwargs) 00669 self.SetName("Modeler") 00670 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 00671 00672 self.menubar = menu.Menu(parent = self, data = menudata.ModelerData()) 00673 00674 self.SetMenuBar(self.menubar) 00675 00676 self.toolbar = toolbars.ModelToolbar(parent = self) 00677 self.SetToolBar(self.toolbar) 00678 00679 self.statusbar = self.CreateStatusBar(number = 1) 00680 00681 self.notebook = menuform.GNotebook(parent = self, 00682 style = FN.FNB_FANCY_TABS | FN.FNB_BOTTOM | 00683 FN.FNB_NO_NAV_BUTTONS | FN.FNB_NO_X_BUTTON) 00684 00685 self.canvas = ModelCanvas(self) 00686 self.canvas.SetBackgroundColour(wx.WHITE) 00687 self.canvas.SetCursor(self.cursors["default"]) 00688 00689 self.model = Model(self.canvas) 00690 00691 self.variablePanel = VariablePanel(parent = self) 00692 00693 self.itemPanel = ItemPanel(parent = self) 00694 00695 self.goutput = goutput.GMConsole(parent = self, notebook = self.notebook) 00696 00697 self.notebook.AddPage(page = self.canvas, text=_('Model'), name = 'model') 00698 self.notebook.AddPage(page = self.itemPanel, text=_('Items'), name = 'items') 00699 self.notebook.AddPage(page = self.variablePanel, text=_('Variables'), name = 'variables') 00700 self.notebook.AddPage(page = self.goutput, text=_('Command output'), name = 'output') 00701 wx.CallAfter(self.notebook.SetSelectionByName, 'model') 00702 wx.CallAfter(self.ModelChanged, False) 00703 00704 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) 00705 self.Bind(wx.EVT_SIZE, self.OnSize) 00706 00707 self._layout() 00708 self.SetMinSize((475, 300)) 00709 self.SetSize((640, 480)) 00710 00711 # fix goutput's pane size 00712 if self.goutput: 00713 self.goutput.SetSashPosition(int(self.GetSize()[1] * .75)) 00714 00715 def _layout(self): 00716 """!Do layout""" 00717 sizer = wx.BoxSizer(wx.VERTICAL) 00718 00719 sizer.Add(item = self.notebook, proportion = 1, 00720 flag = wx.EXPAND) 00721 00722 self.SetAutoLayout(True) 00723 self.SetSizer(sizer) 00724 sizer.Fit(self) 00725 00726 self.Layout() 00727 00728 def _addEvent(self, item): 00729 """!Add event to item""" 00730 evthandler = ModelEvtHandler(self.statusbar, 00731 self) 00732 evthandler.SetShape(item) 00733 evthandler.SetPreviousHandler(item.GetEventHandler()) 00734 item.SetEventHandler(evthandler) 00735 00736 def GetCanvas(self): 00737 """!Get canvas""" 00738 return self.canvas 00739 00740 def GetModel(self): 00741 """!Get model""" 00742 return self.model 00743 00744 def ModelChanged(self, changed = True): 00745 """!Update window title""" 00746 self.modelChanged = changed 00747 00748 if self.modelFile: 00749 if self.modelChanged: 00750 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile) + '*') 00751 else: 00752 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile)) 00753 else: 00754 self.SetTitle(self.baseTitle) 00755 00756 def OnVariables(self, event): 00757 """!Switch to variables page""" 00758 self.notebook.SetSelectionByName('variables') 00759 00760 def OnRemoveItem(self, event): 00761 """!Remove shape 00762 """ 00763 self.GetCanvas().RemoveSelected() 00764 00765 def OnCanvasRefresh(self, event): 00766 """!Refresh canvas""" 00767 self.SetStatusText(_("Redrawing model..."), 0) 00768 self.GetCanvas().Refresh() 00769 self.SetStatusText("", 0) 00770 00771 def OnCmdRun(self, event): 00772 """!Run command""" 00773 try: 00774 action = self.GetModel().GetItems()[event.pid] 00775 if hasattr(action, "task"): 00776 action.Update(running = True) 00777 except IndexError: 00778 pass 00779 00780 def OnCmdDone(self, event): 00781 """!Command done (or aborted)""" 00782 try: 00783 action = self.GetModel().GetItems()[event.pid] 00784 if hasattr(action, "task"): 00785 action.Update(running = True) 00786 except IndexError: 00787 pass 00788 00789 def OnCloseWindow(self, event): 00790 """!Close window""" 00791 if self.modelChanged and \ 00792 UserSettings.Get(group='manager', key='askOnQuit', subkey='enabled'): 00793 if self.modelFile: 00794 message = _("Do you want to save changes in the model?") 00795 else: 00796 message = _("Do you want to store current model settings " 00797 "to model file?") 00798 00799 # ask user to save current settings 00800 dlg = wx.MessageDialog(self, 00801 message = message, 00802 caption=_("Quit Graphical Modeler"), 00803 style = wx.YES_NO | wx.YES_DEFAULT | 00804 wx.CANCEL | wx.ICON_QUESTION | wx.CENTRE) 00805 ret = dlg.ShowModal() 00806 if ret == wx.ID_YES: 00807 if not self.modelFile: 00808 self.OnWorkspaceSaveAs() 00809 else: 00810 self.WriteModelFile(self.modelFile) 00811 elif ret == wx.ID_CANCEL: 00812 dlg.Destroy() 00813 return 00814 dlg.Destroy() 00815 00816 self.Destroy() 00817 00818 def OnSize(self, event): 00819 """Window resized, save to the model""" 00820 self.ModelChanged() 00821 event.Skip() 00822 00823 def OnPreferences(self, event): 00824 """!Open preferences dialog""" 00825 dlg = PreferencesDialog(parent = self) 00826 dlg.CenterOnParent() 00827 00828 dlg.ShowModal() 00829 self.canvas.Refresh() 00830 00831 def OnHelp(self, event): 00832 """!Show help""" 00833 if self.parent and self.parent.GetName() == 'LayerManager': 00834 log = self.parent.GetLogWindow() 00835 log.RunCmd(['g.manual', 00836 'entry=wxGUI.Modeler']) 00837 else: 00838 RunCommand('g.manual', 00839 quiet = True, 00840 entry = 'wxGUI.Modeler') 00841 00842 def OnModelProperties(self, event): 00843 """!Model properties dialog""" 00844 dlg = PropertiesDialog(parent = self) 00845 dlg.CentreOnParent() 00846 properties = self.model.GetProperties() 00847 dlg.Init(properties) 00848 if dlg.ShowModal() == wx.ID_OK: 00849 self.ModelChanged() 00850 for key, value in dlg.GetValues().iteritems(): 00851 properties[key] = value 00852 for action in self.model.GetItems(objType = ModelAction): 00853 action.GetTask().set_flag('overwrite', properties['overwrite']) 00854 00855 dlg.Destroy() 00856 00857 def OnDeleteData(self, event): 00858 """!Delete intermediate data""" 00859 rast, vect, rast3d, msg = self.model.GetIntermediateData() 00860 00861 if not rast and not vect and not rast3d: 00862 GMessage(parent = self, 00863 message = _('Nothing to delete.')) 00864 return 00865 00866 dlg = wx.MessageDialog(parent = self, 00867 message= _("Do you want to permanently delete data?%s" % msg), 00868 caption=_("Delete intermediate data?"), 00869 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) 00870 00871 ret = dlg.ShowModal() 00872 if ret == wx.ID_YES: 00873 dlg.Destroy() 00874 00875 if rast: 00876 self.goutput.RunCmd(['g.remove', 'rast=%s' %','.join(rast)]) 00877 if rast3d: 00878 self.goutput.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)]) 00879 if vect: 00880 self.goutput.RunCmd(['g.remove', 'vect=%s' %','.join(vect)]) 00881 00882 self.SetStatusText(_("%d maps deleted from current mapset") % \ 00883 int(len(rast) + len(rast3d) + len(vect))) 00884 return 00885 00886 dlg.Destroy() 00887 00888 def OnModelNew(self, event): 00889 """!Create new model""" 00890 Debug.msg(4, "ModelFrame.OnModelNew():") 00891 00892 # ask user to save current model 00893 if self.modelFile and self.modelChanged: 00894 self.OnModelSave() 00895 elif self.modelFile is None and \ 00896 (self.model.GetNumItems() > 0 or len(self.model.GetData()) > 0): 00897 dlg = wx.MessageDialog(self, message=_("Current model is not empty. " 00898 "Do you want to store current settings " 00899 "to model file?"), 00900 caption=_("Create new model?"), 00901 style=wx.YES_NO | wx.YES_DEFAULT | 00902 wx.CANCEL | wx.ICON_QUESTION) 00903 ret = dlg.ShowModal() 00904 if ret == wx.ID_YES: 00905 self.OnModelSaveAs() 00906 elif ret == wx.ID_CANCEL: 00907 dlg.Destroy() 00908 return 00909 00910 dlg.Destroy() 00911 00912 # delete all items 00913 self.canvas.GetDiagram().DeleteAllShapes() 00914 self.model.Reset() 00915 self.canvas.Refresh() 00916 self.itemPanel.Update() 00917 self.variablePanel.Reset() 00918 00919 # no model file loaded 00920 self.modelFile = None 00921 self.modelChanged = False 00922 self.SetTitle(self.baseTitle) 00923 00924 def OnModelOpen(self, event): 00925 """!Load model from file""" 00926 filename = '' 00927 dlg = wx.FileDialog(parent = self, message=_("Choose model file"), 00928 defaultDir = os.getcwd(), 00929 wildcard=_("GRASS Model File (*.gxm)|*.gxm")) 00930 if dlg.ShowModal() == wx.ID_OK: 00931 filename = dlg.GetPath() 00932 00933 if not filename: 00934 return 00935 00936 Debug.msg(4, "ModelFrame.OnModelOpen(): filename=%s" % filename) 00937 00938 # close current model 00939 self.OnModelClose() 00940 00941 self.LoadModelFile(filename) 00942 00943 self.modelFile = filename 00944 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile)) 00945 self.SetStatusText(_('%(items)d items (%(actions)d actions) loaded into model') % \ 00946 { 'items' : self.model.GetNumItems(), 00947 'actions' : self.model.GetNumItems(actionOnly = True) }, 0) 00948 00949 def OnModelSave(self, event = None): 00950 """!Save model to file""" 00951 if self.modelFile and self.modelChanged: 00952 dlg = wx.MessageDialog(self, message=_("Model file <%s> already exists. " 00953 "Do you want to overwrite this file?") % \ 00954 self.modelFile, 00955 caption=_("Save model"), 00956 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) 00957 if dlg.ShowModal() == wx.ID_NO: 00958 dlg.Destroy() 00959 else: 00960 Debug.msg(4, "ModelFrame.OnModelSave(): filename=%s" % self.modelFile) 00961 self.WriteModelFile(self.modelFile) 00962 self.SetStatusText(_('File <%s> saved') % self.modelFile, 0) 00963 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile)) 00964 elif not self.modelFile: 00965 self.OnModelSaveAs(None) 00966 00967 def OnModelSaveAs(self, event): 00968 """!Create model to file as""" 00969 filename = '' 00970 dlg = wx.FileDialog(parent = self, 00971 message = _("Choose file to save current model"), 00972 defaultDir = os.getcwd(), 00973 wildcard=_("GRASS Model File (*.gxm)|*.gxm"), 00974 style=wx.FD_SAVE) 00975 00976 00977 if dlg.ShowModal() == wx.ID_OK: 00978 filename = dlg.GetPath() 00979 00980 if not filename: 00981 return 00982 00983 # check for extension 00984 if filename[-4:] != ".gxm": 00985 filename += ".gxm" 00986 00987 if os.path.exists(filename): 00988 dlg = wx.MessageDialog(parent = self, 00989 message=_("Model file <%s> already exists. " 00990 "Do you want to overwrite this file?") % filename, 00991 caption=_("File already exists"), 00992 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) 00993 if dlg.ShowModal() != wx.ID_YES: 00994 dlg.Destroy() 00995 return 00996 00997 Debug.msg(4, "GMFrame.OnModelSaveAs(): filename=%s" % filename) 00998 00999 self.WriteModelFile(filename) 01000 self.modelFile = filename 01001 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile)) 01002 self.SetStatusText(_('File <%s> saved') % self.modelFile, 0) 01003 01004 def OnModelClose(self, event = None): 01005 """!Close model file""" 01006 Debug.msg(4, "ModelFrame.OnModelClose(): file=%s" % self.modelFile) 01007 # ask user to save current model 01008 if self.modelFile and self.modelChanged: 01009 self.OnModelSave() 01010 elif self.modelFile is None and \ 01011 (self.model.GetNumItems() > 0 or len(self.model.GetData()) > 0): 01012 dlg = wx.MessageDialog(self, message=_("Current model is not empty. " 01013 "Do you want to store current settings " 01014 "to model file?"), 01015 caption=_("Create new model?"), 01016 style=wx.YES_NO | wx.YES_DEFAULT | 01017 wx.CANCEL | wx.ICON_QUESTION) 01018 ret = dlg.ShowModal() 01019 if ret == wx.ID_YES: 01020 self.OnModelSaveAs() 01021 elif ret == wx.ID_CANCEL: 01022 dlg.Destroy() 01023 return 01024 01025 dlg.Destroy() 01026 01027 self.modelFile = None 01028 self.SetTitle(self.baseTitle) 01029 01030 self.canvas.GetDiagram().DeleteAllShapes() 01031 self.model.Reset() 01032 01033 self.canvas.Refresh() 01034 01035 def OnRunModel(self, event): 01036 """!Run entire model""" 01037 self.model.Run(self.goutput, self.OnDone, parent = self) 01038 01039 def OnDone(self, cmd, returncode): 01040 """!Computation finished""" 01041 self.SetStatusText('', 0) 01042 01043 def OnValidateModel(self, event, showMsg = True): 01044 """!Validate entire model""" 01045 if self.model.GetNumItems() < 1: 01046 GMessage(parent = self, 01047 message = _('Model is empty. Nothing to validate.')) 01048 return 01049 01050 01051 self.SetStatusText(_('Validating model...'), 0) 01052 errList = self.model.Validate() 01053 self.SetStatusText('', 0) 01054 01055 if errList: 01056 GWarning(parent = self, 01057 message = _('Model is not valid.\n\n%s') % '\n'.join(errList)) 01058 else: 01059 GMessage(parent = self, 01060 message = _('Model is valid.')) 01061 01062 def OnExportImage(self, event): 01063 """!Export model to image (default image) 01064 """ 01065 xminImg = 0 01066 xmaxImg = 0 01067 yminImg = 0 01068 ymaxImg = 0 01069 # get current size of canvas 01070 for shape in self.canvas.GetDiagram().GetShapeList(): 01071 w, h = shape.GetBoundingBoxMax() 01072 x = shape.GetX() 01073 y = shape.GetY() 01074 xmin = x - w / 2 01075 xmax = x + w / 2 01076 ymin = y - h / 2 01077 ymax = y + h / 2 01078 if xmin < xminImg: 01079 xminImg = xmin 01080 if xmax > xmaxImg: 01081 xmaxImg = xmax 01082 if ymin < yminImg: 01083 yminImg = ymin 01084 if ymax > ymaxImg: 01085 ymaxImg = ymax 01086 size = wx.Size(int(xmaxImg - xminImg) + 50, 01087 int(ymaxImg - yminImg) + 50) 01088 bitmap = wx.EmptyBitmap(width = size.width, height = size.height) 01089 01090 filetype, ltype = GetImageHandlers(wx.ImageFromBitmap(bitmap)) 01091 01092 dlg = wx.FileDialog(parent = self, 01093 message = _("Choose a file name to save the image (no need to add extension)"), 01094 defaultDir = "", 01095 defaultFile = "", 01096 wildcard = filetype, 01097 style=wx.SAVE | wx.FD_OVERWRITE_PROMPT) 01098 01099 if dlg.ShowModal() == wx.ID_OK: 01100 path = dlg.GetPath() 01101 if not path: 01102 dlg.Destroy() 01103 return 01104 01105 base, ext = os.path.splitext(path) 01106 fileType = ltype[dlg.GetFilterIndex()]['type'] 01107 extType = ltype[dlg.GetFilterIndex()]['ext'] 01108 if ext != extType: 01109 path = base + '.' + extType 01110 01111 dc = wx.MemoryDC(bitmap) 01112 dc.SetBackground(wx.WHITE_BRUSH) 01113 dc.SetBackgroundMode(wx.SOLID) 01114 01115 dc.BeginDrawing() 01116 self.canvas.GetDiagram().Clear(dc) 01117 self.canvas.GetDiagram().Redraw(dc) 01118 dc.EndDrawing() 01119 01120 bitmap.SaveFile(path, fileType) 01121 self.SetStatusText(_("Model exported to <%s>") % path) 01122 01123 dlg.Destroy() 01124 01125 def OnExportPython(self, event): 01126 """!Export model to Python script""" 01127 filename = '' 01128 dlg = wx.FileDialog(parent = self, 01129 message = _("Choose file to save"), 01130 defaultDir = os.getcwd(), 01131 wildcard=_("Python script (*.py)|*.py"), 01132 style=wx.FD_SAVE) 01133 01134 if dlg.ShowModal() == wx.ID_OK: 01135 filename = dlg.GetPath() 01136 01137 if not filename: 01138 return 01139 01140 # check for extension 01141 if filename[-3:] != ".py": 01142 filename += ".py" 01143 01144 if os.path.exists(filename): 01145 dlg = wx.MessageDialog(self, message=_("File <%s> already exists. " 01146 "Do you want to overwrite this file?") % filename, 01147 caption=_("Save file"), 01148 style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) 01149 if dlg.ShowModal() == wx.ID_NO: 01150 dlg.Destroy() 01151 return 01152 01153 dlg.Destroy() 01154 01155 fd = open(filename, "w") 01156 try: 01157 WritePythonFile(fd, self.model) 01158 finally: 01159 fd.close() 01160 01161 # executable file 01162 os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR) 01163 01164 self.SetStatusText(_("Model exported to <%s>") % filename) 01165 01166 def OnDefineRelation(self, event): 01167 """!Define relation between data and action items""" 01168 self.canvas.SetCursor(self.cursors["cross"]) 01169 self.defineRelation = { 'from' : None, 01170 'to' : None } 01171 01172 def OnDefineLoop(self, event): 01173 """!Define new loop in the model""" 01174 self.ModelChanged() 01175 01176 width, height = self.canvas.GetSize() 01177 loop = ModelLoop(self, x = width/2, y = height/2, 01178 id = self.model.GetNumItems() + 1) 01179 self.canvas.diagram.AddShape(loop) 01180 loop.Show(True) 01181 01182 self._addEvent(loop) 01183 self.model.AddItem(loop) 01184 01185 self.canvas.Refresh() 01186 01187 def OnDefineCondition(self, event): 01188 """!Define new condition in the model""" 01189 self.ModelChanged() 01190 01191 width, height = self.canvas.GetSize() 01192 cond = ModelCondition(self, x = width/2, y = height/2, 01193 id = self.model.GetNumItems() + 1) 01194 self.canvas.diagram.AddShape(cond) 01195 cond.Show(True) 01196 01197 self._addEvent(cond) 01198 self.model.AddItem(cond) 01199 01200 self.canvas.Refresh() 01201 01202 def OnAddAction(self, event): 01203 """!Add action to model""" 01204 if self.searchDialog is None: 01205 self.searchDialog = ModelSearchDialog(self) 01206 self.searchDialog.CentreOnParent() 01207 else: 01208 self.searchDialog.Reset() 01209 01210 if self.searchDialog.ShowModal() == wx.ID_CANCEL: 01211 self.searchDialog.Hide() 01212 return 01213 01214 cmd = self.searchDialog.GetCmd() 01215 self.searchDialog.Hide() 01216 01217 self.ModelChanged() 01218 01219 # add action to canvas 01220 width, height = self.canvas.GetSize() 01221 if cmd[0] == 'r.mapcalc': 01222 GMessage(parent = self, 01223 message = _("Module r.mapcalc cannot be used in the model. " 01224 "Use r.mapcalculator instead.")) 01225 return 01226 01227 action = ModelAction(self.model, cmd = cmd, x = width/2, y = height/2, 01228 id = self.model.GetNextId()) 01229 overwrite = self.model.GetProperties().get('overwrite', None) 01230 if overwrite is not None: 01231 action.GetTask().set_flag('overwrite', overwrite) 01232 01233 self.canvas.diagram.AddShape(action) 01234 action.Show(True) 01235 01236 self._addEvent(action) 01237 self.model.AddItem(action) 01238 01239 self.itemPanel.Update() 01240 self.canvas.Refresh() 01241 time.sleep(.1) 01242 01243 # show properties dialog 01244 win = action.GetPropDialog() 01245 if not win: 01246 if len(action.GetLog(string = False)) > 1: 01247 self.GetOptData(dcmd = action.GetLog(string = False), layer = action, 01248 params = action.GetParams(), propwin = None) 01249 else: 01250 menuform.GUI(parent = self, show = True).ParseCommand(action.GetLog(string = False), 01251 completed = (self.GetOptData, action, action.GetParams())) 01252 elif win and not win.IsShown(): 01253 win.Show() 01254 01255 if win: 01256 win.Raise() 01257 01258 def OnAddData(self, event): 01259 """!Add data item to model 01260 """ 01261 # add action to canvas 01262 width, height = self.canvas.GetSize() 01263 data = ModelData(self, x = width/2, y = height/2) 01264 01265 dlg = ModelDataDialog(parent = self, shape = data) 01266 data.SetPropDialog(dlg) 01267 dlg.CentreOnParent() 01268 ret = dlg.ShowModal() 01269 dlg.Destroy() 01270 if ret != wx.ID_OK: 01271 return 01272 01273 data.Update() 01274 self.canvas.diagram.AddShape(data) 01275 data.Show(True) 01276 01277 self.ModelChanged() 01278 01279 self._addEvent(data) 01280 self.model.AddItem(data) 01281 01282 self.canvas.Refresh() 01283 01284 01285 def OnHelp(self, event): 01286 """!Display manual page""" 01287 grass.run_command('g.manual', 01288 entry = 'wxGUI.Modeler') 01289 01290 def OnAbout(self, event): 01291 """!Display About window""" 01292 info = wx.AboutDialogInfo() 01293 01294 info.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 01295 info.SetName(_('wxGUI Graphical Modeler')) 01296 info.SetWebSite('http://grass.osgeo.org') 01297 year = grass.version()['date'] 01298 info.SetDescription(_('(C) 2010-%s by the GRASS Development Team\n\n') % year + 01299 '\n'.join(textwrap.wrap(_('This program is free software under the GNU General Public License' 01300 '(>=v2). Read the file COPYING that comes with GRASS for details.'), 75))) 01301 01302 wx.AboutBox(info) 01303 01304 def GetOptData(self, dcmd, layer, params, propwin): 01305 """!Process action data""" 01306 if params: # add data items 01307 width, height = self.canvas.GetSize() 01308 x = [width/2 + 200, width/2 - 200] 01309 for p in params['params']: 01310 if p.get('prompt', '') in ('raster', 'vector', 'raster3d') and \ 01311 (p.get('value', None) or \ 01312 (p.get('age', 'old') != 'old' and p.get('required', 'no') == 'yes')): 01313 data = layer.FindData(p.get('name', '')) 01314 if data: 01315 data.SetValue(p.get('value', '')) 01316 data.Update() 01317 continue 01318 01319 data = self.model.FindData(p.get('value', ''), 01320 p.get('prompt', '')) 01321 if data: 01322 if p.get('age', 'old') == 'old': 01323 rel = ModelRelation(parent = self, fromShape = data, 01324 toShape = layer, param = p.get('name', '')) 01325 else: 01326 rel = ModelRelation(parent = self, fromShape = layer, 01327 toShape = data, param = p.get('name', '')) 01328 layer.AddRelation(rel) 01329 data.AddRelation(rel) 01330 self.AddLine(rel) 01331 data.Update() 01332 continue 01333 01334 data = ModelData(self, value = p.get('value', ''), 01335 prompt = p.get('prompt', ''), 01336 x = x.pop(), y = height/2) 01337 self._addEvent(data) 01338 self.canvas.diagram.AddShape(data) 01339 data.Show(True) 01340 01341 if p.get('age', 'old') == 'old': 01342 rel = ModelRelation(parent = self, fromShape = data, 01343 toShape = layer, param = p.get('name', '')) 01344 else: 01345 rel = ModelRelation(parent = self, fromShape = layer, 01346 toShape = data, param = p.get('name', '')) 01347 layer.AddRelation(rel) 01348 data.AddRelation(rel) 01349 self.AddLine(rel) 01350 data.Update() 01351 01352 # valid ? 01353 valid = True 01354 for p in params['params']: 01355 if p.get('required', False) and \ 01356 p.get('value', '') == '' and \ 01357 p.get('default', '') == '': 01358 valid = False 01359 break 01360 layer.SetValid(valid) 01361 01362 # parameterized ? 01363 parameterized = False 01364 for f in params['flags']: 01365 if f.get('parameterized', False): 01366 parameterized = True 01367 break 01368 if not parameterized: 01369 for p in params['params']: 01370 if p.get('parameterized', False): 01371 parameterized = True 01372 break 01373 layer.SetParameterized(parameterized) 01374 01375 self.canvas.Refresh() 01376 01377 if dcmd: 01378 layer.SetProperties(params, propwin) 01379 01380 self.SetStatusText(layer.GetLog(), 0) 01381 01382 def AddLine(self, rel): 01383 """!Add connection between model objects 01384 01385 @param rel relation 01386 """ 01387 fromShape = rel.GetFrom() 01388 toShape = rel.GetTo() 01389 01390 rel.SetCanvas(self) 01391 rel.SetPen(wx.BLACK_PEN) 01392 rel.SetBrush(wx.BLACK_BRUSH) 01393 rel.AddArrow(ogl.ARROW_ARROW) 01394 points = rel.GetControlPoints() 01395 rel.MakeLineControlPoints(2) 01396 if points: 01397 for x, y in points: 01398 rel.InsertLineControlPoint(point = wx.RealPoint(x, y)) 01399 01400 self._addEvent(rel) 01401 try: 01402 fromShape.AddLine(rel, toShape) 01403 except TypeError: 01404 pass # bug when connecting ModelCondition and ModelLoop - to be fixed 01405 01406 self.canvas.diagram.AddShape(rel) 01407 rel.Show(True) 01408 01409 def LoadModelFile(self, filename): 01410 """!Load model definition stored in GRASS Model XML file (gxm) 01411 """ 01412 try: 01413 self.model.LoadModel(filename) 01414 except GException, e: 01415 GError(parent = self, 01416 message = _("Reading model file <%s> failed.\n" 01417 "Invalid file, unable to parse XML document.") % filename) 01418 01419 self.modelFile = filename 01420 self.SetTitle(self.baseTitle + " - " + os.path.basename(self.modelFile)) 01421 01422 self.SetStatusText(_("Please wait, loading model..."), 0) 01423 01424 # load actions 01425 for item in self.model.GetItems(objType = ModelAction): 01426 self._addEvent(item) 01427 self.canvas.diagram.AddShape(item) 01428 item.Show(True) 01429 # relations/data 01430 for rel in item.GetRelations(): 01431 if rel.GetFrom() == item: 01432 dataItem = rel.GetTo() 01433 else: 01434 dataItem = rel.GetFrom() 01435 self._addEvent(dataItem) 01436 self.canvas.diagram.AddShape(dataItem) 01437 self.AddLine(rel) 01438 dataItem.Show(True) 01439 01440 # load loops 01441 for item in self.model.GetItems(objType = ModelLoop): 01442 self._addEvent(item) 01443 self.canvas.diagram.AddShape(item) 01444 item.Show(True) 01445 01446 # connect items in the loop 01447 self.DefineLoop(item) 01448 01449 # load conditions 01450 for item in self.model.GetItems(objType = ModelCondition): 01451 self._addEvent(item) 01452 self.canvas.diagram.AddShape(item) 01453 item.Show(True) 01454 01455 # connect items in the condition 01456 self.DefineCondition(item) 01457 01458 # load variables 01459 self.variablePanel.Update() 01460 self.itemPanel.Update() 01461 self.SetStatusText('', 0) 01462 01463 self.canvas.Refresh(True) 01464 01465 def WriteModelFile(self, filename): 01466 """!Save model to model file, recover original file on error. 01467 01468 @return True on success 01469 @return False on failure 01470 """ 01471 self.ModelChanged(False) 01472 tmpfile = tempfile.TemporaryFile(mode='w+b') 01473 try: 01474 WriteModelFile(fd = tmpfile, model = self.model) 01475 except StandardError: 01476 GError(parent = self, 01477 message = _("Writing current settings to model file failed.")) 01478 return False 01479 01480 try: 01481 mfile = open(filename, "w") 01482 tmpfile.seek(0) 01483 for line in tmpfile.readlines(): 01484 mfile.write(line) 01485 except IOError: 01486 wx.MessageBox(parent = self, 01487 message = _("Unable to open file <%s> for writing.") % filename, 01488 caption = _("Error"), 01489 style = wx.OK | wx.ICON_ERROR | wx.CENTRE) 01490 return False 01491 01492 mfile.close() 01493 01494 return True 01495 01496 def DefineLoop(self, loop): 01497 """!Define loop with given list of items""" 01498 parent = loop 01499 items = loop.GetItems() 01500 if not items: 01501 return 01502 01503 # remove defined relations first 01504 for rel in loop.GetRelations(): 01505 self.canvas.GetDiagram().RemoveShape(rel) 01506 loop.Clear() 01507 01508 for item in items: 01509 rel = ModelRelation(parent = self, fromShape = parent, toShape = item) 01510 dx = item.GetX() - parent.GetX() 01511 dy = item.GetY() - parent.GetY() 01512 loop.AddRelation(rel) 01513 if dx != 0: 01514 rel.SetControlPoints(((parent.GetX(), parent.GetY() + dy / 2), 01515 (parent.GetX() + dx, parent.GetY() + dy / 2))) 01516 self.AddLine(rel) 01517 parent = item 01518 01519 # close loop 01520 item = loop.GetItems()[-1] 01521 rel = ModelRelation(parent = self, fromShape = item, toShape = loop) 01522 loop.AddRelation(rel) 01523 self.AddLine(rel) 01524 dx = (item.GetX() - loop.GetX()) + loop.GetWidth() / 2 + 50 01525 dy = item.GetHeight() / 2 + 50 01526 rel.MakeLineControlPoints(0) 01527 rel.InsertLineControlPoint(point = wx.RealPoint(loop.GetX() - loop.GetWidth() / 2 , 01528 loop.GetY())) 01529 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(), 01530 item.GetY() + item.GetHeight() / 2)) 01531 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX(), 01532 item.GetY() + dy)) 01533 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx, 01534 item.GetY() + dy)) 01535 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - dx, 01536 loop.GetY())) 01537 01538 self.canvas.Refresh() 01539 01540 def DefineCondition(self, condition): 01541 """!Define if-else statement with given list of items""" 01542 parent = condition 01543 items = condition.GetItems() 01544 if not items['if'] and not items['else']: 01545 return 01546 01547 # remove defined relations first 01548 for rel in condition.GetRelations(): 01549 self.canvas.GetDiagram().RemoveShape(rel) 01550 condition.Clear() 01551 dxIf = condition.GetX() + condition.GetWidth() / 2 01552 dxElse = condition.GetX() - condition.GetWidth() / 2 01553 dy = condition.GetY() 01554 for branch in items.keys(): 01555 for item in items[branch]: 01556 rel = ModelRelation(parent = self, fromShape = parent, 01557 toShape = item) 01558 condition.AddRelation(rel) 01559 self.AddLine(rel) 01560 rel.MakeLineControlPoints(0) 01561 if branch == 'if': 01562 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY())) 01563 rel.InsertLineControlPoint(point = wx.RealPoint(dxIf, dy)) 01564 else: 01565 rel.InsertLineControlPoint(point = wx.RealPoint(dxElse, dy)) 01566 rel.InsertLineControlPoint(point = wx.RealPoint(item.GetX() - item.GetWidth() / 2, item.GetY())) 01567 parent = item 01568 01569 self.canvas.Refresh() 01570 01571 class ModelCanvas(ogl.ShapeCanvas): 01572 """!Canvas where model is drawn""" 01573 def __init__(self, parent): 01574 self.parent = parent 01575 ogl.OGLInitialize() 01576 ogl.ShapeCanvas.__init__(self, parent) 01577 01578 self.diagram = ogl.Diagram() 01579 self.SetDiagram(self.diagram) 01580 self.diagram.SetCanvas(self) 01581 01582 self.SetScrollbars(20, 20, 1000/20, 1000/20) 01583 01584 self.Bind(wx.EVT_CHAR, self.OnChar) 01585 01586 def OnChar(self, event): 01587 """!Key pressed""" 01588 kc = event.GetKeyCode() 01589 diagram = self.GetDiagram() 01590 if kc == wx.WXK_DELETE: 01591 self.RemoveSelected() 01592 01593 def RemoveSelected(self): 01594 """!Remove selected shapes""" 01595 self.parent.ModelChanged() 01596 01597 diagram = self.GetDiagram() 01598 for shape in diagram.GetShapeList(): 01599 if not shape.Selected(): 01600 continue 01601 remList, upList = self.parent.GetModel().RemoveItem(shape) 01602 shape.Select(False) 01603 diagram.RemoveShape(shape) 01604 shape.__del__() 01605 for item in remList: 01606 diagram.RemoveShape(item) 01607 item.__del__() 01608 01609 for item in upList: 01610 item.Update() 01611 01612 self.Refresh() 01613 01614 class ModelObject: 01615 def __init__(self, id = -1): 01616 self.id = id 01617 self.rels = list() # list of ModelRelations 01618 01619 self.isEnabled = True 01620 self.inBlock = list() # list of related loops/conditions 01621 01622 def __del__(self): 01623 pass 01624 01625 def GetId(self): 01626 """!Get id""" 01627 return self.id 01628 01629 def AddRelation(self, rel): 01630 """!Record new relation 01631 """ 01632 self.rels.append(rel) 01633 01634 def GetRelations(self, fdir = None): 01635 """!Get list of relations 01636 01637 @param fdir True for 'from' 01638 """ 01639 if fdir is None: 01640 return self.rels 01641 01642 result = list() 01643 for rel in self.rels: 01644 if fdir == 'from': 01645 if rel.GetFrom() == self: 01646 result.append(rel) 01647 else: 01648 if rel.GetTo() == self: 01649 result.append(rel) 01650 01651 return result 01652 01653 def IsEnabled(self): 01654 """!Get True if action is enabled, otherwise False""" 01655 return self.isEnabled 01656 01657 def Enable(self, enabled = True): 01658 """!Enable/disable action""" 01659 self.isEnabled = enabled 01660 self.Update() 01661 01662 def Update(self): 01663 pass 01664 01665 def SetBlock(self, item): 01666 """!Add object to the block (loop/condition) 01667 01668 @param item reference to ModelLoop or ModelCondition which 01669 defines loops/condition 01670 """ 01671 if item not in self.inBlock: 01672 self.inBlock.append(item) 01673 01674 def UnSetBlock(self, item): 01675 """!Remove object from the block (loop/consition) 01676 01677 @param item reference to ModelLoop or ModelCondition which 01678 defines loops/codition 01679 """ 01680 if item in self.inBlock: 01681 self.inBlock.remove(item) 01682 01683 def GetBlock(self): 01684 """!Get list of related ModelObject(s) which defines block 01685 (loop/condition) 01686 01687 @return list of ModelObjects 01688 """ 01689 return self.inBlock 01690 01691 def GetBlockId(self): 01692 """!Get list of related ids which defines block 01693 01694 @return list of ids 01695 """ 01696 ret = list() 01697 for mo in self.inBlock: 01698 ret.append(mo.GetId()) 01699 01700 return ret 01701 01702 class ModelAction(ModelObject, ogl.RectangleShape): 01703 """!Action class (GRASS module)""" 01704 def __init__(self, parent, x, y, id = -1, cmd = None, task = None, width = None, height = None): 01705 ModelObject.__init__(self, id) 01706 01707 self.parent = parent 01708 self.task = task 01709 01710 if not width: 01711 width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width')) 01712 if not height: 01713 height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height')) 01714 01715 if cmd: 01716 self.task = menuform.GUI(show = None).ParseCommand(cmd = cmd) 01717 else: 01718 if task: 01719 self.task = task 01720 else: 01721 self.task = None 01722 01723 self.propWin = None 01724 01725 self.data = list() # list of connected data items 01726 01727 self.isValid = False 01728 self.isParameterized = False 01729 01730 if self.parent.GetCanvas(): 01731 ogl.RectangleShape.__init__(self, width, height) 01732 01733 self.SetCanvas(self.parent) 01734 self.SetX(x) 01735 self.SetY(y) 01736 self.SetPen(wx.BLACK_PEN) 01737 self._setPen() 01738 self._setBrush() 01739 self.SetId(id) 01740 01741 def _setBrush(self, running = False): 01742 """!Set brush""" 01743 if running: 01744 color = UserSettings.Get(group='modeler', key='action', 01745 subkey=('color', 'running')) 01746 elif not self.isEnabled: 01747 color = UserSettings.Get(group='modeler', key='disabled', 01748 subkey='color') 01749 elif self.isValid: 01750 color = UserSettings.Get(group='modeler', key='action', 01751 subkey=('color', 'valid')) 01752 else: 01753 color = UserSettings.Get(group='modeler', key='action', 01754 subkey=('color', 'invalid')) 01755 01756 wxColor = wx.Color(color[0], color[1], color[2]) 01757 self.SetBrush(wx.Brush(wxColor)) 01758 01759 def _setPen(self): 01760 """!Set pen""" 01761 if self.isParameterized: 01762 width = int(UserSettings.Get(group='modeler', key='action', 01763 subkey=('width', 'parameterized'))) 01764 else: 01765 width = int(UserSettings.Get(group='modeler', key='action', 01766 subkey=('width', 'default'))) 01767 pen = self.GetPen() 01768 pen.SetWidth(width) 01769 self.SetPen(pen) 01770 01771 def SetId(self, id): 01772 """!Set id""" 01773 self.id = id 01774 cmd = self.task.getCmd(ignoreErrors = True) 01775 if cmd and len(cmd) > 0: 01776 self.ClearText() 01777 self.AddText('(%d) %s' % (self.id, cmd[0])) 01778 else: 01779 self.AddText('(%d) <<%s>>' % (self.id, _("unknown"))) 01780 01781 def SetProperties(self, params, propwin): 01782 """!Record properties dialog""" 01783 self.task.params = params['params'] 01784 self.task.flags = params['flags'] 01785 self.propWin = propwin 01786 01787 def GetPropDialog(self): 01788 """!Get properties dialog""" 01789 return self.propWin 01790 01791 def GetLog(self, string = True): 01792 """!Get logging info""" 01793 cmd = self.task.getCmd(ignoreErrors = True, ignoreRequired = True) 01794 01795 # substitute variables 01796 variables = self.parent.GetVariables() 01797 fparams = self.parent.GetVariables(params = True) 01798 params = None 01799 for values in fparams.itervalues(): 01800 params = values['params'] 01801 break 01802 01803 for variable in variables: 01804 pattern= re.compile('%' + variable) 01805 value = None 01806 if params: 01807 for p in params: 01808 if variable == p.get('name', ''): 01809 value = p.get('value', '') 01810 break 01811 if not value: 01812 value = variables[variable].get('value', '') 01813 01814 for idx in range(len(cmd)): 01815 if pattern.search(cmd[idx]): 01816 if value: 01817 cmd[idx] = pattern.sub(value, cmd[idx]) 01818 else: 01819 self.isValid = False 01820 break 01821 idx += 1 01822 01823 if string: 01824 if cmd is None: 01825 return '' 01826 else: 01827 return ' '.join(cmd) 01828 01829 return cmd 01830 01831 def GetName(self): 01832 """!Get name""" 01833 cmd = self.task.getCmd(ignoreErrors = True) 01834 if cmd and len(cmd) > 0: 01835 return cmd[0] 01836 01837 return _('unknown') 01838 01839 def GetParams(self, dcopy = False): 01840 """!Get dictionary of parameters""" 01841 if dcopy: 01842 return copy.deepcopy(self.task.get_options()) 01843 01844 return self.task.get_options() 01845 01846 def GetTask(self): 01847 """!Get grassTask instance""" 01848 return self.task 01849 01850 def SetParams(self, params): 01851 """!Set dictionary of parameters""" 01852 self.task.params = params['params'] 01853 self.task.flags = params['flags'] 01854 01855 def MergeParams(self, params): 01856 """!Merge dictionary of parameters""" 01857 if 'flags' in params: 01858 for f in params['flags']: 01859 self.task.set_flag(f['name'], 01860 f.get('value', False)) 01861 if 'params' in params: 01862 for p in params['params']: 01863 self.task.set_param(p['name'], 01864 p.get('value', '')) 01865 01866 def SetValid(self, isvalid): 01867 """!Set instance to be valid/invalid""" 01868 self.isValid = isvalid 01869 self._setBrush() 01870 01871 def SetParameterized(self, isparameterized): 01872 """!Set action parameterized""" 01873 self.isParameterized = isparameterized 01874 if self.parent.GetCanvas(): 01875 self._setPen() 01876 01877 def IsParameterized(self): 01878 """!Check if action is parameterized""" 01879 return self.isParameterized 01880 01881 def FindData(self, name): 01882 """!Find data item by name""" 01883 for rel in self.GetRelations(): 01884 data = rel.GetData() 01885 if name == rel.GetName() and name in data.GetName(): 01886 return data 01887 01888 return None 01889 01890 def Update(self, running = False): 01891 """!Update action""" 01892 if running: 01893 self._setBrush(running = True) 01894 else: 01895 self._setBrush() 01896 self._setPen() 01897 01898 def OnDraw(self, dc): 01899 """!Draw action in canvas""" 01900 self._setBrush() 01901 self._setPen() 01902 ogl.RectangleShape.OnDraw(self, dc) 01903 01904 class ModelData(ModelObject, ogl.EllipseShape): 01905 def __init__(self, parent, x, y, value = '', prompt = '', width = None, height = None): 01906 """Data item class 01907 01908 @param parent window parent 01909 @param x, y position of the shape 01910 @param fname, tname list of parameter names from / to 01911 @param value value 01912 @param prompt type of GIS element 01913 @param width,height dimension of the shape 01914 """ 01915 ModelObject.__init__(self) 01916 01917 self.parent = parent 01918 self.value = value 01919 self.prompt = prompt 01920 self.intermediate = False 01921 self.propWin = None 01922 if not width: 01923 width = UserSettings.Get(group='modeler', key='data', subkey=('size', 'width')) 01924 if not height: 01925 height = UserSettings.Get(group='modeler', key='data', subkey=('size', 'height')) 01926 01927 if self.parent.GetCanvas(): 01928 ogl.EllipseShape.__init__(self, width, height) 01929 01930 self.SetCanvas(self.parent) 01931 self.SetX(x) 01932 self.SetY(y) 01933 self.SetPen(wx.BLACK_PEN) 01934 self._setBrush() 01935 01936 self._setText() 01937 01938 def IsIntermediate(self): 01939 """!Checks if data item is intermediate""" 01940 return self.intermediate 01941 01942 def SetIntermediate(self, im): 01943 """!Set intermediate flag""" 01944 self.intermediate = im 01945 01946 def OnDraw(self, dc): 01947 pen = self.GetPen() 01948 pen.SetWidth(1) 01949 if self.intermediate: 01950 pen.SetStyle(wx.SHORT_DASH) 01951 else: 01952 pen.SetStyle(wx.SOLID) 01953 self.SetPen(pen) 01954 01955 ogl.EllipseShape.OnDraw(self, dc) 01956 01957 def GetLog(self, string = True): 01958 """!Get logging info""" 01959 name = list() 01960 for rel in self.GetRelations(): 01961 name.append(rel.GetName()) 01962 if name: 01963 return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')' 01964 else: 01965 return self.value + ' (' + self.prompt + ')' 01966 01967 def GetName(self): 01968 """!Get list of names""" 01969 name = list() 01970 for rel in self.GetRelations(): 01971 name.append(rel.GetName()) 01972 01973 return name 01974 01975 def GetPrompt(self): 01976 """!Get prompt""" 01977 return self.prompt 01978 01979 def SetPrompt(self, prompt): 01980 """!Set prompt 01981 01982 @param prompt 01983 """ 01984 self.prompt = prompt 01985 01986 def GetValue(self): 01987 """!Get value""" 01988 return self.value 01989 01990 def SetValue(self, value): 01991 """!Set value 01992 01993 @param value 01994 """ 01995 self.value = value 01996 self._setText() 01997 for direction in ('from', 'to'): 01998 for rel in self.GetRelations(direction): 01999 if direction == 'from': 02000 action = rel.GetTo() 02001 else: 02002 action = rel.GetFrom() 02003 02004 task = menuform.GUI(show = None).ParseCommand(cmd = action.GetLog(string = False)) 02005 task.set_param(rel.GetName(), self.value) 02006 action.SetParams(params = task.get_options()) 02007 02008 def GetPropDialog(self): 02009 """!Get properties dialog""" 02010 return self.propWin 02011 02012 def SetPropDialog(self, win): 02013 """!Get properties dialog""" 02014 self.propWin = win 02015 02016 def _setBrush(self): 02017 """!Set brush""" 02018 if self.prompt == 'raster': 02019 color = UserSettings.Get(group = 'modeler', key = 'data', 02020 subkey = ('color', 'raster')) 02021 elif self.prompt == 'raster3d': 02022 color = UserSettings.Get(group = 'modeler', key = 'data', 02023 subkey = ('color', 'raster3d')) 02024 elif self.prompt == 'vector': 02025 color = UserSettings.Get(group = 'modeler', key = 'data', 02026 subkey = ('color', 'vector')) 02027 else: 02028 color = UserSettings.Get(group = 'modeler', key = 'action', 02029 subkey = ('color', 'invalid')) 02030 wxColor = wx.Color(color[0], color[1], color[2]) 02031 self.SetBrush(wx.Brush(wxColor)) 02032 02033 def _setPen(self): 02034 """!Set pen""" 02035 isParameterized = False 02036 for rel in self.GetRelations('from'): 02037 if rel.GetTo().IsParameterized(): 02038 isParameterized = True 02039 break 02040 if not isParameterized: 02041 for rel in self.GetRelations('to'): 02042 if rel.GetFrom().IsParameterized(): 02043 isParameterized = True 02044 break 02045 02046 if isParameterized: 02047 width = int(UserSettings.Get(group = 'modeler', key = 'action', 02048 subkey = ('width', 'parameterized'))) 02049 else: 02050 width = int(UserSettings.Get(group = 'modeler', key = 'action', 02051 subkey = ('width', 'default'))) 02052 pen = self.GetPen() 02053 pen.SetWidth(width) 02054 self.SetPen(pen) 02055 02056 def _setText(self): 02057 """!Update text""" 02058 self.ClearText() 02059 name = [] 02060 for rel in self.GetRelations(): 02061 name.append(rel.GetName()) 02062 self.AddText('/'.join(name)) 02063 if self.value: 02064 self.AddText(self.value) 02065 else: 02066 self.AddText(_('<not defined>')) 02067 02068 def Update(self): 02069 """!Update action""" 02070 self._setBrush() 02071 self._setPen() 02072 self._setText() 02073 02074 class ModelDataDialog(ElementDialog): 02075 """!Data item properties dialog""" 02076 def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Data properties"), 02077 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER): 02078 self.parent = parent 02079 self.shape = shape 02080 02081 label, etype = self._getLabel() 02082 ElementDialog.__init__(self, parent, title, label = label, etype = etype) 02083 02084 self.element = gselect.Select(parent = self.panel, 02085 type = prompt) 02086 self.element.SetValue(shape.GetValue()) 02087 02088 self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOK) 02089 self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel) 02090 02091 self.PostInit() 02092 02093 if shape.GetValue(): 02094 self.btnOK.Enable() 02095 02096 self._layout() 02097 self.SetMinSize(self.GetSize()) 02098 02099 def _getLabel(self): 02100 etype = False 02101 prompt = self.shape.GetPrompt() 02102 if prompt == 'raster': 02103 label = _('Name of raster map:') 02104 elif prompt == 'vector': 02105 label = _('Name of vector map:') 02106 else: 02107 etype = True 02108 label = _('Name of element:') 02109 02110 return label, etype 02111 02112 def _layout(self): 02113 """!Do layout""" 02114 self.dataSizer.Add(self.element, proportion=0, 02115 flag=wx.EXPAND | wx.ALL, border=1) 02116 02117 self.panel.SetSizer(self.sizer) 02118 self.sizer.Fit(self) 02119 02120 def OnOK(self, event): 02121 """!Ok pressed""" 02122 self.shape.SetValue(self.GetElement()) 02123 if self.etype: 02124 elem = self.GetType() 02125 if elem == 'rast': 02126 self.shape.SetPrompt('raster') 02127 elif elem == 'vect': 02128 self.shape.SetPrompt('raster') 02129 02130 self.parent.canvas.Refresh() 02131 self.parent.SetStatusText('', 0) 02132 self.shape.SetPropDialog(None) 02133 02134 if self.IsModal(): 02135 event.Skip() 02136 else: 02137 self.Destroy() 02138 02139 def OnCancel(self, event): 02140 """!Cancel pressed""" 02141 self.shape.SetPropDialog(None) 02142 if self.IsModal(): 02143 event.Skip() 02144 else: 02145 self.Destroy() 02146 02147 class ModelEvtHandler(ogl.ShapeEvtHandler): 02148 """!Model event handler class""" 02149 def __init__(self, log, frame): 02150 ogl.ShapeEvtHandler.__init__(self) 02151 self.log = log 02152 self.frame = frame 02153 self.x = self.y = None 02154 02155 def OnLeftClick(self, x, y, keys = 0, attachment = 0): 02156 """!Left mouse button pressed -> select item & update statusbar""" 02157 shape = self.GetShape() 02158 canvas = shape.GetCanvas() 02159 dc = wx.ClientDC(canvas) 02160 canvas.PrepareDC(dc) 02161 02162 if hasattr(self.frame, 'defineRelation'): 02163 drel = self.frame.defineRelation 02164 if drel['from'] is None: 02165 drel['from'] = shape 02166 elif drel['to'] is None: 02167 drel['to'] = shape 02168 rel = ModelRelation(parent = self.frame, fromShape = drel['from'], 02169 toShape = drel['to']) 02170 dlg = ModelRelationDialog(parent = self.frame, 02171 shape = rel) 02172 if dlg.IsValid(): 02173 ret = dlg.ShowModal() 02174 if ret == wx.ID_OK: 02175 option = dlg.GetOption() 02176 rel.SetName(option) 02177 drel['from'].AddRelation(rel) 02178 drel['to'].AddRelation(rel) 02179 drel['from'].Update() 02180 params = { 'params' : [{ 'name' : option, 02181 'value' : drel['from'].GetValue()}] } 02182 drel['to'].MergeParams(params) 02183 self.frame.AddLine(rel) 02184 02185 dlg.Destroy() 02186 del self.frame.defineRelation 02187 02188 if shape.Selected(): 02189 shape.Select(False, dc) 02190 else: 02191 redraw = False 02192 shapeList = canvas.GetDiagram().GetShapeList() 02193 toUnselect = list() 02194 02195 for s in shapeList: 02196 if s.Selected(): 02197 toUnselect.append(s) 02198 02199 shape.Select(True, dc) 02200 02201 for s in toUnselect: 02202 s.Select(False, dc) 02203 02204 canvas.Refresh(False) 02205 02206 if hasattr(shape, "GetLog"): 02207 self.log.SetStatusText(shape.GetLog(), 0) 02208 else: 02209 self.log.SetStatusText('', 0) 02210 02211 def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0): 02212 """!Left mouse button pressed (double-click) -> show properties""" 02213 self.OnProperties() 02214 02215 def OnProperties(self, event = None): 02216 """!Show properties dialog""" 02217 self.frame.ModelChanged() 02218 shape = self.GetShape() 02219 if isinstance(shape, ModelAction): 02220 module = menuform.GUI(parent = self.frame, show = True).ParseCommand(shape.GetLog(string = False), 02221 completed = (self.frame.GetOptData, shape, shape.GetParams())) 02222 02223 elif isinstance(shape, ModelData): 02224 dlg = ModelDataDialog(parent = self.frame, shape = shape) 02225 shape.SetPropDialog(dlg) 02226 dlg.CentreOnParent() 02227 dlg.Show() 02228 02229 elif isinstance(shape, ModelLoop): 02230 dlg = ModelLoopDialog(parent = self.frame, shape = shape) 02231 dlg.CentreOnParent() 02232 if dlg.ShowModal() == wx.ID_OK: 02233 shape.SetText(dlg.GetCondition()) 02234 alist = list() 02235 ids = dlg.GetItems() 02236 for aId in ids['unchecked']: 02237 action = self.frame.GetModel().GetItem(aId) 02238 action.UnSetBlock(shape) 02239 for aId in ids['checked']: 02240 action = self.frame.GetModel().GetItem(aId) 02241 action.SetBlock(shape) 02242 if action: 02243 alist.append(action) 02244 shape.SetItems(alist) 02245 self.frame.DefineLoop(shape) 02246 self.frame.GetCanvas().Refresh() 02247 02248 dlg.Destroy() 02249 02250 elif isinstance(shape, ModelCondition): 02251 dlg = ModelConditionDialog(parent = self.frame, shape = shape) 02252 dlg.CentreOnParent() 02253 if dlg.ShowModal() == wx.ID_OK: 02254 shape.SetText(dlg.GetCondition()) 02255 ids = dlg.GetItems() 02256 for b in ids.keys(): 02257 alist = list() 02258 for aId in ids[b]['unchecked']: 02259 action = self.frame.GetModel().GetItem(aId) 02260 action.UnSetBlock(shape) 02261 for aId in ids[b]['checked']: 02262 action = self.frame.GetModel().GetItem(aId) 02263 action.SetBlock(shape) 02264 if action: 02265 alist.append(action) 02266 shape.SetItems(alist, branch = b) 02267 self.frame.DefineCondition(shape) 02268 self.frame.GetCanvas().Refresh() 02269 02270 dlg.Destroy() 02271 02272 def OnBeginDragLeft(self, x, y, keys = 0, attachment = 0): 02273 """!Drag shape (begining)""" 02274 self.frame.ModelChanged() 02275 if self._previousHandler: 02276 self._previousHandler.OnBeginDragLeft(x, y, keys, attachment) 02277 02278 def OnEndDragLeft(self, x, y, keys = 0, attachment = 0): 02279 """!Drag shape (end)""" 02280 if self._previousHandler: 02281 self._previousHandler.OnEndDragLeft(x, y, keys, attachment) 02282 02283 shape = self.GetShape() 02284 if isinstance(shape, ModelLoop): 02285 self.frame.DefineLoop(shape) 02286 elif isinstance(shape, ModelCondition): 02287 self.frame.DefineCondition(shape) 02288 02289 for mo in shape.GetBlock(): 02290 if isinstance(mo, ModelLoop): 02291 self.frame.DefineLoop(mo) 02292 elif isinstance(mo, ModelCondition): 02293 self.frame.DefineCondition(mo) 02294 02295 def OnEndSize(self, x, y): 02296 """!Resize shape""" 02297 self.frame.ModelChanged() 02298 if self._previousHandler: 02299 self._previousHandler.OnEndSize(x, y) 02300 02301 def OnRightClick(self, x, y, keys = 0, attachment = 0): 02302 """!Right click -> pop-up menu""" 02303 if not hasattr (self, "popupID"): 02304 self.popupID = dict() 02305 for key in ('remove', 'enable', 'addPoint', 02306 'delPoint', 'intermediate', 'props', 'id'): 02307 self.popupID[key] = wx.NewId() 02308 02309 # record coordinates 02310 self.x = x 02311 self.y = y 02312 02313 shape = self.GetShape() 02314 popupMenu = wx.Menu() 02315 popupMenu.Append(self.popupID['remove'], text=_('Remove')) 02316 self.frame.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID['remove']) 02317 if isinstance(shape, ModelAction) or isinstance(shape, ModelLoop): 02318 if shape.IsEnabled(): 02319 popupMenu.Append(self.popupID['enable'], text=_('Disable')) 02320 self.frame.Bind(wx.EVT_MENU, self.OnDisable, id = self.popupID['enable']) 02321 else: 02322 popupMenu.Append(self.popupID['enable'], text=_('Enable')) 02323 self.frame.Bind(wx.EVT_MENU, self.OnEnable, id = self.popupID['enable']) 02324 02325 if isinstance(shape, ModelRelation): 02326 popupMenu.AppendSeparator() 02327 popupMenu.Append(self.popupID['addPoint'], text=_('Add control point')) 02328 self.frame.Bind(wx.EVT_MENU, self.OnAddPoint, id = self.popupID['addPoint']) 02329 popupMenu.Append(self.popupID['delPoint'], text=_('Remove control point')) 02330 self.frame.Bind(wx.EVT_MENU, self.OnRemovePoint, id = self.popupID['delPoint']) 02331 if len(shape.GetLineControlPoints()) == 2: 02332 popupMenu.Enable(self.popupID['delPoint'], False) 02333 02334 if isinstance(shape, ModelData) and '@' not in shape.GetValue(): 02335 popupMenu.AppendSeparator() 02336 popupMenu.Append(self.popupID['intermediate'], text=_('Intermediate'), 02337 kind = wx.ITEM_CHECK) 02338 if self.GetShape().IsIntermediate(): 02339 popupMenu.Check(self.popupID['intermediate'], True) 02340 02341 self.frame.Bind(wx.EVT_MENU, self.OnIntermediate, id = self.popupID['intermediate']) 02342 02343 if isinstance(shape, ModelData) or \ 02344 isinstance(shape, ModelAction) or \ 02345 isinstance(shape, ModelLoop): 02346 popupMenu.AppendSeparator() 02347 popupMenu.Append(self.popupID['props'], text=_('Properties')) 02348 self.frame.Bind(wx.EVT_MENU, self.OnProperties, id = self.popupID['props']) 02349 02350 if isinstance(shape, ModelAction): 02351 popupMenu.Append(self.popupID['id'], text=_('Change ID')) 02352 self.frame.Bind(wx.EVT_MENU, self.OnChangeId, id = self.popupID['id']) 02353 02354 self.frame.PopupMenu(popupMenu) 02355 popupMenu.Destroy() 02356 02357 def OnChangeId(self, event): 02358 """!Change action id""" 02359 pass 02360 02361 def OnDisable(self, event): 02362 """!Disable action""" 02363 self._onEnable(False) 02364 02365 def OnEnable(self, event): 02366 """!Disable action""" 02367 self._onEnable(True) 02368 02369 def _onEnable(self, enable): 02370 shape = self.GetShape() 02371 shape.Enable(enable) 02372 self.frame.ModelChanged() 02373 self.frame.canvas.Refresh() 02374 02375 def OnAddPoint(self, event): 02376 """!Add control point""" 02377 shape = self.GetShape() 02378 shape.InsertLineControlPoint(point = wx.RealPoint(self.x, self.y)) 02379 shape.ResetShapes() 02380 shape.Select(True) 02381 self.frame.ModelChanged() 02382 self.frame.canvas.Refresh() 02383 02384 def OnRemovePoint(self, event): 02385 """!Remove control point""" 02386 shape = self.GetShape() 02387 shape.DeleteLineControlPoint() 02388 shape.Select(False) 02389 shape.Select(True) 02390 self.frame.ModelChanged() 02391 self.frame.canvas.Refresh() 02392 02393 def OnIntermediate(self, event): 02394 """!Mark data as intermediate""" 02395 self.frame.ModelChanged() 02396 shape = self.GetShape() 02397 shape.SetIntermediate(event.IsChecked()) 02398 self.frame.canvas.Refresh() 02399 02400 def OnRemove(self, event): 02401 """!Remove shape 02402 """ 02403 self.frame.GetCanvas().RemoveSelected() 02404 self.frame.itemPanel.Update() 02405 02406 class ModelSearchDialog(wx.Dialog): 02407 def __init__(self, parent, id = wx.ID_ANY, title = _("Add new GRASS module to the model"), 02408 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 02409 """!Graphical modeler module search window 02410 02411 @param parent parent window 02412 @param id window id 02413 @param title window title 02414 @param kwargs wx.Dialogs' arguments 02415 """ 02416 self.parent = parent 02417 02418 wx.Dialog.__init__(self, parent = parent, id = id, title = title, **kwargs) 02419 self.SetName("ModelerDialog") 02420 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 02421 02422 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 02423 02424 self.cmdBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 02425 label=" %s " % _("Command")) 02426 02427 self.cmd_prompt = prompt.GPromptSTC(parent = self) 02428 self.search = SearchModuleWindow(parent = self.panel, cmdPrompt = self.cmd_prompt, showTip = True) 02429 wx.CallAfter(self.cmd_prompt.SetFocus) 02430 02431 # get commands 02432 items = self.cmd_prompt.GetCommandItems() 02433 02434 self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL) 02435 self.btnOk = wx.Button(self.panel, wx.ID_OK) 02436 self.btnOk.SetDefault() 02437 self.btnOk.Enable(False) 02438 02439 self.cmd_prompt.Bind(wx.EVT_KEY_UP, self.OnText) 02440 self.search.searchChoice.Bind(wx.EVT_CHOICE, self.OnText) 02441 self.Bind(wx.EVT_BUTTON, self.OnOk, self.btnOk) 02442 02443 self._layout() 02444 02445 self.SetSize((500, 275)) 02446 02447 def _layout(self): 02448 cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL) 02449 cmdSizer.Add(item = self.cmd_prompt, proportion = 1, 02450 flag = wx.EXPAND) 02451 02452 btnSizer = wx.StdDialogButtonSizer() 02453 btnSizer.AddButton(self.btnCancel) 02454 btnSizer.AddButton(self.btnOk) 02455 btnSizer.Realize() 02456 02457 mainSizer = wx.BoxSizer(wx.VERTICAL) 02458 mainSizer.Add(item = self.search, proportion = 0, 02459 flag = wx.EXPAND | wx.ALL, border = 3) 02460 mainSizer.Add(item = cmdSizer, proportion = 1, 02461 flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border = 3) 02462 mainSizer.Add(item = btnSizer, proportion = 0, 02463 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) 02464 02465 self.panel.SetSizer(mainSizer) 02466 mainSizer.Fit(self.panel) 02467 02468 self.Layout() 02469 02470 def GetPanel(self): 02471 """!Get dialog panel""" 02472 return self.panel 02473 02474 def GetCmd(self): 02475 """!Get command""" 02476 line = self.cmd_prompt.GetCurLine()[0].strip() 02477 if len(line) == 0: 02478 list() 02479 02480 try: 02481 cmd = utils.split(str(line)) 02482 except UnicodeError: 02483 cmd = utils.split(utils.EncodeString((line))) 02484 02485 return cmd 02486 02487 def OnOk(self, event): 02488 """!Button 'OK' pressed""" 02489 self.btnOk.SetFocus() 02490 cmd = self.GetCmd() 02491 02492 if len(cmd) < 1: 02493 GError(parent = self, 02494 message = _("Command not defined.\n\n" 02495 "Unable to add new action to the model.")) 02496 return 02497 02498 if cmd[0] not in globalvar.grassCmd['all']: 02499 GError(parent = self, 02500 message = _("'%s' is not a GRASS module.\n\n" 02501 "Unable to add new action to the model.") % cmd[0]) 02502 return 02503 02504 self.EndModal(wx.ID_OK) 02505 02506 def OnText(self, event): 02507 """!Text in prompt changed""" 02508 if self.cmd_prompt.AutoCompActive(): 02509 event.Skip() 02510 return 02511 02512 if isinstance(event, wx.KeyEvent): 02513 entry = self.cmd_prompt.GetTextLeft() 02514 elif isinstance(event, wx.stc.StyledTextEvent): 02515 entry = event.GetText() 02516 else: 02517 entry = event.GetString() 02518 02519 if entry: 02520 self.btnOk.Enable() 02521 else: 02522 self.btnOk.Enable(False) 02523 02524 event.Skip() 02525 02526 def Reset(self): 02527 """!Reset dialog""" 02528 self.search.Reset() 02529 self.cmd_prompt.OnCmdErase(None) 02530 self.btnOk.Enable(False) 02531 self.cmd_prompt.SetFocus() 02532 02533 class ModelRelation(ogl.LineShape): 02534 """!Data - action relation""" 02535 def __init__(self, parent, fromShape, toShape, param = ''): 02536 self.fromShape = fromShape 02537 self.toShape = toShape 02538 self.param = param 02539 self.parent = parent 02540 02541 self._points = None 02542 02543 if self.parent.GetCanvas(): 02544 ogl.LineShape.__init__(self) 02545 02546 def __del__(self): 02547 if self in self.fromShape.rels: 02548 self.fromShape.rels.remove(self) 02549 if self in self.toShape.rels: 02550 self.toShape.rels.remove(self) 02551 02552 def GetFrom(self): 02553 """!Get id of 'from' shape""" 02554 return self.fromShape 02555 02556 def GetTo(self): 02557 """!Get id of 'to' shape""" 02558 return self.toShape 02559 02560 def GetData(self): 02561 """!Get related ModelData instance 02562 02563 @return ModelData instance 02564 @return None if not found 02565 """ 02566 if isinstance(self.fromShape, ModelData): 02567 return self.fromShape 02568 elif isinstance(self.toShape, ModelData): 02569 return self.toShape 02570 02571 return None 02572 02573 def GetName(self): 02574 """!Get parameter name""" 02575 return self.param 02576 02577 def ResetShapes(self): 02578 """!Reset related objects""" 02579 self.fromShape.ResetControlPoints() 02580 self.toShape.ResetControlPoints() 02581 self.ResetControlPoints() 02582 02583 def SetControlPoints(self, points): 02584 """!Set control points""" 02585 self._points = points 02586 02587 def GetControlPoints(self): 02588 """!Get list of control points""" 02589 return self._points 02590 02591 def _setPen(self): 02592 """!Set pen""" 02593 pen = self.GetPen() 02594 pen.SetWidth(1) 02595 pen.SetStyle(wx.SOLID) 02596 self.SetPen(pen) 02597 02598 def OnDraw(self, dc): 02599 """!Draw relation""" 02600 self._setPen() 02601 ogl.LineShape.OnDraw(self, dc) 02602 02603 def SetName(self, param): 02604 self.param = param 02605 02606 class ModelRelationDialog(wx.Dialog): 02607 """!Relation properties dialog""" 02608 def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Relation properties"), 02609 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 02610 self.parent = parent 02611 self.shape = shape 02612 02613 options = self._getOptions() 02614 if not options: 02615 self.valid = False 02616 return 02617 02618 self.valid = True 02619 wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs) 02620 self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass.ico'), wx.BITMAP_TYPE_ICO)) 02621 02622 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 02623 02624 self.fromBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 02625 label = " %s " % _("From")) 02626 self.toBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 02627 label = " %s " % _("To")) 02628 02629 self.option = wx.ComboBox(parent = self.panel, id = wx.ID_ANY, 02630 style = wx.CB_READONLY, 02631 choices = options) 02632 self.option.Bind(wx.EVT_COMBOBOX, self.OnOption) 02633 02634 self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL) 02635 self.btnOk = wx.Button(self.panel, wx.ID_OK) 02636 self.btnOk.Enable(False) 02637 02638 self._layout() 02639 02640 def _layout(self): 02641 mainSizer = wx.BoxSizer(wx.VERTICAL) 02642 02643 fromSizer = wx.StaticBoxSizer(self.fromBox, wx.VERTICAL) 02644 self._layoutShape(shape = self.shape.GetFrom(), sizer = fromSizer) 02645 toSizer = wx.StaticBoxSizer(self.toBox, wx.VERTICAL) 02646 self._layoutShape(shape = self.shape.GetTo(), sizer = toSizer) 02647 02648 btnSizer = wx.StdDialogButtonSizer() 02649 btnSizer.AddButton(self.btnCancel) 02650 btnSizer.AddButton(self.btnOk) 02651 btnSizer.Realize() 02652 02653 mainSizer.Add(item = fromSizer, proportion = 0, 02654 flag = wx.EXPAND | wx.ALL, border = 5) 02655 mainSizer.Add(item = toSizer, proportion = 0, 02656 flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) 02657 mainSizer.Add(item = btnSizer, proportion = 0, 02658 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) 02659 02660 self.panel.SetSizer(mainSizer) 02661 mainSizer.Fit(self.panel) 02662 02663 self.Layout() 02664 self.SetSize(self.GetBestSize()) 02665 02666 def _layoutShape(self, shape, sizer): 02667 if isinstance(shape, ModelData): 02668 sizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, 02669 label = _("Data: %s") % shape.GetLog()), 02670 proportion = 1, flag = wx.EXPAND | wx.ALL, 02671 border = 5) 02672 elif isinstance(shape, ModelAction): 02673 gridSizer = wx.GridBagSizer (hgap = 5, vgap = 5) 02674 gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, 02675 label = _("Command:")), 02676 pos = (0, 0)) 02677 gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, 02678 label = shape.GetName()), 02679 pos = (0, 1)) 02680 gridSizer.Add(item = wx.StaticText(parent = self.panel, id = wx.ID_ANY, 02681 label = _("Option:")), 02682 flag = wx.ALIGN_CENTER_VERTICAL, 02683 pos = (1, 0)) 02684 gridSizer.Add(item = self.option, 02685 pos = (1, 1)) 02686 sizer.Add(item = gridSizer, 02687 proportion = 1, flag = wx.EXPAND | wx.ALL, 02688 border = 5) 02689 02690 def _getOptions(self): 02691 """!Get relevant options""" 02692 items = [] 02693 fromShape = self.shape.GetFrom() 02694 if not isinstance(fromShape, ModelData): 02695 GError(parent = self.parent, 02696 message = _("Relation doesn't start with data item.\n" 02697 "Unable to add relation.")) 02698 return items 02699 02700 toShape = self.shape.GetTo() 02701 if not isinstance(toShape, ModelAction): 02702 GError(parent = self.parent, 02703 message = _("Relation doesn't point to GRASS command.\n" 02704 "Unable to add relation.")) 02705 return items 02706 02707 prompt = fromShape.GetPrompt() 02708 task = toShape.GetTask() 02709 for p in task.get_options()['params']: 02710 if p.get('prompt', '') == prompt and \ 02711 'name' in p: 02712 items.append(p['name']) 02713 02714 if not items: 02715 GError(parent = self.parent, 02716 message = _("No relevant option found.\n" 02717 "Unable to add relation.")) 02718 return items 02719 02720 def GetOption(self): 02721 """!Get selected option""" 02722 return self.option.GetStringSelection() 02723 02724 def IsValid(self): 02725 """!Check if relation is valid""" 02726 return self.valid 02727 02728 def OnOption(self, event): 02729 """!Set option""" 02730 if event.GetString(): 02731 self.btnOk.Enable() 02732 else: 02733 self.btnOk.Enable(False) 02734 02735 class ProcessModelFile: 02736 """!Process GRASS model file (gxm)""" 02737 def __init__(self, tree): 02738 """!A ElementTree handler for the GXM XML file, as defined in 02739 grass-gxm.dtd. 02740 """ 02741 self.tree = tree 02742 self.root = self.tree.getroot() 02743 02744 # list of actions, data 02745 self.properties = dict() 02746 self.variables = dict() 02747 self.actions = list() 02748 self.data = list() 02749 self.loops = list() 02750 self.conditions = list() 02751 02752 self._processWindow() 02753 self._processProperties() 02754 self._processVariables() 02755 self._processItems() 02756 self._processData() 02757 02758 def _filterValue(self, value): 02759 """!Filter value 02760 02761 @param value 02762 """ 02763 value = value.replace('<', '<') 02764 value = value.replace('>', '>') 02765 02766 return value 02767 02768 def _getNodeText(self, node, tag, default = ''): 02769 """!Get node text""" 02770 p = node.find(tag) 02771 if p is not None: 02772 if p.text: 02773 return utils.normalize_whitespace(p.text) 02774 else: 02775 return '' 02776 02777 return default 02778 02779 def _processWindow(self): 02780 """!Process window properties""" 02781 node = self.root.find('window') 02782 if node is None: 02783 self.pos = self.size = None 02784 return 02785 02786 self.pos, self.size = self._getDim(node) 02787 02788 def _processProperties(self): 02789 """!Process model properties""" 02790 node = self.root.find('properties') 02791 if node is None: 02792 return 02793 for key in ('name', 'description', 'author'): 02794 self._processProperty(node, key) 02795 02796 for f in node.findall('flag'): 02797 name = f.get('name', '') 02798 if name == 'overwrite': 02799 self.properties['overwrite'] = True 02800 02801 def _processProperty(self, pnode, name): 02802 """!Process given property""" 02803 node = pnode.find(name) 02804 if node is not None: 02805 self.properties[name] = node.text 02806 else: 02807 self.properties[name] = '' 02808 02809 def _processVariables(self): 02810 """!Process model variables""" 02811 vnode = self.root.find('variables') 02812 if vnode is None: 02813 return 02814 for node in vnode.findall('variable'): 02815 name = node.get('name', '') 02816 if not name: 02817 continue # should not happen 02818 self.variables[name] = { 'type' : node.get('type', 'string') } 02819 for key in ('description', 'value'): 02820 self._processVariable(node, name, key) 02821 02822 def _processVariable(self, pnode, name, key): 02823 """!Process given variable""" 02824 node = pnode.find(key) 02825 if node is not None: 02826 if node.text: 02827 self.variables[name][key] = node.text 02828 02829 def _processItems(self): 02830 """!Process model items (actions, loops, conditions)""" 02831 self._processActions() 02832 self._processLoops() 02833 self._processConditions() 02834 02835 def _processActions(self): 02836 """!Process model file""" 02837 for action in self.root.findall('action'): 02838 pos, size = self._getDim(action) 02839 disabled = False 02840 02841 task = action.find('task') 02842 if task is not None: 02843 if task.find('disabled') is not None: 02844 disabled = True 02845 task = self._processTask(task) 02846 else: 02847 task = None 02848 02849 aId = int(action.get('id', -1)) 02850 02851 self.actions.append({ 'pos' : pos, 02852 'size' : size, 02853 'task' : task, 02854 'id' : aId, 02855 'disabled' : disabled }) 02856 02857 def _getDim(self, node): 02858 """!Get position and size of shape""" 02859 pos = size = None 02860 posAttr = node.get('pos', None) 02861 if posAttr: 02862 posVal = map(int, posAttr.split(',')) 02863 try: 02864 pos = (posVal[0], posVal[1]) 02865 except: 02866 pos = None 02867 02868 sizeAttr = node.get('size', None) 02869 if sizeAttr: 02870 sizeVal = map(int, sizeAttr.split(',')) 02871 try: 02872 size = (sizeVal[0], sizeVal[1]) 02873 except: 02874 size = None 02875 02876 return pos, size 02877 02878 def _processData(self): 02879 """!Process model file""" 02880 for data in self.root.findall('data'): 02881 pos, size = self._getDim(data) 02882 param = data.find('data-parameter') 02883 prompt = value = None 02884 if param is not None: 02885 prompt = param.get('prompt', None) 02886 value = self._filterValue(self._getNodeText(param, 'value')) 02887 02888 if data.find('intermediate') is None: 02889 intermediate = False 02890 else: 02891 intermediate = True 02892 02893 rels = list() 02894 for rel in data.findall('relation'): 02895 defrel = { 'id' : int(rel.get('id', -1)), 02896 'dir' : rel.get('dir', 'to'), 02897 'name' : rel.get('name', '') } 02898 points = list() 02899 for point in rel.findall('point'): 02900 x = self._filterValue(self._getNodeText(point, 'x')) 02901 y = self._filterValue(self._getNodeText(point, 'y')) 02902 points.append((float(x), float(y))) 02903 defrel['points'] = points 02904 rels.append(defrel) 02905 02906 self.data.append({ 'pos' : pos, 02907 'size': size, 02908 'prompt' : prompt, 02909 'value' : value, 02910 'intermediate' : intermediate, 02911 'rels' : rels }) 02912 02913 def _processTask(self, node): 02914 """!Process task 02915 02916 @return grassTask instance 02917 @return None on error 02918 """ 02919 cmd = list() 02920 parameterized = list() 02921 02922 name = node.get('name', None) 02923 if not name: 02924 return None 02925 02926 cmd.append(name) 02927 02928 # flags 02929 for f in node.findall('flag'): 02930 flag = f.get('name', '') 02931 if f.get('parameterized', '0') == '1': 02932 parameterized.append(('flag', flag)) 02933 if f.get('value', '1') == '0': 02934 continue 02935 if len(flag) > 1: 02936 cmd.append('--' + flag) 02937 else: 02938 cmd.append('-' + flag) 02939 # parameters 02940 for p in node.findall('parameter'): 02941 name = p.get('name', '') 02942 if p.find('parameterized') is not None: 02943 parameterized.append(('param', name)) 02944 cmd.append('%s=%s' % (name, 02945 self._filterValue(self._getNodeText(p, 'value')))) 02946 02947 task, err = menuform.GUI(show = None, checkError = True).ParseCommand(cmd = cmd) 02948 if err: 02949 GWarning(os.linesep.join(err)) 02950 02951 for opt, name in parameterized: 02952 if opt == 'flag': 02953 task.set_flag(name, True, element = 'parameterized') 02954 else: 02955 task.set_param(name, True, element = 'parameterized') 02956 02957 return task 02958 02959 def _processLoops(self): 02960 """!Process model loops""" 02961 for node in self.root.findall('loop'): 02962 pos, size = self._getDim(node) 02963 text = self._filterValue(self._getNodeText(node, 'condition')).strip() 02964 aid = list() 02965 for anode in node.findall('item'): 02966 try: 02967 aid.append(int(anode.text)) 02968 except ValueError: 02969 pass 02970 02971 self.loops.append({ 'pos' : pos, 02972 'size' : size, 02973 'text' : text, 02974 'id' : int(node.get('id', -1)), 02975 'items' : aid }) 02976 02977 def _processConditions(self): 02978 """!Process model conditions""" 02979 for node in self.root.findall('if-else'): 02980 pos, size = self._getDim(node) 02981 text = self._filterValue(self._getNodeText(node, 'condition')).strip() 02982 aid = { 'if' : list(), 02983 'else' : list() } 02984 for b in aid.keys(): 02985 bnode = node.find(b) 02986 if bnode is None: 02987 continue 02988 for anode in bnode.findall('item'): 02989 try: 02990 aid[b].append(int(anode.text)) 02991 except ValueError: 02992 pass 02993 02994 self.conditions.append({ 'pos' : pos, 02995 'size' : size, 02996 'text' : text, 02997 'id' : int(node.get('id', -1)), 02998 'items' : aid }) 02999 03000 class WriteModelFile: 03001 """!Generic class for writing model file""" 03002 def __init__(self, fd, model): 03003 self.fd = fd 03004 self.model = model 03005 self.properties = model.GetProperties() 03006 self.variables = model.GetVariables() 03007 self.items = model.GetItems() 03008 03009 self.indent = 0 03010 03011 self._header() 03012 03013 self._window() 03014 self._properties() 03015 self._variables() 03016 self._items() 03017 03018 dataList = list() 03019 for action in model.GetItems(objType = ModelAction): 03020 for rel in action.GetRelations(): 03021 dataItem = rel.GetData() 03022 if dataItem not in dataList: 03023 dataList.append(dataItem) 03024 self._data(dataList) 03025 03026 self._footer() 03027 03028 def _filterValue(self, value): 03029 """!Make value XML-valid""" 03030 value = value.replace('<', '<') 03031 value = value.replace('>', '>') 03032 03033 return value 03034 03035 def _header(self): 03036 """!Write header""" 03037 self.fd.write('<?xml version="1.0" encoding="UTF-8"?>\n') 03038 self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n') 03039 self.fd.write('%s<gxm>\n' % (' ' * self.indent)) 03040 self.indent += 4 03041 03042 def _footer(self): 03043 """!Write footer""" 03044 self.indent -= 4 03045 self.fd.write('%s</gxm>\n' % (' ' * self.indent)) 03046 03047 def _window(self): 03048 """!Write window properties""" 03049 canvas = self.model.GetCanvas() 03050 if canvas is None: 03051 return 03052 win = canvas.parent 03053 pos = win.GetPosition() 03054 size = win.GetSize() 03055 self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' % \ 03056 (' ' * self.indent, pos[0], pos[1], size[0], size[1])) 03057 03058 def _properties(self): 03059 """!Write model properties""" 03060 self.fd.write('%s<properties>\n' % (' ' * self.indent)) 03061 self.indent += 4 03062 if self.properties['name']: 03063 self.fd.write('%s<name>%s</name>\n' % (' ' * self.indent, self.properties['name'])) 03064 if self.properties['description']: 03065 self.fd.write('%s<description>%s</description>\n' % (' ' * self.indent, 03066 utils.EncodeString(self.properties['description']))) 03067 if self.properties['author']: 03068 self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent, 03069 utils.EncodeString(self.properties['author']))) 03070 03071 if 'overwrite' in self.properties and \ 03072 self.properties['overwrite']: 03073 self.fd.write('%s<flag name="overwrite" />\n' % (' ' * self.indent)) 03074 self.indent -= 4 03075 self.fd.write('%s</properties>\n' % (' ' * self.indent)) 03076 03077 def _variables(self): 03078 """!Write model variables""" 03079 if not self.variables: 03080 return 03081 self.fd.write('%s<variables>\n' % (' ' * self.indent)) 03082 self.indent += 4 03083 for name, values in self.variables.iteritems(): 03084 self.fd.write('%s<variable name="%s" type="%s">\n' % \ 03085 (' ' * self.indent, name, values['type'])) 03086 self.indent += 4 03087 if 'value' in values: 03088 self.fd.write('%s<value>%s</value>\n' % \ 03089 (' ' * self.indent, values['value'])) 03090 if 'description' in values: 03091 self.fd.write('%s<description>%s</description>\n' % \ 03092 (' ' * self.indent, values['description'])) 03093 self.indent -= 4 03094 self.fd.write('%s</variable>\n' % (' ' * self.indent)) 03095 self.indent -= 4 03096 self.fd.write('%s</variables>\n' % (' ' * self.indent)) 03097 03098 def _items(self): 03099 """!Write actions/loops/conditions""" 03100 for item in self.items: 03101 if isinstance(item, ModelAction): 03102 self._action(item) 03103 elif isinstance(item, ModelLoop): 03104 self._loop(item) 03105 elif isinstance(item, ModelCondition): 03106 self._condition(item) 03107 03108 def _action(self, action): 03109 """!Write actions""" 03110 self.fd.write('%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' % \ 03111 (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(), 03112 action.GetWidth(), action.GetHeight())) 03113 self.indent += 4 03114 self.fd.write('%s<task name="%s">\n' % (' ' * self.indent, action.GetLog(string = False)[0])) 03115 self.indent += 4 03116 if not action.IsEnabled(): 03117 self.fd.write('%s<disabled />\n' % (' ' * self.indent)) 03118 for key, val in action.GetParams().iteritems(): 03119 if key == 'flags': 03120 for f in val: 03121 if f.get('value', False) or f.get('parameterized', False): 03122 if f.get('parameterized', False): 03123 if f.get('value', False) == False: 03124 self.fd.write('%s<flag name="%s" value="0" parameterized="1" />\n' % 03125 (' ' * self.indent, f.get('name', ''))) 03126 else: 03127 self.fd.write('%s<flag name="%s" parameterized="1" />\n' % 03128 (' ' * self.indent, f.get('name', ''))) 03129 else: 03130 self.fd.write('%s<flag name="%s" />\n' % 03131 (' ' * self.indent, f.get('name', ''))) 03132 else: # parameter 03133 for p in val: 03134 if not p.get('value', ''): 03135 continue 03136 self.fd.write('%s<parameter name="%s">\n' % 03137 (' ' * self.indent, p.get('name', ''))) 03138 self.indent += 4 03139 if p.get('parameterized', False): 03140 self.fd.write('%s<parameterized />\n' % (' ' * self.indent)) 03141 self.fd.write('%s<value>%s</value>\n' % 03142 (' ' * self.indent, self._filterValue(p.get('value', '')))) 03143 self.indent -= 4 03144 self.fd.write('%s</parameter>\n' % (' ' * self.indent)) 03145 self.indent -= 4 03146 self.fd.write('%s</task>\n' % (' ' * self.indent)) 03147 self.indent -= 4 03148 self.fd.write('%s</action>\n' % (' ' * self.indent)) 03149 03150 def _data(self, dataList): 03151 """!Write data""" 03152 for data in dataList: 03153 self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' % \ 03154 (' ' * self.indent, data.GetX(), data.GetY(), 03155 data.GetWidth(), data.GetHeight())) 03156 self.indent += 4 03157 self.fd.write('%s<data-parameter prompt="%s">\n' % \ 03158 (' ' * self.indent, data.GetPrompt())) 03159 self.indent += 4 03160 self.fd.write('%s<value>%s</value>\n' % 03161 (' ' * self.indent, self._filterValue(data.GetValue()))) 03162 self.indent -= 4 03163 self.fd.write('%s</data-parameter>\n' % (' ' * self.indent)) 03164 03165 if data.IsIntermediate(): 03166 self.fd.write('%s<intermediate />\n' % (' ' * self.indent)) 03167 03168 # relations 03169 for ft in ('from', 'to'): 03170 for rel in data.GetRelations(ft): 03171 if ft == 'from': 03172 aid = rel.GetTo().GetId() 03173 else: 03174 aid = rel.GetFrom().GetId() 03175 self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' % \ 03176 (' ' * self.indent, ft, aid, rel.GetName())) 03177 self.indent += 4 03178 for point in rel.GetLineControlPoints()[1:-1]: 03179 self.fd.write('%s<point>\n' % (' ' * self.indent)) 03180 self.indent += 4 03181 x, y = point.Get() 03182 self.fd.write('%s<x>%d</x>\n' % (' ' * self.indent, int(x))) 03183 self.fd.write('%s<y>%d</y>\n' % (' ' * self.indent, int(y))) 03184 self.indent -= 4 03185 self.fd.write('%s</point>\n' % (' ' * self.indent)) 03186 self.indent -= 4 03187 self.fd.write('%s</relation>\n' % (' ' * self.indent)) 03188 03189 self.indent -= 4 03190 self.fd.write('%s</data>\n' % (' ' * self.indent)) 03191 03192 def _loop(self, loop): 03193 """!Write loops""" 03194 self.fd.write('%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' % \ 03195 (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(), 03196 loop.GetWidth(), loop.GetHeight())) 03197 text = loop.GetText() 03198 self.indent += 4 03199 if text: 03200 self.fd.write('%s<condition>%s</condition>\n' % 03201 (' ' * self.indent, self._filterValue(text))) 03202 for item in loop.GetItems(): 03203 self.fd.write('%s<item>%d</item>\n' % 03204 (' ' * self.indent, item.GetId())) 03205 self.indent -= 4 03206 self.fd.write('%s</loop>\n' % (' ' * self.indent)) 03207 03208 def _condition(self, condition): 03209 """!Write conditions""" 03210 bbox = condition.GetBoundingBoxMin() 03211 self.fd.write('%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' % \ 03212 (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(), 03213 bbox[0], bbox[1])) 03214 text = condition.GetText() 03215 self.indent += 4 03216 if text: 03217 self.fd.write('%s<condition>%s</condition>\n' % 03218 (' ' * self.indent, self._filterValue(text))) 03219 items = condition.GetItems() 03220 for b in items.keys(): 03221 if len(items[b]) < 1: 03222 continue 03223 self.fd.write('%s<%s>\n' % (' ' * self.indent, b)) 03224 self.indent += 4 03225 for item in items[b]: 03226 self.fd.write('%s<item>%d</item>\n' % 03227 (' ' * self.indent, item.GetId())) 03228 self.indent -= 4 03229 self.fd.write('%s</%s>\n' % (' ' * self.indent, b)) 03230 03231 self.indent -= 4 03232 self.fd.write('%s</if-else>\n' % (' ' * self.indent)) 03233 03234 class PreferencesDialog(PreferencesBaseDialog): 03235 """!User preferences dialog""" 03236 def __init__(self, parent, settings = UserSettings, 03237 title = _("Modeler settings")): 03238 03239 PreferencesBaseDialog.__init__(self, parent = parent, title = title, 03240 settings = settings) 03241 03242 # create notebook pages 03243 self._createGeneralPage(self.notebook) 03244 self._createActionPage(self.notebook) 03245 self._createDataPage(self.notebook) 03246 self._createLoopPage(self.notebook) 03247 03248 self.SetMinSize(self.GetBestSize()) 03249 self.SetSize(self.size) 03250 03251 def _createGeneralPage(self, notebook): 03252 """!Create notebook page for action settings""" 03253 panel = wx.Panel(parent = notebook, id = wx.ID_ANY) 03254 notebook.AddPage(page = panel, text = _("General")) 03255 03256 # colors 03257 border = wx.BoxSizer(wx.VERTICAL) 03258 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03259 label = " %s " % _("Item properties")) 03260 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03261 03262 gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3) 03263 gridSizer.AddGrowableCol(0) 03264 03265 row = 0 03266 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03267 label = _("Disabled:")), 03268 flag = wx.ALIGN_LEFT | 03269 wx.ALIGN_CENTER_VERTICAL, 03270 pos = (row, 0)) 03271 rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03272 colour = self.settings.Get(group='modeler', key='disabled', subkey='color'), 03273 size = globalvar.DIALOG_COLOR_SIZE) 03274 rColor.SetName('GetColour') 03275 self.winId['modeler:disabled:color'] = rColor.GetId() 03276 03277 gridSizer.Add(item = rColor, 03278 flag = wx.ALIGN_RIGHT | 03279 wx.ALIGN_CENTER_VERTICAL, 03280 pos = (row, 1)) 03281 03282 sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5) 03283 border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 03284 03285 panel.SetSizer(border) 03286 03287 return panel 03288 03289 def _createActionPage(self, notebook): 03290 """!Create notebook page for action settings""" 03291 panel = wx.Panel(parent = notebook, id = wx.ID_ANY) 03292 notebook.AddPage(page = panel, text = _("Action")) 03293 03294 # colors 03295 border = wx.BoxSizer(wx.VERTICAL) 03296 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03297 label = " %s " % _("Color")) 03298 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03299 03300 gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3) 03301 gridSizer.AddGrowableCol(0) 03302 03303 row = 0 03304 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03305 label = _("Valid:")), 03306 flag = wx.ALIGN_LEFT | 03307 wx.ALIGN_CENTER_VERTICAL, 03308 pos = (row, 0)) 03309 vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03310 colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'valid')), 03311 size = globalvar.DIALOG_COLOR_SIZE) 03312 vColor.SetName('GetColour') 03313 self.winId['modeler:action:color:valid'] = vColor.GetId() 03314 03315 gridSizer.Add(item = vColor, 03316 flag = wx.ALIGN_RIGHT | 03317 wx.ALIGN_CENTER_VERTICAL, 03318 pos = (row, 1)) 03319 03320 row += 1 03321 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03322 label = _("Invalid:")), 03323 flag = wx.ALIGN_LEFT | 03324 wx.ALIGN_CENTER_VERTICAL, 03325 pos = (row, 0)) 03326 iColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03327 colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'invalid')), 03328 size = globalvar.DIALOG_COLOR_SIZE) 03329 iColor.SetName('GetColour') 03330 self.winId['modeler:action:color:invalid'] = iColor.GetId() 03331 03332 gridSizer.Add(item = iColor, 03333 flag = wx.ALIGN_RIGHT | 03334 wx.ALIGN_CENTER_VERTICAL, 03335 pos = (row, 1)) 03336 03337 row += 1 03338 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03339 label = _("Running:")), 03340 flag = wx.ALIGN_LEFT | 03341 wx.ALIGN_CENTER_VERTICAL, 03342 pos = (row, 0)) 03343 rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03344 colour = self.settings.Get(group='modeler', key='action', subkey=('color', 'running')), 03345 size = globalvar.DIALOG_COLOR_SIZE) 03346 rColor.SetName('GetColour') 03347 self.winId['modeler:action:color:running'] = rColor.GetId() 03348 03349 gridSizer.Add(item = rColor, 03350 flag = wx.ALIGN_RIGHT | 03351 wx.ALIGN_CENTER_VERTICAL, 03352 pos = (row, 1)) 03353 03354 sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5) 03355 border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 03356 03357 # size 03358 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03359 label = " %s " % _("Shape size")) 03360 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03361 03362 gridSizer = wx.GridBagSizer (hgap=3, vgap=3) 03363 gridSizer.AddGrowableCol(0) 03364 03365 row = 0 03366 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03367 label = _("Width:")), 03368 flag = wx.ALIGN_LEFT | 03369 wx.ALIGN_CENTER_VERTICAL, 03370 pos = (row, 0)) 03371 03372 width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, 03373 min = 0, max = 500, 03374 initial = self.settings.Get(group='modeler', key='action', subkey=('size', 'width'))) 03375 width.SetName('GetValue') 03376 self.winId['modeler:action:size:width'] = width.GetId() 03377 03378 gridSizer.Add(item = width, 03379 flag = wx.ALIGN_RIGHT | 03380 wx.ALIGN_CENTER_VERTICAL, 03381 pos = (row, 1)) 03382 03383 row += 1 03384 gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY, 03385 label=_("Height:")), 03386 flag = wx.ALIGN_LEFT | 03387 wx.ALIGN_CENTER_VERTICAL, 03388 pos=(row, 0)) 03389 03390 height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, 03391 min = 0, max = 500, 03392 initial = self.settings.Get(group='modeler', key='action', subkey=('size', 'height'))) 03393 height.SetName('GetValue') 03394 self.winId['modeler:action:size:height'] = height.GetId() 03395 03396 gridSizer.Add(item = height, 03397 flag = wx.ALIGN_RIGHT | 03398 wx.ALIGN_CENTER_VERTICAL, 03399 pos = (row, 1)) 03400 03401 sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5) 03402 border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3) 03403 03404 panel.SetSizer(border) 03405 03406 return panel 03407 03408 def _createDataPage(self, notebook): 03409 """!Create notebook page for data settings""" 03410 panel = wx.Panel(parent = notebook, id = wx.ID_ANY) 03411 notebook.AddPage(page = panel, text = _("Data")) 03412 03413 # colors 03414 border = wx.BoxSizer(wx.VERTICAL) 03415 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03416 label = " %s " % _("Type")) 03417 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03418 03419 gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3) 03420 gridSizer.AddGrowableCol(0) 03421 03422 row = 0 03423 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03424 label = _("Raster:")), 03425 flag = wx.ALIGN_LEFT | 03426 wx.ALIGN_CENTER_VERTICAL, 03427 pos = (row, 0)) 03428 rColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03429 colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'raster')), 03430 size = globalvar.DIALOG_COLOR_SIZE) 03431 rColor.SetName('GetColour') 03432 self.winId['modeler:data:color:raster'] = rColor.GetId() 03433 03434 gridSizer.Add(item = rColor, 03435 flag = wx.ALIGN_RIGHT | 03436 wx.ALIGN_CENTER_VERTICAL, 03437 pos = (row, 1)) 03438 03439 row += 1 03440 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03441 label = _("3D raster:")), 03442 flag = wx.ALIGN_LEFT | 03443 wx.ALIGN_CENTER_VERTICAL, 03444 pos = (row, 0)) 03445 r3Color = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03446 colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'raster3d')), 03447 size = globalvar.DIALOG_COLOR_SIZE) 03448 r3Color.SetName('GetColour') 03449 self.winId['modeler:data:color:raster3d'] = r3Color.GetId() 03450 03451 gridSizer.Add(item = r3Color, 03452 flag = wx.ALIGN_RIGHT | 03453 wx.ALIGN_CENTER_VERTICAL, 03454 pos = (row, 1)) 03455 03456 row += 1 03457 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03458 label = _("Vector:")), 03459 flag = wx.ALIGN_LEFT | 03460 wx.ALIGN_CENTER_VERTICAL, 03461 pos = (row, 0)) 03462 vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03463 colour = self.settings.Get(group='modeler', key='data', subkey=('color', 'vector')), 03464 size = globalvar.DIALOG_COLOR_SIZE) 03465 vColor.SetName('GetColour') 03466 self.winId['modeler:data:color:vector'] = vColor.GetId() 03467 03468 gridSizer.Add(item = vColor, 03469 flag = wx.ALIGN_RIGHT | 03470 wx.ALIGN_CENTER_VERTICAL, 03471 pos = (row, 1)) 03472 03473 sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5) 03474 border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 03475 03476 # size 03477 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03478 label = " %s " % _("Shape size")) 03479 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03480 03481 gridSizer = wx.GridBagSizer (hgap=3, vgap=3) 03482 gridSizer.AddGrowableCol(0) 03483 03484 row = 0 03485 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03486 label = _("Width:")), 03487 flag = wx.ALIGN_LEFT | 03488 wx.ALIGN_CENTER_VERTICAL, 03489 pos = (row, 0)) 03490 03491 width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, 03492 min = 0, max = 500, 03493 initial = self.settings.Get(group='modeler', key='data', subkey=('size', 'width'))) 03494 width.SetName('GetValue') 03495 self.winId['modeler:data:size:width'] = width.GetId() 03496 03497 gridSizer.Add(item = width, 03498 flag = wx.ALIGN_RIGHT | 03499 wx.ALIGN_CENTER_VERTICAL, 03500 pos = (row, 1)) 03501 03502 row += 1 03503 gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY, 03504 label=_("Height:")), 03505 flag = wx.ALIGN_LEFT | 03506 wx.ALIGN_CENTER_VERTICAL, 03507 pos=(row, 0)) 03508 03509 height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, 03510 min = 0, max = 500, 03511 initial = self.settings.Get(group='modeler', key='data', subkey=('size', 'height'))) 03512 height.SetName('GetValue') 03513 self.winId['modeler:data:size:height'] = height.GetId() 03514 03515 gridSizer.Add(item = height, 03516 flag = wx.ALIGN_RIGHT | 03517 wx.ALIGN_CENTER_VERTICAL, 03518 pos = (row, 1)) 03519 03520 sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5) 03521 border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3) 03522 03523 panel.SetSizer(border) 03524 03525 return panel 03526 03527 def _createLoopPage(self, notebook): 03528 """!Create notebook page for loop settings""" 03529 panel = wx.Panel(parent = notebook, id = wx.ID_ANY) 03530 notebook.AddPage(page = panel, text = _("Loop")) 03531 03532 # colors 03533 border = wx.BoxSizer(wx.VERTICAL) 03534 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03535 label = " %s " % _("Color")) 03536 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03537 03538 gridSizer = wx.GridBagSizer (hgap = 3, vgap = 3) 03539 gridSizer.AddGrowableCol(0) 03540 03541 row = 0 03542 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03543 label = _("Valid:")), 03544 flag = wx.ALIGN_LEFT | 03545 wx.ALIGN_CENTER_VERTICAL, 03546 pos = (row, 0)) 03547 vColor = csel.ColourSelect(parent = panel, id = wx.ID_ANY, 03548 colour = self.settings.Get(group='modeler', key='loop', subkey=('color', 'valid')), 03549 size = globalvar.DIALOG_COLOR_SIZE) 03550 vColor.SetName('GetColour') 03551 self.winId['modeler:loop:color:valid'] = vColor.GetId() 03552 03553 gridSizer.Add(item = vColor, 03554 flag = wx.ALIGN_RIGHT | 03555 wx.ALIGN_CENTER_VERTICAL, 03556 pos = (row, 1)) 03557 03558 sizer.Add(item = gridSizer, proportion = 1, flag = wx.ALL | wx.EXPAND, border = 5) 03559 border.Add(item = sizer, proportion = 0, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 3) 03560 03561 # size 03562 box = wx.StaticBox (parent = panel, id = wx.ID_ANY, 03563 label = " %s " % _("Shape size")) 03564 sizer = wx.StaticBoxSizer(box, wx.VERTICAL) 03565 03566 gridSizer = wx.GridBagSizer (hgap=3, vgap=3) 03567 gridSizer.AddGrowableCol(0) 03568 03569 row = 0 03570 gridSizer.Add(item = wx.StaticText(parent = panel, id = wx.ID_ANY, 03571 label = _("Width:")), 03572 flag = wx.ALIGN_LEFT | 03573 wx.ALIGN_CENTER_VERTICAL, 03574 pos = (row, 0)) 03575 03576 width = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, 03577 min = 0, max = 500, 03578 initial = self.settings.Get(group='modeler', key='loop', subkey=('size', 'width'))) 03579 width.SetName('GetValue') 03580 self.winId['modeler:loop:size:width'] = width.GetId() 03581 03582 gridSizer.Add(item = width, 03583 flag = wx.ALIGN_RIGHT | 03584 wx.ALIGN_CENTER_VERTICAL, 03585 pos = (row, 1)) 03586 03587 row += 1 03588 gridSizer.Add(item = wx.StaticText(parent=panel, id=wx.ID_ANY, 03589 label=_("Height:")), 03590 flag = wx.ALIGN_LEFT | 03591 wx.ALIGN_CENTER_VERTICAL, 03592 pos=(row, 0)) 03593 03594 height = wx.SpinCtrl(parent = panel, id = wx.ID_ANY, 03595 min = 0, max = 500, 03596 initial = self.settings.Get(group='modeler', key='loop', subkey=('size', 'height'))) 03597 height.SetName('GetValue') 03598 self.winId['modeler:loop:size:height'] = height.GetId() 03599 03600 gridSizer.Add(item = height, 03601 flag = wx.ALIGN_RIGHT | 03602 wx.ALIGN_CENTER_VERTICAL, 03603 pos = (row, 1)) 03604 03605 sizer.Add(item=gridSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=5) 03606 border.Add(item=sizer, proportion=0, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=3) 03607 03608 panel.SetSizer(border) 03609 03610 return panel 03611 03612 def OnApply(self, event): 03613 """!Button 'Apply' pressed""" 03614 PreferencesBaseDialog.OnApply(self, event) 03615 03616 self.parent.GetModel().Update() 03617 self.parent.GetCanvas().Refresh() 03618 03619 def OnSave(self, event): 03620 """!Button 'Save' pressed""" 03621 PreferencesBaseDialog.OnSave(self, event) 03622 03623 self.parent.GetModel().Update() 03624 self.parent.GetCanvas().Refresh() 03625 03626 class PropertiesDialog(wx.Dialog): 03627 """!Model properties dialog 03628 """ 03629 def __init__(self, parent, id = wx.ID_ANY, 03630 title = _('Model properties'), 03631 size = (350, 400), 03632 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER): 03633 wx.Dialog.__init__(self, parent, id, title, size = size, 03634 style = style) 03635 03636 self.metaBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 03637 label=" %s " % _("Metadata")) 03638 self.cmdBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 03639 label=" %s " % _("Commands")) 03640 03641 self.name = wx.TextCtrl(parent = self, id = wx.ID_ANY, 03642 size = (300, 25)) 03643 self.desc = wx.TextCtrl(parent = self, id = wx.ID_ANY, 03644 style = wx.TE_MULTILINE, 03645 size = (300, 50)) 03646 self.author = wx.TextCtrl(parent = self, id = wx.ID_ANY, 03647 size = (300, 25)) 03648 03649 # commands 03650 self.overwrite = wx.CheckBox(parent = self, id=wx.ID_ANY, 03651 label=_("Allow output files to overwrite existing files")) 03652 self.overwrite.SetValue(UserSettings.Get(group='cmd', key='overwrite', subkey='enabled')) 03653 03654 # buttons 03655 self.btnOk = wx.Button(self, wx.ID_OK) 03656 self.btnCancel = wx.Button(self, wx.ID_CANCEL) 03657 self.btnOk.SetDefault() 03658 03659 self.btnOk.SetToolTipString(_("Apply properties")) 03660 self.btnOk.SetDefault() 03661 self.btnCancel.SetToolTipString(_("Close dialog and ignore changes")) 03662 03663 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) 03664 03665 self._layout() 03666 03667 def _layout(self): 03668 metaSizer = wx.StaticBoxSizer(self.metaBox, wx.VERTICAL) 03669 gridSizer = wx.GridBagSizer(hgap = 3, vgap = 3) 03670 gridSizer.AddGrowableCol(0) 03671 gridSizer.AddGrowableRow(1) 03672 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03673 label = _("Name:")), 03674 flag = wx.ALIGN_LEFT | 03675 wx.ALIGN_CENTER_VERTICAL, 03676 pos = (0, 0)) 03677 gridSizer.Add(item = self.name, 03678 flag = wx.ALIGN_LEFT | 03679 wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 03680 pos = (0, 1)) 03681 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03682 label = _("Description:")), 03683 flag = wx.ALIGN_LEFT | 03684 wx.ALIGN_CENTER_VERTICAL, 03685 pos = (1, 0)) 03686 gridSizer.Add(item = self.desc, 03687 flag = wx.ALIGN_LEFT | 03688 wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 03689 pos = (1, 1)) 03690 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03691 label = _("Author(s):")), 03692 flag = wx.ALIGN_LEFT | 03693 wx.ALIGN_CENTER_VERTICAL, 03694 pos = (2, 0)) 03695 gridSizer.Add(item = self.author, 03696 flag = wx.ALIGN_LEFT | 03697 wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 03698 pos = (2, 1)) 03699 metaSizer.Add(item = gridSizer) 03700 03701 cmdSizer = wx.StaticBoxSizer(self.cmdBox, wx.VERTICAL) 03702 cmdSizer.Add(item = self.overwrite, 03703 flag = wx.EXPAND | wx.ALL, border = 3) 03704 03705 btnStdSizer = wx.StdDialogButtonSizer() 03706 btnStdSizer.AddButton(self.btnCancel) 03707 btnStdSizer.AddButton(self.btnOk) 03708 btnStdSizer.Realize() 03709 03710 mainSizer = wx.BoxSizer(wx.VERTICAL) 03711 mainSizer.Add(item=metaSizer, proportion=1, 03712 flag=wx.EXPAND | wx.ALL, border=5) 03713 mainSizer.Add(item=cmdSizer, proportion=0, 03714 flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5) 03715 mainSizer.Add(item=btnStdSizer, proportion=0, 03716 flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5) 03717 03718 self.SetSizer(mainSizer) 03719 mainSizer.Fit(self) 03720 03721 def OnCloseWindow(self, event): 03722 self.Hide() 03723 03724 def GetValues(self): 03725 """!Get values""" 03726 return { 'name' : self.name.GetValue(), 03727 'description' : self.desc.GetValue(), 03728 'author' : self.author.GetValue(), 03729 'overwrite' : self.overwrite.IsChecked() } 03730 03731 def Init(self, prop): 03732 """!Initialize dialog""" 03733 self.name.SetValue(prop['name']) 03734 self.desc.SetValue(prop['description']) 03735 self.author.SetValue(prop['author']) 03736 if 'overwrite' in prop: 03737 self.overwrite.SetValue(prop['overwrite']) 03738 03739 class ModelParamDialog(wx.Dialog): 03740 def __init__(self, parent, params, id = wx.ID_ANY, title = _("Model parameters"), 03741 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 03742 """!Model parameters dialog 03743 """ 03744 self.parent = parent 03745 self.params = params 03746 self.tasks = list() # list of tasks/pages 03747 03748 wx.Dialog.__init__(self, parent = parent, id = id, title = title, style = style, **kwargs) 03749 03750 if globalvar.hasAgw: 03751 self.notebook = FN.FlatNotebook(self, id = wx.ID_ANY, 03752 agwStyle = FN.FNB_FANCY_TABS | 03753 FN.FNB_BOTTOM | 03754 FN.FNB_NO_NAV_BUTTONS | 03755 FN.FNB_NO_X_BUTTON) 03756 else: 03757 self.notebook = FN.FlatNotebook(self, id = wx.ID_ANY, 03758 style = FN.FNB_FANCY_TABS | 03759 FN.FNB_BOTTOM | 03760 FN.FNB_NO_NAV_BUTTONS | 03761 FN.FNB_NO_X_BUTTON) 03762 03763 panel = self._createPages() 03764 wx.CallAfter(self.notebook.SetSelection, 0) 03765 03766 self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL) 03767 self.btnRun = wx.Button(parent = self, id = wx.ID_OK, 03768 label = _("&Run")) 03769 self.btnRun.SetDefault() 03770 03771 self._layout() 03772 03773 size = self.GetBestSize() 03774 self.SetMinSize(size) 03775 self.SetSize((size.width, size.height + 03776 panel.constrained_size[1] - 03777 panel.panelMinHeight)) 03778 03779 def _layout(self): 03780 btnSizer = wx.StdDialogButtonSizer() 03781 btnSizer.AddButton(self.btnCancel) 03782 btnSizer.AddButton(self.btnRun) 03783 btnSizer.Realize() 03784 03785 mainSizer = wx.BoxSizer(wx.VERTICAL) 03786 mainSizer.Add(item = self.notebook, proportion = 1, 03787 flag = wx.EXPAND) 03788 mainSizer.Add(item=btnSizer, proportion=0, 03789 flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5) 03790 03791 self.SetSizer(mainSizer) 03792 mainSizer.Fit(self) 03793 03794 def _createPages(self): 03795 """!Create for each parameterized module its own page""" 03796 nameOrdered = [''] * len(self.params.keys()) 03797 for name, params in self.params.iteritems(): 03798 nameOrdered[params['idx']] = name 03799 for name in nameOrdered: 03800 params = self.params[name] 03801 panel = self._createPage(name, params) 03802 self.notebook.AddPage(panel, text = name) 03803 03804 return panel 03805 03806 def _createPage(self, name, params): 03807 """!Define notebook page""" 03808 if name in globalvar.grassCmd['all']: 03809 task = gtask.grassTask(name) 03810 else: 03811 task = gtask.grassTask() 03812 task.flags = params['flags'] 03813 task.params = params['params'] 03814 03815 panel = menuform.cmdPanel(parent = self, id = wx.ID_ANY, task = task) 03816 self.tasks.append(task) 03817 03818 return panel 03819 03820 def GetErrors(self): 03821 """!Check for errors, get list of messages""" 03822 errList = list() 03823 for task in self.tasks: 03824 errList += task.getCmdError() 03825 03826 return errList 03827 03828 class ModelListCtrl(wx.ListCtrl, 03829 listmix.ListCtrlAutoWidthMixin, 03830 listmix.TextEditMixin, 03831 listmix.ColumnSorterMixin): 03832 def __init__(self, parent, columns, id = wx.ID_ANY, 03833 style = wx.LC_REPORT | wx.BORDER_NONE | 03834 wx.LC_SORT_ASCENDING |wx.LC_HRULES | 03835 wx.LC_VRULES, **kwargs): 03836 """!List of model variables""" 03837 self.parent = parent 03838 self.columns = columns 03839 self.shape = None 03840 try: 03841 self.frame = parent.parent 03842 except AttributeError: 03843 self.frame = None 03844 03845 wx.ListCtrl.__init__(self, parent, id = id, style = style, **kwargs) 03846 listmix.ListCtrlAutoWidthMixin.__init__(self) 03847 listmix.TextEditMixin.__init__(self) 03848 listmix.ColumnSorterMixin.__init__(self, 4) 03849 03850 i = 0 03851 for col in columns: 03852 self.InsertColumn(i, col) 03853 self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) 03854 i += 1 03855 03856 self.itemDataMap = {} # requested by sorter 03857 self.itemCount = 0 03858 03859 self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginEdit) 03860 self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndEdit) 03861 self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick) 03862 self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightUp) #wxMSW 03863 self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) #wxGTK 03864 03865 def OnBeginEdit(self, event): 03866 """!Editing of item started""" 03867 event.Allow() 03868 03869 def OnEndEdit(self, event): 03870 """!Finish editing of item""" 03871 pass 03872 03873 def OnColClick(self, event): 03874 """!Click on column header (order by)""" 03875 event.Skip() 03876 03877 class VariablePanel(wx.Panel): 03878 def __init__(self, parent, id = wx.ID_ANY, 03879 **kwargs): 03880 """!Manage model variables panel 03881 """ 03882 self.parent = parent 03883 03884 wx.Panel.__init__(self, parent = parent, id = id, **kwargs) 03885 03886 self.listBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 03887 label=" %s " % _("List of variables - right-click to delete")) 03888 03889 self.list = VariableListCtrl(parent = self, 03890 columns = [_("Name"), _("Data type"), 03891 _("Default value"), _("Description")]) 03892 03893 # add new category 03894 self.addBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 03895 label = " %s " % _("Add new variable")) 03896 self.name = wx.TextCtrl(parent = self, id = wx.ID_ANY) 03897 wx.CallAfter(self.name.SetFocus) 03898 self.type = wx.Choice(parent = self, id = wx.ID_ANY, 03899 choices = [_("integer"), 03900 _("float"), 03901 _("string"), 03902 _("raster"), 03903 _("vector")]) 03904 self.value = wx.TextCtrl(parent = self, id = wx.ID_ANY) 03905 self.desc = wx.TextCtrl(parent = self, id = wx.ID_ANY) 03906 03907 # buttons 03908 self.btnAdd = wx.Button(parent = self, id = wx.ID_ADD) 03909 self.btnAdd.SetToolTipString(_("Add new variable to the model")) 03910 self.btnAdd.Enable(False) 03911 03912 # bindings 03913 self.name.Bind(wx.EVT_TEXT, self.OnText) 03914 self.value.Bind(wx.EVT_TEXT, self.OnText) 03915 self.desc.Bind(wx.EVT_TEXT, self.OnText) 03916 self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAdd) 03917 03918 self._layout() 03919 03920 def _layout(self): 03921 """!Layout dialog""" 03922 listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL) 03923 listSizer.Add(item = self.list, proportion = 1, 03924 flag = wx.EXPAND) 03925 03926 addSizer = wx.StaticBoxSizer(self.addBox, wx.VERTICAL) 03927 gridSizer = wx.GridBagSizer(hgap = 5, vgap = 5) 03928 gridSizer.AddGrowableCol(1) 03929 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03930 label = "%s:" % _("Name")), 03931 flag = wx.ALIGN_CENTER_VERTICAL, 03932 pos = (0, 0)) 03933 gridSizer.Add(item = self.name, 03934 pos = (0, 1), 03935 flag = wx.EXPAND) 03936 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03937 label = "%s:" % _("Data type")), 03938 flag = wx.ALIGN_CENTER_VERTICAL, 03939 pos = (0, 2)) 03940 gridSizer.Add(item = self.type, 03941 pos = (0, 3)) 03942 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03943 label = "%s:" % _("Default value")), 03944 flag = wx.ALIGN_CENTER_VERTICAL, 03945 pos = (1, 0)) 03946 gridSizer.Add(item = self.value, 03947 pos = (1, 1), span = (1, 3), 03948 flag = wx.EXPAND) 03949 gridSizer.Add(item = wx.StaticText(parent = self, id = wx.ID_ANY, 03950 label = "%s:" % _("Description")), 03951 flag = wx.ALIGN_CENTER_VERTICAL, 03952 pos = (2, 0)) 03953 gridSizer.Add(item = self.desc, 03954 pos = (2, 1), span = (1, 3), 03955 flag = wx.EXPAND) 03956 addSizer.Add(item = gridSizer, 03957 flag = wx.EXPAND) 03958 addSizer.Add(item = self.btnAdd, proportion = 0, 03959 flag = wx.TOP | wx.ALIGN_RIGHT, border = 5) 03960 03961 mainSizer = wx.BoxSizer(wx.VERTICAL) 03962 mainSizer.Add(item = listSizer, proportion = 1, 03963 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) 03964 mainSizer.Add(item = addSizer, proportion = 0, 03965 flag = wx.EXPAND | wx.ALIGN_CENTER | 03966 wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 5) 03967 03968 self.SetSizer(mainSizer) 03969 mainSizer.Fit(self) 03970 03971 def OnText(self, event): 03972 """!Text entered""" 03973 if self.name.GetValue(): 03974 self.btnAdd.Enable() 03975 else: 03976 self.btnAdd.Enable(False) 03977 03978 def OnAdd(self, event): 03979 """!Add new variable to the list""" 03980 msg = self.list.Append(self.name.GetValue(), 03981 self.type.GetStringSelection(), 03982 self.value.GetValue(), 03983 self.desc.GetValue()) 03984 self.name.SetValue('') 03985 self.name.SetFocus() 03986 03987 if msg: 03988 GError(parent = self, 03989 message = msg) 03990 else: 03991 self.type.SetSelection(0) 03992 self.value.SetValue('') 03993 self.desc.SetValue('') 03994 self.UpdateModelVariables() 03995 03996 def UpdateModelVariables(self): 03997 """!Update model variables""" 03998 variables = dict() 03999 for values in self.list.GetData().itervalues(): 04000 name = values[0] 04001 variables[name] = { 'type' : str(values[1]) } 04002 if values[2]: 04003 variables[name]['value'] = values[2] 04004 if values[3]: 04005 variables[name]['description'] = values[3] 04006 04007 self.parent.GetModel().SetVariables(variables) 04008 self.parent.ModelChanged() 04009 04010 def Update(self): 04011 """!Reload list of variables""" 04012 self.list.OnReload(None) 04013 04014 def Reset(self): 04015 """!Remove all variables""" 04016 self.list.DeleteAllItems() 04017 self.parent.GetModel().SetVariables([]) 04018 04019 class VariableListCtrl(ModelListCtrl): 04020 def __init__(self, parent, columns, **kwargs): 04021 """!List of model variables""" 04022 ModelListCtrl.__init__(self, parent, columns, **kwargs) 04023 04024 def GetListCtrl(self): 04025 """!Used by ColumnSorterMixin""" 04026 return self 04027 04028 def GetData(self): 04029 """!Get list data""" 04030 return self.itemDataMap 04031 04032 def Populate(self, data): 04033 """!Populate the list""" 04034 self.itemDataMap = dict() 04035 i = 0 04036 for name, values in data.iteritems(): 04037 self.itemDataMap[i] = [name, values['type'], 04038 values.get('value', ''), 04039 values.get('description', '')] 04040 i += 1 04041 04042 self.itemCount = len(self.itemDataMap.keys()) 04043 self.DeleteAllItems() 04044 i = 0 04045 for name, vtype, value, desc in self.itemDataMap.itervalues(): 04046 index = self.InsertStringItem(sys.maxint, name) 04047 self.SetStringItem(index, 0, name) 04048 self.SetStringItem(index, 1, vtype) 04049 self.SetStringItem(index, 2, value) 04050 self.SetStringItem(index, 3, desc) 04051 self.SetItemData(index, i) 04052 i += 1 04053 04054 def Append(self, name, vtype, value, desc): 04055 """!Append new item to the list 04056 04057 @return None on success 04058 @return error string 04059 """ 04060 for iname, ivtype, ivalue, idesc in self.itemDataMap.itervalues(): 04061 if iname == name: 04062 return _("Variable <%s> already exists in the model. " 04063 "Adding variable failed.") % name 04064 04065 index = self.InsertStringItem(sys.maxint, name) 04066 self.SetStringItem(index, 0, name) 04067 self.SetStringItem(index, 1, vtype) 04068 self.SetStringItem(index, 2, value) 04069 self.SetStringItem(index, 3, desc) 04070 self.SetItemData(index, self.itemCount) 04071 04072 self.itemDataMap[self.itemCount] = [name, vtype, value, desc] 04073 self.itemCount += 1 04074 04075 return None 04076 04077 def OnRemove(self, event): 04078 """!Remove selected variable(s) from the model""" 04079 item = self.GetFirstSelected() 04080 while item != -1: 04081 self.DeleteItem(item) 04082 del self.itemDataMap[item] 04083 item = self.GetFirstSelected() 04084 self.parent.UpdateModelVariables() 04085 04086 event.Skip() 04087 04088 def OnRemoveAll(self, event): 04089 """!Remove all variable(s) from the model""" 04090 dlg = wx.MessageBox(parent=self, 04091 message=_("Do you want to delete all variables from " 04092 "the model?"), 04093 caption=_("Delete variables"), 04094 style=wx.YES_NO | wx.CENTRE) 04095 if dlg != wx.YES: 04096 return 04097 04098 self.DeleteAllItems() 04099 self.itemDataMap = dict() 04100 04101 self.parent.UpdateModelVariables() 04102 04103 def OnEndEdit(self, event): 04104 """!Finish editing of item""" 04105 itemIndex = event.GetIndex() 04106 columnIndex = event.GetColumn() 04107 nameOld = self.GetItem(itemIndex, 0).GetText() 04108 04109 if columnIndex == 0: # TODO 04110 event.Veto() 04111 04112 self.itemDataMap[itemIndex][columnIndex] = event.GetText() 04113 04114 self.parent.UpdateModelVariables() 04115 04116 def OnReload(self, event): 04117 """!Reload list of variables""" 04118 self.Populate(self.parent.parent.GetModel().GetVariables()) 04119 04120 def OnRightUp(self, event): 04121 """!Mouse right button up""" 04122 if not hasattr(self, "popupID1"): 04123 self.popupID1 = wx.NewId() 04124 self.popupID2 = wx.NewId() 04125 self.popupID3 = wx.NewId() 04126 self.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID1) 04127 self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2) 04128 self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3) 04129 04130 # generate popup-menu 04131 menu = wx.Menu() 04132 menu.Append(self.popupID1, _("Delete selected")) 04133 menu.Append(self.popupID2, _("Delete all")) 04134 if self.GetFirstSelected() == -1: 04135 menu.Enable(self.popupID1, False) 04136 menu.Enable(self.popupID2, False) 04137 04138 menu.AppendSeparator() 04139 menu.Append(self.popupID3, _("Reload")) 04140 04141 self.PopupMenu(menu) 04142 menu.Destroy() 04143 04144 class ModelItem(ModelObject): 04145 def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []): 04146 """!Abstract class for loops and conditions""" 04147 ModelObject.__init__(self, id) 04148 self.parent = parent 04149 self.text = text 04150 self.items = items # list of items in the loop 04151 04152 def GetText(self): 04153 """!Get loop text""" 04154 return self.text 04155 04156 def GetItems(self): 04157 """!Get items (id)""" 04158 return self.items 04159 04160 def SetId(self, id): 04161 """!Set loop id""" 04162 self.id = id 04163 04164 def SetText(self, cond): 04165 """!Set loop text (condition)""" 04166 self.text = cond 04167 self.ClearText() 04168 self.AddText('(' + str(self.id) + ') ' + self.text) 04169 04170 def GetLog(self): 04171 """!Get log info""" 04172 if self.text: 04173 return _("Condition: ") + self.text 04174 else: 04175 return _("Condition: not defined") 04176 04177 def AddRelation(self, rel): 04178 """!Record relation""" 04179 self.rels.append(rel) 04180 04181 def Clear(self): 04182 """!Clear object, remove rels""" 04183 self.rels = list() 04184 04185 class ModelItemDialog(wx.Dialog): 04186 """!Abstract item properties dialog""" 04187 def __init__(self, parent, shape, title, id = wx.ID_ANY, 04188 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 04189 self.parent = parent 04190 self.shape = shape 04191 04192 wx.Dialog.__init__(self, parent, id, title = title, style = style, **kwargs) 04193 04194 self.panel = wx.Panel(parent = self, id = wx.ID_ANY) 04195 04196 self.condBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 04197 label=" %s " % _("Condition")) 04198 self.condText = wx.TextCtrl(parent = self.panel, id = wx.ID_ANY, 04199 value = shape.GetText()) 04200 04201 self.itemList = ItemCheckListCtrl(parent = self.panel, 04202 window = self, 04203 columns = [_("ID"), _("Name"), 04204 _("Command")], 04205 shape = shape) 04206 self.itemList.Populate(self.parent.GetModel().GetItems()) 04207 04208 self.btnCancel = wx.Button(parent = self.panel, id = wx.ID_CANCEL) 04209 self.btnOk = wx.Button(parent = self.panel, id = wx.ID_OK) 04210 self.btnOk.SetDefault() 04211 04212 def _layout(self): 04213 """!Do layout (virtual method)""" 04214 pass 04215 04216 def GetCondition(self): 04217 """!Get loop condition""" 04218 return self.condText.GetValue() 04219 04220 class ModelLoop(ModelItem, ogl.RectangleShape): 04221 def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []): 04222 """!Defines a loop""" 04223 ModelItem.__init__(self, parent, x, y, id, width, height, text, items) 04224 04225 if not width: 04226 width = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'width')) 04227 if not height: 04228 height = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'height')) 04229 04230 if self.parent.GetCanvas(): 04231 ogl.RectangleShape.__init__(self, width, height) 04232 04233 self.SetCanvas(self.parent) 04234 self.SetX(x) 04235 self.SetY(y) 04236 self.SetPen(wx.BLACK_PEN) 04237 self.SetCornerRadius(100) 04238 if text: 04239 self.AddText('(' + str(self.id) + ') ' + text) 04240 else: 04241 self.AddText('(' + str(self.id) + ')') 04242 04243 self._setBrush() 04244 04245 def _setBrush(self): 04246 """!Set brush""" 04247 if not self.isEnabled: 04248 color = UserSettings.Get(group='modeler', key='disabled', 04249 subkey='color') 04250 else: 04251 color = UserSettings.Get(group='modeler', key='loop', 04252 subkey=('color', 'valid')) 04253 04254 wxColor = wx.Color(color[0], color[1], color[2]) 04255 self.SetBrush(wx.Brush(wxColor)) 04256 04257 def Enable(self, enabled = True): 04258 """!Enable/disable action""" 04259 for item in self.items: 04260 if not isinstance(item, ModelAction): 04261 continue 04262 item.Enable(enabled) 04263 04264 ModelObject.Enable(self, enabled) 04265 04266 def Update(self): 04267 self._setBrush() 04268 04269 def GetName(self): 04270 """!Get name""" 04271 return _("loop") 04272 04273 def SetItems(self, items): 04274 """!Set items (id)""" 04275 self.items = items 04276 04277 class ModelLoopDialog(ModelItemDialog): 04278 """!Loop properties dialog""" 04279 def __init__(self, parent, shape, id = wx.ID_ANY, title = _("Loop properties"), 04280 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 04281 ModelItemDialog.__init__(self, parent, shape, title, 04282 style = style, **kwargs) 04283 04284 self.listBox = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 04285 label=" %s " % _("List of items in loop")) 04286 04287 self._layout() 04288 self.SetMinSize(self.GetSize()) 04289 self.SetSize((500, 400)) 04290 04291 def _layout(self): 04292 """!Do layout""" 04293 sizer = wx.BoxSizer(wx.VERTICAL) 04294 04295 condSizer = wx.StaticBoxSizer(self.condBox, wx.VERTICAL) 04296 condSizer.Add(item = self.condText, proportion = 1, 04297 flag = wx.EXPAND) 04298 04299 listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL) 04300 listSizer.Add(item = self.itemList, proportion = 1, 04301 flag = wx.EXPAND) 04302 04303 btnSizer = wx.StdDialogButtonSizer() 04304 btnSizer.AddButton(self.btnCancel) 04305 btnSizer.AddButton(self.btnOk) 04306 btnSizer.Realize() 04307 04308 sizer.Add(item = condSizer, proportion = 0, 04309 flag = wx.EXPAND | wx.ALL, border = 3) 04310 sizer.Add(item = listSizer, proportion = 1, 04311 flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3) 04312 sizer.Add(item = btnSizer, proportion=0, 04313 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5) 04314 04315 self.panel.SetSizer(sizer) 04316 sizer.Fit(self.panel) 04317 04318 self.Layout() 04319 04320 def GetItems(self): 04321 """!Get list of selected actions""" 04322 return self.itemList.GetItems() 04323 04324 class ItemPanel(wx.Panel): 04325 def __init__(self, parent, id = wx.ID_ANY, 04326 **kwargs): 04327 """!Manage model items 04328 """ 04329 self.parent = parent 04330 04331 wx.Panel.__init__(self, parent = parent, id = id, **kwargs) 04332 04333 self.listBox = wx.StaticBox(parent = self, id = wx.ID_ANY, 04334 label=" %s " % _("List of items - right-click to delete")) 04335 04336 self.list = ItemListCtrl(parent = self, 04337 columns = [_("ID"), _("Name"), _("In block"), 04338 _("Command / Condition")]) 04339 04340 self._layout() 04341 04342 def _layout(self): 04343 """!Layout dialog""" 04344 listSizer = wx.StaticBoxSizer(self.listBox, wx.VERTICAL) 04345 listSizer.Add(item = self.list, proportion = 1, 04346 flag = wx.EXPAND) 04347 04348 mainSizer = wx.BoxSizer(wx.VERTICAL) 04349 mainSizer.Add(item = listSizer, proportion = 1, 04350 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) 04351 04352 self.SetSizer(mainSizer) 04353 mainSizer.Fit(self) 04354 04355 def Update(self): 04356 """!Reload list of variables""" 04357 self.list.OnReload(None) 04358 04359 class ItemListCtrl(ModelListCtrl): 04360 def __init__(self, parent, columns, disablePopup = False, **kwargs): 04361 """!List of model actions""" 04362 self.disablePopup = disablePopup 04363 04364 ModelListCtrl.__init__(self, parent, columns, **kwargs) 04365 self.SetColumnWidth(1, 100) 04366 self.SetColumnWidth(2, 65) 04367 04368 def GetListCtrl(self): 04369 """!Used by ColumnSorterMixin""" 04370 return self 04371 04372 def GetData(self): 04373 """!Get list data""" 04374 return self.itemDataMap 04375 04376 def Populate(self, data): 04377 """!Populate the list""" 04378 self.itemDataMap = dict() 04379 04380 if self.shape: 04381 if isinstance(self.shape, ModelCondition): 04382 if self.GetName() == 'ElseBlockList': 04383 shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['else']) 04384 else: 04385 shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()['if']) 04386 else: 04387 shapeItems = map(lambda x: x.GetId(), self.shape.GetItems()) 04388 else: 04389 shapeItems = list() 04390 i = 0 04391 if len(self.columns) == 3: # ItemCheckList 04392 checked = list() 04393 for action in data: 04394 if isinstance(action, ModelData): 04395 continue 04396 04397 if len(self.columns) == 3: 04398 self.itemDataMap[i] = [str(action.GetId()), 04399 action.GetName(), 04400 action.GetLog()] 04401 aId = action.GetBlockId() 04402 if action.GetId() in shapeItems: 04403 checked.append(aId) 04404 else: 04405 checked.append(None) 04406 else: 04407 bId = action.GetBlockId() 04408 if not bId: 04409 bId = '' 04410 self.itemDataMap[i] = [str(action.GetId()), 04411 action.GetName(), 04412 ','.join(map(str, bId)), 04413 action.GetLog()] 04414 04415 i += 1 04416 04417 self.itemCount = len(self.itemDataMap.keys()) 04418 self.DeleteAllItems() 04419 i = 0 04420 if len(self.columns) == 3: 04421 for aid, name, desc in self.itemDataMap.itervalues(): 04422 index = self.InsertStringItem(sys.maxint, aid) 04423 self.SetStringItem(index, 0, aid) 04424 self.SetStringItem(index, 1, name) 04425 self.SetStringItem(index, 2, desc) 04426 self.SetItemData(index, i) 04427 if checked[i]: 04428 self.CheckItem(index, True) 04429 i += 1 04430 else: 04431 for aid, name, inloop, desc in self.itemDataMap.itervalues(): 04432 index = self.InsertStringItem(sys.maxint, aid) 04433 self.SetStringItem(index, 0, aid) 04434 self.SetStringItem(index, 1, name) 04435 self.SetStringItem(index, 2, inloop) 04436 self.SetStringItem(index, 3, desc) 04437 self.SetItemData(index, i) 04438 i += 1 04439 04440 def OnRemove(self, event): 04441 """!Remove selected action(s) from the model""" 04442 model = self.frame.GetModel() 04443 canvas = self.frame.GetCanvas() 04444 04445 item = self.GetFirstSelected() 04446 while item != -1: 04447 self.DeleteItem(item) 04448 del self.itemDataMap[item] 04449 04450 aId = self.GetItem(item, 0).GetText() 04451 action = model.GetItem(int(aId)) 04452 if not action: 04453 item = self.GetFirstSelected() 04454 continue 04455 04456 model.RemoveItem(action) 04457 canvas.GetDiagram().RemoveShape(action) 04458 self.frame.ModelChanged() 04459 04460 item = self.GetFirstSelected() 04461 04462 canvas.Refresh() 04463 04464 event.Skip() 04465 04466 def OnRemoveAll(self, event): 04467 """!Remove all variable(s) from the model""" 04468 deleteDialog = wx.MessageBox(parent=self, 04469 message=_("Selected data records (%d) will permanently deleted " 04470 "from table. Do you want to delete them?") % \ 04471 (len(self.listOfSQLStatements)), 04472 caption=_("Delete records"), 04473 style=wx.YES_NO | wx.CENTRE) 04474 if deleteDialog != wx.YES: 04475 return False 04476 04477 self.DeleteAllItems() 04478 self.itemDataMap = dict() 04479 04480 self.parent.UpdateModelVariables() 04481 04482 def OnEndEdit(self, event): 04483 """!Finish editing of item""" 04484 itemIndex = event.GetIndex() 04485 columnIndex = event.GetColumn() 04486 04487 self.itemDataMap[itemIndex][columnIndex] = event.GetText() 04488 04489 aId = int(self.GetItem(itemIndex, 0).GetText()) 04490 action = self.frame.GetModel().GetItem(aId) 04491 if not action: 04492 event.Veto() 04493 if columnIndex == 0: 04494 action.SetId(int(event.GetText())) 04495 04496 self.frame.ModelChanged() 04497 04498 def OnReload(self, event = None): 04499 """!Reload list of actions""" 04500 self.Populate(self.frame.GetModel().GetItems()) 04501 04502 def OnRightUp(self, event): 04503 """!Mouse right button up""" 04504 if self.disablePopup: 04505 return 04506 04507 if not hasattr(self, "popupID1"): 04508 self.popupID1 = wx.NewId() 04509 self.popupID2 = wx.NewId() 04510 self.popupID3 = wx.NewId() 04511 self.popupID4 = wx.NewId() 04512 self.Bind(wx.EVT_MENU, self.OnRemove, id = self.popupID1) 04513 self.Bind(wx.EVT_MENU, self.OnRemoveAll, id = self.popupID2) 04514 self.Bind(wx.EVT_MENU, self.OnReload, id = self.popupID3) 04515 self.Bind(wx.EVT_MENU, self.OnNormalize, id = self.popupID4) 04516 04517 # generate popup-menu 04518 menu = wx.Menu() 04519 menu.Append(self.popupID1, _("Delete selected")) 04520 menu.Append(self.popupID2, _("Delete all")) 04521 if self.GetFirstSelected() == -1: 04522 menu.Enable(self.popupID1, False) 04523 menu.Enable(self.popupID2, False) 04524 04525 menu.AppendSeparator() 04526 menu.Append(self.popupID4, _("Normalize")) 04527 menu.Append(self.popupID3, _("Reload")) 04528 04529 self.PopupMenu(menu) 04530 menu.Destroy() 04531 04532 def OnNormalize(self, event): 04533 """!Update id of actions""" 04534 model = self.frame.GetModel() 04535 04536 aId = 1 04537 for item in model.GetItems(): 04538 item.SetId(aId) 04539 aId += 1 04540 04541 self.OnReload(None) 04542 self.frame.GetCanvas().Refresh() 04543 self.frame.ModelChanged() 04544 04545 class ItemCheckListCtrl(ItemListCtrl, listmix.CheckListCtrlMixin): 04546 def __init__(self, parent, shape, columns, window = None, **kwargs): 04547 self.parent = parent 04548 self.window = window 04549 04550 ItemListCtrl.__init__(self, parent, columns, disablePopup = True, **kwargs) 04551 listmix.CheckListCtrlMixin.__init__(self) 04552 self.SetColumnWidth(0, 50) 04553 04554 self.shape = shape 04555 04556 def OnBeginEdit(self, event): 04557 """!Disable editing""" 04558 event.Veto() 04559 04560 def OnCheckItem(self, index, flag): 04561 """!Item checked/unchecked""" 04562 name = self.GetName() 04563 if name == 'IfBlockList' and self.window: 04564 self.window.OnCheckItemIf(index, flag) 04565 elif name == 'ElseBlockList' and self.window: 04566 self.window.OnCheckItemElse(index, flag) 04567 04568 def GetItems(self): 04569 """!Get list of selected actions""" 04570 ids = { 'checked' : list(), 04571 'unchecked' : list() } 04572 for i in range(self.GetItemCount()): 04573 iId = int(self.GetItem(i, 0).GetText()) 04574 if self.IsChecked(i): 04575 ids['checked'].append(iId) 04576 else: 04577 ids['unchecked'].append(iId) 04578 04579 return ids 04580 04581 def CheckItemById(self, aId, flag): 04582 """!Check/uncheck given item by id""" 04583 for i in range(self.GetItemCount()): 04584 iId = int(self.GetItem(i, 0).GetText()) 04585 if iId == aId: 04586 self.CheckItem(i, flag) 04587 break 04588 04589 class ModelCondition(ModelItem, ogl.PolygonShape): 04590 def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', 04591 items = { 'if' : [], 'else' : [] }): 04592 """!Defines a if-else condition""" 04593 ModelItem.__init__(self, parent, x, y, id, width, height, text, items) 04594 04595 if not width: 04596 self.width = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'width')) 04597 else: 04598 self.width = width 04599 if not height: 04600 self.height = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'height')) 04601 else: 04602 self.height = height 04603 04604 if self.parent.GetCanvas(): 04605 ogl.PolygonShape.__init__(self) 04606 04607 points = [(0, - self.height / 2), 04608 (self.width / 2, 0), 04609 (0, self.height / 2), 04610 (- self.width / 2, 0)] 04611 self.Create(points) 04612 04613 self.SetCanvas(self.parent) 04614 self.SetX(x) 04615 self.SetY(y) 04616 self.SetPen(wx.BLACK_PEN) 04617 if text: 04618 self.AddText('(' + str(self.id) + ') ' + text) 04619 else: 04620 self.AddText('(' + str(self.id) + ')') 04621 04622 def GetName(self): 04623 """!Get name""" 04624 return _("if-else") 04625 04626 def GetWidth(self): 04627 """!Get object width""" 04628 return self.width 04629 04630 def GetHeight(self): 04631 """!Get object height""" 04632 return self.height 04633 04634 def SetItems(self, items, branch = 'if'): 04635 """!Set items (id) 04636 04637 @param items list of items 04638 @param branch 'if' / 'else' 04639 """ 04640 if branch in ['if', 'else']: 04641 self.items[branch] = items 04642 04643 class ModelConditionDialog(ModelItemDialog): 04644 """!Condition properties dialog""" 04645 def __init__(self, parent, shape, id = wx.ID_ANY, title = _("If-else properties"), 04646 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 04647 ModelItemDialog.__init__(self, parent, shape, title, 04648 style = style, **kwargs) 04649 04650 self.listBoxIf = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 04651 label=" %s " % _("List of items in 'if' block")) 04652 self.itemListIf = self.itemList 04653 self.itemListIf.SetName('IfBlockList') 04654 04655 self.listBoxElse = wx.StaticBox(parent = self.panel, id = wx.ID_ANY, 04656 label=" %s " % _("List of items in 'else' block")) 04657 self.itemListElse = ItemCheckListCtrl(parent = self.panel, 04658 window = self, 04659 columns = [_("ID"), _("Name"), 04660 _("Command")], 04661 shape = shape) 04662 self.itemListElse.SetName('ElseBlockList') 04663 self.itemListElse.Populate(self.parent.GetModel().GetItems()) 04664 04665 self._layout() 04666 self.SetMinSize(self.GetSize()) 04667 self.SetSize((500, 400)) 04668 04669 def _layout(self): 04670 """!Do layout""" 04671 sizer = wx.BoxSizer(wx.VERTICAL) 04672 04673 condSizer = wx.StaticBoxSizer(self.condBox, wx.VERTICAL) 04674 condSizer.Add(item = self.condText, proportion = 1, 04675 flag = wx.EXPAND) 04676 04677 listIfSizer = wx.StaticBoxSizer(self.listBoxIf, wx.VERTICAL) 04678 listIfSizer.Add(item = self.itemListIf, proportion = 1, 04679 flag = wx.EXPAND) 04680 listElseSizer = wx.StaticBoxSizer(self.listBoxElse, wx.VERTICAL) 04681 listElseSizer.Add(item = self.itemListElse, proportion = 1, 04682 flag = wx.EXPAND) 04683 04684 btnSizer = wx.StdDialogButtonSizer() 04685 btnSizer.AddButton(self.btnCancel) 04686 btnSizer.AddButton(self.btnOk) 04687 btnSizer.Realize() 04688 04689 sizer.Add(item = condSizer, proportion = 0, 04690 flag = wx.EXPAND | wx.ALL, border = 3) 04691 sizer.Add(item = listIfSizer, proportion = 1, 04692 flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3) 04693 sizer.Add(item = listElseSizer, proportion = 1, 04694 flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 3) 04695 sizer.Add(item = btnSizer, proportion=0, 04696 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5) 04697 04698 self.panel.SetSizer(sizer) 04699 sizer.Fit(self.panel) 04700 04701 self.Layout() 04702 04703 def OnCheckItemIf(self, index, flag): 04704 """!Item in if-block checked/unchecked""" 04705 if flag is False: 04706 return 04707 04708 aId = int(self.itemListIf.GetItem(index, 0).GetText()) 04709 if aId in self.itemListElse.GetItems()['checked']: 04710 self.itemListElse.CheckItemById(aId, False) 04711 04712 def OnCheckItemElse(self, index, flag): 04713 """!Item in else-block checked/unchecked""" 04714 if flag is False: 04715 return 04716 04717 aId = int(self.itemListElse.GetItem(index, 0).GetText()) 04718 if aId in self.itemListIf.GetItems()['checked']: 04719 self.itemListIf.CheckItemById(aId, False) 04720 04721 def GetItems(self): 04722 """!Get items""" 04723 return { 'if' : self.itemListIf.GetItems(), 04724 'else' : self.itemListElse.GetItems() } 04725 04726 class WritePythonFile: 04727 def __init__(self, fd, model): 04728 """!Class for exporting model to Python script 04729 04730 @param fd file desciptor 04731 """ 04732 self.fd = fd 04733 self.model = model 04734 self.indent = 4 04735 04736 self._writePython() 04737 04738 def _writePython(self): 04739 """!Write model to file""" 04740 properties = self.model.GetProperties() 04741 04742 self.fd.write( 04743 r"""#!/usr/bin/env python 04744 # 04745 ############################################################################ 04746 # 04747 # MODULE: %s 04748 # 04749 # AUTHOR(S): %s 04750 # 04751 # PURPOSE: %s 04752 # 04753 # DATE: %s 04754 # 04755 ############################################################################# 04756 """ % (properties['name'], 04757 properties['author'], 04758 properties['description'], 04759 time.asctime())) 04760 04761 self.fd.write( 04762 r""" 04763 import sys 04764 import os 04765 import atexit 04766 04767 import grass.script as grass 04768 """) 04769 04770 # cleanup() 04771 rast, vect, rast3d, msg = self.model.GetIntermediateData() 04772 self.fd.write( 04773 r""" 04774 def cleanup(): 04775 """) 04776 if rast: 04777 self.fd.write( 04778 r""" grass.run_command('g.remove', 04779 rast=%s) 04780 """ % ','.join(map(lambda x: "'" + x + "'", rast))) 04781 if vect: 04782 self.fd.write( 04783 r""" grass.run_command('g.remove', 04784 vect = %s) 04785 """ % ','.join(map(lambda x: "'" + x + "'", vect))) 04786 if rast3d: 04787 self.fd.write( 04788 r""" grass.run_command('g.remove', 04789 rast3d = %s) 04790 """ % ','.join(map(lambda x: "'" + x + "'", rast3d))) 04791 if not rast and not vect and not rast3d: 04792 self.fd.write(' pass\n') 04793 04794 self.fd.write("\ndef main():\n") 04795 for item in self.model.GetItems(): 04796 self._writePythonItem(item) 04797 04798 self.fd.write("\n return 0\n") 04799 04800 self.fd.write( 04801 r""" 04802 if __name__ == "__main__": 04803 options, flags = grass.parser() 04804 atexit.register(cleanup) 04805 sys.exit(main()) 04806 """) 04807 04808 def _writePythonItem(self, item, ignoreBlock = True): 04809 """!Write model object to Python file""" 04810 if isinstance(item, ModelAction): 04811 if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions 04812 return 04813 self._writePythonAction(item) 04814 elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition): 04815 # substitute condition 04816 variables = self.model.GetVariables() 04817 cond = item.GetText() 04818 for variable in variables: 04819 pattern= re.compile('%' + variable) 04820 if pattern.search(cond): 04821 value = variables[variable].get('value', '') 04822 if variables[variable].get('type', 'string') == 'string': 04823 value = '"' + value + '"' 04824 cond = pattern.sub(value, cond) 04825 if isinstance(item, ModelLoop): 04826 self.fd.write('%sfor %s:\n' % (' ' * self.indent, cond)) 04827 self.indent += 4 04828 for action in item.GetItems(): 04829 self._writePythonItem(action, ignoreBlock = False) 04830 self.indent -= 4 04831 else: # ModelCondition 04832 self.fd.write('%sif %s:\n' % (' ' * self.indent, cond)) 04833 self.indent += 4 04834 condItems = item.GetItems() 04835 for action in condItems['if']: 04836 self._writePythonItem(action, ignoreBlock = False) 04837 if condItems['else']: 04838 self.indent -= 4 04839 self.fd.write('%selse:\n' % (' ' * self.indent)) 04840 self.indent += 4 04841 for action in condItems['else']: 04842 self._writePythonItem(action, ignoreBlock = False) 04843 self.indent += 4 04844 04845 def _writePythonAction(self, item): 04846 """!Write model action to Python file""" 04847 task = menuform.GUI(show = None).ParseCommand(cmd = item.GetLog(string = False)) 04848 opts = task.get_options() 04849 flags = '' 04850 params = list() 04851 strcmd = "%sgrass.run_command(" % (' ' * self.indent) 04852 cmdIndent = len(strcmd) 04853 for f in opts['flags']: 04854 if f.get('value', False) == True: 04855 name = f.get('name', '') 04856 if len(name) > 1: 04857 params.append('%s = True' % name) 04858 else: 04859 flags += name 04860 04861 for p in opts['params']: 04862 name = p.get('name', None) 04863 value = p.get('value', None) 04864 if name and value: 04865 ptype = p.get('type', 'string') 04866 if ptype == 'string': 04867 params.append('%s = "%s"' % (name, value)) 04868 else: 04869 params.append("%s = %s" % (name, value)) 04870 04871 self.fd.write(strcmd + '"%s"' % task.get_name()) 04872 if flags: 04873 self.fd.write(",\n%sflags = '%s'" % (' ' * cmdIndent, flags)) 04874 if len(params) > 0: 04875 self.fd.write(",\n") 04876 for opt in params[:-1]: 04877 self.fd.write("%s%s,\n" % (' ' * cmdIndent, opt)) 04878 self.fd.write("%s%s)\n" % (' ' * cmdIndent, params[-1])) 04879 else: 04880 self.fd.write(")\n") 04881 04882 04883 def main(): 04884 app = wx.PySimpleApp() 04885 wx.InitAllImageHandlers() 04886 frame = ModelFrame(parent = None) 04887 if len(sys.argv) > 1: 04888 frame.LoadModelFile(sys.argv[1]) 04889 frame.Show() 04890 04891 app.MainLoop() 04892 04893 if __name__ == "__main__": 04894 main()