"""! @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 import mimetypes 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 gmodeler.model_file import ProcessModelFile from core.utils import CmdToTuple from core.settings import UserSettings from gui_core.forms import GUI 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 = 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): ? vlist = list() if condText[0] == '`' and condText[-1] == '`': # run command cmd, dcmd = 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