|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
00001 """! 00002 @package gmodeler.model 00003 00004 @brief wxGUI Graphical Modeler (base classes & read/write) 00005 00006 Classes: 00007 - model::Model 00008 - model::ModelObject 00009 - model::ModelAction 00010 - model::ModelData 00011 - model::ModelRelation 00012 - model::ModelItem 00013 - model::ModelLoop 00014 - model::ModelCondition 00015 - model::ProcessModelFile 00016 - model::WriteModelFile 00017 - model::WritePythonFile 00018 - model::ModelParamDialog 00019 00020 (C) 2010-2012 by the GRASS Development Team 00021 00022 This program is free software under the GNU General Public License 00023 (>=v2). Read the file COPYING that comes with GRASS for details. 00024 00025 @author Martin Landa <landa.martin gmail.com> 00026 """ 00027 00028 import os 00029 import getpass 00030 import copy 00031 import re 00032 import mimetypes 00033 import time 00034 try: 00035 import xml.etree.ElementTree as etree 00036 except ImportError: 00037 import elementtree.ElementTree as etree # Python <= 2.4 00038 00039 import wx 00040 from wx.lib import ogl 00041 00042 from core import globalvar 00043 from core import utils 00044 from core.gcmd import GMessage, GException, GError, RunCommand, EncodeString, GWarning 00045 from core.settings import UserSettings 00046 from gui_core.forms import GUI, CmdPanel 00047 from gui_core.widgets import GNotebook 00048 00049 from grass.script import core as grass 00050 from grass.script import task as gtask 00051 00052 class Model(object): 00053 """!Class representing the model""" 00054 def __init__(self, canvas = None): 00055 self.items = list() # list of actions/loops/... 00056 00057 # model properties 00058 self.properties = { 'name' : _("model"), 00059 'description' : _("Script generated by wxGUI Graphical Modeler."), 00060 'author' : getpass.getuser() } 00061 # model variables 00062 self.variables = dict() 00063 self.variablesParams = dict() 00064 00065 self.canvas = canvas 00066 00067 def GetCanvas(self): 00068 """!Get canvas or None""" 00069 return self.canvas 00070 00071 def GetItems(self, objType = None): 00072 """!Get list of model items 00073 00074 @param objType Object type to filter model objects 00075 """ 00076 if not objType: 00077 return self.items 00078 00079 result = list() 00080 for item in self.items: 00081 if isinstance(item, objType): 00082 result.append(item) 00083 00084 return result 00085 00086 def GetItem(self, aId): 00087 """!Get item of given id 00088 00089 @param aId item id 00090 00091 @return Model* instance 00092 @return None if no item found 00093 """ 00094 ilist = self.GetItems() 00095 for item in ilist: 00096 if item.GetId() == aId: 00097 return item 00098 00099 return None 00100 00101 def GetNumItems(self, actionOnly = False): 00102 """!Get number of items""" 00103 if actionOnly: 00104 return len(self.GetItems(objType = ModelAction)) 00105 00106 return len(self.GetItems()) 00107 00108 def GetNextId(self): 00109 """!Get next id (data ignored) 00110 00111 @return next id to be used (default: 1) 00112 """ 00113 if len(self.items) < 1: 00114 return 1 00115 00116 currId = self.items[-1].GetId() 00117 if currId > 0: 00118 return currId + 1 00119 00120 return 1 00121 00122 def GetProperties(self): 00123 """!Get model properties""" 00124 return self.properties 00125 00126 def GetVariables(self, params = False): 00127 """!Get model variables""" 00128 if params: 00129 return self.variablesParams 00130 00131 return self.variables 00132 00133 def SetVariables(self, data): 00134 """!Set model variables""" 00135 self.variables = data 00136 00137 def Reset(self): 00138 """!Reset model""" 00139 self.items = list() 00140 00141 def RemoveItem(self, item): 00142 """!Remove item from model 00143 00144 @return list of related items to remove/update 00145 """ 00146 relList = list() 00147 upList = list() 00148 00149 if not isinstance(item, ModelData): 00150 self.items.remove(item) 00151 00152 if isinstance(item, ModelAction): 00153 for rel in item.GetRelations(): 00154 relList.append(rel) 00155 data = rel.GetData() 00156 if len(data.GetRelations()) < 2: 00157 relList.append(data) 00158 else: 00159 upList.append(data) 00160 00161 elif isinstance(item, ModelData): 00162 for rel in item.GetRelations(): 00163 relList.append(rel) 00164 if rel.GetFrom() == self: 00165 relList.append(rel.GetTo()) 00166 else: 00167 relList.append(rel.GetFrom()) 00168 00169 elif isinstance(item, ModelLoop): 00170 for rel in item.GetRelations(): 00171 relList.append(rel) 00172 for action in self.GetItems(): 00173 action.UnSetBlock(item) 00174 00175 return relList, upList 00176 00177 def FindAction(self, aId): 00178 """!Find action by id""" 00179 alist = self.GetItems(objType = ModelAction) 00180 for action in alist: 00181 if action.GetId() == aId: 00182 return action 00183 00184 return None 00185 00186 def GetData(self): 00187 """!Get list of data items""" 00188 result = list() 00189 dataItems = self.GetItems(objType = ModelData) 00190 00191 for action in self.GetItems(objType = ModelAction): 00192 for rel in action.GetRelations(): 00193 dataItem = rel.GetData() 00194 if dataItem not in result: 00195 result.append(dataItem) 00196 if dataItem in dataItems: 00197 dataItems.remove(dataItem) 00198 00199 # standalone data 00200 if dataItems: 00201 result += dataItems 00202 00203 return result 00204 00205 def FindData(self, value, prompt): 00206 """!Find data item in the model 00207 00208 @param value value 00209 @param prompt prompt 00210 00211 @return ModelData instance 00212 @return None if not found 00213 """ 00214 for data in self.GetData(): 00215 if data.GetValue() == value and \ 00216 data.GetPrompt() == prompt: 00217 return data 00218 00219 return None 00220 00221 def LoadModel(self, filename): 00222 """!Load model definition stored in GRASS Model XML file (gxm) 00223 00224 @todo Validate against DTD 00225 00226 Raise exception on error. 00227 """ 00228 dtdFilename = os.path.join(globalvar.ETCWXDIR, "xml", "grass-gxm.dtd") 00229 00230 # parse workspace file 00231 try: 00232 gxmXml = ProcessModelFile(etree.parse(filename)) 00233 except StandardError, e: 00234 raise GException(e) 00235 00236 if self.canvas: 00237 win = self.canvas.parent 00238 if gxmXml.pos: 00239 win.SetPosition(gxmXml.pos) 00240 if gxmXml.size: 00241 win.SetSize(gxmXml.size) 00242 00243 # load properties 00244 self.properties = gxmXml.properties 00245 self.variables = gxmXml.variables 00246 00247 # load model.GetActions() 00248 for action in gxmXml.actions: 00249 actionItem = ModelAction(parent = self, 00250 x = action['pos'][0], 00251 y = action['pos'][1], 00252 width = action['size'][0], 00253 height = action['size'][1], 00254 task = action['task'], 00255 id = action['id']) 00256 00257 if action['disabled']: 00258 actionItem.Enable(False) 00259 00260 self.AddItem(actionItem) 00261 00262 actionItem.SetValid(actionItem.GetTask().get_options()) 00263 actionItem.GetLog() # substitute variables (-> valid/invalid) 00264 00265 # load data & relations 00266 for data in gxmXml.data: 00267 dataItem = ModelData(parent = self, 00268 x = data['pos'][0], 00269 y = data['pos'][1], 00270 width = data['size'][0], 00271 height = data['size'][1], 00272 prompt = data['prompt'], 00273 value = data['value']) 00274 dataItem.SetIntermediate(data['intermediate']) 00275 00276 for rel in data['rels']: 00277 actionItem = self.FindAction(rel['id']) 00278 if rel['dir'] == 'from': 00279 relation = ModelRelation(parent = self, fromShape = dataItem, 00280 toShape = actionItem, param = rel['name']) 00281 else: 00282 relation = ModelRelation(parent = self, fromShape = actionItem, 00283 toShape = dataItem, param = rel['name']) 00284 relation.SetControlPoints(rel['points']) 00285 actionItem.AddRelation(relation) 00286 dataItem.AddRelation(relation) 00287 00288 if self.canvas: 00289 dataItem.Update() 00290 00291 # load loops 00292 for loop in gxmXml.loops: 00293 loopItem = ModelLoop(parent = self, 00294 x = loop['pos'][0], 00295 y = loop['pos'][1], 00296 width = loop['size'][0], 00297 height = loop['size'][1], 00298 text = loop['text'], 00299 id = loop['id']) 00300 self.AddItem(loopItem) 00301 00302 # load conditions 00303 for condition in gxmXml.conditions: 00304 conditionItem = ModelCondition(parent = self, 00305 x = condition['pos'][0], 00306 y = condition['pos'][1], 00307 width = condition['size'][0], 00308 height = condition['size'][1], 00309 text = condition['text'], 00310 id = condition['id']) 00311 self.AddItem(conditionItem) 00312 00313 # define loops & if/else items 00314 for loop in gxmXml.loops: 00315 alist = list() 00316 for aId in loop['items']: 00317 action = self.GetItem(aId) 00318 alist.append(action) 00319 00320 loopItem = self.GetItem(loop['id']) 00321 loopItem.SetItems(alist) 00322 00323 for action in loopItem.GetItems(): 00324 action.SetBlock(loopItem) 00325 00326 for condition in gxmXml.conditions: 00327 conditionItem = self.GetItem(condition['id']) 00328 for b in condition['items'].keys(): 00329 alist = list() 00330 for aId in condition['items'][b]: 00331 action = self.GetItem(aId) 00332 alist.append(action) 00333 conditionItem.SetItems(alist, branch = b) 00334 00335 items = conditionItem.GetItems() 00336 for b in items.keys(): 00337 for action in items[b]: 00338 action.SetBlock(conditionItem) 00339 00340 def AddItem(self, newItem): 00341 """!Add item to the list""" 00342 iId = newItem.GetId() 00343 00344 i = 0 00345 for item in self.items: 00346 if item.GetId() > iId: 00347 self.items.insert(i, newItem) 00348 return 00349 i += 1 00350 00351 self.items.append(newItem) 00352 00353 def IsValid(self): 00354 """Return True if model is valid""" 00355 if self.Validate(): 00356 return False 00357 00358 return True 00359 00360 def Validate(self): 00361 """!Validate model, return None if model is valid otherwise 00362 error string""" 00363 errList = list() 00364 00365 variables = self.GetVariables().keys() 00366 pattern = re.compile(r'(.*)(%.+\s?)(.*)') 00367 for action in self.GetItems(objType = ModelAction): 00368 cmd = action.GetLog(string = False) 00369 00370 task = GUI(show = None).ParseCommand(cmd = cmd) 00371 errList += map(lambda x: cmd[0] + ': ' + x, task.get_cmd_error()) 00372 00373 # check also variables 00374 for opt in cmd[1:]: 00375 if '=' not in opt: 00376 continue 00377 key, value = opt.split('=', 1) 00378 sval = pattern.search(value) 00379 if sval: 00380 var = sval.group(2).strip()[1:] # ignore '%' 00381 if var not in variables: 00382 report = True 00383 for item in filter(lambda x: isinstance(x, ModelLoop), action.GetBlock()): 00384 if var in item.GetText(): 00385 report = False 00386 break 00387 if report: 00388 errList.append(cmd[0] + ": " + _("undefined variable '%s'") % var) 00389 ### TODO: check variables in file only optionally 00390 ### errList += self._substituteFile(action, checkOnly = True) 00391 00392 return errList 00393 00394 def _substituteFile(self, item, params = None, checkOnly = False): 00395 """!Subsitute variables in command file inputs 00396 00397 @param checkOnly tuble - True to check variable, don't touch files 00398 00399 @return list of undefined variables 00400 """ 00401 errList = list() 00402 00403 self.fileInput = dict() 00404 00405 # collect ascii inputs 00406 for p in item.GetParams()['params']: 00407 if p.get('element', '') == 'file' and \ 00408 p.get('prompt', '') == 'input' and \ 00409 p.get('age', '') == 'old_file': 00410 filename = p.get('value', p.get('default', '')) 00411 if filename and \ 00412 mimetypes.guess_type(filename)[0] == 'text/plain': 00413 self.fileInput[filename] = None 00414 00415 for finput in self.fileInput: 00416 # read lines 00417 fd = open(finput, "r") 00418 try: 00419 data = self.fileInput[finput] = fd.read() 00420 finally: 00421 fd.close() 00422 00423 # substitute variables 00424 write = False 00425 variables = self.GetVariables() 00426 for variable in variables: 00427 pattern = re.compile('%' + variable) 00428 value = '' 00429 if params and 'variables' in params: 00430 for p in params['variables']['params']: 00431 if variable == p.get('name', ''): 00432 if p.get('type', 'string') == 'string': 00433 value = p.get('value', '') 00434 else: 00435 value = str(p.get('value', '')) 00436 break 00437 00438 if not value: 00439 value = variables[variable].get('value', '') 00440 00441 data = pattern.sub(value, data) 00442 if not checkOnly: 00443 write = True 00444 00445 pattern = re.compile(r'(.*)(%.+\s?)(.*)') 00446 sval = pattern.search(data) 00447 if sval: 00448 var = sval.group(2).strip()[1:] # ignore '%' 00449 cmd = item.GetLog(string = False)[0] 00450 errList.append(cmd + ": " + _("undefined variable '%s'") % var) 00451 00452 if not checkOnly: 00453 if write: 00454 fd = open(finput, "w") 00455 try: 00456 fd.write(data) 00457 finally: 00458 fd.close() 00459 else: 00460 self.fileInput[finput] = None 00461 00462 return errList 00463 00464 def OnPrepare(self, item, params): 00465 self._substituteFile(item, params, checkOnly = False) 00466 00467 def RunAction(self, item, params, log, onDone, onPrepare = None, statusbar = None): 00468 """!Run given action 00469 00470 @param item action item 00471 @param params parameters dict 00472 @param log logging window 00473 @param onDone on-done method 00474 @param onPrepare on-prepare method 00475 @param statusbar wx.StatusBar instance or None 00476 """ 00477 name = item.GetName() 00478 if name in params: 00479 paramsOrig = item.GetParams(dcopy = True) 00480 item.MergeParams(params[name]) 00481 00482 if statusbar: 00483 statusbar.SetStatusText(_('Running model...'), 0) 00484 00485 data = { 'item' : item, 00486 'params' : copy.deepcopy(params) } 00487 log.RunCmd(command = item.GetLog(string = False, substitute = params), 00488 onDone = onDone, onPrepare = self.OnPrepare, userData = data) 00489 00490 if name in params: 00491 item.SetParams(paramsOrig) 00492 00493 def Run(self, log, onDone, parent = None): 00494 """!Run model 00495 00496 @param log logging window (see goutput.GMConsole) 00497 @param onDone on-done method 00498 @param parent window for messages or None 00499 """ 00500 if self.GetNumItems() < 1: 00501 GMessage(parent = parent, 00502 message = _('Model is empty. Nothing to run.')) 00503 return 00504 00505 statusbar = None 00506 if isinstance(parent, wx.Frame): 00507 statusbar = parent.GetStatusBar() 00508 00509 # validation 00510 if statusbar: 00511 statusbar.SetStatusText(_('Validating model...'), 0) 00512 errList = self.Validate() 00513 if statusbar: 00514 statusbar.SetStatusText('', 0) 00515 if errList: 00516 dlg = wx.MessageDialog(parent = parent, 00517 message = _('Model is not valid. Do you want to ' 00518 'run the model anyway?\n\n%s') % '\n'.join(errList), 00519 caption = _("Run model?"), 00520 style = wx.YES_NO | wx.NO_DEFAULT | 00521 wx.ICON_QUESTION | wx.CENTRE) 00522 ret = dlg.ShowModal() 00523 dlg.Destroy() 00524 if ret != wx.ID_YES: 00525 return 00526 00527 # parametrization 00528 params = self.Parameterize() 00529 delInterData = False 00530 if params: 00531 dlg = ModelParamDialog(parent = parent, 00532 params = params) 00533 dlg.CenterOnParent() 00534 00535 ret = dlg.ShowModal() 00536 if ret != wx.ID_OK: 00537 dlg.Destroy() 00538 return 00539 00540 err = dlg.GetErrors() 00541 delInterData = dlg.DeleteIntermediateData() 00542 dlg.Destroy() 00543 if err: 00544 GError(parent = parent, message = unicode('\n'.join(err))) 00545 return 00546 00547 err = list() 00548 for key, item in params.iteritems(): 00549 for p in item['params']: 00550 if p.get('value', '') == '': 00551 err.append((key, p.get('name', ''), p.get('description', ''))) 00552 if err: 00553 GError(parent = parent, 00554 message = _("Variables below not defined:") + \ 00555 "\n\n" + unicode('\n'.join(map(lambda x: "%s: %s (%s)" % (x[0], x[1], x[2]), err)))) 00556 return 00557 00558 log.cmdThread.SetId(-1) 00559 for item in self.GetItems(): 00560 if not item.IsEnabled(): 00561 continue 00562 if isinstance(item, ModelAction): 00563 if item.GetBlockId(): 00564 continue 00565 self.RunAction(item, params, log, onDone) 00566 elif isinstance(item, ModelLoop): 00567 cond = item.GetText() 00568 # substitute variables in condition 00569 variables = self.GetVariables() 00570 for variable in variables: 00571 pattern = re.compile('%' + variable) 00572 if pattern.search(cond): 00573 value = '' 00574 if params and 'variables' in params: 00575 for p in params['variables']['params']: 00576 if variable == p.get('name', ''): 00577 value = p.get('value', '') 00578 break 00579 00580 if not value: 00581 value = variables[variable].get('value', '') 00582 00583 if not value: 00584 continue 00585 00586 vtype = variables[variable].get('type', 'string') 00587 if vtype == 'string': 00588 value = '"' + value + '"' 00589 cond = pattern.sub(value, cond) 00590 00591 # split condition 00592 condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond)) 00593 pattern = re.compile('%' + condVar) 00594 ### for vars()[condVar] in eval(condText): ? 00595 if condText[0] == '`' and condText[-1] == '`': 00596 # run command 00597 cmd, dcmd = utils.CmdToTuple(condText[1:-1].split(' ')) 00598 ret = RunCommand(cmd, 00599 read = True, 00600 **dcmd) 00601 if ret: 00602 vlist = ret.splitlines() 00603 else: 00604 vlist = eval(condText) 00605 00606 if 'variables' not in params: 00607 params['variables'] = { 'params' : [] } 00608 varDict = { 'name' : condVar, 'value' : '' } 00609 params['variables']['params'].append(varDict) 00610 00611 for var in vlist: 00612 for action in item.GetItems(): 00613 if not isinstance(action, ModelAction) or \ 00614 not action.IsEnabled(): 00615 continue 00616 00617 varDict['value'] = var 00618 00619 self.RunAction(item = action, params = params, 00620 log = log, onDone = onDone) 00621 params['variables']['params'].remove(varDict) 00622 00623 if delInterData: 00624 self.DeleteIntermediateData(log) 00625 00626 # discard values 00627 if params: 00628 for item in params.itervalues(): 00629 for p in item['params']: 00630 p['value'] = '' 00631 00632 def DeleteIntermediateData(self, log): 00633 """!Detele intermediate data""" 00634 rast, vect, rast3d, msg = self.GetIntermediateData() 00635 00636 if rast: 00637 log.RunCmd(['g.remove', 'rast=%s' %','.join(rast)]) 00638 if rast3d: 00639 log.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)]) 00640 if vect: 00641 log.RunCmd(['g.remove', 'vect=%s' %','.join(vect)]) 00642 00643 def GetIntermediateData(self): 00644 """!Get info about intermediate data""" 00645 rast = list() 00646 rast3d = list() 00647 vect = list() 00648 for data in self.GetData(): 00649 if not data.IsIntermediate(): 00650 continue 00651 name = data.GetValue() 00652 prompt = data.GetPrompt() 00653 if prompt == 'raster': 00654 rast.append(name) 00655 elif prompt == 'vector': 00656 vect.append(name) 00657 elif prompt == 'rast3d': 00658 rast3d.append(name) 00659 00660 msg = '' 00661 if rast: 00662 msg += '\n\n%s: ' % _('Raster maps') 00663 msg += ', '.join(rast) 00664 if rast3d: 00665 msg += '\n\n%s: ' % _('3D raster maps') 00666 msg += ', '.join(rast3d) 00667 if vect: 00668 msg += '\n\n%s: ' % _('Vector maps') 00669 msg += ', '.join(vect) 00670 00671 return rast, vect, rast3d, msg 00672 00673 def Update(self): 00674 """!Update model""" 00675 for item in self.items: 00676 item.Update() 00677 00678 def IsParameterized(self): 00679 """!Return True if model is parameterized""" 00680 if self.Parameterize(): 00681 return True 00682 00683 return False 00684 00685 def Parameterize(self): 00686 """!Return parameterized options""" 00687 result = dict() 00688 idx = 0 00689 if self.variables: 00690 params = list() 00691 result["variables"] = { 'flags' : list(), 00692 'params' : params, 00693 'idx' : idx } 00694 for name, values in self.variables.iteritems(): 00695 gtype = values.get('type', 'string') 00696 if gtype in ('raster', 'vector', 'mapset', 'file'): 00697 gisprompt = True 00698 prompt = gtype 00699 if gtype == 'raster': 00700 element = 'cell' 00701 else: 00702 element = gtype 00703 ptype = 'string' 00704 else: 00705 gisprompt = False 00706 prompt = None 00707 element = None 00708 ptype = gtype 00709 params.append({ 'gisprompt' : gisprompt, 00710 'multiple' : False, 00711 'description' : values.get('description', ''), 00712 'guidependency' : '', 00713 'default' : '', 00714 'age' : None, 00715 'required' : True, 00716 'value' : values.get('value', ''), 00717 'label' : '', 00718 'guisection' : '', 00719 'key_desc' : '', 00720 'values' : list(), 00721 'parameterized' : False, 00722 'values_desc' : list(), 00723 'prompt' : prompt, 00724 'element' : element, 00725 'type' : ptype, 00726 'name' : name }) 00727 00728 idx += 1 00729 00730 for action in self.GetItems(objType = ModelAction): 00731 if not action.IsEnabled(): 00732 continue 00733 name = action.GetName() 00734 params = action.GetParams() 00735 for f in params['flags']: 00736 if f.get('parameterized', False): 00737 if name not in result: 00738 result[name] = { 'flags' : list(), 00739 'params': list(), 00740 'idx' : idx } 00741 result[name]['flags'].append(f) 00742 for p in params['params']: 00743 if p.get('parameterized', False): 00744 if name not in result: 00745 result[name] = { 'flags' : list(), 00746 'params': list(), 00747 'idx' : idx } 00748 result[name]['params'].append(p) 00749 if name in result: 00750 idx += 1 00751 00752 self.variablesParams = result # record parameters 00753 00754 return result 00755 00756 class ModelObject(object): 00757 def __init__(self, id = -1): 00758 self.id = id 00759 self.rels = list() # list of ModelRelations 00760 00761 self.isEnabled = True 00762 self.inBlock = list() # list of related loops/conditions 00763 00764 def __del__(self): 00765 pass 00766 00767 def GetId(self): 00768 """!Get id""" 00769 return self.id 00770 00771 def AddRelation(self, rel): 00772 """!Record new relation 00773 """ 00774 self.rels.append(rel) 00775 00776 def GetRelations(self, fdir = None): 00777 """!Get list of relations 00778 00779 @param fdir True for 'from' 00780 """ 00781 if fdir is None: 00782 return self.rels 00783 00784 result = list() 00785 for rel in self.rels: 00786 if fdir == 'from': 00787 if rel.GetFrom() == self: 00788 result.append(rel) 00789 else: 00790 if rel.GetTo() == self: 00791 result.append(rel) 00792 00793 return result 00794 00795 def IsEnabled(self): 00796 """!Get True if action is enabled, otherwise False""" 00797 return self.isEnabled 00798 00799 def Enable(self, enabled = True): 00800 """!Enable/disable action""" 00801 self.isEnabled = enabled 00802 self.Update() 00803 00804 def Update(self): 00805 pass 00806 00807 def SetBlock(self, item): 00808 """!Add object to the block (loop/condition) 00809 00810 @param item reference to ModelLoop or ModelCondition which 00811 defines loops/condition 00812 """ 00813 if item not in self.inBlock: 00814 self.inBlock.append(item) 00815 00816 def UnSetBlock(self, item): 00817 """!Remove object from the block (loop/consition) 00818 00819 @param item reference to ModelLoop or ModelCondition which 00820 defines loops/codition 00821 """ 00822 if item in self.inBlock: 00823 self.inBlock.remove(item) 00824 00825 def GetBlock(self): 00826 """!Get list of related ModelObject(s) which defines block 00827 (loop/condition) 00828 00829 @return list of ModelObjects 00830 """ 00831 return self.inBlock 00832 00833 def GetBlockId(self): 00834 """!Get list of related ids which defines block 00835 00836 @return list of ids 00837 """ 00838 ret = list() 00839 for mo in self.inBlock: 00840 ret.append(mo.GetId()) 00841 00842 return ret 00843 00844 class ModelAction(ModelObject, ogl.RectangleShape): 00845 """!Action class (GRASS module)""" 00846 def __init__(self, parent, x, y, id = -1, cmd = None, task = None, width = None, height = None): 00847 ModelObject.__init__(self, id) 00848 00849 self.parent = parent 00850 self.task = task 00851 00852 if not width: 00853 width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width')) 00854 if not height: 00855 height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height')) 00856 00857 if cmd and cmd[0] in ('r.mapcalc', 'v.type'): 00858 cmd[0] += '_wrapper' 00859 00860 if cmd: 00861 self.task = GUI(show = None).ParseCommand(cmd = cmd) 00862 else: 00863 if task: 00864 self.task = task 00865 else: 00866 self.task = None 00867 00868 self.propWin = None 00869 00870 self.data = list() # list of connected data items 00871 00872 self.isValid = False 00873 self.isParameterized = False 00874 00875 if self.parent.GetCanvas(): 00876 ogl.RectangleShape.__init__(self, width, height) 00877 00878 self.SetCanvas(self.parent) 00879 self.SetX(x) 00880 self.SetY(y) 00881 self.SetPen(wx.BLACK_PEN) 00882 self._setPen() 00883 self._setBrush() 00884 self.SetId(id) 00885 00886 if self.task: 00887 self.SetValid(self.task.get_options()) 00888 00889 def _setBrush(self, running = False): 00890 """!Set brush""" 00891 if running: 00892 color = UserSettings.Get(group='modeler', key='action', 00893 subkey=('color', 'running')) 00894 elif not self.isEnabled: 00895 color = UserSettings.Get(group='modeler', key='disabled', 00896 subkey='color') 00897 elif self.isValid: 00898 color = UserSettings.Get(group='modeler', key='action', 00899 subkey=('color', 'valid')) 00900 else: 00901 color = UserSettings.Get(group='modeler', key='action', 00902 subkey=('color', 'invalid')) 00903 00904 wxColor = wx.Color(color[0], color[1], color[2]) 00905 self.SetBrush(wx.Brush(wxColor)) 00906 00907 def _setPen(self): 00908 """!Set pen""" 00909 if self.isParameterized: 00910 width = int(UserSettings.Get(group='modeler', key='action', 00911 subkey=('width', 'parameterized'))) 00912 else: 00913 width = int(UserSettings.Get(group='modeler', key='action', 00914 subkey=('width', 'default'))) 00915 pen = self.GetPen() 00916 pen.SetWidth(width) 00917 self.SetPen(pen) 00918 00919 def SetId(self, id): 00920 """!Set id""" 00921 self.id = id 00922 cmd = self.task.get_cmd(ignoreErrors = True) 00923 if cmd and len(cmd) > 0: 00924 self.ClearText() 00925 self.AddText('(%d) %s' % (self.id, cmd[0])) 00926 else: 00927 self.AddText('(%d) <<%s>>' % (self.id, _("unknown"))) 00928 00929 def SetProperties(self, params, propwin): 00930 """!Record properties dialog""" 00931 self.task.params = params['params'] 00932 self.task.flags = params['flags'] 00933 self.propWin = propwin 00934 00935 def GetPropDialog(self): 00936 """!Get properties dialog""" 00937 return self.propWin 00938 00939 def GetLog(self, string = True, substitute = None): 00940 """!Get logging info 00941 00942 @param string True to get cmd as a string otherwise a list 00943 @param substitute dictionary of parameter to substitute or None 00944 """ 00945 cmd = self.task.get_cmd(ignoreErrors = True, ignoreRequired = True, 00946 ignoreDefault = False) 00947 00948 # substitute variables 00949 if substitute: 00950 variables = [] 00951 if 'variables' in substitute: 00952 for p in substitute['variables']['params']: 00953 variables.append(p.get('name', '')) 00954 else: 00955 variables = self.parent.GetVariables() 00956 for variable in variables: 00957 pattern= re.compile('%' + variable) 00958 value = '' 00959 if substitute and 'variables' in substitute: 00960 for p in substitute['variables']['params']: 00961 if variable == p.get('name', ''): 00962 if p.get('type', 'string') == 'string': 00963 value = p.get('value', '') 00964 else: 00965 value = str(p.get('value', '')) 00966 break 00967 00968 if not value: 00969 value = variables[variable].get('value', '') 00970 00971 if not value: 00972 continue 00973 00974 for idx in range(len(cmd)): 00975 if pattern.search(cmd[idx]): 00976 cmd[idx] = pattern.sub(value, cmd[idx]) 00977 break 00978 idx += 1 00979 00980 if string: 00981 if cmd is None: 00982 return '' 00983 else: 00984 return ' '.join(cmd) 00985 00986 return cmd 00987 00988 def GetName(self): 00989 """!Get name""" 00990 cmd = self.task.get_cmd(ignoreErrors = True) 00991 if cmd and len(cmd) > 0: 00992 return cmd[0] 00993 00994 return _('unknown') 00995 00996 def GetParams(self, dcopy = False): 00997 """!Get dictionary of parameters""" 00998 if dcopy: 00999 return copy.deepcopy(self.task.get_options()) 01000 01001 return self.task.get_options() 01002 01003 def GetTask(self): 01004 """!Get grassTask instance""" 01005 return self.task 01006 01007 def SetParams(self, params): 01008 """!Set dictionary of parameters""" 01009 self.task.params = params['params'] 01010 self.task.flags = params['flags'] 01011 01012 def MergeParams(self, params): 01013 """!Merge dictionary of parameters""" 01014 if 'flags' in params: 01015 for f in params['flags']: 01016 self.task.set_flag(f['name'], 01017 f.get('value', False)) 01018 if 'params' in params: 01019 for p in params['params']: 01020 self.task.set_param(p['name'], 01021 p.get('value', '')) 01022 01023 def SetValid(self, options): 01024 """!Set validity for action 01025 01026 @param options dictionary with flags and params (gtask) 01027 """ 01028 self.isValid = True 01029 self.isParameterized = False 01030 01031 for f in options['flags']: 01032 if f.get('parameterized', False): 01033 self.IsParameterized = True 01034 break 01035 01036 for p in options['params']: 01037 if self.isValid and p.get('required', False) and \ 01038 p.get('value', '') == '' and \ 01039 p.get('default', '') == '': 01040 self.isValid = False 01041 if not self.isParameterized and p.get('parameterized', False): 01042 self.isParameterized = True 01043 01044 if self.parent.GetCanvas(): 01045 self._setBrush() 01046 self._setPen() 01047 01048 def IsValid(self): 01049 """!Check validity (all required parameters set)""" 01050 return self.isValid 01051 01052 def IsParameterized(self): 01053 """!Check if action is parameterized""" 01054 return self.isParameterized 01055 01056 def FindData(self, name): 01057 """!Find data item by name""" 01058 for rel in self.GetRelations(): 01059 data = rel.GetData() 01060 if name == rel.GetName() and name in data.GetName(): 01061 return data 01062 01063 return None 01064 01065 def Update(self, running = False): 01066 """!Update action""" 01067 if running: 01068 self._setBrush(running = True) 01069 else: 01070 self._setBrush() 01071 self._setPen() 01072 01073 def OnDraw(self, dc): 01074 """!Draw action in canvas""" 01075 self._setBrush() 01076 self._setPen() 01077 ogl.RectangleShape.Recentre(self, dc) # re-center text 01078 ogl.RectangleShape.OnDraw(self, dc) 01079 01080 class ModelData(ModelObject, ogl.EllipseShape): 01081 def __init__(self, parent, x, y, value = '', prompt = '', width = None, height = None): 01082 """Data item class 01083 01084 @param parent window parent 01085 @param x, y position of the shape 01086 @param fname, tname list of parameter names from / to 01087 @param value value 01088 @param prompt type of GIS element 01089 @param width,height dimension of the shape 01090 """ 01091 ModelObject.__init__(self) 01092 01093 self.parent = parent 01094 self.value = value 01095 self.prompt = prompt 01096 self.intermediate = False 01097 self.propWin = None 01098 if not width: 01099 width = UserSettings.Get(group='modeler', key='data', subkey=('size', 'width')) 01100 if not height: 01101 height = UserSettings.Get(group='modeler', key='data', subkey=('size', 'height')) 01102 01103 if self.parent.GetCanvas(): 01104 ogl.EllipseShape.__init__(self, width, height) 01105 01106 self.SetCanvas(self.parent) 01107 self.SetX(x) 01108 self.SetY(y) 01109 self.SetPen(wx.BLACK_PEN) 01110 self._setBrush() 01111 01112 self._setText() 01113 01114 def IsIntermediate(self): 01115 """!Checks if data item is intermediate""" 01116 return self.intermediate 01117 01118 def SetIntermediate(self, im): 01119 """!Set intermediate flag""" 01120 self.intermediate = im 01121 01122 def OnDraw(self, dc): 01123 pen = self.GetPen() 01124 pen.SetWidth(1) 01125 if self.intermediate: 01126 pen.SetStyle(wx.SHORT_DASH) 01127 else: 01128 pen.SetStyle(wx.SOLID) 01129 self.SetPen(pen) 01130 01131 ogl.EllipseShape.OnDraw(self, dc) 01132 01133 def GetLog(self, string = True): 01134 """!Get logging info""" 01135 name = list() 01136 for rel in self.GetRelations(): 01137 name.append(rel.GetName()) 01138 if name: 01139 return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')' 01140 else: 01141 return self.value + ' (' + self.prompt + ')' 01142 01143 def GetName(self): 01144 """!Get list of names""" 01145 name = list() 01146 for rel in self.GetRelations(): 01147 name.append(rel.GetName()) 01148 01149 return name 01150 01151 def GetPrompt(self): 01152 """!Get prompt""" 01153 return self.prompt 01154 01155 def SetPrompt(self, prompt): 01156 """!Set prompt 01157 01158 @param prompt 01159 """ 01160 self.prompt = prompt 01161 01162 def GetValue(self): 01163 """!Get value""" 01164 return self.value 01165 01166 def SetValue(self, value): 01167 """!Set value 01168 01169 @param value 01170 """ 01171 self.value = value 01172 self._setText() 01173 for direction in ('from', 'to'): 01174 for rel in self.GetRelations(direction): 01175 if direction == 'from': 01176 action = rel.GetTo() 01177 else: 01178 action = rel.GetFrom() 01179 01180 task = GUI(show = None).ParseCommand(cmd = action.GetLog(string = False)) 01181 task.set_param(rel.GetName(), self.value) 01182 action.SetParams(params = task.get_options()) 01183 01184 def GetPropDialog(self): 01185 """!Get properties dialog""" 01186 return self.propWin 01187 01188 def SetPropDialog(self, win): 01189 """!Get properties dialog""" 01190 self.propWin = win 01191 01192 def _setBrush(self): 01193 """!Set brush""" 01194 if self.prompt == 'raster': 01195 color = UserSettings.Get(group = 'modeler', key = 'data', 01196 subkey = ('color', 'raster')) 01197 elif self.prompt == 'raster3d': 01198 color = UserSettings.Get(group = 'modeler', key = 'data', 01199 subkey = ('color', 'raster3d')) 01200 elif self.prompt == 'vector': 01201 color = UserSettings.Get(group = 'modeler', key = 'data', 01202 subkey = ('color', 'vector')) 01203 else: 01204 color = UserSettings.Get(group = 'modeler', key = 'action', 01205 subkey = ('color', 'invalid')) 01206 wxColor = wx.Color(color[0], color[1], color[2]) 01207 self.SetBrush(wx.Brush(wxColor)) 01208 01209 def _setPen(self): 01210 """!Set pen""" 01211 isParameterized = False 01212 for rel in self.GetRelations('from'): 01213 if rel.GetTo().IsParameterized(): 01214 isParameterized = True 01215 break 01216 if not isParameterized: 01217 for rel in self.GetRelations('to'): 01218 if rel.GetFrom().IsParameterized(): 01219 isParameterized = True 01220 break 01221 01222 if isParameterized: 01223 width = int(UserSettings.Get(group = 'modeler', key = 'action', 01224 subkey = ('width', 'parameterized'))) 01225 else: 01226 width = int(UserSettings.Get(group = 'modeler', key = 'action', 01227 subkey = ('width', 'default'))) 01228 pen = self.GetPen() 01229 pen.SetWidth(width) 01230 self.SetPen(pen) 01231 01232 def _setText(self): 01233 """!Update text""" 01234 self.ClearText() 01235 name = [] 01236 for rel in self.GetRelations(): 01237 name.append(rel.GetName()) 01238 self.AddText('/'.join(name)) 01239 if self.value: 01240 self.AddText(self.value) 01241 else: 01242 self.AddText(_('<not defined>')) 01243 01244 def Update(self): 01245 """!Update action""" 01246 self._setBrush() 01247 self._setPen() 01248 self._setText() 01249 01250 class ModelRelation(ogl.LineShape): 01251 """!Data - action relation""" 01252 def __init__(self, parent, fromShape, toShape, param = ''): 01253 self.fromShape = fromShape 01254 self.toShape = toShape 01255 self.param = param 01256 self.parent = parent 01257 01258 self._points = None 01259 01260 if self.parent.GetCanvas(): 01261 ogl.LineShape.__init__(self) 01262 01263 def __del__(self): 01264 if self in self.fromShape.rels: 01265 self.fromShape.rels.remove(self) 01266 if self in self.toShape.rels: 01267 self.toShape.rels.remove(self) 01268 01269 def GetFrom(self): 01270 """!Get id of 'from' shape""" 01271 return self.fromShape 01272 01273 def GetTo(self): 01274 """!Get id of 'to' shape""" 01275 return self.toShape 01276 01277 def GetData(self): 01278 """!Get related ModelData instance 01279 01280 @return ModelData instance 01281 @return None if not found 01282 """ 01283 if isinstance(self.fromShape, ModelData): 01284 return self.fromShape 01285 elif isinstance(self.toShape, ModelData): 01286 return self.toShape 01287 01288 return None 01289 01290 def GetName(self): 01291 """!Get parameter name""" 01292 return self.param 01293 01294 def ResetShapes(self): 01295 """!Reset related objects""" 01296 self.fromShape.ResetControlPoints() 01297 self.toShape.ResetControlPoints() 01298 self.ResetControlPoints() 01299 01300 def SetControlPoints(self, points): 01301 """!Set control points""" 01302 self._points = points 01303 01304 def GetControlPoints(self): 01305 """!Get list of control points""" 01306 return self._points 01307 01308 def _setPen(self): 01309 """!Set pen""" 01310 pen = self.GetPen() 01311 pen.SetWidth(1) 01312 pen.SetStyle(wx.SOLID) 01313 self.SetPen(pen) 01314 01315 def OnDraw(self, dc): 01316 """!Draw relation""" 01317 self._setPen() 01318 ogl.LineShape.OnDraw(self, dc) 01319 01320 def SetName(self, param): 01321 self.param = param 01322 01323 class ModelItem(ModelObject): 01324 def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []): 01325 """!Abstract class for loops and conditions""" 01326 ModelObject.__init__(self, id) 01327 self.parent = parent 01328 self.text = text 01329 self.items = items # list of items in the loop 01330 01331 def GetText(self): 01332 """!Get loop text""" 01333 return self.text 01334 01335 def GetItems(self): 01336 """!Get items (id)""" 01337 return self.items 01338 01339 def SetId(self, id): 01340 """!Set loop id""" 01341 self.id = id 01342 01343 def SetText(self, cond): 01344 """!Set loop text (condition)""" 01345 self.text = cond 01346 self.ClearText() 01347 self.AddText('(' + str(self.id) + ') ' + self.text) 01348 01349 def GetLog(self): 01350 """!Get log info""" 01351 if self.text: 01352 return _("Condition: ") + self.text 01353 else: 01354 return _("Condition: not defined") 01355 01356 def AddRelation(self, rel): 01357 """!Record relation""" 01358 self.rels.append(rel) 01359 01360 def Clear(self): 01361 """!Clear object, remove rels""" 01362 self.rels = list() 01363 01364 class ModelLoop(ModelItem, ogl.RectangleShape): 01365 def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []): 01366 """!Defines a loop""" 01367 ModelItem.__init__(self, parent, x, y, id, width, height, text, items) 01368 01369 if not width: 01370 width = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'width')) 01371 if not height: 01372 height = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'height')) 01373 01374 if self.parent.GetCanvas(): 01375 ogl.RectangleShape.__init__(self, width, height) 01376 01377 self.SetCanvas(self.parent) 01378 self.SetX(x) 01379 self.SetY(y) 01380 self.SetPen(wx.BLACK_PEN) 01381 self.SetCornerRadius(100) 01382 if text: 01383 self.AddText('(' + str(self.id) + ') ' + text) 01384 else: 01385 self.AddText('(' + str(self.id) + ')') 01386 01387 self._setBrush() 01388 01389 def _setBrush(self): 01390 """!Set brush""" 01391 if not self.isEnabled: 01392 color = UserSettings.Get(group='modeler', key='disabled', 01393 subkey='color') 01394 else: 01395 color = UserSettings.Get(group='modeler', key='loop', 01396 subkey=('color', 'valid')) 01397 01398 wxColor = wx.Color(color[0], color[1], color[2]) 01399 self.SetBrush(wx.Brush(wxColor)) 01400 01401 def Enable(self, enabled = True): 01402 """!Enable/disable action""" 01403 for item in self.items: 01404 if not isinstance(item, ModelAction): 01405 continue 01406 item.Enable(enabled) 01407 01408 ModelObject.Enable(self, enabled) 01409 01410 def Update(self): 01411 self._setBrush() 01412 01413 def GetName(self): 01414 """!Get name""" 01415 return _("loop") 01416 01417 def SetItems(self, items): 01418 """!Set items (id)""" 01419 self.items = items 01420 01421 class ModelCondition(ModelItem, ogl.PolygonShape): 01422 def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', 01423 items = { 'if' : [], 'else' : [] }): 01424 """!Defines a if-else condition""" 01425 ModelItem.__init__(self, parent, x, y, id, width, height, text, items) 01426 01427 if not width: 01428 self.width = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'width')) 01429 else: 01430 self.width = width 01431 if not height: 01432 self.height = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'height')) 01433 else: 01434 self.height = height 01435 01436 if self.parent.GetCanvas(): 01437 ogl.PolygonShape.__init__(self) 01438 01439 points = [(0, - self.height / 2), 01440 (self.width / 2, 0), 01441 (0, self.height / 2), 01442 (- self.width / 2, 0)] 01443 self.Create(points) 01444 01445 self.SetCanvas(self.parent) 01446 self.SetX(x) 01447 self.SetY(y) 01448 self.SetPen(wx.BLACK_PEN) 01449 if text: 01450 self.AddText('(' + str(self.id) + ') ' + text) 01451 else: 01452 self.AddText('(' + str(self.id) + ')') 01453 01454 def GetName(self): 01455 """!Get name""" 01456 return _("if-else") 01457 01458 def GetWidth(self): 01459 """!Get object width""" 01460 return self.width 01461 01462 def GetHeight(self): 01463 """!Get object height""" 01464 return self.height 01465 01466 def SetItems(self, items, branch = 'if'): 01467 """!Set items (id) 01468 01469 @param items list of items 01470 @param branch 'if' / 'else' 01471 """ 01472 if branch in ['if', 'else']: 01473 self.items[branch] = items 01474 01475 class ProcessModelFile: 01476 """!Process GRASS model file (gxm)""" 01477 def __init__(self, tree): 01478 """!A ElementTree handler for the GXM XML file, as defined in 01479 grass-gxm.dtd. 01480 """ 01481 self.tree = tree 01482 self.root = self.tree.getroot() 01483 01484 # list of actions, data 01485 self.properties = dict() 01486 self.variables = dict() 01487 self.actions = list() 01488 self.data = list() 01489 self.loops = list() 01490 self.conditions = list() 01491 01492 self._processWindow() 01493 self._processProperties() 01494 self._processVariables() 01495 self._processItems() 01496 self._processData() 01497 01498 def _filterValue(self, value): 01499 """!Filter value 01500 01501 @param value 01502 """ 01503 value = value.replace('<', '<') 01504 value = value.replace('>', '>') 01505 01506 return value 01507 01508 def _getNodeText(self, node, tag, default = ''): 01509 """!Get node text""" 01510 p = node.find(tag) 01511 if p is not None: 01512 if p.text: 01513 return utils.normalize_whitespace(p.text) 01514 else: 01515 return '' 01516 01517 return default 01518 01519 def _processWindow(self): 01520 """!Process window properties""" 01521 node = self.root.find('window') 01522 if node is None: 01523 self.pos = self.size = None 01524 return 01525 01526 self.pos, self.size = self._getDim(node) 01527 01528 def _processProperties(self): 01529 """!Process model properties""" 01530 node = self.root.find('properties') 01531 if node is None: 01532 return 01533 for key in ('name', 'description', 'author'): 01534 self._processProperty(node, key) 01535 01536 for f in node.findall('flag'): 01537 name = f.get('name', '') 01538 if name == 'overwrite': 01539 self.properties['overwrite'] = True 01540 01541 def _processProperty(self, pnode, name): 01542 """!Process given property""" 01543 node = pnode.find(name) 01544 if node is not None: 01545 self.properties[name] = node.text 01546 else: 01547 self.properties[name] = '' 01548 01549 def _processVariables(self): 01550 """!Process model variables""" 01551 vnode = self.root.find('variables') 01552 if vnode is None: 01553 return 01554 for node in vnode.findall('variable'): 01555 name = node.get('name', '') 01556 if not name: 01557 continue # should not happen 01558 self.variables[name] = { 'type' : node.get('type', 'string') } 01559 for key in ('description', 'value'): 01560 self._processVariable(node, name, key) 01561 01562 def _processVariable(self, pnode, name, key): 01563 """!Process given variable""" 01564 node = pnode.find(key) 01565 if node is not None: 01566 if node.text: 01567 self.variables[name][key] = node.text 01568 01569 def _processItems(self): 01570 """!Process model items (actions, loops, conditions)""" 01571 self._processActions() 01572 self._processLoops() 01573 self._processConditions() 01574 01575 def _processActions(self): 01576 """!Process model file""" 01577 for action in self.root.findall('action'): 01578 pos, size = self._getDim(action) 01579 disabled = False 01580 01581 task = action.find('task') 01582 if task is not None: 01583 if task.find('disabled') is not None: 01584 disabled = True 01585 task = self._processTask(task) 01586 else: 01587 task = None 01588 01589 aId = int(action.get('id', -1)) 01590 01591 self.actions.append({ 'pos' : pos, 01592 'size' : size, 01593 'task' : task, 01594 'id' : aId, 01595 'disabled' : disabled }) 01596 01597 def _getDim(self, node): 01598 """!Get position and size of shape""" 01599 pos = size = None 01600 posAttr = node.get('pos', None) 01601 if posAttr: 01602 posVal = map(int, posAttr.split(',')) 01603 try: 01604 pos = (posVal[0], posVal[1]) 01605 except: 01606 pos = None 01607 01608 sizeAttr = node.get('size', None) 01609 if sizeAttr: 01610 sizeVal = map(int, sizeAttr.split(',')) 01611 try: 01612 size = (sizeVal[0], sizeVal[1]) 01613 except: 01614 size = None 01615 01616 return pos, size 01617 01618 def _processData(self): 01619 """!Process model file""" 01620 for data in self.root.findall('data'): 01621 pos, size = self._getDim(data) 01622 param = data.find('data-parameter') 01623 prompt = value = None 01624 if param is not None: 01625 prompt = param.get('prompt', None) 01626 value = self._filterValue(self._getNodeText(param, 'value')) 01627 01628 if data.find('intermediate') is None: 01629 intermediate = False 01630 else: 01631 intermediate = True 01632 01633 rels = list() 01634 for rel in data.findall('relation'): 01635 defrel = { 'id' : int(rel.get('id', -1)), 01636 'dir' : rel.get('dir', 'to'), 01637 'name' : rel.get('name', '') } 01638 points = list() 01639 for point in rel.findall('point'): 01640 x = self._filterValue(self._getNodeText(point, 'x')) 01641 y = self._filterValue(self._getNodeText(point, 'y')) 01642 points.append((float(x), float(y))) 01643 defrel['points'] = points 01644 rels.append(defrel) 01645 01646 self.data.append({ 'pos' : pos, 01647 'size': size, 01648 'prompt' : prompt, 01649 'value' : value, 01650 'intermediate' : intermediate, 01651 'rels' : rels }) 01652 01653 def _processTask(self, node): 01654 """!Process task 01655 01656 @return grassTask instance 01657 @return None on error 01658 """ 01659 cmd = list() 01660 parameterized = list() 01661 01662 name = node.get('name', None) 01663 if not name: 01664 return None 01665 01666 cmd.append(name) 01667 01668 # flags 01669 for f in node.findall('flag'): 01670 flag = f.get('name', '') 01671 if f.get('parameterized', '0') == '1': 01672 parameterized.append(('flag', flag)) 01673 if f.get('value', '1') == '0': 01674 continue 01675 if len(flag) > 1: 01676 cmd.append('--' + flag) 01677 else: 01678 cmd.append('-' + flag) 01679 # parameters 01680 for p in node.findall('parameter'): 01681 name = p.get('name', '') 01682 if p.find('parameterized') is not None: 01683 parameterized.append(('param', name)) 01684 cmd.append('%s=%s' % (name, 01685 self._filterValue(self._getNodeText(p, 'value')))) 01686 01687 task, err = GUI(show = None, checkError = True).ParseCommand(cmd = cmd) 01688 if err: 01689 GWarning(os.linesep.join(err)) 01690 01691 for opt, name in parameterized: 01692 if opt == 'flag': 01693 task.set_flag(name, True, element = 'parameterized') 01694 else: 01695 task.set_param(name, True, element = 'parameterized') 01696 01697 return task 01698 01699 def _processLoops(self): 01700 """!Process model loops""" 01701 for node in self.root.findall('loop'): 01702 pos, size = self._getDim(node) 01703 text = self._filterValue(self._getNodeText(node, 'condition')).strip() 01704 aid = list() 01705 for anode in node.findall('item'): 01706 try: 01707 aid.append(int(anode.text)) 01708 except ValueError: 01709 pass 01710 01711 self.loops.append({ 'pos' : pos, 01712 'size' : size, 01713 'text' : text, 01714 'id' : int(node.get('id', -1)), 01715 'items' : aid }) 01716 01717 def _processConditions(self): 01718 """!Process model conditions""" 01719 for node in self.root.findall('if-else'): 01720 pos, size = self._getDim(node) 01721 text = self._filterValue(self._getNodeText(node, 'condition')).strip() 01722 aid = { 'if' : list(), 01723 'else' : list() } 01724 for b in aid.keys(): 01725 bnode = node.find(b) 01726 if bnode is None: 01727 continue 01728 for anode in bnode.findall('item'): 01729 try: 01730 aid[b].append(int(anode.text)) 01731 except ValueError: 01732 pass 01733 01734 self.conditions.append({ 'pos' : pos, 01735 'size' : size, 01736 'text' : text, 01737 'id' : int(node.get('id', -1)), 01738 'items' : aid }) 01739 01740 class WriteModelFile: 01741 """!Generic class for writing model file""" 01742 def __init__(self, fd, model): 01743 self.fd = fd 01744 self.model = model 01745 self.properties = model.GetProperties() 01746 self.variables = model.GetVariables() 01747 self.items = model.GetItems() 01748 01749 self.indent = 0 01750 01751 self._header() 01752 01753 self._window() 01754 self._properties() 01755 self._variables() 01756 self._items() 01757 01758 dataList = list() 01759 for action in model.GetItems(objType = ModelAction): 01760 for rel in action.GetRelations(): 01761 dataItem = rel.GetData() 01762 if dataItem not in dataList: 01763 dataList.append(dataItem) 01764 self._data(dataList) 01765 01766 self._footer() 01767 01768 def _filterValue(self, value): 01769 """!Make value XML-valid""" 01770 value = value.replace('<', '<') 01771 value = value.replace('>', '>') 01772 01773 return value 01774 01775 def _header(self): 01776 """!Write header""" 01777 self.fd.write('<?xml version="1.0" encoding="UTF-8"?>\n') 01778 self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n') 01779 self.fd.write('%s<gxm>\n' % (' ' * self.indent)) 01780 self.indent += 4 01781 01782 def _footer(self): 01783 """!Write footer""" 01784 self.indent -= 4 01785 self.fd.write('%s</gxm>\n' % (' ' * self.indent)) 01786 01787 def _window(self): 01788 """!Write window properties""" 01789 canvas = self.model.GetCanvas() 01790 if canvas is None: 01791 return 01792 win = canvas.parent 01793 pos = win.GetPosition() 01794 size = win.GetSize() 01795 self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' % \ 01796 (' ' * self.indent, pos[0], pos[1], size[0], size[1])) 01797 01798 def _properties(self): 01799 """!Write model properties""" 01800 self.fd.write('%s<properties>\n' % (' ' * self.indent)) 01801 self.indent += 4 01802 if self.properties['name']: 01803 self.fd.write('%s<name>%s</name>\n' % (' ' * self.indent, self.properties['name'])) 01804 if self.properties['description']: 01805 self.fd.write('%s<description>%s</description>\n' % (' ' * self.indent, 01806 utils.EncodeString(self.properties['description']))) 01807 if self.properties['author']: 01808 self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent, 01809 utils.EncodeString(self.properties['author']))) 01810 01811 if 'overwrite' in self.properties and \ 01812 self.properties['overwrite']: 01813 self.fd.write('%s<flag name="overwrite" />\n' % (' ' * self.indent)) 01814 self.indent -= 4 01815 self.fd.write('%s</properties>\n' % (' ' * self.indent)) 01816 01817 def _variables(self): 01818 """!Write model variables""" 01819 if not self.variables: 01820 return 01821 self.fd.write('%s<variables>\n' % (' ' * self.indent)) 01822 self.indent += 4 01823 for name, values in self.variables.iteritems(): 01824 self.fd.write('%s<variable name="%s" type="%s">\n' % \ 01825 (' ' * self.indent, name, values['type'])) 01826 self.indent += 4 01827 if 'value' in values: 01828 self.fd.write('%s<value>%s</value>\n' % \ 01829 (' ' * self.indent, values['value'])) 01830 if 'description' in values: 01831 self.fd.write('%s<description>%s</description>\n' % \ 01832 (' ' * self.indent, values['description'])) 01833 self.indent -= 4 01834 self.fd.write('%s</variable>\n' % (' ' * self.indent)) 01835 self.indent -= 4 01836 self.fd.write('%s</variables>\n' % (' ' * self.indent)) 01837 01838 def _items(self): 01839 """!Write actions/loops/conditions""" 01840 for item in self.items: 01841 if isinstance(item, ModelAction): 01842 self._action(item) 01843 elif isinstance(item, ModelLoop): 01844 self._loop(item) 01845 elif isinstance(item, ModelCondition): 01846 self._condition(item) 01847 01848 def _action(self, action): 01849 """!Write actions""" 01850 self.fd.write('%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' % \ 01851 (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(), 01852 action.GetWidth(), action.GetHeight())) 01853 self.indent += 4 01854 self.fd.write('%s<task name="%s">\n' % (' ' * self.indent, action.GetLog(string = False)[0])) 01855 self.indent += 4 01856 if not action.IsEnabled(): 01857 self.fd.write('%s<disabled />\n' % (' ' * self.indent)) 01858 for key, val in action.GetParams().iteritems(): 01859 if key == 'flags': 01860 for f in val: 01861 if f.get('value', False) or f.get('parameterized', False): 01862 if f.get('parameterized', False): 01863 if f.get('value', False) == False: 01864 self.fd.write('%s<flag name="%s" value="0" parameterized="1" />\n' % 01865 (' ' * self.indent, f.get('name', ''))) 01866 else: 01867 self.fd.write('%s<flag name="%s" parameterized="1" />\n' % 01868 (' ' * self.indent, f.get('name', ''))) 01869 else: 01870 self.fd.write('%s<flag name="%s" />\n' % 01871 (' ' * self.indent, f.get('name', ''))) 01872 else: # parameter 01873 for p in val: 01874 if not p.get('value', '') and not p.get('parameterized', False): 01875 continue 01876 self.fd.write('%s<parameter name="%s">\n' % 01877 (' ' * self.indent, p.get('name', ''))) 01878 self.indent += 4 01879 if p.get('parameterized', False): 01880 self.fd.write('%s<parameterized />\n' % (' ' * self.indent)) 01881 self.fd.write('%s<value>%s</value>\n' % 01882 (' ' * self.indent, self._filterValue(p.get('value', '')))) 01883 self.indent -= 4 01884 self.fd.write('%s</parameter>\n' % (' ' * self.indent)) 01885 self.indent -= 4 01886 self.fd.write('%s</task>\n' % (' ' * self.indent)) 01887 self.indent -= 4 01888 self.fd.write('%s</action>\n' % (' ' * self.indent)) 01889 01890 def _data(self, dataList): 01891 """!Write data""" 01892 for data in dataList: 01893 self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' % \ 01894 (' ' * self.indent, data.GetX(), data.GetY(), 01895 data.GetWidth(), data.GetHeight())) 01896 self.indent += 4 01897 self.fd.write('%s<data-parameter prompt="%s">\n' % \ 01898 (' ' * self.indent, data.GetPrompt())) 01899 self.indent += 4 01900 self.fd.write('%s<value>%s</value>\n' % 01901 (' ' * self.indent, self._filterValue(data.GetValue()))) 01902 self.indent -= 4 01903 self.fd.write('%s</data-parameter>\n' % (' ' * self.indent)) 01904 01905 if data.IsIntermediate(): 01906 self.fd.write('%s<intermediate />\n' % (' ' * self.indent)) 01907 01908 # relations 01909 for ft in ('from', 'to'): 01910 for rel in data.GetRelations(ft): 01911 if ft == 'from': 01912 aid = rel.GetTo().GetId() 01913 else: 01914 aid = rel.GetFrom().GetId() 01915 self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' % \ 01916 (' ' * self.indent, ft, aid, rel.GetName())) 01917 self.indent += 4 01918 for point in rel.GetLineControlPoints()[1:-1]: 01919 self.fd.write('%s<point>\n' % (' ' * self.indent)) 01920 self.indent += 4 01921 x, y = point.Get() 01922 self.fd.write('%s<x>%d</x>\n' % (' ' * self.indent, int(x))) 01923 self.fd.write('%s<y>%d</y>\n' % (' ' * self.indent, int(y))) 01924 self.indent -= 4 01925 self.fd.write('%s</point>\n' % (' ' * self.indent)) 01926 self.indent -= 4 01927 self.fd.write('%s</relation>\n' % (' ' * self.indent)) 01928 01929 self.indent -= 4 01930 self.fd.write('%s</data>\n' % (' ' * self.indent)) 01931 01932 def _loop(self, loop): 01933 """!Write loops""" 01934 self.fd.write('%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' % \ 01935 (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(), 01936 loop.GetWidth(), loop.GetHeight())) 01937 text = loop.GetText() 01938 self.indent += 4 01939 if text: 01940 self.fd.write('%s<condition>%s</condition>\n' % 01941 (' ' * self.indent, self._filterValue(text))) 01942 for item in loop.GetItems(): 01943 self.fd.write('%s<item>%d</item>\n' % 01944 (' ' * self.indent, item.GetId())) 01945 self.indent -= 4 01946 self.fd.write('%s</loop>\n' % (' ' * self.indent)) 01947 01948 def _condition(self, condition): 01949 """!Write conditions""" 01950 bbox = condition.GetBoundingBoxMin() 01951 self.fd.write('%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' % \ 01952 (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(), 01953 bbox[0], bbox[1])) 01954 text = condition.GetText() 01955 self.indent += 4 01956 if text: 01957 self.fd.write('%s<condition>%s</condition>\n' % 01958 (' ' * self.indent, self._filterValue(text))) 01959 items = condition.GetItems() 01960 for b in items.keys(): 01961 if len(items[b]) < 1: 01962 continue 01963 self.fd.write('%s<%s>\n' % (' ' * self.indent, b)) 01964 self.indent += 4 01965 for item in items[b]: 01966 self.fd.write('%s<item>%d</item>\n' % 01967 (' ' * self.indent, item.GetId())) 01968 self.indent -= 4 01969 self.fd.write('%s</%s>\n' % (' ' * self.indent, b)) 01970 01971 self.indent -= 4 01972 self.fd.write('%s</if-else>\n' % (' ' * self.indent)) 01973 01974 class WritePythonFile: 01975 def __init__(self, fd, model): 01976 """!Class for exporting model to Python script 01977 01978 @param fd file desciptor 01979 """ 01980 self.fd = fd 01981 self.model = model 01982 self.indent = 4 01983 01984 self._writePython() 01985 01986 def _writePython(self): 01987 """!Write model to file""" 01988 properties = self.model.GetProperties() 01989 01990 self.fd.write( 01991 r"""#!/usr/bin/env python 01992 # 01993 ############################################################################ 01994 # 01995 # MODULE: %s 01996 # 01997 # AUTHOR(S): %s 01998 # 01999 # PURPOSE: %s 02000 # 02001 # DATE: %s 02002 # 02003 ############################################################################# 02004 """ % (properties['name'], 02005 properties['author'], 02006 properties['description'], 02007 time.asctime())) 02008 02009 self.fd.write( 02010 r""" 02011 import sys 02012 import os 02013 import atexit 02014 02015 import grass.script as grass 02016 """) 02017 02018 # cleanup() 02019 rast, vect, rast3d, msg = self.model.GetIntermediateData() 02020 self.fd.write( 02021 r""" 02022 def cleanup(): 02023 """) 02024 if rast: 02025 self.fd.write( 02026 r""" grass.run_command('g.remove', 02027 rast=%s) 02028 """ % ','.join(map(lambda x: "'" + x + "'", rast))) 02029 if vect: 02030 self.fd.write( 02031 r""" grass.run_command('g.remove', 02032 vect = %s) 02033 """ % ','.join(map(lambda x: "'" + x + "'", vect))) 02034 if rast3d: 02035 self.fd.write( 02036 r""" grass.run_command('g.remove', 02037 rast3d = %s) 02038 """ % ','.join(map(lambda x: "'" + x + "'", rast3d))) 02039 if not rast and not vect and not rast3d: 02040 self.fd.write(' pass\n') 02041 02042 self.fd.write("\ndef main():\n") 02043 for item in self.model.GetItems(): 02044 self._writePythonItem(item) 02045 02046 self.fd.write("\n return 0\n") 02047 02048 self.fd.write( 02049 r""" 02050 if __name__ == "__main__": 02051 options, flags = grass.parser() 02052 atexit.register(cleanup) 02053 sys.exit(main()) 02054 """) 02055 02056 def _writePythonItem(self, item, ignoreBlock = True, variables = []): 02057 """!Write model object to Python file""" 02058 if isinstance(item, ModelAction): 02059 if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions 02060 return 02061 self._writePythonAction(item, variables = variables) 02062 elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition): 02063 # substitute condition 02064 variables = self.model.GetVariables() 02065 cond = item.GetText() 02066 for variable in variables: 02067 pattern = re.compile('%' + variable) 02068 if pattern.search(cond): 02069 value = variables[variable].get('value', '') 02070 if variables[variable].get('type', 'string') == 'string': 02071 value = '"' + value + '"' 02072 cond = pattern.sub(value, cond) 02073 if isinstance(item, ModelLoop): 02074 condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond)) 02075 cond = "%sfor %s in " % (' ' * self.indent, condVar) 02076 if condText[0] == '`' and condText[-1] == '`': 02077 task = GUI(show = None).ParseCommand(cmd = utils.split(condText[1:-1])) 02078 cond += "grass.read_command(" 02079 cond += self._getPythonActionCmd(task, len(cond), variables = [condVar]) + ".splitlines()" 02080 else: 02081 cond += condText 02082 self.fd.write('%s:\n' % cond) 02083 self.indent += 4 02084 for action in item.GetItems(): 02085 self._writePythonItem(action, ignoreBlock = False, variables = [condVar]) 02086 self.indent -= 4 02087 else: # ModelCondition 02088 self.fd.write('%sif %s:\n' % (' ' * self.indent, cond)) 02089 self.indent += 4 02090 condItems = item.GetItems() 02091 for action in condItems['if']: 02092 self._writePythonItem(action, ignoreBlock = False) 02093 if condItems['else']: 02094 self.indent -= 4 02095 self.fd.write('%selse:\n' % (' ' * self.indent)) 02096 self.indent += 4 02097 for action in condItems['else']: 02098 self._writePythonItem(action, ignoreBlock = False) 02099 self.indent += 4 02100 02101 def _writePythonAction(self, item, variables = []): 02102 """!Write model action to Python file""" 02103 task = GUI(show = None).ParseCommand(cmd = item.GetLog(string = False)) 02104 strcmd = "%sgrass.run_command(" % (' ' * self.indent) 02105 self.fd.write(strcmd + self._getPythonActionCmd(task, len(strcmd), variables) + '\n') 02106 02107 def _getPythonActionCmd(self, task, cmdIndent, variables = []): 02108 opts = task.get_options() 02109 02110 ret = '' 02111 flags = '' 02112 params = list() 02113 02114 for f in opts['flags']: 02115 if f.get('value', False): 02116 name = f.get('name', '') 02117 if len(name) > 1: 02118 params.append('%s = True' % name) 02119 else: 02120 flags += name 02121 02122 for p in opts['params']: 02123 name = p.get('name', None) 02124 value = p.get('value', None) 02125 if name and value: 02126 ptype = p.get('type', 'string') 02127 if value[0] == '%': 02128 params.append("%s = %s" % (name, value[1:])) 02129 elif ptype == 'string': 02130 params.append('%s = "%s"' % (name, value)) 02131 else: 02132 params.append("%s = %s" % (name, value)) 02133 02134 ret += '"%s"' % task.get_name() 02135 if flags: 02136 ret += ",\n%sflags = '%s'" % (' ' * cmdIndent, flags) 02137 if len(params) > 0: 02138 ret += ",\n" 02139 for opt in params[:-1]: 02140 ret += "%s%s,\n" % (' ' * cmdIndent, opt) 02141 ret += "%s%s)" % (' ' * cmdIndent, params[-1]) 02142 else: 02143 ret += ")" 02144 02145 return ret 02146 02147 class ModelParamDialog(wx.Dialog): 02148 def __init__(self, parent, params, id = wx.ID_ANY, title = _("Model parameters"), 02149 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): 02150 """!Model parameters dialog 02151 """ 02152 self.parent = parent 02153 self.params = params 02154 self.tasks = list() # list of tasks/pages 02155 02156 wx.Dialog.__init__(self, parent = parent, id = id, title = title, style = style, **kwargs) 02157 02158 self.notebook = GNotebook(parent = self, 02159 style = globalvar.FNPageDStyle) 02160 02161 panel = self._createPages() 02162 wx.CallAfter(self.notebook.SetSelection, 0) 02163 02164 # intermediate data? 02165 self.interData = wx.CheckBox(parent = self, label = _("Delete intermediate data when finish")) 02166 self.interData.SetValue(True) 02167 rast, vect, rast3d, msg = self.parent.GetModel().GetIntermediateData() 02168 if not rast and not vect and not rast3d: 02169 self.interData.Hide() 02170 02171 self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL) 02172 self.btnRun = wx.Button(parent = self, id = wx.ID_OK, 02173 label = _("&Run")) 02174 self.btnRun.SetDefault() 02175 02176 self._layout() 02177 02178 size = self.GetBestSize() 02179 self.SetMinSize(size) 02180 self.SetSize((size.width, size.height + 02181 panel.constrained_size[1] - 02182 panel.panelMinHeight)) 02183 02184 def _layout(self): 02185 btnSizer = wx.StdDialogButtonSizer() 02186 btnSizer.AddButton(self.btnCancel) 02187 btnSizer.AddButton(self.btnRun) 02188 btnSizer.Realize() 02189 02190 mainSizer = wx.BoxSizer(wx.VERTICAL) 02191 mainSizer.Add(item = self.notebook, proportion = 1, 02192 flag = wx.EXPAND) 02193 if self.interData.IsShown(): 02194 mainSizer.Add(item = self.interData, proportion = 0, 02195 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) 02196 02197 mainSizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY, 02198 style = wx.LI_HORIZONTAL), 02199 proportion = 0, 02200 flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5) 02201 02202 mainSizer.Add(item = btnSizer, proportion = 0, 02203 flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) 02204 02205 self.SetSizer(mainSizer) 02206 mainSizer.Fit(self) 02207 02208 def _createPages(self): 02209 """!Create for each parameterized module its own page""" 02210 nameOrdered = [''] * len(self.params.keys()) 02211 for name, params in self.params.iteritems(): 02212 nameOrdered[params['idx']] = name 02213 for name in nameOrdered: 02214 params = self.params[name] 02215 panel = self._createPage(name, params) 02216 if name == 'variables': 02217 name = _('Variables') 02218 self.notebook.AddPage(page = panel, text = name) 02219 02220 return panel 02221 02222 def _createPage(self, name, params): 02223 """!Define notebook page""" 02224 if name in globalvar.grassCmd: 02225 task = gtask.grassTask(name) 02226 else: 02227 task = gtask.grassTask() 02228 task.flags = params['flags'] 02229 task.params = params['params'] 02230 02231 panel = CmdPanel(parent = self, id = wx.ID_ANY, task = task) 02232 self.tasks.append(task) 02233 02234 return panel 02235 02236 def GetErrors(self): 02237 """!Check for errors, get list of messages""" 02238 errList = list() 02239 for task in self.tasks: 02240 errList += task.get_cmd_error() 02241 02242 return errList 02243 02244 def DeleteIntermediateData(self): 02245 """!Check if to detele intermediate data""" 02246 if self.interData.IsShown() and self.interData.IsChecked(): 02247 return True 02248 02249 return False