"""! @package gmodeler.model @brief wxGUI Graphical Modeler (base classes & read/write) Classes: - model::Model - model::ModelObject - model::ModelAction - model::ModelData - model::ModelRelation - model::ModelItem - model::ModelLoop - model::ModelCondition - model::ProcessModelFile - model::WriteModelFile - model::WritePythonFile - model::ModelParamDialog (C) 2010-2012 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 import time 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 import globalvar from core import utils from core.gcmd import GMessage, GException, GError, RunCommand, EncodeString, GWarning from core.settings import UserSettings from gui_core.forms import GUI, CmdPanel from gui_core.widgets import GNotebook 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(globalvar.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(cmd[0] + ": " + _("undefined variable '%s'") % 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(cmd + ": " + _("undefined variable '%s'") % 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() delInterData = False if params: dlg = ModelParamDialog(parent = parent, params = params) dlg.CenterOnParent() ret = dlg.ShowModal() if ret != wx.ID_OK: dlg.Destroy() return err = dlg.GetErrors() delInterData = dlg.DeleteIntermediateData() 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 = 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) if delInterData: self.DeleteIntermediateData(log) # 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(object): 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.Recentre(self, dc) # re-center text 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 def OnDraw(self, dc): """!Draw loop in canvas""" self._setBrush() ogl.RectangleShape.Recentre(self, dc) # re-center text ogl.RectangleShape.OnDraw(self, dc) 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 class ProcessModelFile: """!Process GRASS model file (gxm)""" def __init__(self, tree): """!A ElementTree handler for the GXM XML file, as defined in grass-gxm.dtd. """ self.tree = tree self.root = self.tree.getroot() # list of actions, data self.properties = dict() self.variables = dict() self.actions = list() self.data = list() self.loops = list() self.conditions = list() self._processWindow() self._processProperties() self._processVariables() self._processItems() self._processData() def _filterValue(self, value): """!Filter value @param value """ value = value.replace('<', '<') value = value.replace('>', '>') return value def _getNodeText(self, node, tag, default = ''): """!Get node text""" p = node.find(tag) if p is not None: if p.text: return utils.normalize_whitespace(p.text) else: return '' return default def _processWindow(self): """!Process window properties""" node = self.root.find('window') if node is None: self.pos = self.size = None return self.pos, self.size = self._getDim(node) def _processProperties(self): """!Process model properties""" node = self.root.find('properties') if node is None: return for key in ('name', 'description', 'author'): self._processProperty(node, key) for f in node.findall('flag'): name = f.get('name', '') if name == 'overwrite': self.properties['overwrite'] = True def _processProperty(self, pnode, name): """!Process given property""" node = pnode.find(name) if node is not None: self.properties[name] = node.text else: self.properties[name] = '' def _processVariables(self): """!Process model variables""" vnode = self.root.find('variables') if vnode is None: return for node in vnode.findall('variable'): name = node.get('name', '') if not name: continue # should not happen self.variables[name] = { 'type' : node.get('type', 'string') } for key in ('description', 'value'): self._processVariable(node, name, key) def _processVariable(self, pnode, name, key): """!Process given variable""" node = pnode.find(key) if node is not None: if node.text: self.variables[name][key] = node.text def _processItems(self): """!Process model items (actions, loops, conditions)""" self._processActions() self._processLoops() self._processConditions() def _processActions(self): """!Process model file""" for action in self.root.findall('action'): pos, size = self._getDim(action) disabled = False task = action.find('task') if task is not None: if task.find('disabled') is not None: disabled = True task = self._processTask(task) else: task = None aId = int(action.get('id', -1)) self.actions.append({ 'pos' : pos, 'size' : size, 'task' : task, 'id' : aId, 'disabled' : disabled }) def _getDim(self, node): """!Get position and size of shape""" pos = size = None posAttr = node.get('pos', None) if posAttr: posVal = map(int, posAttr.split(',')) try: pos = (posVal[0], posVal[1]) except: pos = None sizeAttr = node.get('size', None) if sizeAttr: sizeVal = map(int, sizeAttr.split(',')) try: size = (sizeVal[0], sizeVal[1]) except: size = None return pos, size def _processData(self): """!Process model file""" for data in self.root.findall('data'): pos, size = self._getDim(data) param = data.find('data-parameter') prompt = value = None if param is not None: prompt = param.get('prompt', None) value = self._filterValue(self._getNodeText(param, 'value')) if data.find('intermediate') is None: intermediate = False else: intermediate = True rels = list() for rel in data.findall('relation'): defrel = { 'id' : int(rel.get('id', -1)), 'dir' : rel.get('dir', 'to'), 'name' : rel.get('name', '') } points = list() for point in rel.findall('point'): x = self._filterValue(self._getNodeText(point, 'x')) y = self._filterValue(self._getNodeText(point, 'y')) points.append((float(x), float(y))) defrel['points'] = points rels.append(defrel) self.data.append({ 'pos' : pos, 'size': size, 'prompt' : prompt, 'value' : value, 'intermediate' : intermediate, 'rels' : rels }) def _processTask(self, node): """!Process task @return grassTask instance @return None on error """ cmd = list() parameterized = list() name = node.get('name', None) if not name: return None cmd.append(name) # flags for f in node.findall('flag'): flag = f.get('name', '') if f.get('parameterized', '0') == '1': parameterized.append(('flag', flag)) if f.get('value', '1') == '0': continue if len(flag) > 1: cmd.append('--' + flag) else: cmd.append('-' + flag) # parameters for p in node.findall('parameter'): name = p.get('name', '') if p.find('parameterized') is not None: parameterized.append(('param', name)) cmd.append('%s=%s' % (name, self._filterValue(self._getNodeText(p, 'value')))) task, err = GUI(show = None, checkError = True).ParseCommand(cmd = cmd) if err: GWarning(os.linesep.join(err)) for opt, name in parameterized: if opt == 'flag': task.set_flag(name, True, element = 'parameterized') else: task.set_param(name, True, element = 'parameterized') return task def _processLoops(self): """!Process model loops""" for node in self.root.findall('loop'): pos, size = self._getDim(node) text = self._filterValue(self._getNodeText(node, 'condition')).strip() aid = list() for anode in node.findall('item'): try: aid.append(int(anode.text)) except ValueError: pass self.loops.append({ 'pos' : pos, 'size' : size, 'text' : text, 'id' : int(node.get('id', -1)), 'items' : aid }) def _processConditions(self): """!Process model conditions""" for node in self.root.findall('if-else'): pos, size = self._getDim(node) text = self._filterValue(self._getNodeText(node, 'condition')).strip() aid = { 'if' : list(), 'else' : list() } for b in aid.keys(): bnode = node.find(b) if bnode is None: continue for anode in bnode.findall('item'): try: aid[b].append(int(anode.text)) except ValueError: pass self.conditions.append({ 'pos' : pos, 'size' : size, 'text' : text, 'id' : int(node.get('id', -1)), 'items' : aid }) class WriteModelFile: """!Generic class for writing model file""" def __init__(self, fd, model): self.fd = fd self.model = model self.properties = model.GetProperties() self.variables = model.GetVariables() self.items = model.GetItems() self.indent = 0 self._header() self._window() self._properties() self._variables() self._items() dataList = list() for action in model.GetItems(objType = ModelAction): for rel in action.GetRelations(): dataItem = rel.GetData() if dataItem not in dataList: dataList.append(dataItem) self._data(dataList) self._footer() def _filterValue(self, value): """!Make value XML-valid""" value = value.replace('<', '<') value = value.replace('>', '>') return value def _header(self): """!Write header""" self.fd.write('\n') self.fd.write('\n') self.fd.write('%s\n' % (' ' * self.indent)) self.indent += 4 def _footer(self): """!Write footer""" self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) def _window(self): """!Write window properties""" canvas = self.model.GetCanvas() if canvas is None: return win = canvas.parent pos = win.GetPosition() size = win.GetSize() self.fd.write('%s\n' % \ (' ' * self.indent, pos[0], pos[1], size[0], size[1])) def _properties(self): """!Write model properties""" self.fd.write('%s\n' % (' ' * self.indent)) self.indent += 4 if self.properties['name']: self.fd.write('%s%s\n' % (' ' * self.indent, self.properties['name'])) if self.properties['description']: self.fd.write('%s%s\n' % (' ' * self.indent, EncodeString(self.properties['description']))) if self.properties['author']: self.fd.write('%s%s\n' % (' ' * self.indent, EncodeString(self.properties['author']))) if 'overwrite' in self.properties and \ self.properties['overwrite']: self.fd.write('%s\n' % (' ' * self.indent)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) def _variables(self): """!Write model variables""" if not self.variables: return self.fd.write('%s\n' % (' ' * self.indent)) self.indent += 4 for name, values in self.variables.iteritems(): self.fd.write('%s\n' % \ (' ' * self.indent, name, values['type'])) self.indent += 4 if 'value' in values: self.fd.write('%s%s\n' % \ (' ' * self.indent, values['value'])) if 'description' in values: self.fd.write('%s%s\n' % \ (' ' * self.indent, values['description'])) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) def _items(self): """!Write actions/loops/conditions""" for item in self.items: if isinstance(item, ModelAction): self._action(item) elif isinstance(item, ModelLoop): self._loop(item) elif isinstance(item, ModelCondition): self._condition(item) def _action(self, action): """!Write actions""" self.fd.write('%s\n' % \ (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(), action.GetWidth(), action.GetHeight())) self.indent += 4 self.fd.write('%s\n' % (' ' * self.indent, action.GetLog(string = False)[0])) self.indent += 4 if not action.IsEnabled(): self.fd.write('%s\n' % (' ' * self.indent)) for key, val in action.GetParams().iteritems(): if key == 'flags': for f in val: if f.get('value', False) or f.get('parameterized', False): if f.get('parameterized', False): if f.get('value', False) == False: self.fd.write('%s\n' % (' ' * self.indent, f.get('name', ''))) else: self.fd.write('%s\n' % (' ' * self.indent, f.get('name', ''))) else: self.fd.write('%s\n' % (' ' * self.indent, f.get('name', ''))) else: # parameter for p in val: if not p.get('value', '') and not p.get('parameterized', False): continue self.fd.write('%s\n' % (' ' * self.indent, p.get('name', ''))) self.indent += 4 if p.get('parameterized', False): self.fd.write('%s\n' % (' ' * self.indent)) self.fd.write('%s%s\n' % (' ' * self.indent, self._filterValue(p.get('value', '')))) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) def _data(self, dataList): """!Write data""" for data in dataList: self.fd.write('%s\n' % \ (' ' * self.indent, data.GetX(), data.GetY(), data.GetWidth(), data.GetHeight())) self.indent += 4 self.fd.write('%s\n' % \ (' ' * self.indent, data.GetPrompt())) self.indent += 4 self.fd.write('%s%s\n' % (' ' * self.indent, self._filterValue(data.GetValue()))) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) if data.IsIntermediate(): self.fd.write('%s\n' % (' ' * self.indent)) # relations for ft in ('from', 'to'): for rel in data.GetRelations(ft): if ft == 'from': aid = rel.GetTo().GetId() else: aid = rel.GetFrom().GetId() self.fd.write('%s\n' % \ (' ' * self.indent, ft, aid, rel.GetName())) self.indent += 4 for point in rel.GetLineControlPoints()[1:-1]: self.fd.write('%s\n' % (' ' * self.indent)) self.indent += 4 x, y = point.Get() self.fd.write('%s%d\n' % (' ' * self.indent, int(x))) self.fd.write('%s%d\n' % (' ' * self.indent, int(y))) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) def _loop(self, loop): """!Write loops""" self.fd.write('%s\n' % \ (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(), loop.GetWidth(), loop.GetHeight())) text = loop.GetText() self.indent += 4 if text: self.fd.write('%s%s\n' % (' ' * self.indent, self._filterValue(text))) for item in loop.GetItems(): self.fd.write('%s%d\n' % (' ' * self.indent, item.GetId())) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) def _condition(self, condition): """!Write conditions""" bbox = condition.GetBoundingBoxMin() self.fd.write('%s\n' % \ (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(), bbox[0], bbox[1])) text = condition.GetText() self.indent += 4 if text: self.fd.write('%s%s\n' % (' ' * self.indent, self._filterValue(text))) items = condition.GetItems() for b in items.keys(): if len(items[b]) < 1: continue self.fd.write('%s<%s>\n' % (' ' * self.indent, b)) self.indent += 4 for item in items[b]: self.fd.write('%s%d\n' % (' ' * self.indent, item.GetId())) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent, b)) self.indent -= 4 self.fd.write('%s\n' % (' ' * self.indent)) class WritePythonFile: def __init__(self, fd, model): """!Class for exporting model to Python script @param fd file desciptor """ self.fd = fd self.model = model self.indent = 4 self._writePython() def _writePython(self): """!Write model to file""" properties = self.model.GetProperties() self.fd.write( r"""#!/usr/bin/env python # #%s # # MODULE: %s # # AUTHOR(S): %s # # PURPOSE: %s # # DATE: %s # #%s """ % ('#' * 79, properties['name'], EncodeString(properties['author']), EncodeString('\n# '.join(properties['description'].splitlines())), time.asctime(), '#' * 79)) self.fd.write( r""" import sys import os import atexit import grass.script as grass """) # cleanup() rast, vect, rast3d, msg = self.model.GetIntermediateData() self.fd.write( r""" def cleanup(): """) if rast: self.fd.write( r""" grass.run_command('g.remove', rast=%s) """ % ','.join(map(lambda x: "'" + x + "'", rast))) if vect: self.fd.write( r""" grass.run_command('g.remove', vect = %s) """ % ','.join(map(lambda x: "'" + x + "'", vect))) if rast3d: self.fd.write( r""" grass.run_command('g.remove', rast3d = %s) """ % ','.join(map(lambda x: "'" + x + "'", rast3d))) if not rast and not vect and not rast3d: self.fd.write(' pass\n') self.fd.write("\ndef main():\n") for item in self.model.GetItems(): self._writePythonItem(item) self.fd.write("\n return 0\n") self.fd.write( r""" if __name__ == "__main__": options, flags = grass.parser() atexit.register(cleanup) sys.exit(main()) """) def _writePythonItem(self, item, ignoreBlock = True, variables = []): """!Write model object to Python file""" if isinstance(item, ModelAction): if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions return self._writePythonAction(item, variables = variables) elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition): # substitute condition variables = self.model.GetVariables() cond = item.GetText() for variable in variables: pattern = re.compile('%' + variable) if pattern.search(cond): value = variables[variable].get('value', '') if variables[variable].get('type', 'string') == 'string': value = '"' + value + '"' cond = pattern.sub(value, cond) if isinstance(item, ModelLoop): condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond)) cond = "%sfor %s in " % (' ' * self.indent, condVar) if condText[0] == '`' and condText[-1] == '`': task = GUI(show = None).ParseCommand(cmd = utils.split(condText[1:-1])) cond += "grass.read_command(" cond += self._getPythonActionCmd(task, len(cond), variables = [condVar]) + ".splitlines()" else: cond += condText self.fd.write('%s:\n' % cond) self.indent += 4 for action in item.GetItems(): self._writePythonItem(action, ignoreBlock = False, variables = [condVar]) self.indent -= 4 else: # ModelCondition self.fd.write('%sif %s:\n' % (' ' * self.indent, cond)) self.indent += 4 condItems = item.GetItems() for action in condItems['if']: self._writePythonItem(action, ignoreBlock = False) if condItems['else']: self.indent -= 4 self.fd.write('%selse:\n' % (' ' * self.indent)) self.indent += 4 for action in condItems['else']: self._writePythonItem(action, ignoreBlock = False) self.indent += 4 def _writePythonAction(self, item, variables = []): """!Write model action to Python file""" task = GUI(show = None).ParseCommand(cmd = item.GetLog(string = False, substitute = self.model.GetVariables())) strcmd = "%sgrass.run_command(" % (' ' * self.indent) self.fd.write(strcmd + self._getPythonActionCmd(task, len(strcmd), variables) + '\n') def _getPythonActionCmd(self, task, cmdIndent, variables = []): opts = task.get_options() ret = '' flags = '' params = list() for f in opts['flags']: if f.get('value', False): name = f.get('name', '') if len(name) > 1: params.append('%s = True' % name) else: flags += name for p in opts['params']: name = p.get('name', None) value = p.get('value', None) if name and value: ptype = p.get('type', 'string') if value[0] == '%': params.append("%s = %s" % (name, value[1:])) elif ptype == 'string': params.append('%s = "%s"' % (name, value)) else: params.append("%s = %s" % (name, value)) ret += '"%s"' % task.get_name() if flags: ret += ",\n%sflags = '%s'" % (' ' * cmdIndent, flags) if len(params) > 0: ret += ",\n" for opt in params[:-1]: ret += "%s%s,\n" % (' ' * cmdIndent, opt) ret += "%s%s)" % (' ' * cmdIndent, params[-1]) else: ret += ")" return ret class ModelParamDialog(wx.Dialog): def __init__(self, parent, params, id = wx.ID_ANY, title = _("Model parameters"), style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs): """!Model parameters dialog """ self.parent = parent self.params = params self.tasks = list() # list of tasks/pages wx.Dialog.__init__(self, parent = parent, id = id, title = title, style = style, **kwargs) self.notebook = GNotebook(parent = self, style = globalvar.FNPageDStyle) panel = self._createPages() wx.CallAfter(self.notebook.SetSelection, 0) # intermediate data? self.interData = wx.CheckBox(parent = self, label = _("Delete intermediate data when finish")) self.interData.SetValue(True) rast, vect, rast3d, msg = self.parent.GetModel().GetIntermediateData() if not rast and not vect and not rast3d: self.interData.Hide() self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL) self.btnRun = wx.Button(parent = self, id = wx.ID_OK, label = _("&Run")) self.btnRun.SetDefault() self._layout() size = self.GetBestSize() self.SetMinSize(size) self.SetSize((size.width, size.height + panel.constrained_size[1] - panel.panelMinHeight)) def _layout(self): btnSizer = wx.StdDialogButtonSizer() btnSizer.AddButton(self.btnCancel) btnSizer.AddButton(self.btnRun) btnSizer.Realize() mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND) if self.interData.IsShown(): mainSizer.Add(item = self.interData, proportion = 0, flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) mainSizer.Add(item = wx.StaticLine(parent = self, id = wx.ID_ANY, style = wx.LI_HORIZONTAL), proportion = 0, flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5) mainSizer.Add(item = btnSizer, proportion = 0, flag = wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border = 5) self.SetSizer(mainSizer) mainSizer.Fit(self) def _createPages(self): """!Create for each parameterized module its own page""" nameOrdered = [''] * len(self.params.keys()) for name, params in self.params.iteritems(): nameOrdered[params['idx']] = name for name in nameOrdered: params = self.params[name] panel = self._createPage(name, params) if name == 'variables': name = _('Variables') self.notebook.AddPage(page = panel, text = name) return panel def _createPage(self, name, params): """!Define notebook page""" if name in globalvar.grassCmd: task = gtask.grassTask(name) else: task = gtask.grassTask() task.flags = params['flags'] task.params = params['params'] panel = CmdPanel(parent = self, id = wx.ID_ANY, task = task) self.tasks.append(task) return panel def GetErrors(self): """!Check for errors, get list of messages""" errList = list() for task in self.tasks: errList += task.get_cmd_error() return errList def DeleteIntermediateData(self): """!Check if to detele intermediate data""" if self.interData.IsShown() and self.interData.IsChecked(): return True return False