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