"""! @package gmodeler.model @brief wxGUI Graphical Modeler (base classes) Classes: - model::Model - model::ModelObject - model::ModelAction - model::ModelData - model::ModelRelation - model::ModelItem - model::ModelLoop - model::ModelCondition (C) 2010-2011 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Martin Landa """ import os import getpass import copy import re try: import xml.etree.ElementTree as etree except ImportError: import elementtree.ElementTree as etree # Python <= 2.4 import wx from wx.lib import ogl from core.globalvar import ETCWXDIR from core.gcmd import GMessage, GException, GError, RunCommand from gmodeler.dialogs import ModelParamDialog from grass.script import core as grass from grass.script import task as gtask class Model(object): """!Class representing the model""" def __init__(self, canvas = None): self.items = list() # list of actions/loops/... # model properties self.properties = { 'name' : _("model"), 'description' : _("Script generated by wxGUI Graphical Modeler."), 'author' : getpass.getuser() } # model variables self.variables = dict() self.variablesParams = dict() self.canvas = canvas def GetCanvas(self): """!Get canvas or None""" return self.canvas def GetItems(self, objType = None): """!Get list of model items @param objType Object type to filter model objects """ if not objType: return self.items result = list() for item in self.items: if isinstance(item, objType): result.append(item) return result def GetItem(self, aId): """!Get item of given id @param aId item id @return Model* instance @return None if no item found """ ilist = self.GetItems() for item in ilist: if item.GetId() == aId: return item return None def GetNumItems(self, actionOnly = False): """!Get number of items""" if actionOnly: return len(self.GetItems(objType = ModelAction)) return len(self.GetItems()) def GetNextId(self): """!Get next id (data ignored) @return next id to be used (default: 1) """ if len(self.items) < 1: return 1 currId = self.items[-1].GetId() if currId > 0: return currId + 1 return 1 def GetProperties(self): """!Get model properties""" return self.properties def GetVariables(self, params = False): """!Get model variables""" if params: return self.variablesParams return self.variables def SetVariables(self, data): """!Set model variables""" self.variables = data def Reset(self): """!Reset model""" self.items = list() def RemoveItem(self, item): """!Remove item from model @return list of related items to remove/update """ relList = list() upList = list() if not isinstance(item, ModelData): self.items.remove(item) if isinstance(item, ModelAction): for rel in item.GetRelations(): relList.append(rel) data = rel.GetData() if len(data.GetRelations()) < 2: relList.append(data) else: upList.append(data) elif isinstance(item, ModelData): for rel in item.GetRelations(): relList.append(rel) if rel.GetFrom() == self: relList.append(rel.GetTo()) else: relList.append(rel.GetFrom()) elif isinstance(item, ModelLoop): for rel in item.GetRelations(): relList.append(rel) for action in self.GetItems(): action.UnSetBlock(item) return relList, upList def FindAction(self, aId): """!Find action by id""" alist = self.GetItems(objType = ModelAction) for action in alist: if action.GetId() == aId: return action return None def GetData(self): """!Get list of data items""" result = list() dataItems = self.GetItems(objType = ModelData) for action in self.GetItems(objType = ModelAction): for rel in action.GetRelations(): dataItem = rel.GetData() if dataItem not in result: result.append(dataItem) if dataItem in dataItems: dataItems.remove(dataItem) # standalone data if dataItems: result += dataItems return result def FindData(self, value, prompt): """!Find data item in the model @param value value @param prompt prompt @return ModelData instance @return None if not found """ for data in self.GetData(): if data.GetValue() == value and \ data.GetPrompt() == prompt: return data return None def LoadModel(self, filename): """!Load model definition stored in GRASS Model XML file (gxm) @todo Validate against DTD Raise exception on error. """ dtdFilename = os.path.join(ETCWXDIR, "xml", "grass-gxm.dtd") # parse workspace file try: gxmXml = ProcessModelFile(etree.parse(filename)) except StandardError, e: raise GException(e) if self.canvas: win = self.canvas.parent if gxmXml.pos: win.SetPosition(gxmXml.pos) if gxmXml.size: win.SetSize(gxmXml.size) # load properties self.properties = gxmXml.properties self.variables = gxmXml.variables # load model.GetActions() for action in gxmXml.actions: actionItem = ModelAction(parent = self, x = action['pos'][0], y = action['pos'][1], width = action['size'][0], height = action['size'][1], task = action['task'], id = action['id']) if action['disabled']: actionItem.Enable(False) self.AddItem(actionItem) actionItem.SetValid(actionItem.GetTask().get_options()) actionItem.GetLog() # substitute variables (-> valid/invalid) # load data & relations for data in gxmXml.data: dataItem = ModelData(parent = self, x = data['pos'][0], y = data['pos'][1], width = data['size'][0], height = data['size'][1], prompt = data['prompt'], value = data['value']) dataItem.SetIntermediate(data['intermediate']) for rel in data['rels']: actionItem = self.FindAction(rel['id']) if rel['dir'] == 'from': relation = ModelRelation(parent = self, fromShape = dataItem, toShape = actionItem, param = rel['name']) else: relation = ModelRelation(parent = self, fromShape = actionItem, toShape = dataItem, param = rel['name']) relation.SetControlPoints(rel['points']) actionItem.AddRelation(relation) dataItem.AddRelation(relation) if self.canvas: dataItem.Update() # load loops for loop in gxmXml.loops: loopItem = ModelLoop(parent = self, x = loop['pos'][0], y = loop['pos'][1], width = loop['size'][0], height = loop['size'][1], text = loop['text'], id = loop['id']) self.AddItem(loopItem) # load conditions for condition in gxmXml.conditions: conditionItem = ModelCondition(parent = self, x = condition['pos'][0], y = condition['pos'][1], width = condition['size'][0], height = condition['size'][1], text = condition['text'], id = condition['id']) self.AddItem(conditionItem) # define loops & if/else items for loop in gxmXml.loops: alist = list() for aId in loop['items']: action = self.GetItem(aId) alist.append(action) loopItem = self.GetItem(loop['id']) loopItem.SetItems(alist) for action in loopItem.GetItems(): action.SetBlock(loopItem) for condition in gxmXml.conditions: conditionItem = self.GetItem(condition['id']) for b in condition['items'].keys(): alist = list() for aId in condition['items'][b]: action = self.GetItem(aId) alist.append(action) conditionItem.SetItems(alist, branch = b) items = conditionItem.GetItems() for b in items.keys(): for action in items[b]: action.SetBlock(conditionItem) def AddItem(self, newItem): """!Add item to the list""" iId = newItem.GetId() i = 0 for item in self.items: if item.GetId() > iId: self.items.insert(i, newItem) return i += 1 self.items.append(newItem) def IsValid(self): """Return True if model is valid""" if self.Validate(): return False return True def Validate(self): """!Validate model, return None if model is valid otherwise error string""" errList = list() variables = self.GetVariables().keys() pattern = re.compile(r'(.*)(%.+\s?)(.*)') for action in self.GetItems(objType = ModelAction): cmd = action.GetLog(string = False) task = menuform.GUI(show = None).ParseCommand(cmd = cmd) errList += map(lambda x: cmd[0] + ': ' + x, task.get_cmd_error()) # check also variables for opt in cmd[1:]: if '=' not in opt: continue key, value = opt.split('=', 1) sval = pattern.search(value) if sval: var = sval.group(2).strip()[1:] # ignore '%' if var not in variables: report = True for item in filter(lambda x: isinstance(x, ModelLoop), action.GetBlock()): if var in item.GetText(): report = False break if report: errList.append(_("%s: undefined variable '%s'") % (cmd[0], var)) ### TODO: check variables in file only optionally ### errList += self._substituteFile(action, checkOnly = True) return errList def _substituteFile(self, item, params = None, checkOnly = False): """!Subsitute variables in command file inputs @param checkOnly tuble - True to check variable, don't touch files @return list of undefined variables """ errList = list() self.fileInput = dict() # collect ascii inputs for p in item.GetParams()['params']: if p.get('element', '') == 'file' and \ p.get('prompt', '') == 'input' and \ p.get('age', '') == 'old': filename = p.get('value', p.get('default', '')) if filename and \ mimetypes.guess_type(filename)[0] == 'text/plain': self.fileInput[filename] = None for finput in self.fileInput: # read lines fd = open(finput, "r") try: data = self.fileInput[finput] = fd.read() finally: fd.close() # substitute variables write = False variables = self.GetVariables() for variable in variables: pattern = re.compile('%' + variable) value = '' if params and 'variables' in params: for p in params['variables']['params']: if variable == p.get('name', ''): if p.get('type', 'string') == 'string': value = p.get('value', '') else: value = str(p.get('value', '')) break if not value: value = variables[variable].get('value', '') data = pattern.sub(value, data) if not checkOnly: write = True pattern = re.compile(r'(.*)(%.+\s?)(.*)') sval = pattern.search(data) if sval: var = sval.group(2).strip()[1:] # ignore '%' cmd = item.GetLog(string = False)[0] errList.append(_("%s: undefined variable '%s'") % (cmd, var)) if not checkOnly: if write: fd = open(finput, "w") try: fd.write(data) finally: fd.close() else: self.fileInput[finput] = None return errList def OnPrepare(self, item, params): self._substituteFile(item, params, checkOnly = False) def RunAction(self, item, params, log, onDone, onPrepare = None, statusbar = None): """!Run given action @param item action item @param params parameters dict @param log logging window @param onDone on-done method @param onPrepare on-prepare method @param statusbar wx.StatusBar instance or None """ name = item.GetName() if name in params: paramsOrig = item.GetParams(dcopy = True) item.MergeParams(params[name]) if statusbar: statusbar.SetStatusText(_('Running model...'), 0) data = { 'item' : item, 'params' : copy.deepcopy(params) } log.RunCmd(command = item.GetLog(string = False, substitute = params), onDone = onDone, onPrepare = self.OnPrepare, userData = data) if name in params: item.SetParams(paramsOrig) def Run(self, log, onDone, parent = None): """!Run model @param log logging window (see goutput.GMConsole) @param onDone on-done method @param parent window for messages or None """ if self.GetNumItems() < 1: GMessage(parent = parent, message = _('Model is empty. Nothing to run.')) return statusbar = None if isinstance(parent, wx.Frame): statusbar = parent.GetStatusBar() # validation if statusbar: statusbar.SetStatusText(_('Validating model...'), 0) errList = self.Validate() if statusbar: statusbar.SetStatusText('', 0) if errList: dlg = wx.MessageDialog(parent = parent, message = _('Model is not valid. Do you want to ' 'run the model anyway?\n\n%s') % '\n'.join(errList), caption = _("Run model?"), style = wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_YES: return # parametrization params = self.Parameterize() if params: dlg = ModelParamDialog(parent = parent, params = params) dlg.CenterOnParent() ret = dlg.ShowModal() if ret != wx.ID_OK: dlg.Destroy() return err = dlg.GetErrors() dlg.Destroy() if err: GError(parent = parent, message = unicode('\n'.join(err))) return err = list() for key, item in params.iteritems(): for p in item['params']: if p.get('value', '') == '': err.append((key, p.get('name', ''), p.get('description', ''))) if err: GError(parent = parent, message = _("Variables below not defined:") + \ "\n\n" + unicode('\n'.join(map(lambda x: "%s: %s (%s)" % (x[0], x[1], x[2]), err)))) return log.cmdThread.SetId(-1) for item in self.GetItems(): if not item.IsEnabled(): continue if isinstance(item, ModelAction): if item.GetBlockId(): continue self.RunAction(item, params, log, onDone) elif isinstance(item, ModelLoop): cond = item.GetText() # substitute variables in condition variables = self.GetVariables() for variable in variables: pattern = re.compile('%' + variable) if pattern.search(cond): value = '' if params and 'variables' in params: for p in params['variables']['params']: if variable == p.get('name', ''): value = p.get('value', '') break if not value: value = variables[variable].get('value', '') if not value: continue vtype = variables[variable].get('type', 'string') if vtype == 'string': value = '"' + value + '"' cond = pattern.sub(value, cond) # split condition condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond)) pattern = re.compile('%' + condVar) ### for vars()[condVar] in eval(condText): ? if condText[0] == '`' and condText[-1] == '`': # run command cmd, dcmd = utils.CmdToTuple(condText[1:-1].split(' ')) ret = RunCommand(cmd, read = True, **dcmd) if ret: vlist = ret.splitlines() else: vlist = eval(condText) if 'variables' not in params: params['variables'] = { 'params' : [] } varDict = { 'name' : condVar, 'value' : '' } params['variables']['params'].append(varDict) for var in vlist: for action in item.GetItems(): if not isinstance(action, ModelAction) or \ not action.IsEnabled(): continue varDict['value'] = var self.RunAction(item = action, params = params, log = log, onDone = onDone) params['variables']['params'].remove(varDict) # discard values if params: for item in params.itervalues(): for p in item['params']: p['value'] = '' def DeleteIntermediateData(self, log): """!Detele intermediate data""" rast, vect, rast3d, msg = self.GetIntermediateData() if rast: log.RunCmd(['g.remove', 'rast=%s' %','.join(rast)]) if rast3d: log.RunCmd(['g.remove', 'rast3d=%s' %','.join(rast3d)]) if vect: log.RunCmd(['g.remove', 'vect=%s' %','.join(vect)]) def GetIntermediateData(self): """!Get info about intermediate data""" rast = list() rast3d = list() vect = list() for data in self.GetData(): if not data.IsIntermediate(): continue name = data.GetValue() prompt = data.GetPrompt() if prompt == 'raster': rast.append(name) elif prompt == 'vector': vect.append(name) elif prompt == 'rast3d': rast3d.append(name) msg = '' if rast: msg += '\n\n%s: ' % _('Raster maps') msg += ', '.join(rast) if rast3d: msg += '\n\n%s: ' % _('3D raster maps') msg += ', '.join(rast3d) if vect: msg += '\n\n%s: ' % _('Vector maps') msg += ', '.join(vect) return rast, vect, rast3d, msg def Update(self): """!Update model""" for item in self.items: item.Update() def IsParameterized(self): """!Return True if model is parameterized""" if self.Parameterize(): return True return False def Parameterize(self): """!Return parameterized options""" result = dict() idx = 0 if self.variables: params = list() result["variables"] = { 'flags' : list(), 'params' : params, 'idx' : idx } for name, values in self.variables.iteritems(): gtype = values.get('type', 'string') if gtype in ('raster', 'vector', 'mapset', 'file'): gisprompt = True prompt = gtype if gtype == 'raster': element = 'cell' else: element = gtype ptype = 'string' else: gisprompt = False prompt = None element = None ptype = gtype params.append({ 'gisprompt' : gisprompt, 'multiple' : False, 'description' : values.get('description', ''), 'guidependency' : '', 'default' : '', 'age' : None, 'required' : True, 'value' : values.get('value', ''), 'label' : '', 'guisection' : '', 'key_desc' : '', 'values' : list(), 'parameterized' : False, 'values_desc' : list(), 'prompt' : prompt, 'element' : element, 'type' : ptype, 'name' : name }) idx += 1 for action in self.GetItems(objType = ModelAction): if not action.IsEnabled(): continue name = action.GetName() params = action.GetParams() for f in params['flags']: if f.get('parameterized', False): if name not in result: result[name] = { 'flags' : list(), 'params': list(), 'idx' : idx } result[name]['flags'].append(f) for p in params['params']: if p.get('parameterized', False): if name not in result: result[name] = { 'flags' : list(), 'params': list(), 'idx' : idx } result[name]['params'].append(p) if name in result: idx += 1 self.variablesParams = result # record parameters return result class ModelObject: def __init__(self, id = -1): self.id = id self.rels = list() # list of ModelRelations self.isEnabled = True self.inBlock = list() # list of related loops/conditions def __del__(self): pass def GetId(self): """!Get id""" return self.id def AddRelation(self, rel): """!Record new relation """ self.rels.append(rel) def GetRelations(self, fdir = None): """!Get list of relations @param fdir True for 'from' """ if fdir is None: return self.rels result = list() for rel in self.rels: if fdir == 'from': if rel.GetFrom() == self: result.append(rel) else: if rel.GetTo() == self: result.append(rel) return result def IsEnabled(self): """!Get True if action is enabled, otherwise False""" return self.isEnabled def Enable(self, enabled = True): """!Enable/disable action""" self.isEnabled = enabled self.Update() def Update(self): pass def SetBlock(self, item): """!Add object to the block (loop/condition) @param item reference to ModelLoop or ModelCondition which defines loops/condition """ if item not in self.inBlock: self.inBlock.append(item) def UnSetBlock(self, item): """!Remove object from the block (loop/consition) @param item reference to ModelLoop or ModelCondition which defines loops/codition """ if item in self.inBlock: self.inBlock.remove(item) def GetBlock(self): """!Get list of related ModelObject(s) which defines block (loop/condition) @return list of ModelObjects """ return self.inBlock def GetBlockId(self): """!Get list of related ids which defines block @return list of ids """ ret = list() for mo in self.inBlock: ret.append(mo.GetId()) return ret class ModelAction(ModelObject, ogl.RectangleShape): """!Action class (GRASS module)""" def __init__(self, parent, x, y, id = -1, cmd = None, task = None, width = None, height = None): ModelObject.__init__(self, id) self.parent = parent self.task = task if not width: width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width')) if not height: height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height')) if cmd: self.task = GUI(show = None).ParseCommand(cmd = cmd) else: if task: self.task = task else: self.task = None self.propWin = None self.data = list() # list of connected data items self.isValid = False self.isParameterized = False if self.parent.GetCanvas(): ogl.RectangleShape.__init__(self, width, height) self.SetCanvas(self.parent) self.SetX(x) self.SetY(y) self.SetPen(wx.BLACK_PEN) self._setPen() self._setBrush() self.SetId(id) if self.task: self.SetValid(self.task.get_options()) def _setBrush(self, running = False): """!Set brush""" if running: color = UserSettings.Get(group='modeler', key='action', subkey=('color', 'running')) elif not self.isEnabled: color = UserSettings.Get(group='modeler', key='disabled', subkey='color') elif self.isValid: color = UserSettings.Get(group='modeler', key='action', subkey=('color', 'valid')) else: color = UserSettings.Get(group='modeler', key='action', subkey=('color', 'invalid')) wxColor = wx.Color(color[0], color[1], color[2]) self.SetBrush(wx.Brush(wxColor)) def _setPen(self): """!Set pen""" if self.isParameterized: width = int(UserSettings.Get(group='modeler', key='action', subkey=('width', 'parameterized'))) else: width = int(UserSettings.Get(group='modeler', key='action', subkey=('width', 'default'))) pen = self.GetPen() pen.SetWidth(width) self.SetPen(pen) def SetId(self, id): """!Set id""" self.id = id cmd = self.task.get_cmd(ignoreErrors = True) if cmd and len(cmd) > 0: self.ClearText() self.AddText('(%d) %s' % (self.id, cmd[0])) else: self.AddText('(%d) <<%s>>' % (self.id, _("unknown"))) def SetProperties(self, params, propwin): """!Record properties dialog""" self.task.params = params['params'] self.task.flags = params['flags'] self.propWin = propwin def GetPropDialog(self): """!Get properties dialog""" return self.propWin def GetLog(self, string = True, substitute = None): """!Get logging info @param string True to get cmd as a string otherwise a list @param substitute dictionary of parameter to substitute or None """ cmd = self.task.get_cmd(ignoreErrors = True, ignoreRequired = True, ignoreDefault = False) # substitute variables if substitute: variables = [] if 'variables' in substitute: for p in substitute['variables']['params']: variables.append(p.get('name', '')) else: variables = self.parent.GetVariables() for variable in variables: pattern= re.compile('%' + variable) value = '' if substitute and 'variables' in substitute: for p in substitute['variables']['params']: if variable == p.get('name', ''): if p.get('type', 'string') == 'string': value = p.get('value', '') else: value = str(p.get('value', '')) break if not value: value = variables[variable].get('value', '') if not value: continue for idx in range(len(cmd)): if pattern.search(cmd[idx]): cmd[idx] = pattern.sub(value, cmd[idx]) break idx += 1 if string: if cmd is None: return '' else: return ' '.join(cmd) return cmd def GetName(self): """!Get name""" cmd = self.task.get_cmd(ignoreErrors = True) if cmd and len(cmd) > 0: return cmd[0] return _('unknown') def GetParams(self, dcopy = False): """!Get dictionary of parameters""" if dcopy: return copy.deepcopy(self.task.get_options()) return self.task.get_options() def GetTask(self): """!Get grassTask instance""" return self.task def SetParams(self, params): """!Set dictionary of parameters""" self.task.params = params['params'] self.task.flags = params['flags'] def MergeParams(self, params): """!Merge dictionary of parameters""" if 'flags' in params: for f in params['flags']: self.task.set_flag(f['name'], f.get('value', False)) if 'params' in params: for p in params['params']: self.task.set_param(p['name'], p.get('value', '')) def SetValid(self, options): """!Set validity for action @param options dictionary with flags and params (gtask) """ self.isValid = True self.isParameterized = False for f in options['flags']: if f.get('parameterized', False): self.IsParameterized = True break for p in options['params']: if self.isValid and p.get('required', False) and \ p.get('value', '') == '' and \ p.get('default', '') == '': self.isValid = False if not self.isParameterized and p.get('parameterized', False): self.isParameterized = True if self.parent.GetCanvas(): self._setBrush() self._setPen() def IsValid(self): """!Check validity (all required parameters set)""" return self.isValid def IsParameterized(self): """!Check if action is parameterized""" return self.isParameterized def FindData(self, name): """!Find data item by name""" for rel in self.GetRelations(): data = rel.GetData() if name == rel.GetName() and name in data.GetName(): return data return None def Update(self, running = False): """!Update action""" if running: self._setBrush(running = True) else: self._setBrush() self._setPen() def OnDraw(self, dc): """!Draw action in canvas""" self._setBrush() self._setPen() ogl.RectangleShape.OnDraw(self, dc) class ModelData(ModelObject, ogl.EllipseShape): def __init__(self, parent, x, y, value = '', prompt = '', width = None, height = None): """Data item class @param parent window parent @param x, y position of the shape @param fname, tname list of parameter names from / to @param value value @param prompt type of GIS element @param width,height dimension of the shape """ ModelObject.__init__(self) self.parent = parent self.value = value self.prompt = prompt self.intermediate = False self.propWin = None if not width: width = UserSettings.Get(group='modeler', key='data', subkey=('size', 'width')) if not height: height = UserSettings.Get(group='modeler', key='data', subkey=('size', 'height')) if self.parent.GetCanvas(): ogl.EllipseShape.__init__(self, width, height) self.SetCanvas(self.parent) self.SetX(x) self.SetY(y) self.SetPen(wx.BLACK_PEN) self._setBrush() self._setText() def IsIntermediate(self): """!Checks if data item is intermediate""" return self.intermediate def SetIntermediate(self, im): """!Set intermediate flag""" self.intermediate = im def OnDraw(self, dc): pen = self.GetPen() pen.SetWidth(1) if self.intermediate: pen.SetStyle(wx.SHORT_DASH) else: pen.SetStyle(wx.SOLID) self.SetPen(pen) ogl.EllipseShape.OnDraw(self, dc) def GetLog(self, string = True): """!Get logging info""" name = list() for rel in self.GetRelations(): name.append(rel.GetName()) if name: return '/'.join(name) + '=' + self.value + ' (' + self.prompt + ')' else: return self.value + ' (' + self.prompt + ')' def GetName(self): """!Get list of names""" name = list() for rel in self.GetRelations(): name.append(rel.GetName()) return name def GetPrompt(self): """!Get prompt""" return self.prompt def SetPrompt(self, prompt): """!Set prompt @param prompt """ self.prompt = prompt def GetValue(self): """!Get value""" return self.value def SetValue(self, value): """!Set value @param value """ self.value = value self._setText() for direction in ('from', 'to'): for rel in self.GetRelations(direction): if direction == 'from': action = rel.GetTo() else: action = rel.GetFrom() task = GUI(show = None).ParseCommand(cmd = action.GetLog(string = False)) task.set_param(rel.GetName(), self.value) action.SetParams(params = task.get_options()) def GetPropDialog(self): """!Get properties dialog""" return self.propWin def SetPropDialog(self, win): """!Get properties dialog""" self.propWin = win def _setBrush(self): """!Set brush""" if self.prompt == 'raster': color = UserSettings.Get(group = 'modeler', key = 'data', subkey = ('color', 'raster')) elif self.prompt == 'raster3d': color = UserSettings.Get(group = 'modeler', key = 'data', subkey = ('color', 'raster3d')) elif self.prompt == 'vector': color = UserSettings.Get(group = 'modeler', key = 'data', subkey = ('color', 'vector')) else: color = UserSettings.Get(group = 'modeler', key = 'action', subkey = ('color', 'invalid')) wxColor = wx.Color(color[0], color[1], color[2]) self.SetBrush(wx.Brush(wxColor)) def _setPen(self): """!Set pen""" isParameterized = False for rel in self.GetRelations('from'): if rel.GetTo().IsParameterized(): isParameterized = True break if not isParameterized: for rel in self.GetRelations('to'): if rel.GetFrom().IsParameterized(): isParameterized = True break if isParameterized: width = int(UserSettings.Get(group = 'modeler', key = 'action', subkey = ('width', 'parameterized'))) else: width = int(UserSettings.Get(group = 'modeler', key = 'action', subkey = ('width', 'default'))) pen = self.GetPen() pen.SetWidth(width) self.SetPen(pen) def _setText(self): """!Update text""" self.ClearText() name = [] for rel in self.GetRelations(): name.append(rel.GetName()) self.AddText('/'.join(name)) if self.value: self.AddText(self.value) else: self.AddText(_('')) def Update(self): """!Update action""" self._setBrush() self._setPen() self._setText() class ModelRelation(ogl.LineShape): """!Data - action relation""" def __init__(self, parent, fromShape, toShape, param = ''): self.fromShape = fromShape self.toShape = toShape self.param = param self.parent = parent self._points = None if self.parent.GetCanvas(): ogl.LineShape.__init__(self) def __del__(self): if self in self.fromShape.rels: self.fromShape.rels.remove(self) if self in self.toShape.rels: self.toShape.rels.remove(self) def GetFrom(self): """!Get id of 'from' shape""" return self.fromShape def GetTo(self): """!Get id of 'to' shape""" return self.toShape def GetData(self): """!Get related ModelData instance @return ModelData instance @return None if not found """ if isinstance(self.fromShape, ModelData): return self.fromShape elif isinstance(self.toShape, ModelData): return self.toShape return None def GetName(self): """!Get parameter name""" return self.param def ResetShapes(self): """!Reset related objects""" self.fromShape.ResetControlPoints() self.toShape.ResetControlPoints() self.ResetControlPoints() def SetControlPoints(self, points): """!Set control points""" self._points = points def GetControlPoints(self): """!Get list of control points""" return self._points def _setPen(self): """!Set pen""" pen = self.GetPen() pen.SetWidth(1) pen.SetStyle(wx.SOLID) self.SetPen(pen) def OnDraw(self, dc): """!Draw relation""" self._setPen() ogl.LineShape.OnDraw(self, dc) def SetName(self, param): self.param = param class ModelItem(ModelObject): def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []): """!Abstract class for loops and conditions""" ModelObject.__init__(self, id) self.parent = parent self.text = text self.items = items # list of items in the loop def GetText(self): """!Get loop text""" return self.text def GetItems(self): """!Get items (id)""" return self.items def SetId(self, id): """!Set loop id""" self.id = id def SetText(self, cond): """!Set loop text (condition)""" self.text = cond self.ClearText() self.AddText('(' + str(self.id) + ') ' + self.text) def GetLog(self): """!Get log info""" if self.text: return _("Condition: ") + self.text else: return _("Condition: not defined") def AddRelation(self, rel): """!Record relation""" self.rels.append(rel) def Clear(self): """!Clear object, remove rels""" self.rels = list() class ModelLoop(ModelItem, ogl.RectangleShape): def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = []): """!Defines a loop""" ModelItem.__init__(self, parent, x, y, id, width, height, text, items) if not width: width = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'width')) if not height: height = UserSettings.Get(group='modeler', key='loop', subkey=('size', 'height')) if self.parent.GetCanvas(): ogl.RectangleShape.__init__(self, width, height) self.SetCanvas(self.parent) self.SetX(x) self.SetY(y) self.SetPen(wx.BLACK_PEN) self.SetCornerRadius(100) if text: self.AddText('(' + str(self.id) + ') ' + text) else: self.AddText('(' + str(self.id) + ')') self._setBrush() def _setBrush(self): """!Set brush""" if not self.isEnabled: color = UserSettings.Get(group='modeler', key='disabled', subkey='color') else: color = UserSettings.Get(group='modeler', key='loop', subkey=('color', 'valid')) wxColor = wx.Color(color[0], color[1], color[2]) self.SetBrush(wx.Brush(wxColor)) def Enable(self, enabled = True): """!Enable/disable action""" for item in self.items: if not isinstance(item, ModelAction): continue item.Enable(enabled) ModelObject.Enable(self, enabled) def Update(self): self._setBrush() def GetName(self): """!Get name""" return _("loop") def SetItems(self, items): """!Set items (id)""" self.items = items class ModelCondition(ModelItem, ogl.PolygonShape): def __init__(self, parent, x, y, id = -1, width = None, height = None, text = '', items = { 'if' : [], 'else' : [] }): """!Defines a if-else condition""" ModelItem.__init__(self, parent, x, y, id, width, height, text, items) if not width: self.width = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'width')) else: self.width = width if not height: self.height = UserSettings.Get(group='modeler', key='if-else', subkey=('size', 'height')) else: self.height = height if self.parent.GetCanvas(): ogl.PolygonShape.__init__(self) points = [(0, - self.height / 2), (self.width / 2, 0), (0, self.height / 2), (- self.width / 2, 0)] self.Create(points) self.SetCanvas(self.parent) self.SetX(x) self.SetY(y) self.SetPen(wx.BLACK_PEN) if text: self.AddText('(' + str(self.id) + ') ' + text) else: self.AddText('(' + str(self.id) + ')') def GetName(self): """!Get name""" return _("if-else") def GetWidth(self): """!Get object width""" return self.width def GetHeight(self): """!Get object height""" return self.height def SetItems(self, items, branch = 'if'): """!Set items (id) @param items list of items @param branch 'if' / 'else' """ if branch in ['if', 'else']: self.items[branch] = items