瀏覽代碼

wxGUI/modeler: parametrization implemented (including model file
settings)


git-svn-id: https://svn.osgeo.org/grass/grass/trunk@42272 15284696-431f-4ddb-bdfa-cd5b030d7da7

Martin Landa 15 年之前
父節點
當前提交
4cb6b91ce2
共有 3 個文件被更改,包括 360 次插入119 次删除
  1. 5 0
      gui/wxpython/gui_modules/ghelp.py
  2. 263 63
      gui/wxpython/gui_modules/gmodeler.py
  3. 92 56
      gui/wxpython/gui_modules/menuform.py

+ 5 - 0
gui/wxpython/gui_modules/ghelp.py

@@ -195,6 +195,11 @@ class SearchModuleWindow(wx.Panel):
         desc = self.cmdPrompt.GetCommandDesc(cmd)
         desc = self.cmdPrompt.GetCommandDesc(cmd)
         if self.showTip:
         if self.showTip:
             self.searchTip.SetLabel(desc)
             self.searchTip.SetLabel(desc)
+    
+    def Reset(self):
+        """!Reset widget"""
+        self.searchBy.SetSelection(0)
+        self.search.SetValue('')
         
         
 class MenuTreeWindow(wx.Panel):
 class MenuTreeWindow(wx.Panel):
     """!Show menu tree"""
     """!Show menu tree"""

+ 263 - 63
gui/wxpython/gui_modules/gmodeler.py

@@ -16,7 +16,8 @@ Classes:
  - WriteModelFile
  - WriteModelFile
  - PreferencesDialog
  - PreferencesDialog
  - PropertiesDialog
  - PropertiesDialog
- 
+ - ModelParamDialog
+
 (C) 2010 by the GRASS Development Team
 (C) 2010 by the GRASS Development Team
 This program is free software under the GNU General Public License
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
 (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -32,6 +33,8 @@ import traceback
 import getpass
 import getpass
 import stat
 import stat
 import textwrap
 import textwrap
+import tempfile
+import copy
 
 
 try:
 try:
     import xml.etree.ElementTree as etree
     import xml.etree.ElementTree as etree
@@ -133,7 +136,7 @@ class Model(object):
                                      y = action['pos'][1],
                                      y = action['pos'][1],
                                      width = action['size'][0],
                                      width = action['size'][0],
                                      height = action['size'][1],
                                      height = action['size'][1],
-                                     cmd = action['cmd'])
+                                     task = action['task'])
             actionItem.SetId(action['id'])
             actionItem.SetId(action['id'])
 
 
             self.actions.append(actionItem)
             self.actions.append(actionItem)
@@ -243,6 +246,34 @@ class Model(object):
         
         
         for data in self.data:
         for data in self.data:
             data.Update()
             data.Update()
+
+    def IsParametrized(self):
+        """!Return True if model is parametrized"""
+        if self.Parametrize():
+            return True
+        
+        return False
+    
+    def Parametrize(self):
+        """!Return parametrized options"""
+        result = dict()
+        for action in self.actions:
+            name   = action.GetName()
+            params = action.GetParams()
+            for f in params['flags']:
+                if f.get('parametrized', False):
+                    if not result.has_key(name):
+                        result[name] = { 'flags' : list(),
+                                         'params': list() }
+                    result[name]['flags'].append(f)
+            for p in params['params']:
+                if p.get('parametrized', False):
+                    if not result.has_key(name):
+                        result[name] = { 'flags' : list(),
+                                         'params': list() }
+                    result[name]['params'].append(p)
+        
+        return result
     
     
 class ModelFrame(wx.Frame):
 class ModelFrame(wx.Frame):
     def __init__(self, parent, id = wx.ID_ANY,
     def __init__(self, parent, id = wx.ID_ANY,
@@ -571,11 +602,33 @@ class ModelFrame(wx.Frame):
             if ret != wx.ID_YES:
             if ret != wx.ID_YES:
                 return
                 return
         
         
+        params = self.model.Parametrize()
+        if params:
+            dlg = ModelParamDialog(parent = self,
+                                   params = params)
+            dlg.CenterOnParent()
+            
+            ret = dlg.ShowModal()
+            if ret != wx.ID_OK:
+                dlg.Destroy()
+                return
+        
         self.goutput.cmdThread.SetId(-1)
         self.goutput.cmdThread.SetId(-1)
         for action in self.model.GetActions():
         for action in self.model.GetActions():
+            name = action.GetName()
+            if params.has_key(name):
+                paramsOrig = action.GetParams(dcopy = True)
+                action.MergeParams(params[name])
+            
             self.SetStatusText(_('Running model...'), 0) 
             self.SetStatusText(_('Running model...'), 0) 
             self.goutput.RunCmd(command = action.GetLog(string = False),
             self.goutput.RunCmd(command = action.GetLog(string = False),
                                 onDone = self.OnDone)
                                 onDone = self.OnDone)
+            
+            if params.has_key(name):
+                action.SetParams(paramsOrig)
+        
+        if params:
+            dlg.Destroy()
         
         
     def OnDone(self, returncode):
     def OnDone(self, returncode):
         """!Computation finished"""
         """!Computation finished"""
@@ -836,12 +889,13 @@ if __name__ == "__main__":
         
         
         # show properties dialog
         # show properties dialog
         win = action.GetPropDialog()
         win = action.GetPropDialog()
-        if not win:
+        if not win and action.GetLog(string = False):
             module = menuform.GUI().ParseCommand(action.GetLog(string = False),
             module = menuform.GUI().ParseCommand(action.GetLog(string = False),
                                                  completed = (self.GetOptData, action, action.GetParams()),
                                                  completed = (self.GetOptData, action, action.GetParams()),
                                                  parentframe = self, show = True)
                                                  parentframe = self, show = True)
-        elif not win.IsShown():
+        elif win and not win.IsShown():
             win.Show()
             win.Show()
+        
         if win:
         if win:
             win.Raise()
             win.Raise()
 
 
@@ -949,7 +1003,7 @@ if __name__ == "__main__":
             self.canvas.Refresh()
             self.canvas.Refresh()
         
         
         if dcmd:
         if dcmd:
-            layer.SetProperties(dcmd, params, propwin)
+            layer.SetProperties(params, propwin)
             
             
         self.SetStatusText(layer.GetLog(), 0)
         self.SetStatusText(layer.GetLog(), 0)
         
         
@@ -1009,13 +1063,24 @@ if __name__ == "__main__":
         self.canvas.Refresh(True)
         self.canvas.Refresh(True)
         
         
     def WriteModelFile(self, filename):
     def WriteModelFile(self, filename):
-        """!Save model to model file
+        """!Save model to model file, recover original file on error.
         
         
         @return True on success
         @return True on success
         @return False on failure
         @return False on failure
         """
         """
+        tmpfile = tempfile.TemporaryFile(mode='w+b')
+        try:
+            WriteModelFile(fd = tmpfile, actions = self.model.GetActions(), data = self.model.GetData())
+        except StandardError:
+            GMessage(parent = self,
+                     message = _("Writing current settings to model file failed."))
+            return False
+        
         try:
         try:
-            file = open(filename, "w")
+            mfile = open(filename, "w")
+            tmpfile.seek(0)
+            for line in tmpfile.readlines():
+                mfile.write(line)
         except IOError:
         except IOError:
             wx.MessageBox(parent = self,
             wx.MessageBox(parent = self,
                           message = _("Unable to open file <%s> for writing.") % filename,
                           message = _("Unable to open file <%s> for writing.") % filename,
@@ -1023,17 +1088,7 @@ if __name__ == "__main__":
                           style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
                           style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
             return False
             return False
         
         
-        try:
-            WriteModelFile(fd = file, actions = self.model.GetActions(), data = self.model.GetData())
-        except StandardError:
-            file.close()
-            
-            GMessage(parent = self,
-                     message = _("Writing current settings to model file failed."))
-            
-            return False
-        
-        file.close()
+        mfile.close()
         
         
         return True
         return True
     
     
@@ -1074,20 +1129,23 @@ class ModelCanvas(ogl.ShapeCanvas):
         
         
 class ModelAction(ogl.RectangleShape):
 class ModelAction(ogl.RectangleShape):
     """!Action class (GRASS module)"""
     """!Action class (GRASS module)"""
-    def __init__(self, parent, x, y, cmd = None, width = None, height = None):
+    def __init__(self, parent, x, y, cmd = None, task = None, width = None, height = None):
         self.parent  = parent
         self.parent  = parent
-        self.cmd     = cmd
+        self.task    = task
         if not width:
         if not width:
             width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width'))
             width = UserSettings.Get(group='modeler', key='action', subkey=('size', 'width'))
         if not height:
         if not height:
             height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height'))
             height = UserSettings.Get(group='modeler', key='action', subkey=('size', 'height'))
         
         
-        if self.cmd:
-            task = menuform.GUI().ParseCommand(cmd = self.cmd,
-                                               show = None)
-            self.params = task.get_options()
+        if cmd:
+            self.task = menuform.GUI().ParseCommand(cmd = self.cmd,
+                                                    show = None)
         else:
         else:
-            self.params  = None
+            if task:
+                self.task = task
+            else:
+                self.task = None
+        
         self.propWin = None
         self.propWin = None
         self.id      = -1    # used for gxm file
         self.id      = -1    # used for gxm file
         
         
@@ -1103,10 +1161,11 @@ class ModelAction(ogl.RectangleShape):
             self.SetY(y)
             self.SetY(y)
             self.SetPen(wx.BLACK_PEN)
             self.SetPen(wx.BLACK_PEN)
             self._setBrush(False)
             self._setBrush(False)
-            if self.cmd and len(self.cmd) > 0:
-                self.AddText(self.cmd[0])
+            cmd = self.task.getCmd(ignoreErrors = False)
+            if cmd and len(cmd) > 0:
+                self.AddText(cmd[0])
             else:
             else:
-                self.AddText('<<module>>')
+                self.AddText('<<%s>>' % _("module"))
         
         
     def _setBrush(self, isvalid):
     def _setBrush(self, isvalid):
         """!Set brush"""
         """!Set brush"""
@@ -1130,10 +1189,10 @@ class ModelAction(ogl.RectangleShape):
         """!Set id"""
         """!Set id"""
         self.id = id
         self.id = id
     
     
-    def SetProperties(self, dcmd, params, propwin):
+    def SetProperties(self, params, propwin):
         """!Record properties dialog"""
         """!Record properties dialog"""
-        self.cmd = dcmd
-        self.params = params
+        self.task.params = params['params']
+        self.task.flags  = params['flags']
         self.propWin = propwin
         self.propWin = propwin
 
 
     def GetPropDialog(self):
     def GetPropDialog(self):
@@ -1142,30 +1201,44 @@ class ModelAction(ogl.RectangleShape):
 
 
     def GetLog(self, string = True):
     def GetLog(self, string = True):
         """!Get logging info"""
         """!Get logging info"""
+        cmd = self.task.getCmd(ignoreErrors = True)
         if string:
         if string:
-            if self.cmd is None:
+            if cmd is None:
                 return ''
                 return ''
             else:
             else:
-                return ' '.join(self.cmd)
+                return ' '.join(cmd)
         
         
-        return self.cmd
+        return cmd
     
     
     def GetName(self):
     def GetName(self):
         """!Get name"""
         """!Get name"""
-        if self.cmd and len(self.cmd) > 0:
-            return self.cmd[0]
+        cmd = self.task.getCmd(ignoreErrors = True)
+        if cmd and len(cmd) > 0:
+            return cmd[0]
         
         
         return _('unknown')
         return _('unknown')
 
 
-    def GetParams(self):
+    def GetParams(self, dcopy = False):
         """!Get dictionary of parameters"""
         """!Get dictionary of parameters"""
-        return self.params
+        if dcopy:
+            return copy.deepcopy(self.task.get_options())
+        
+        return self.task.get_options()
 
 
-    def SetParams(self, params, cmd):
+    def SetParams(self, params):
         """!Set dictionary of parameters"""
         """!Set dictionary of parameters"""
-        self.params = params
-        self.cmd    = cmd
-
+        self.task.params = params['params']
+        self.task.flags  = params['flags']
+        
+    def MergeParams(self, params):
+        """!Merge dictionary of parameters"""
+        for f in params['flags']:
+            self.task.set_flag(f['name'],
+                               f.get('value', False))
+        for p in params['params']:
+            self.task.set_param(p['name'],
+                                p.get('value', ''))
+        
     def SetValid(self, isvalid):
     def SetValid(self, isvalid):
         """!Set instance to be valid/invalid"""
         """!Set instance to be valid/invalid"""
         self.isValid = isvalid
         self.isValid = isvalid
@@ -1277,9 +1350,8 @@ class ModelData(ogl.EllipseShape):
                 task = menuform.GUI().ParseCommand(cmd = action.GetLog(string = False),
                 task = menuform.GUI().ParseCommand(cmd = action.GetLog(string = False),
                                                    show = None)
                                                    show = None)
                 task.set_param(self.name, self.value)
                 task.set_param(self.name, self.value)
-                action.SetParams(params = task.get_options(),
-                                 cmd = task.getCmd(ignoreErrors = True))
-            
+                action.SetParams(params = task.get_options())
+        
     def GetActions(self, direction):
     def GetActions(self, direction):
         """!Get related actions
         """!Get related actions
 
 
@@ -1546,6 +1618,10 @@ class ModelSearchDialog(wx.Dialog):
         self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
         self.btnCancel = wx.Button(self.panel, wx.ID_CANCEL)
         self.btnOk     = wx.Button(self.panel, wx.ID_OK)
         self.btnOk     = wx.Button(self.panel, wx.ID_OK)
         self.btnOk.SetDefault()
         self.btnOk.SetDefault()
+        self.btnOk.Enable(False)
+
+        self.cmd_prompt.Bind(wx.EVT_KEY_UP, self.OnText)
+        self.Bind(wx.EVT_BUTTON, self.OnOk, self.btnOk)
         
         
         self._layout()
         self._layout()
         
         
@@ -1589,11 +1665,37 @@ class ModelSearchDialog(wx.Dialog):
     
     
     def OnOk(self, event):
     def OnOk(self, event):
         self.btnOk.SetFocus()
         self.btnOk.SetFocus()
+        cmd = self.GetCmd()
+        
+        if len(cmd) < 1:
+            GMessage(parent = self,
+                     message = _("Command not defined.\n\n"
+                                 "Unable to add new action to the model."))
+            return
+        
+        if cmd[0] not in globalvar.grassCmd['all']:
+            GMessage(parent = self,
+                     message = _("'%s' is not a GRASS module.\n\n"
+                                 "Unable to add new action to the model.") % cmd[0])
+            return
+        
+        self.EndModal(wx.ID_OK)
+        
+    def OnText(self, event):
+        if self.cmd_prompt.AutoCompActive():
+            return
+        
+        entry = self.cmd_prompt.GetTextLeft()
+        if len(entry) > 0:
+            self.btnOk.Enable()
+        else:
+            self.btnOk.Enable(False)
+            
+        event.Skip()
         
         
     def Reset(self):
     def Reset(self):
         """!Reset dialog"""
         """!Reset dialog"""
-        self.searchBy.SetSelection(0)
-        self.search.SetValue('')
+        self.search.Reset()
         self.cmd_prompt.OnCmdErase(None)
         self.cmd_prompt.OnCmdErase(None)
 
 
 class ModelRelation(ogl.LineShape):
 class ModelRelation(ogl.LineShape):
@@ -1653,16 +1755,16 @@ class ProcessModelFile:
             
             
             task = action.find('task')
             task = action.find('task')
             if task:
             if task:
-                cmd = self._processTask(task)
+                task = self._processTask(task)
             else:
             else:
-                cmd = None
-
+                task = None
+            
             aId = int(action.get('id', -1))
             aId = int(action.get('id', -1))
             
             
-            self.actions.append({ 'pos' : pos,
-                                  'size': size,
-                                  'cmd' : cmd,
-                                  'id'  : aId })
+            self.actions.append({ 'pos'  : pos,
+                                  'size' : size,
+                                  'task' : task,
+                                  'id'   : aId })
             
             
     def _getDim(self, node):
     def _getDim(self, node):
         """!Get position and size of shape"""
         """!Get position and size of shape"""
@@ -1720,25 +1822,49 @@ class ProcessModelFile:
                                'from' : fromDir })
                                'from' : fromDir })
         
         
     def _processTask(self, node):
     def _processTask(self, node):
-        """!Process task"""
+        """!Process task
+
+        @return grassTask instance
+        @return None on error
+        """
         cmd = list()
         cmd = list()
+        parametrized = list()
+        
         name = node.get('name', None)
         name = node.get('name', None)
         if not name:
         if not name:
-            return cmd
+            return None
+        
         cmd.append(name)
         cmd.append(name)
         
         
         # flags
         # flags
-        for p in node.findall('flag'):
-            flag = p.get('name', '')
+        for f in node.findall('flag'):
+            flag = f.get('name', '')
+            if f.get('parametrized', '0') == '1':
+                parametrized.append(('flag', flag))
+                if f.get('value', '1') == '0':
+                    continue
             if len(flag) > 1:
             if len(flag) > 1:
                 cmd.append('--' + flag)
                 cmd.append('--' + flag)
             else:
             else:
                 cmd.append('-' + flag)
                 cmd.append('-' + flag)
         # parameters
         # parameters
         for p in node.findall('parameter'):
         for p in node.findall('parameter'):
-            cmd.append('%s=%s' % (p.get('name', ''),
+            name = p.get('name', '')
+            if p.find('parametrized') is not None:
+                parametrized.append(('param', name))
+            cmd.append('%s=%s' % (name,
                                   self._filterValue(self._getNodeText(p, 'value'))))
                                   self._filterValue(self._getNodeText(p, 'value'))))
-        return cmd
+            
+        task = menuform.GUI().ParseCommand(cmd = cmd,
+                                           show = None)
+        
+        for opt, name in parametrized:
+            if opt == 'flag':
+                task.set_flag(name, True, element = 'parametrized')
+            else:
+                task.set_param(name, True, element = 'parametrized')
+        
+        return task
     
     
 class WriteModelFile:
 class WriteModelFile:
     """!Generic class for writing model file"""
     """!Generic class for writing model file"""
@@ -1788,9 +1914,17 @@ class WriteModelFile:
             for key, val in action.GetParams().iteritems():
             for key, val in action.GetParams().iteritems():
                 if key == 'flags':
                 if key == 'flags':
                     for f in val:
                     for f in val:
-                        if f.get('value', False):
-                            self.fd.write('%s<flag name="%s" />\n' %
-                                          (' ' * self.indent, f.get('name', '')))
+                        if f.get('value', False) or f.get('parametrized', False):
+                            if f.get('parametrized', False):
+                                if f.get('value', False) == False:
+                                    self.fd.write('%s<flag name="%s" value="0" parametrized="1" />\n' %
+                                                  (' ' * self.indent, f.get('name', '')))
+                                else:
+                                    self.fd.write('%s<flag name="%s" parametrized="1" />\n' %
+                                                  (' ' * self.indent, f.get('name', '')))
+                            else:
+                                self.fd.write('%s<flag name="%s" />\n' %
+                                              (' ' * self.indent, f.get('name', '')))
                 else: # parameter
                 else: # parameter
                     for p in val:
                     for p in val:
                         if not p.get('value', ''):
                         if not p.get('value', ''):
@@ -1798,6 +1932,8 @@ class WriteModelFile:
                         self.fd.write('%s<parameter name="%s">\n' %
                         self.fd.write('%s<parameter name="%s">\n' %
                                       (' ' * self.indent, p.get('name', '')))
                                       (' ' * self.indent, p.get('name', '')))
                         self.indent += 4
                         self.indent += 4
+                        if p.get('parametrized', False):
+                            self.fd.write('%s<parametrized />\n' % (' ' * self.indent))
                         self.fd.write('%s<value>%s</value>\n' %
                         self.fd.write('%s<value>%s</value>\n' %
                                       (' ' * self.indent, self._filterValue(p.get('value', ''))))
                                       (' ' * self.indent, self._filterValue(p.get('value', ''))))
                         self.indent -= 4
                         self.indent -= 4
@@ -2200,7 +2336,71 @@ class PropertiesDialog(wx.Dialog):
         self.name.SetValue(prop['name'])
         self.name.SetValue(prop['name'])
         self.desc.SetValue(prop['desc'])
         self.desc.SetValue(prop['desc'])
         self.author.SetValue(prop['author'])
         self.author.SetValue(prop['author'])
+
+class ModelParamDialog(wx.Dialog):
+    def __init__(self, parent, params, id = wx.ID_ANY, title = _("Model parameters"),
+                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
+        """!Model parameters dialog
+        """
+        self.parent = parent
+        self.params = params
+        
+        wx.Dialog.__init__(self, parent = parent, id = id, title = title, style = style, **kwargs)
         
         
+        self.notebook = FN.FlatNotebook(self, id = wx.ID_ANY,
+                                        style = FN.FNB_FANCY_TABS |
+                                        FN.FNB_BOTTOM |
+                                        FN.FNB_NO_NAV_BUTTONS |
+                                        FN.FNB_NO_X_BUTTON)
+        panel = self._createPages()
+        wx.CallAfter(self.notebook.SetSelection, 0)
+        
+        self.btnCancel = wx.Button(parent = self, id = wx.ID_CANCEL)
+        self.btnRun     = wx.Button(parent = self, id = wx.ID_OK,
+                                    label = _("&Run"))
+        self.btnRun.SetDefault()
+        
+        self._layout()
+        
+        size = self.GetBestSize()
+        self.SetMinSize(size)
+        self.SetSize((size.width, size.height +
+                      panel.constrained_size[1] -
+                      panel.panelMinHeight))
+                
+    def _layout(self):
+        btnSizer = wx.StdDialogButtonSizer()
+        btnSizer.AddButton(self.btnCancel)
+        btnSizer.AddButton(self.btnRun)
+        btnSizer.Realize()
+        
+        mainSizer = wx.BoxSizer(wx.VERTICAL)
+        mainSizer.Add(item = self.notebook, proportion = 1,
+                      flag = wx.EXPAND)
+        mainSizer.Add(item=btnSizer, proportion=0,
+                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
+        
+        self.SetSizer(mainSizer)
+        mainSizer.Fit(self)
+        
+    def _createPages(self):
+        """!Create for each parametrized module its own page"""
+        for name, params in self.params.iteritems():
+            panel = self._createPage(name, params)
+            self.notebook.AddPage(panel, text = name)
+    
+        return panel
+    
+    def _createPage(self, name, params):
+        """!Define notebook page"""
+        task = menuform.grassTask(name)
+        task.flags  = params['flags']
+        task.params = params['params']
+        
+        panel = menuform.cmdPanel(parent = self, id = wx.ID_ANY, task = task)
+        
+        return panel
+    
 def main():
 def main():
     app = wx.PySimpleApp()
     app = wx.PySimpleApp()
     wx.InitAllImageHandlers()
     wx.InitAllImageHandlers()

+ 92 - 56
gui/wxpython/gui_modules/menuform.py

@@ -405,7 +405,7 @@ class grassTask:
         else:
         else:
             return None
             return None
 
 
-    def set_param(self, aParam, aValue):
+    def set_param(self, aParam, aValue, element = 'value'):
         """
         """
         Set param value/values.
         Set param value/values.
         """
         """
@@ -414,7 +414,7 @@ class grassTask:
         except ValueError:
         except ValueError:
             return
             return
         
         
-        param['value'] = aValue
+        param[element] = aValue
             
             
     def get_flag(self, aFlag):
     def get_flag(self, aFlag):
         """
         """
@@ -425,7 +425,7 @@ class grassTask:
                 return f
                 return f
         raise ValueError, _("Flag not found: %s") % aFlag
         raise ValueError, _("Flag not found: %s") % aFlag
 
 
-    def set_flag(self, aFlag, aValue):
+    def set_flag(self, aFlag, aValue, element = 'value'):
         """
         """
         Enable / disable flag.
         Enable / disable flag.
         """
         """
@@ -434,7 +434,7 @@ class grassTask:
         except ValueError:
         except ValueError:
             return
             return
         
         
-        param['value'] = aValue
+        param[element] = aValue
         
         
     def getCmdError(self):
     def getCmdError(self):
         """!Get error string produced by getCmd(ignoreErrors = False)
         """!Get error string produced by getCmd(ignoreErrors = False)
@@ -617,12 +617,17 @@ class mainFrame(wx.Frame):
     The command is checked and sent to the clipboard when clicking
     The command is checked and sent to the clipboard when clicking
     'Copy'.
     'Copy'.
     """
     """
-    def __init__(self, parent, ID, task_description, get_dcmd=None, layer=None):
+    def __init__(self, parent, ID, task_description,
+                 get_dcmd = None, layer = None):
         self.get_dcmd = get_dcmd
         self.get_dcmd = get_dcmd
-        self.layer = layer
-        self.task = task_description
-        self.parent = parent # LayerTree | None
-
+        self.layer    = layer
+        self.task     = task_description
+        self.parent   = parent            # LayerTree | Modeler | None | ...
+        if parent and parent.GetName() == 'Modeler':
+            self.modeler = self.parent
+        else:
+            self.modeler = None
+        
         # module name + keywords
         # module name + keywords
         if self.task.name.split('.')[-1] in ('py', 'sh'):
         if self.task.name.split('.')[-1] in ('py', 'sh'):
             title = str(self.task.name.rsplit('.',1)[0])
             title = str(self.task.name.rsplit('.',1)[0])
@@ -652,7 +657,7 @@ class mainFrame(wx.Frame):
 
 
         # set apropriate output window
         # set apropriate output window
         if self.parent:
         if self.parent:
-            self.standalone   = False
+            self.standalone = False
         else:
         else:
             self.standalone = True
             self.standalone = True
         
         
@@ -685,10 +690,8 @@ class mainFrame(wx.Frame):
         self.Layout()
         self.Layout()
 
 
         # notebooks
         # notebooks
-        self.notebookpanel = cmdPanel (parent=self.panel, task=self.task, standalone=self.standalone,
-                                       mainFrame=self)
-        ### add 'command output' tab also for dialog open from menu
-        #         if self.standalone:
+        self.notebookpanel = cmdPanel(parent = self.panel, task = self.task,
+                                      mainFrame = self)
         self.goutput = self.notebookpanel.goutput
         self.goutput = self.notebookpanel.goutput
         self.notebookpanel.OnUpdateValues = self.updateValuesHook
         self.notebookpanel.OnUpdateValues = self.updateValuesHook
         guisizer.Add (item=self.notebookpanel, proportion=1, flag=wx.EXPAND)
         guisizer.Add (item=self.notebookpanel, proportion=1, flag=wx.EXPAND)
@@ -763,7 +766,7 @@ class mainFrame(wx.Frame):
         guisizer.Add(item=btnsizer, proportion=0, flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
         guisizer.Add(item=btnsizer, proportion=0, flag=wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
                      border = 30)
                      border = 30)
 
 
-        if self.parent and self.parent.GetName() != 'Modeler':
+        if self.parent and not self.modeler:
             addLayer = False
             addLayer = False
             for p in self.task.params:
             for p in self.task.params:
                 if p.get('age', 'old') == 'new' and \
                 if p.get('age', 'old') == 'new' and \
@@ -819,10 +822,10 @@ class mainFrame(wx.Frame):
         self.Layout()
         self.Layout()
 
 
         #keep initial window size limited for small screens
         #keep initial window size limited for small screens
-        width,height = self.GetSizeTuple()
+        width, height = self.GetSizeTuple()
         if width > 640: width = 640
         if width > 640: width = 640
         if height > 480: height = 480
         if height > 480: height = 480
-        self.SetSize((width,height))
+        self.SetSize((width, height))
         
         
         # fix goutput's pane size
         # fix goutput's pane size
         if self.goutput:
         if self.goutput:
@@ -865,7 +868,7 @@ class mainFrame(wx.Frame):
 
 
     def OnApply(self, event):
     def OnApply(self, event):
         """!Apply the command"""
         """!Apply the command"""
-        if self.parent and self.parent.GetName() == 'Modeler':
+        if self.modeler:
             cmd = self.createCmd(ignoreErrors = True)
             cmd = self.createCmd(ignoreErrors = True)
         else:
         else:
             cmd = self.createCmd()
             cmd = self.createCmd()
@@ -969,12 +972,14 @@ class cmdPanel(wx.Panel):
     """!A panel containing a notebook dividing in tabs the different
     """!A panel containing a notebook dividing in tabs the different
     guisections of the GRASS cmd.
     guisections of the GRASS cmd.
     """
     """
-    def __init__( self, parent, task, standalone, mainFrame, *args, **kwargs ):
-        wx.Panel.__init__( self, parent, *args, **kwargs )
-
-        self.parent = mainFrame
+    def __init__(self, parent, task, id = wx.ID_ANY, mainFrame = None, *args, **kwargs):
+        if mainFrame:
+            self.parent = mainFrame
+        else:
+            self.parent = parent
         self.task = task
         self.task = task
-        fontsize = 10
+        
+        wx.Panel.__init__(self, parent, id = id, *args, **kwargs)
         
         
         # Determine tab layout
         # Determine tab layout
         sections = []
         sections = []
@@ -1025,7 +1030,7 @@ class cmdPanel(wx.Panel):
 
 
         # are we running from command line?
         # are we running from command line?
         ### add 'command output' tab regardless standalone dialog
         ### add 'command output' tab regardless standalone dialog
-        if self.parent.get_dcmd is None:
+        if self.parent.GetName() == "MainFrame" and self.parent.get_dcmd is None:
             self.goutput = goutput.GMConsole(parent=self, margin=False,
             self.goutput = goutput.GMConsole(parent=self, margin=False,
                                              pageid=self.notebook.GetPageCount())
                                              pageid=self.notebook.GetPageCount())
             self.goutputId = self.notebook.GetPageCount()
             self.goutputId = self.notebook.GetPageCount()
@@ -1077,6 +1082,20 @@ class cmdPanel(wx.Panel):
                             flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5)
                             flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5)
             f['wxId'] = [ chk.GetId(), ]
             f['wxId'] = [ chk.GetId(), ]
             chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
             chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+            
+            if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+                parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+                                     label = _("Parametrized in model"))
+                parChk.SetName('ModelParam')
+                parChk.SetValue(f.get('parametrized', False))
+                if f.has_key('wxId'):
+                    f['wxId'].append(parChk.GetId())
+                else:
+                    f['wxId'] = [ parChk.GetId() ]
+                parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+                which_sizer.Add(item = parChk, proportion = 0,
+                                flag = wx.LEFT, border = 20)
+            
             if f['name'] in ('verbose', 'quiet'):
             if f['name'] in ('verbose', 'quiet'):
                 chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
                 chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
                 vq = UserSettings.Get(group='cmd', key='verbosity', subkey='selection')
                 vq = UserSettings.Get(group='cmd', key='verbosity', subkey='selection')
@@ -1487,7 +1506,6 @@ class cmdPanel(wx.Panel):
                             none_check.SetValue(True)
                             none_check.SetValue(True)
                         else:
                         else:
                             none_check.SetValue(False)
                             none_check.SetValue(False)
-                        # none_check.SetFont( wx.Font( fontsize, wx.FONTFAMILY_DEFAULT, wx.NORMAL, text_style, 0, ''))
                         this_sizer.Add(item=none_check, proportion=0,
                         this_sizer.Add(item=none_check, proportion=0,
                                        flag=wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP, border=5)
                                        flag=wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP, border=5)
                         which_sizer.Add( this_sizer )
                         which_sizer.Add( this_sizer )
@@ -1512,7 +1530,20 @@ class cmdPanel(wx.Panel):
                     # a textctl and a button;
                     # a textctl and a button;
                     # we have to target the button here
                     # we have to target the button here
                     p['wxId'] = [ fbb.GetChildren()[1].GetId(), ]
                     p['wxId'] = [ fbb.GetChildren()[1].GetId(), ]
-
+            
+            if self.parent.GetName() == 'MainFrame' and self.parent.modeler:
+                parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
+                                     label = _("Parametrized in model"))
+                parChk.SetName('ModelParam')
+                parChk.SetValue(p.get('parametrized', False))
+                if p.has_key('wxId'):
+                    p['wxId'].append(parChk.GetId())
+                else:
+                    p['wxId'] = [ parChk.GetId() ]
+                parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
+                which_sizer.Add(item = parChk, proportion = 0,
+                                flag = wx.LEFT, border = 20)
+                
             if title_txt is not None:
             if title_txt is not None:
                 # create tooltip if given
                 # create tooltip if given
                 if len(p['values_desc']) > 0:
                 if len(p['values_desc']) > 0:
@@ -1637,7 +1668,7 @@ class cmdPanel(wx.Panel):
         self.hasMain = tab.has_key( _('Required') ) # publish, to enclosing Frame for instance
         self.hasMain = tab.has_key( _('Required') ) # publish, to enclosing Frame for instance
 
 
         self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
         self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
-
+        
     def OnVectorFormat(self, event):
     def OnVectorFormat(self, event):
         """!Change vector format (native / ogr)"""
         """!Change vector format (native / ogr)"""
         sel = event.GetSelection()
         sel = event.GetSelection()
@@ -1806,23 +1837,24 @@ class cmdPanel(wx.Panel):
         name = me.GetName()
         name = me.GetName()
         
         
         for porf in self.task.params + self.task.flags:
         for porf in self.task.params + self.task.flags:
-            if 'wxId' in porf:
-                found = False
-                porf['wxId']
-                for id in porf['wxId']:
-                    if id == myId:
-                        found = True
-                        break
+            if not porf.has_key('wxId'):
+                continue
+            found = False
+            for id in porf['wxId']:
+                if id == myId:
+                    found = True
+                    break
+            
+            if found:
+                if name in ('LayerSelect', 'DriverSelect', 'TableSelect'):
+                    porf['value'] = me.GetStringSelection()
+                elif name == 'GdalSelect':
+                    porf['value'] = event.dsn
+                elif name == 'ModelParam':
+                    porf['parametrized'] = me.IsChecked()
+                else:
+                    porf['value'] = me.GetValue()
                 
                 
-                if found:
-                    if name in ('LayerSelect', 'DriverSelect', 'TableSelect'):
-                        porf['value'] = me.GetStringSelection()
-                    elif name == 'GdalSelect':
-                        porf['value'] = event.dsn
-                    else:
-                        porf['value'] = me.GetValue()
-                    
-                  
         self.OnUpdateValues()
         self.OnUpdateValues()
         
         
         event.Skip()
         event.Skip()
@@ -1937,12 +1969,12 @@ class GUI:
         
         
         return processTask(tree).GetTask()
         return processTask(tree).GetTask()
     
     
-    def ParseCommand(self, cmd, gmpath=None, completed=None, parentframe=None,
-                     show=True, modal=False):
+    def ParseCommand(self, cmd, gmpath = None, completed = None, parentframe = None,
+                     show = True, modal = False):
         """!Parse command
         """!Parse command
-
+        
         Note: cmd is given as list
         Note: cmd is given as list
-
+        
         If command is given with options, return validated cmd list:
         If command is given with options, return validated cmd list:
          - add key name for first parameter if not given
          - add key name for first parameter if not given
          - change mapname to mapname@mapset
          - change mapname to mapname@mapset
@@ -1960,17 +1992,17 @@ class GUI:
                 dcmd_params.update(completed[2])
                 dcmd_params.update(completed[2])
         
         
         self.parent = parentframe
         self.parent = parentframe
-
+        
         # parse the interface decription
         # parse the interface decription
         self.grass_task = self.ParseInterface(cmd)
         self.grass_task = self.ParseInterface(cmd)
-
+        
         # if layer parameters previously set, re-insert them into dialog
         # if layer parameters previously set, re-insert them into dialog
         if completed is not None:
         if completed is not None:
             if 'params' in dcmd_params:
             if 'params' in dcmd_params:
                 self.grass_task.params = dcmd_params['params']
                 self.grass_task.params = dcmd_params['params']
             if 'flags' in dcmd_params:
             if 'flags' in dcmd_params:
                 self.grass_task.flags = dcmd_params['flags']
                 self.grass_task.flags = dcmd_params['flags']
-
+        
         # update parameters if needed && validate command
         # update parameters if needed && validate command
         if len(cmd) > 1:
         if len(cmd) > 1:
             i = 0
             i = 0
@@ -1992,17 +2024,21 @@ class GUI:
                         else:
                         else:
                             raise ValueError, _("Unable to parse command %s") % ' '.join(cmd)
                             raise ValueError, _("Unable to parse command %s") % ' '.join(cmd)
 
 
-                    if self.grass_task.get_param(key)['element'] in ['cell', 'vector']:
+                    element = self.grass_task.get_param(key)['element']
+                    if element in ['cell', 'vector']:
                         # mapname -> mapname@mapset
                         # mapname -> mapname@mapset
                         try:
                         try:
                             name, mapset = value.split('@')
                             name, mapset = value.split('@')
                         except ValueError:
                         except ValueError:
-                            value = value + '@' + grass.gisenv()['MAPSET']
-                    
+                            mapset = grass.find_file(value, element)['mapset']
+                            curr_mapset = grass.gisenv()['MAPSET']
+                            if mapset and mapset != curr_mapset:
+                                value = value + '@' + mapset
+                        
                     self.grass_task.set_param(key, value)
                     self.grass_task.set_param(key, value)
                     cmd_validated.append(key + '=' + value)
                     cmd_validated.append(key + '=' + value)
                     i = i + 1
                     i = i + 1
-
+            
             # update original command list
             # update original command list
             cmd = cmd_validated
             cmd = cmd_validated
         
         
@@ -2017,7 +2053,7 @@ class GUI:
             # update only propwin reference
             # update only propwin reference
             get_dcmd(dcmd=None, layer=layer, params=None,
             get_dcmd(dcmd=None, layer=layer, params=None,
                      propwin=self.mf)
                      propwin=self.mf)
-
+        
         if show is not None:
         if show is not None:
             self.mf.notebookpanel.OnUpdateSelection(None)
             self.mf.notebookpanel.OnUpdateSelection(None)
             if show is True:
             if show is True:
@@ -2031,7 +2067,7 @@ class GUI:
         self.cmd = cmd
         self.cmd = cmd
         
         
         return self.grass_task
         return self.grass_task
-
+    
     def GetCommandInputMapParamKey(self, cmd):
     def GetCommandInputMapParamKey(self, cmd):
         """!Get parameter key for input raster/vector map
         """!Get parameter key for input raster/vector map