GRASS Programmer's Manual  6.4.2(2012)
menuform.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 """
00003 @brief Construct simple wx.Python GUI from a GRASS command interface
00004 description.
00005 
00006 Classes:
00007  - helpPanel
00008  - mainFrame
00009  - cmdPanel
00010  - GrassGUIApp
00011  - GUI
00012  - FloatValidator
00013  - GNotebook
00014 
00015 This program is just a coarse approach to automatically build a GUI
00016 from a xml-based GRASS user interface description.
00017 
00018 You need to have Python 2.4, wxPython 2.8 and python-xml.
00019 
00020 The XML stream is read from executing the command given in the
00021 command line, thus you may call it for instance this way:
00022 
00023 python <this file.py> r.basins.fill
00024 
00025 Or you set an alias or wrap the call up in a nice shell script, GUI
00026 environment ... please contribute your idea.
00027 
00028 Copyright(C) 2000-2011 by the GRASS Development Team
00029 This program is free software under the GPL(>=v2) Read the file
00030 COPYING coming with GRASS for details.
00031 
00032 @author Jan-Oliver Wagner <jan@intevation.de>
00033 @author Bernhard Reiter <bernhard@intevation.de>
00034 @author Michael Barton, Arizona State University
00035 @author Daniel Calvelo <dca.gis@gmail.com>
00036 @author Martin Landa <landa.martin@gmail.com>
00037 
00038 @todo
00039  - verify option value types
00040 """
00041 
00042 import sys
00043 import re
00044 import string
00045 import textwrap
00046 import os
00047 import time
00048 import copy
00049 import locale
00050 import types
00051 from threading import Thread
00052 import Queue
00053 import tempfile
00054 
00055 ### i18N
00056 import gettext
00057 gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
00058 
00059 import globalvar
00060 import wx
00061 import wx.html
00062 try:
00063     import wx.lib.agw.flatnotebook as FN
00064 except ImportError:
00065     import wx.lib.flatnotebook as FN
00066 import wx.lib.colourselect as csel
00067 import wx.lib.filebrowsebutton as filebrowse
00068 import wx.lib.scrolledpanel as scrolled
00069 from wx.lib.expando import ExpandoTextCtrl, EVT_ETC_LAYOUT_NEEDED
00070 from wx.lib.newevent import NewEvent
00071 
00072 try:
00073     import xml.etree.ElementTree as etree
00074 except ImportError:
00075     import elementtree.ElementTree as etree # Python <= 2.4
00076 
00077 import gdialogs
00078 from ghelp import HelpPanel
00079 
00080 gisbase = os.getenv("GISBASE")
00081 if gisbase is None:
00082     print >>sys.stderr, "We don't seem to be properly installed, or we are being run outside GRASS. Expect glitches."
00083     gisbase = os.path.join(os.path.dirname(sys.argv[0]), os.path.pardir)
00084     wxbase = gisbase
00085 else:
00086     wxbase = os.path.join(globalvar.ETCWXDIR)
00087 
00088 sys.path.append(wxbase)
00089 
00090 from grass.script import core as grass
00091 from grass.script import task as gtask
00092 
00093 import gselect
00094 import gcmd
00095 import goutput
00096 import utils
00097 from preferences import globalSettings as UserSettings
00098 try:
00099     import subprocess
00100 except:
00101     from compat import subprocess
00102 
00103 wxUpdateDialog, EVT_DIALOG_UPDATE = NewEvent()
00104 
00105 # From lib/gis/col_str.c, except purple which is mentioned
00106 # there but not given RGB values
00107 str2rgb = {'aqua': (100, 128, 255),
00108            'black': (0, 0, 0),
00109            'blue': (0, 0, 255),
00110            'brown': (180, 77, 25),
00111            'cyan': (0, 255, 255),
00112            'gray': (128, 128, 128),
00113            'green': (0, 255, 0),
00114            'grey': (128, 128, 128),
00115            'indigo': (0, 128, 255),
00116            'magenta': (255, 0, 255),
00117            'orange': (255, 128, 0),
00118            'purple': (128, 0, 128),
00119            'red': (255, 0, 0),
00120            'violet': (128, 0, 255),
00121            'white': (255, 255, 255),
00122            'yellow': (255, 255, 0)}
00123 rgb2str = {}
00124 for (s,r) in str2rgb.items():
00125     rgb2str[ r ] = s
00126 
00127 """!Hide some options in the GUI"""
00128 _blackList = { 'enabled' : False,
00129                'items'   : { 'd.legend' : { 'flags' : ['m'] } }
00130                }
00131 
00132 def color_resolve(color):
00133     if len(color) > 0 and color[0] in "0123456789":
00134         rgb = tuple(map(int, color.split(':')))
00135         label = color
00136     else:
00137         # Convert color names to RGB
00138         try:
00139             rgb = str2rgb[ color ]
00140             label = color
00141         except KeyError:
00142             rgb = (200,200,200)
00143             label = _('Select Color')
00144     return (rgb, label)
00145 
00146 def text_beautify(someString , width = 70):
00147     """
00148     Make really long texts shorter, clean up whitespace and
00149     remove trailing punctuation.
00150     """
00151     if width > 0:
00152         return escape_ampersand(string.strip(
00153                 os.linesep.join(textwrap.wrap(utils.normalize_whitespace(someString), width)),
00154                 ".,;:"))
00155     else:
00156         return escape_ampersand(string.strip(utils.normalize_whitespace(someString), ".,;:"))
00157     
00158 def escape_ampersand(text):
00159     """!Escapes ampersands with additional ampersand for GUI"""
00160     return string.replace(text, "&", "&&")
00161 
00162 class UpdateThread(Thread):
00163     """!Update dialog widgets in the thread"""
00164     def __init__(self, parent, event, eventId, task):
00165         Thread.__init__(self)
00166         
00167         self.parent = parent
00168         self.event = event
00169         self.eventId = eventId
00170         self.task = task
00171         self.setDaemon(True)
00172         
00173         # list of functions which updates the dialog
00174         self.data = {}
00175         
00176     def run(self):
00177         # get widget id
00178         if not self.eventId:
00179             for p in self.task.params:
00180                 if p.get('gisprompt', False) == False:
00181                     continue
00182                 prompt = p.get('element', '')
00183                 if prompt == 'vector':
00184                     name = p.get('name', '')
00185                     if name in ('map', 'input'):
00186                         self.eventId = p['wxId'][0]
00187             if self.eventId is None:
00188                 return
00189         
00190         p = self.task.get_param(self.eventId, element = 'wxId', raiseError = False)
00191         if not p or 'wxId-bind' not in p:
00192             return
00193         
00194         # get widget prompt
00195         pType = p.get('prompt', '')
00196         if not pType:
00197             return
00198         
00199         # check for map/input parameter
00200         pMap = self.task.get_param('map', raiseError = False)
00201         
00202         if not pMap:
00203             pMap = self.task.get_param('input', raiseError = False)
00204 
00205         if pMap:
00206             map = pMap.get('value', '')
00207         else:
00208             map = None
00209 
00210         # avoid running db.describe several times
00211         cparams = dict()
00212         cparams[map] = { 'dbInfo' : None,
00213                          'layers' : None, }
00214         
00215         # update reference widgets
00216         for uid in p['wxId-bind']:
00217             win = self.parent.FindWindowById(uid)
00218             if not win:
00219                 continue
00220             
00221             name = win.GetName()
00222             
00223             if name == 'LayerSelect':
00224                 if map in cparams and not cparams[map]['layers']:
00225                     win.InsertLayers(vector = map)
00226                     win.Reset()
00227                     cparams[map]['layers'] = win.GetItems()
00228             
00229             elif name == 'TableSelect':
00230                 pDriver = self.task.get_param('dbdriver', element='prompt', raiseError=False)
00231                 driver = db = None
00232                 if pDriver:
00233                     driver = pDriver['value']
00234                 pDb = self.task.get_param('dbname', element='prompt', raiseError=False)
00235                 if pDb:
00236                     db = pDb['value']
00237                 
00238                 self.data[win.InsertTables] = { 'driver' : driver,
00239                                                 'database' : db }
00240                 
00241             elif name == 'ColumnSelect':
00242                 pLayer = self.task.get_param('layer', element='element', raiseError=False)
00243                 if pLayer:
00244                     if pLayer.get('value', '') != '':
00245                         layer = pLayer.get('value', '')
00246                     else:
00247                         layer = pLayer.get('default', '')
00248                 else:
00249                     layer = 1
00250                 
00251                 if map:
00252                     if map in cparams:
00253                         if not cparams[map]['dbInfo']:
00254                             cparams[map]['dbInfo'] = gselect.VectorDBInfo(map)
00255                         self.data[win.InsertColumns] = { 'vector' : map, 'layer' : layer,
00256                                                          'dbInfo' : cparams[map]['dbInfo'] }
00257                 else: # table
00258                     driver = db = None
00259                     pDriver = self.task.get_param('dbdriver', element='prompt', raiseError=False)
00260                     if pDriver:
00261                         driver = pDriver.get('value', None)
00262                     pDb = self.task.get_param('dbname', element='prompt', raiseError=False)
00263                     if pDb:
00264                         db = pDb.get('value', None)
00265                     pTable = self.task.get_param('dbtable', element='element', raiseError=False)
00266                     if pTable and \
00267                             pTable.get('value', '') != '':
00268                         if driver and db:
00269                             self.data[win.InsertTableColumns] = { 'table' : pTable.get('value'),
00270                                                                   'driver' : driver,
00271                                                                   'database' : db }
00272                         else:
00273                             self.data[win.InsertTableColumns] = { 'table'  : pTable.get('value') }
00274             
00275             elif name == 'SubGroupSelect':
00276                 pGroup = self.task.get_param('group', element = 'element', raiseError = False)
00277                 if pGroup:
00278                     self.data[win.Insert] = { 'group' : pGroup.get('value', '')}
00279             
00280             elif name == 'LocationSelect':
00281                 pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
00282                 if pDbase:
00283                     self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', '')}
00284 
00285             elif name == 'MapsetSelect':
00286                 pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
00287                 pLocation = self.task.get_param('location', element = 'element', raiseError = False)
00288                 if pDbase and pLocation:
00289                     self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', ''),
00290                                                    'location' : pLocation.get('value', '')}
00291 
00292             elif name ==  'ProjSelect':
00293                 pDbase = self.task.get_param('dbase', element = 'element', raiseError = False)
00294                 pLocation = self.task.get_param('location', element = 'element', raiseError = False)
00295                 pMapset = self.task.get_param('mapset', element = 'element', raiseError = False)
00296                 if pDbase and pLocation and pMapset:
00297                     self.data[win.UpdateItems] = { 'dbase' : pDbase.get('value', ''),
00298                                                    'location' : pLocation.get('value', ''),
00299                                                    'mapset' : pMapset.get('value', '')}
00300             
00301 def UpdateDialog(parent, event, eventId, task):
00302     return UpdateThread(parent, event, eventId, task)
00303 
00304 class UpdateQThread(Thread):
00305     """!Update dialog widgets in the thread"""
00306     requestId = 0
00307     def __init__(self, parent, requestQ, resultQ, **kwds):
00308         Thread.__init__(self, **kwds)
00309         
00310         self.parent = parent # cmdPanel
00311         self.setDaemon(True)
00312         
00313         self.requestQ = requestQ
00314         self.resultQ = resultQ
00315         
00316         self.start()
00317         
00318     def Update(self, callable, *args, **kwds):
00319         UpdateQThread.requestId +=  1
00320         
00321         self.request = None
00322         self.requestQ.put((UpdateQThread.requestId, callable, args, kwds))
00323         
00324         return UpdateQThread.requestId
00325     
00326     def run(self):
00327         while True:
00328             requestId, callable, args, kwds = self.requestQ.get()
00329             
00330             requestTime = time.time()
00331             
00332             self.request = callable(*args, **kwds)
00333 
00334             self.resultQ.put((requestId, self.request.run()))
00335            
00336             if self.request:
00337                 event = wxUpdateDialog(data = self.request.data)
00338                 wx.PostEvent(self.parent, event)
00339     
00340 class mainFrame(wx.Frame):
00341     """!This is the Frame containing the dialog for options input.
00342 
00343     The dialog is organized in a notebook according to the guisections
00344     defined by each GRASS command.
00345 
00346     If run with a parent, it may Apply, Ok or Cancel; the latter two
00347     close the dialog.  The former two trigger a callback.
00348 
00349     If run standalone, it will allow execution of the command.
00350 
00351     The command is checked and sent to the clipboard when clicking
00352     'Copy'.
00353     """
00354     def __init__(self, parent, ID, task_description,
00355                  get_dcmd = None, layer = None):
00356         self.get_dcmd = get_dcmd
00357         self.layer    = layer
00358         self.task     = task_description
00359         self.parent   = parent            # LayerTree | Modeler | None | ...
00360         if parent and parent.GetName() ==  'Modeler':
00361             self.modeler = self.parent
00362         else:
00363             self.modeler = None
00364         
00365         # module name + keywords
00366         if self.task.name.split('.')[-1] in ('py', 'sh'):
00367             title = str(self.task.name.rsplit('.',1)[0])
00368         else:
00369             title = self.task.name
00370         try:
00371             if self.task.keywords !=  ['']:
00372                 title +=   " [" + ', '.join(self.task.keywords) + "]"
00373         except ValueError:
00374             pass
00375         
00376         wx.Frame.__init__(self, parent = parent, id = ID, title = title,
00377                           pos = wx.DefaultPosition, style = wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
00378                           name = "MainFrame")
00379         
00380         self.locale = wx.Locale(language = wx.LANGUAGE_DEFAULT)
00381         
00382         self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
00383         
00384         # statusbar
00385         self.CreateStatusBar()
00386         
00387         # icon
00388         self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_dialog.ico'), wx.BITMAP_TYPE_ICO))
00389         
00390         guisizer = wx.BoxSizer(wx.VERTICAL)
00391         
00392         # set apropriate output window
00393         if self.parent:
00394             self.standalone = False
00395         else:
00396             self.standalone = True
00397         
00398         # logo + description
00399         topsizer = wx.BoxSizer(wx.HORIZONTAL)
00400         
00401         # GRASS logo
00402         self.logo = wx.StaticBitmap(parent = self.panel,
00403                                     bitmap = wx.Bitmap(name = os.path.join(globalvar.ETCIMGDIR,
00404                                                                            'grass_form.png'),
00405                                                      type = wx.BITMAP_TYPE_PNG))
00406         topsizer.Add(item = self.logo, proportion = 0, border = 3,
00407                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL)
00408         
00409         # add module description
00410         if self.task.label:
00411             module_desc = self.task.label + ' ' + self.task.description
00412         else:
00413             module_desc = self.task.description
00414         self.description = gdialogs.StaticWrapText(parent = self.panel,
00415                                                    label = module_desc)
00416         topsizer.Add(item = self.description, proportion = 1, border = 5,
00417                      flag = wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
00418         
00419         guisizer.Add(item = topsizer, proportion = 0, flag = wx.EXPAND)
00420         
00421         self.panel.SetSizerAndFit(guisizer)
00422         self.Layout()
00423         
00424         # notebooks
00425         self.notebookpanel = cmdPanel(parent = self.panel, task = self.task,
00426                                       mainFrame = self)
00427         self.goutput = self.notebookpanel.goutput
00428         self.notebookpanel.OnUpdateValues = self.updateValuesHook
00429         guisizer.Add(item = self.notebookpanel, proportion = 1, flag = wx.EXPAND)
00430         
00431         # status bar
00432         status_text = _("Enter parameters for '") + self.task.name + "'"
00433         try:
00434             self.task.getCmd()
00435             self.updateValuesHook()
00436         except ValueError:
00437             self.SetStatusText(status_text)
00438         
00439         # buttons
00440         btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)
00441         # cancel
00442         self.btn_cancel = wx.Button(parent = self.panel, id = wx.ID_CLOSE)
00443         self.btn_cancel.SetToolTipString(_("Close this window without executing the command (Ctrl+Q)"))
00444         btnsizer.Add(item = self.btn_cancel, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
00445         self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel)
00446         if self.get_dcmd is not None: # A callback has been set up
00447             btn_apply = wx.Button(parent = self.panel, id = wx.ID_APPLY)
00448             btn_ok = wx.Button(parent = self.panel, id = wx.ID_OK)
00449             btn_ok.SetDefault()
00450             
00451             btnsizer.Add(item = btn_apply, proportion = 0,
00452                          flag = wx.ALL | wx.ALIGN_CENTER,
00453                          border = 10)
00454             btnsizer.Add(item = btn_ok, proportion = 0,
00455                          flag = wx.ALL | wx.ALIGN_CENTER,
00456                          border = 10)
00457             
00458             btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
00459             btn_ok.Bind(wx.EVT_BUTTON, self.OnOK)
00460         else: # We're standalone
00461             # run
00462             self.btn_run = wx.Button(parent = self.panel, id = wx.ID_OK, label =  _("&Run"))
00463             self.btn_run.SetToolTipString(_("Run the command (Ctrl+R)"))
00464             self.btn_run.SetDefault()
00465             # copy
00466             self.btn_clipboard = wx.Button(parent = self.panel, id = wx.ID_COPY, label = _("C&opy"))
00467             self.btn_clipboard.SetToolTipString(_("Copy the current command string to the clipboard (Ctrl+C)"))
00468             
00469             btnsizer.Add(item = self.btn_run, proportion = 0,
00470                          flag = wx.ALL | wx.ALIGN_CENTER,
00471                          border = 10)
00472             
00473             btnsizer.Add(item = self.btn_clipboard, proportion = 0,
00474                          flag = wx.ALL | wx.ALIGN_CENTER,
00475                          border = 10)
00476             
00477             self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
00478             self.btn_clipboard.Bind(wx.EVT_BUTTON, self.OnCopy)
00479         # help
00480         self.btn_help = wx.Button(parent = self.panel, id = wx.ID_HELP)
00481         self.btn_help.SetToolTipString(_("Show manual page of the command (Ctrl+H)"))
00482         self.btn_help.Bind(wx.EVT_BUTTON, self.OnHelp)
00483         if self.notebookpanel.notebook.GetPageIndexByName('manual') < 0:
00484             self.btn_help.Hide()
00485         
00486         # add help button
00487         btnsizer.Add(item = self.btn_help, proportion = 0, flag = wx.ALL | wx.ALIGN_CENTER, border = 10)
00488         
00489         guisizer.Add(item = btnsizer, proportion = 0, flag = wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT,
00490                      border = 30)
00491         
00492         if self.parent and not self.modeler:
00493             addLayer = False
00494             for p in self.task.params:
00495                 if p.get('age', 'old') ==  'new' and \
00496                    p.get('prompt', '') in ('raster', 'vector', '3d-raster'):
00497                     addLayer = True
00498             
00499             if addLayer:
00500                 # add newly created map into layer tree
00501                 self.addbox = wx.CheckBox(parent = self.panel,
00502                                           label = _('Add created map(s) into layer tree'), style = wx.NO_BORDER)
00503                 self.addbox.SetValue(UserSettings.Get(group = 'cmd', key = 'addNewLayer', subkey = 'enabled'))
00504                 guisizer.Add(item = self.addbox, proportion = 0,
00505                              flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
00506                              border = 5)
00507         
00508         hasNew = False
00509         for p in self.task.params:
00510             if p.get('age', 'old') ==  'new':
00511                 hasNew = True
00512                 break
00513         
00514         if self.get_dcmd is None and hasNew:
00515             # close dialog when command is terminated
00516             self.closebox = wx.CheckBox(parent = self.panel,
00517                                         label = _('Close dialog on finish'), style = wx.NO_BORDER)
00518             self.closebox.SetValue(UserSettings.Get(group = 'cmd', key = 'closeDlg', subkey = 'enabled'))
00519             self.closebox.SetToolTipString(_("Close dialog when command is successfully finished. "
00520                                              "Change this settings in Preferences dialog ('Command' tab)."))
00521             guisizer.Add(item = self.closebox, proportion = 0,
00522                          flag = wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
00523                          border = 5)
00524         
00525         self.Bind(wx.EVT_CLOSE,  self.OnCancel)
00526         self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
00527         
00528         # do layout
00529         # called automatically by SetSizer()
00530         self.panel.SetAutoLayout(True) 
00531         self.panel.SetSizerAndFit(guisizer)
00532         
00533         sizeFrame = self.GetBestSize()
00534         self.SetMinSize(sizeFrame)
00535         self.SetSize(wx.Size(sizeFrame[0], sizeFrame[1] + 0.33 * max(self.notebookpanel.panelMinHeight,
00536                                                                      self.notebookpanel.constrained_size[1])))
00537         
00538         # thread to update dialog
00539         # create queues
00540         self.requestQ = Queue.Queue()
00541         self.resultQ = Queue.Queue()
00542         self.updateThread = UpdateQThread(self.notebookpanel, self.requestQ, self.resultQ)
00543         
00544         self.Layout()
00545         
00546         # keep initial window size limited for small screens
00547         width, height = self.GetSizeTuple()
00548         self.SetSize(wx.Size(min(width, 650),
00549                              min(height, 500)))
00550         
00551         # fix goutput's pane size
00552         if self.goutput:
00553             self.goutput.SetSashPosition(int(self.GetSize()[1] * .75))
00554 
00555     def updateValuesHook(self, event = None):
00556         """!Update status bar data"""
00557         self.SetStatusText(' '.join(self.notebookpanel.createCmd(ignoreErrors = True)))
00558         if event:
00559             event.Skip()
00560         
00561     def OnKeyUp(self, event):
00562         """!Key released (check hot-keys)"""
00563         try:
00564             kc = chr(event.GetKeyCode())
00565         except ValueError:
00566             event.Skip()
00567             return
00568         
00569         if not event.ControlDown():
00570             event.Skip()
00571             return
00572         
00573         if kc ==  'Q':
00574             self.OnCancel(None)
00575         elif kc ==  'S':
00576             self.OnAbort(None)
00577         elif kc ==  'H':
00578             self.OnHelp(None)
00579         elif kc ==  'R':
00580             self.OnRun(None)
00581         elif kc ==  'C':
00582             self.OnCopy(None)
00583         
00584         event.Skip()
00585 
00586     def OnDone(self, cmd, returncode):
00587         """!This function is launched from OnRun() when command is
00588         finished
00589 
00590         @param returncode command's return code (0 for success)
00591         """
00592         if not self.parent or returncode !=  0:
00593             return
00594         if self.parent.GetName() not in ('LayerTree', 'LayerManager'):
00595             return
00596         
00597         if self.parent.GetName() ==  'LayerTree':
00598             display = self.parent.GetMapDisplay()
00599         else: # Layer Manager
00600             display = self.parent.GetLayerTree().GetMapDisplay()
00601             
00602         if not display or not display.IsAutoRendered():
00603             return
00604         
00605         mapLayers = map(lambda x: x.GetName(),
00606                         display.GetRender().GetListOfLayers(l_type = 'raster') +
00607                         display.GetRender().GetListOfLayers(l_type = 'vector'))
00608         
00609         task = GUI(show = None).ParseCommand(cmd)
00610         for p in task.get_options()['params']:
00611             if p.get('prompt', '') not in ('raster', 'vector'):
00612                 continue
00613             mapName = p.get('value', '')
00614             if '@' not in mapName:
00615                 mapName = mapName + '@' + grass.gisenv()['MAPSET']
00616             if mapName in mapLayers:
00617                 display.GetWindow().UpdateMap(render = True)
00618                 return
00619         
00620     def OnOK(self, event):
00621         """!OK button pressed"""
00622         cmd = self.OnApply(event)
00623         if cmd is not None and self.get_dcmd is not None:
00624             self.OnCancel(event)
00625 
00626     def OnApply(self, event):
00627         """!Apply the command"""
00628         if self.modeler:
00629             cmd = self.createCmd(ignoreErrors = True, ignoreRequired = True)
00630         else:
00631             cmd = self.createCmd()
00632         
00633         if cmd is not None and self.get_dcmd is not None:
00634             # return d.* command to layer tree for rendering
00635             self.get_dcmd(cmd, self.layer, {"params": self.task.params, 
00636                                             "flags" : self.task.flags},
00637                           self)
00638             # echo d.* command to output console
00639             # self.parent.writeDCommand(cmd)
00640 
00641         return cmd
00642 
00643     def OnRun(self, event):
00644         """!Run the command"""
00645         cmd = self.createCmd()
00646         
00647         if not cmd or len(cmd) < 1:
00648             return
00649         
00650         if self.standalone or cmd[0][0:2] !=  "d.":
00651             # Send any non-display command to parent window (probably wxgui.py)
00652             # put to parents switch to 'Command output'
00653             self.notebookpanel.notebook.SetSelectionByName('output')
00654             
00655             try:
00656                 
00657                 self.goutput.RunCmd(cmd, onDone = self.OnDone)
00658             except AttributeError, e:
00659                 print >> sys.stderr, "%s: Probably not running in wxgui.py session?" % (e)
00660                 print >> sys.stderr, "parent window is: %s" % (str(self.parent))
00661         else:
00662             gcmd.Command(cmd)
00663         
00664         # update buttons status
00665         for btn in (self.btn_run,
00666                     self.btn_cancel,
00667                     self.btn_clipboard,
00668                     self.btn_help):
00669             btn.Enable(False)
00670         
00671     def OnAbort(self, event):
00672         """!Abort running command"""
00673         event = goutput.wxCmdAbort(aborted = True)
00674         wx.PostEvent(self.goutput, event)
00675 
00676     def OnCopy(self, event):
00677         """!Copy the command"""
00678         cmddata = wx.TextDataObject()
00679         # list -> string
00680         cmdstring = ' '.join(self.createCmd(ignoreErrors = True))
00681         cmddata.SetText(cmdstring)
00682         if wx.TheClipboard.Open():
00683 #            wx.TheClipboard.UsePrimarySelection(True)
00684             wx.TheClipboard.SetData(cmddata)
00685             wx.TheClipboard.Close()
00686             self.SetStatusText(_("'%s' copied to clipboard") % \
00687                                     (cmdstring))
00688 
00689     def OnCancel(self, event):
00690         """!Cancel button pressed"""
00691         self.MakeModal(False)
00692         
00693         if self.get_dcmd and \
00694                 self.parent and \
00695                 self.parent.GetName() in ('LayerTree',
00696                                           'MapWindow'):
00697             # display decorations and 
00698             # pressing OK or cancel after setting layer properties
00699             if self.task.name in ['d.barscale','d.legend','d.histogram'] \
00700                 or len(self.parent.GetPyData(self.layer)[0]['cmd']) >=  1:
00701                 self.Hide()
00702             # canceled layer with nothing set
00703             elif len(self.parent.GetPyData(self.layer)[0]['cmd']) < 1:
00704                 self.parent.Delete(self.layer)
00705                 self.Destroy()
00706         else:
00707             # cancel for non-display commands
00708             self.Destroy()
00709 
00710     def OnHelp(self, event):
00711         """!Show manual page (switch to the 'Manual' notebook page)"""
00712         if self.notebookpanel.notebook.GetPageIndexByName('manual') > -1:
00713             self.notebookpanel.notebook.SetSelectionByName('manual')
00714             self.notebookpanel.OnPageChange(None)
00715         
00716         if event:    
00717             event.Skip()
00718         
00719     def createCmd(self, ignoreErrors = False, ignoreRequired = False):
00720         """!Create command string (python list)"""
00721         return self.notebookpanel.createCmd(ignoreErrors = ignoreErrors,
00722                                             ignoreRequired = ignoreRequired)
00723 
00724 class cmdPanel(wx.Panel):
00725     """!A panel containing a notebook dividing in tabs the different
00726     guisections of the GRASS cmd.
00727     """
00728     def __init__(self, parent, task, id = wx.ID_ANY, mainFrame = None, *args, **kwargs):
00729         if mainFrame:
00730             self.parent = mainFrame
00731         else:
00732             self.parent = parent
00733         self.task = task
00734         
00735         wx.Panel.__init__(self, parent, id = id, *args, **kwargs)
00736         
00737         # Determine tab layout
00738         sections = []
00739         is_section = {}
00740         not_hidden = [ p for p in self.task.params + self.task.flags if not p.get('hidden', False) ==  True ]
00741 
00742         self.label_id = [] # wrap titles on resize
00743 
00744         self.Bind(wx.EVT_SIZE, self.OnSize)
00745         
00746         for task in not_hidden:
00747             if task.get('required', False):
00748                 # All required go into Main, even if they had defined another guisection
00749                 task['guisection'] = _('Required')
00750             if task.get('guisection','') ==  '':
00751                 # Undefined guisections end up into Options
00752                 task['guisection'] = _('Optional')
00753             if task['guisection'] not in is_section:
00754                 # We do it like this to keep the original order, except for Main which goes first
00755                 is_section[task['guisection']] = 1
00756                 sections.append(task['guisection'])
00757             else:
00758                 is_section[ task['guisection'] ] +=  1
00759         del is_section
00760 
00761         # 'Required' tab goes first, 'Optional' as the last one
00762         for (newidx,content) in [ (0,_('Required')), (len(sections)-1,_('Optional')) ]:
00763             if content in sections:
00764                 idx = sections.index(content)
00765                 sections[idx:idx+1] = []
00766                 sections[newidx:newidx] =  [content]
00767 
00768         panelsizer = wx.BoxSizer(orient = wx.VERTICAL)
00769 
00770         # Build notebook
00771         self.notebook = GNotebook(self, style = globalvar.FNPageStyle)
00772         self.notebook.SetTabAreaColour(globalvar.FNPageColor)
00773         self.notebook.Bind(FN.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChange)
00774 
00775         tab = {}
00776         tabsizer = {}
00777         for section in sections:
00778             tab[section] = scrolled.ScrolledPanel(parent = self.notebook)
00779             tab[section].SetScrollRate(10, 10)
00780             tabsizer[section] = wx.BoxSizer(orient = wx.VERTICAL)
00781             self.notebook.AddPage(page = tab[section], text = section)
00782         
00783         # are we running from command line?
00784         ### add 'command output' tab regardless standalone dialog
00785         if self.parent.GetName() ==  "MainFrame" and self.parent.get_dcmd is None:
00786             self.goutput = goutput.GMConsole(parent = self, margin = False)
00787             self.outpage = self.notebook.AddPage(page = self.goutput, text = _("Command output"), name = 'output')
00788         else:
00789             self.goutput = None
00790         
00791         self.manual_tab = HelpPanel(parent = self, grass_command = self.task.name)
00792         if not self.manual_tab.IsFile():
00793             self.manual_tab.Hide()
00794         else:
00795             self.notebook.AddPage(page = self.manual_tab, text = _("Manual"), name = 'manual')
00796         
00797         self.notebook.SetSelection(0)
00798 
00799         panelsizer.Add(item = self.notebook, proportion = 1, flag = wx.EXPAND)
00800 
00801         #
00802         # flags
00803         #
00804         text_style = wx.FONTWEIGHT_NORMAL
00805         visible_flags = [ f for f in self.task.flags if not f.get('hidden', False) ==  True ]
00806         for f in visible_flags:
00807             which_sizer = tabsizer[ f['guisection'] ]
00808             which_panel = tab[ f['guisection'] ]
00809             # if label is given: description -> tooltip
00810             if f.get('label','') !=  '':
00811                 title = text_beautify(f['label'])
00812                 tooltip = text_beautify(f['description'], width = -1)
00813             else:
00814                 title = text_beautify(f['description'])
00815                 tooltip = None
00816             title_sizer = wx.BoxSizer(wx.HORIZONTAL)
00817             rtitle_txt = wx.StaticText(parent = which_panel,
00818                                        label = '(' + f['name'] + ')')
00819             chk = wx.CheckBox(parent = which_panel, label = title, style = wx.NO_BORDER)
00820             self.label_id.append(chk.GetId())
00821             if tooltip:
00822                 chk.SetToolTipString(tooltip)
00823             chk.SetValue(f.get('value', False))
00824             title_sizer.Add(item = chk, proportion = 1,
00825                             flag = wx.EXPAND)
00826             title_sizer.Add(item = rtitle_txt, proportion = 0,
00827                             flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL)
00828             which_sizer.Add(item = title_sizer, proportion = 0,
00829                             flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
00830             f['wxId'] = [ chk.GetId(), ]
00831             chk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
00832             
00833             if self.parent.GetName() ==  'MainFrame' and self.parent.modeler:
00834                 parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
00835                                      label = _("Parameterized in model"))
00836                 parChk.SetName('ModelParam')
00837                 parChk.SetValue(f.get('parameterized', False))
00838                 if 'wxId' in f:
00839                     f['wxId'].append(parChk.GetId())
00840                 else:
00841                     f['wxId'] = [ parChk.GetId() ]
00842                 parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
00843                 which_sizer.Add(item = parChk, proportion = 0,
00844                                 flag = wx.LEFT, border = 20)
00845             
00846             if f['name'] in ('verbose', 'quiet'):
00847                 chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity)
00848                 vq = UserSettings.Get(group = 'cmd', key = 'verbosity', subkey = 'selection')
00849                 if f['name'] ==  vq:
00850                     chk.SetValue(True)
00851                     f['value'] = True
00852             elif f['name'] ==  'overwrite' and 'value' not in f:
00853                 chk.SetValue(UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled'))
00854                 f['value'] = UserSettings.Get(group = 'cmd', key = 'overwrite', subkey = 'enabled')
00855                 
00856         #
00857         # parameters
00858         #
00859         visible_params = [ p for p in self.task.params if not p.get('hidden', False) ==  True ]
00860         
00861         try:
00862             first_param = visible_params[0]
00863         except IndexError:
00864             first_param = None
00865         
00866         for p in visible_params:
00867             which_sizer = tabsizer[ p['guisection'] ]
00868             which_panel = tab[ p['guisection'] ]
00869             # if label is given -> label and description -> tooltip
00870             # otherwise description -> lavel
00871             if p.get('label','') !=  '':
00872                 title = text_beautify(p['label'])
00873                 tooltip = text_beautify(p['description'], width = -1)
00874             else:
00875                 title = text_beautify(p['description'])
00876                 tooltip = None
00877             txt = None
00878 
00879             # text style (required -> bold)
00880             if not p.get('required', False):
00881                 text_style = wx.FONTWEIGHT_NORMAL
00882             else:
00883                 text_style = wx.FONTWEIGHT_BOLD
00884 
00885             # title sizer (description, name, type)
00886             if (len(p.get('values', [])) > 0) and \
00887                     p.get('multiple', False) and \
00888                     p.get('gisprompt',False) ==  False and \
00889                     p.get('type', '') ==  'string':
00890                 title_txt = wx.StaticBox(parent = which_panel, id = wx.ID_ANY)
00891             else:
00892                 title_sizer = wx.BoxSizer(wx.HORIZONTAL)
00893                 title_txt = wx.StaticText(parent = which_panel)
00894                 if p['key_desc']:
00895                     ltype = ','.join(p['key_desc'])
00896                 else:
00897                     ltype = p['type']
00898                 rtitle_txt = wx.StaticText(parent = which_panel,
00899                                            label = '(' + p['name'] + '=' + ltype + ')')
00900                 title_sizer.Add(item = title_txt, proportion = 1,
00901                                 flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)
00902                 title_sizer.Add(item = rtitle_txt, proportion = 0,
00903                                 flag = wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP, border = 5)
00904                 which_sizer.Add(item = title_sizer, proportion = 0,
00905                                 flag = wx.EXPAND)
00906             self.label_id.append(title_txt.GetId())
00907 
00908             # title expansion
00909             if p.get('multiple', False) and len(p.get('values','')) ==  0:
00910                 title = _("[multiple]") + " " + title
00911                 if p.get('value','') ==   '' :
00912                     p['value'] = p.get('default','')
00913 
00914             if (len(p.get('values', [])) > 0):
00915                 valuelist      = map(str, p.get('values',[]))
00916                 valuelist_desc = map(unicode, p.get('values_desc',[]))
00917 
00918                 if p.get('multiple', False) and \
00919                         p.get('gisprompt',False) ==  False and \
00920                         p.get('type', '') ==  'string':
00921                     title_txt.SetLabel(" %s: (%s, %s) " % (title, p['name'], p['type']))
00922                     if valuelist_desc:
00923                         hSizer = wx.StaticBoxSizer(box = title_txt, orient = wx.VERTICAL)
00924                     else:
00925                         hSizer = wx.StaticBoxSizer(box = title_txt, orient = wx.HORIZONTAL)
00926                     isEnabled = {}
00927                     # copy default values
00928                     if p['value'] ==  '':
00929                         p['value'] = p.get('default', '')
00930                         
00931                     for defval in p.get('value', '').split(','):
00932                         isEnabled[ defval ] = 'yes'
00933                         # for multi checkboxes, this is an array of all wx IDs
00934                         # for each individual checkbox
00935                         p['wxId'] = list()
00936                     idx = 0
00937                     for val in valuelist:
00938                         try:
00939                             label = valuelist_desc[idx]
00940                         except IndexError:
00941                             label = val
00942                         
00943                         chkbox = wx.CheckBox(parent = which_panel,
00944                                              label = text_beautify(label))
00945                         p[ 'wxId' ].append(chkbox.GetId())
00946                         if val in isEnabled:
00947                             chkbox.SetValue(True)
00948                         hSizer.Add(item = chkbox, proportion = 0,
00949                                     flag = wx.ADJUST_MINSIZE | wx.ALL, border = 1)
00950                         chkbox.Bind(wx.EVT_CHECKBOX, self.OnCheckBoxMulti)
00951                         idx +=  1
00952                         
00953                     which_sizer.Add(item = hSizer, proportion = 0,
00954                                     flag = wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, border = 5)
00955                 elif p.get('gisprompt', False) ==  False:
00956                     if len(valuelist) ==  1: # -> textctrl
00957                         title_txt.SetLabel("%s (%s %s):" % (title, _('valid range'),
00958                                                             str(valuelist[0])))
00959                         
00960                         if p.get('type', '') ==  'integer' and \
00961                                 not p.get('multiple', False):
00962 
00963                             # for multiple integers use textctrl instead of spinsctrl
00964                             try:
00965                                 minValue, maxValue = map(int, valuelist[0].split('-'))
00966                             except ValueError:
00967                                 minValue = -1e6
00968                                 maxValue = 1e6
00969                             txt2 = wx.SpinCtrl(parent = which_panel, id = wx.ID_ANY, size = globalvar.DIALOG_SPIN_SIZE,
00970                                                min = minValue, max = maxValue)
00971                             txt2.SetName("SpinCtrl")
00972                             style = wx.BOTTOM | wx.LEFT
00973                         else:
00974                             txt2 = wx.TextCtrl(parent = which_panel, value = p.get('default',''))
00975                             txt2.SetName("TextCtrl")
00976                             style = wx.EXPAND | wx.BOTTOM | wx.LEFT
00977                         
00978                         value = self._getValue(p)
00979                         # parameter previously set
00980                         if value:
00981                             if txt2.GetName() ==  "SpinCtrl":
00982                                 txt2.SetValue(int(value))
00983                             else:
00984                                 txt2.SetValue(value)
00985                         
00986                         which_sizer.Add(item = txt2, proportion = 0,
00987                                         flag = style, border = 5)
00988 
00989                         p['wxId'] = [ txt2.GetId(), ]
00990                         txt2.Bind(wx.EVT_TEXT, self.OnSetValue)
00991                     else:
00992                         # list of values (combo)
00993                         title_txt.SetLabel(title + ':')
00994                         cb = wx.ComboBox(parent = which_panel, id = wx.ID_ANY, value = p.get('default',''),
00995                                          size = globalvar.DIALOG_COMBOBOX_SIZE,
00996                                          choices = valuelist, style = wx.CB_DROPDOWN)
00997                         value = self._getValue(p)
00998                         if value:
00999                             cb.SetValue(value) # parameter previously set
01000                         which_sizer.Add( item=cb, proportion=0,
01001                                          flag=wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border=5)
01002                         p['wxId'] = [ cb.GetId(), ]
01003                         cb.Bind( wx.EVT_COMBOBOX, self.OnSetValue)
01004                         cb.Bind(wx.EVT_TEXT, self.OnSetValue)
01005             
01006             # text entry
01007             if (p.get('type','string') in ('string','integer','float')
01008                 and len(p.get('values',[])) ==  0
01009                 and p.get('gisprompt',False) ==  False
01010                 and p.get('prompt','') !=  'color'):
01011 
01012                 title_txt.SetLabel(title + ':')
01013                 if p.get('multiple', False) or \
01014                         p.get('type', 'string') ==  'string' or \
01015                         len(p.get('key_desc', [])) > 1:
01016                     txt3 = wx.TextCtrl(parent = which_panel, value = p.get('default',''))
01017                     
01018                     value = self._getValue(p)
01019                     if value:
01020                         # parameter previously set
01021                         txt3.SetValue(str(value))
01022                     
01023                     txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
01024                     style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
01025                 else:
01026                     minValue = -1e9
01027                     maxValue = 1e9
01028                     if p.get('type', '') ==  'integer':
01029                         txt3 = wx.SpinCtrl(parent = which_panel, value = p.get('default',''),
01030                                            size = globalvar.DIALOG_SPIN_SIZE,
01031                                            min = minValue, max = maxValue)
01032                         style = wx.BOTTOM | wx.LEFT | wx.RIGHT
01033                         
01034                         value = self._getValue(p)
01035                         if value:
01036                             txt3.SetValue(int(value)) # parameter previously set
01037                         
01038                         txt3.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
01039                     else:
01040                         txt3 = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
01041                                            validator = FloatValidator())
01042                         style = wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT
01043                         
01044                         value = self._getValue(p)
01045                         if value:
01046                             txt3.SetValue(str(value)) # parameter previously set
01047                     
01048                 txt3.Bind(wx.EVT_TEXT, self.OnSetValue)
01049                 
01050                 which_sizer.Add(item = txt3, proportion = 0,
01051                                 flag = style, border = 5)
01052                 p['wxId'] = [ txt3.GetId(), ]
01053 
01054             #
01055             # element selection tree combobox (maps, icons, regions, etc.)
01056             #
01057             if p.get('gisprompt', False) ==  True:
01058                 title_txt.SetLabel(title + ':')
01059                 # GIS element entry
01060                 if p.get('prompt','') not in ('color',
01061                                               'color_none',
01062                                               'subgroup',
01063                                               'dbdriver',
01064                                               'dbname',
01065                                               'dbtable',
01066                                               'dbcolumn',
01067                                               'layer',
01068                                               'layer_all',
01069                                               'layer_zero',
01070                                               'location',
01071                                               'mapset',
01072                                               'dbase') and \
01073                        p.get('element', '') !=  'file':
01074                     multiple = p.get('multiple', False)
01075                     if p.get('age', '') ==  'new':
01076                         mapsets = [grass.gisenv()['MAPSET'],]
01077                     else:
01078                         mapsets = None
01079                     if self.task.name in ('r.proj', 'v.proj') \
01080                             and p.get('name', '') ==  'input':
01081                         if self.task.name ==  'r.proj':
01082                             isRaster = True
01083                         else:
01084                             isRaster = False
01085                         selection = gselect.ProjSelect(parent = which_panel,
01086                                                        isRaster = isRaster)
01087                         p['wxId'] = [ selection.GetId(), ]
01088                         selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
01089                         selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
01090                     else:
01091                         selection = gselect.Select(parent = which_panel, id = wx.ID_ANY,
01092                                                    size = globalvar.DIALOG_GSELECT_SIZE,
01093                                                    type = p.get('element', ''),
01094                                                    multiple = multiple, mapsets = mapsets)
01095                         
01096                         # A select.Select is a combobox with two children: a textctl and a popupwindow;
01097                         # we target the textctl here
01098                         textWin = selection.GetTextCtrl()
01099                         p['wxId'] = [ textWin.GetId(), ]
01100                         textWin.Bind(wx.EVT_TEXT, self.OnSetValue)
01101                     
01102                     value = self._getValue(p)
01103                     if value:
01104                         selection.SetValue(value) # parameter previously set
01105 
01106                     which_sizer.Add(item=selection, proportion=0,
01107                                     flag=wx.ADJUST_MINSIZE| wx.BOTTOM | wx.LEFT | wx.RIGHT, border=5)
01108                     
01109                     if p.get('prompt', '') in ('vector', 'group'):
01110                         selection.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
01111                 # subgroup
01112                 elif p.get('prompt', '') ==  'subgroup':
01113                     selection = gselect.SubGroupSelect(parent = which_panel)
01114                     p['wxId'] = [ selection.GetId() ]
01115                     selection.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
01116                     which_sizer.Add(item = selection, proportion = 0,
01117                                     flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.TOP | wx.ALIGN_CENTER_VERTICAL,
01118                                     border = 5)
01119                 
01120                 # layer, dbdriver, dbname, dbcolumn, dbtable entry
01121                 elif p.get('prompt', '') in ('dbdriver',
01122                                              'dbname',
01123                                              'dbtable',
01124                                              'dbcolumn',
01125                                              'layer',
01126                                              'layer_all',
01127                                              'layer_zero',
01128                                              'location',
01129                                              'mapset',
01130                                              'dbase'):
01131                     if p.get('multiple', 'no') ==  'yes':
01132                         win = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
01133                                           size = globalvar.DIALOG_TEXTCTRL_SIZE)
01134                         win.Bind(wx.EVT_TEXT, self.OnSetValue)
01135                     else:
01136                         value = self._getValue(p)
01137                         
01138                         if p.get('prompt', '') in ('layer',
01139                                                    'layer_all',
01140                                                    'layer_zero'):
01141                             
01142                             if p.get('age', 'old_layer') == 'old_layer':
01143                                 initial = list()
01144                                 if p.get('prompt', '') ==  'layer_all':
01145                                     initial.insert(0, '-1')
01146                                 elif p.get('prompt', '') == 'layer_zero':
01147                                     initial.insert(0, '0')
01148                                 lyrvalue = p.get('default')
01149                                 if lyrvalue != '':
01150                                     if lyrvalue not in initial:
01151                                         initial.append(str(lyrvalue))
01152                                 lyrvalue = p.get('value')
01153                                 if lyrvalue != '':
01154                                     if lyrvalue not in initial:
01155                                         initial.append(str(lyrvalue))
01156 
01157                                 win = gselect.LayerSelect(parent = which_panel,
01158                                                           initial = initial,
01159                                                           default = p['default'])
01160                                 p['wxGetValue'] = win.GetStringSelection
01161                                 win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
01162                                 win.Bind(wx.EVT_TEXT, self.OnSetValue)
01163                                 win.SetValue(str(value))    # default or previously set value
01164                             else:
01165                                 win = wx.SpinCtrl(parent = which_panel, id = wx.ID_ANY,
01166                                                   min = 1, max = 100, initial = int(p['default']))
01167                                 win.Bind(wx.EVT_SPINCTRL, self.OnSetValue)
01168                                 win.SetValue(int(value))    # default or previously set value
01169 
01170                         elif p.get('prompt', '') ==  'dbdriver':
01171                             win = gselect.DriverSelect(parent = which_panel,
01172                                                        choices = p.get('values', []),
01173                                                        value = value)
01174                             p['wxGetValue'] = win.GetStringSelection
01175                             win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
01176                             win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
01177                         elif p.get('prompt', '') ==  'dbname':
01178                             win = gselect.DatabaseSelect(parent = which_panel,
01179                                                          value = value)
01180                             win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
01181                             win.Bind(wx.EVT_TEXT, self.OnSetValue)
01182                         
01183                         elif p.get('prompt', '') == 'dbtable':
01184                             if p.get('age', 'old_dbtable') == 'old_dbtable':
01185                                 win = gselect.TableSelect(parent=which_panel)
01186                                 
01187                                 p['wxGetValue'] = win.GetStringSelection
01188                                 win.Bind(wx.EVT_COMBOBOX, self.OnUpdateSelection)
01189                                 win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
01190                             else:
01191                                 win = wx.TextCtrl(parent = which_panel, value = p.get('default',''),
01192                                                   size = globalvar.DIALOG_TEXTCTRL_SIZE)
01193                                 win.Bind(wx.EVT_TEXT, self.OnSetValue)
01194                         elif p.get('prompt', '') ==  'dbcolumn':
01195                             win = gselect.ColumnSelect(parent = which_panel,
01196                                                        value = value,
01197                                                        param = p)
01198                             win.Bind(wx.EVT_COMBOBOX, self.OnSetValue)
01199                             win.Bind(wx.EVT_TEXT,     self.OnSetValue)
01200 
01201                         elif p.get('prompt', '') ==  'location':
01202                             win = gselect.LocationSelect(parent = which_panel,
01203                                                          value = value)
01204                             win.Bind(wx.EVT_COMBOBOX,     self.OnUpdateSelection)
01205                             win.Bind(wx.EVT_COMBOBOX,     self.OnSetValue)
01206                         
01207                         elif p.get('prompt', '') ==  'mapset':
01208                             win = gselect.MapsetSelect(parent = which_panel,
01209                                                        value = value)
01210                             win.Bind(wx.EVT_COMBOBOX,     self.OnUpdateSelection)
01211                             win.Bind(wx.EVT_COMBOBOX,     self.OnSetValue)
01212                             
01213                         elif p.get('prompt', '') ==  'dbase':
01214                             win = gselect.DbaseSelect(parent = which_panel,
01215                                                       changeCallback = self.OnSetValue)
01216                             win.Bind(wx.EVT_TEXT, self.OnUpdateSelection)
01217                             p['wxId'] = [ win.GetChildren()[1].GetId() ]
01218                             
01219                     if 'wxId' not in p:
01220                         try:
01221                             p['wxId'] = [ win.GetId(), ]
01222                         except AttributeError:
01223                             pass
01224                     
01225                     which_sizer.Add(item = win, proportion = 0,
01226                                     flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
01227                 # color entry
01228                 elif p.get('prompt', '') in ('color',
01229                                              'color_none'):
01230                     default_color = (200,200,200)
01231                     label_color = _("Select Color")
01232                     if p.get('default','') !=  '':
01233                         default_color, label_color = color_resolve(p['default'])
01234                     if p.get('value','') !=  '': # parameter previously set
01235                         default_color, label_color = color_resolve(p['value'])
01236                     if p.get('prompt', '') ==  'color_none':
01237                         this_sizer = wx.BoxSizer(orient = wx.HORIZONTAL)
01238                     else:
01239                         this_sizer = which_sizer
01240                     btn_colour = csel.ColourSelect(parent = which_panel, id = wx.ID_ANY,
01241                                                    label = label_color, colour = default_color,
01242                                                    pos = wx.DefaultPosition, size = (150,-1))
01243                     this_sizer.Add(item = btn_colour, proportion = 0,
01244                                    flag = wx.ADJUST_MINSIZE | wx.BOTTOM | wx.LEFT, border = 5)
01245                     # For color selectors, this is a two-member array, holding the IDs of
01246                     # the selector proper and either a "transparent" button or None
01247                     p['wxId'] = [btn_colour.GetId(),]
01248                     btn_colour.Bind(csel.EVT_COLOURSELECT,  self.OnColorChange)
01249                     if p.get('prompt', '') ==  'color_none':
01250                         none_check = wx.CheckBox(which_panel, wx.ID_ANY, _("Transparent"))
01251                         if p.get('value','') !=  '' and p.get('value',[''])[0] ==  "none":
01252                             none_check.SetValue(True)
01253                         else:
01254                             none_check.SetValue(False)
01255                         this_sizer.Add(item = none_check, proportion = 0,
01256                                        flag = wx.ADJUST_MINSIZE | wx.LEFT | wx.RIGHT | wx.TOP, border = 5)
01257                         which_sizer.Add(this_sizer)
01258                         none_check.Bind(wx.EVT_CHECKBOX, self.OnColorChange)
01259                         p['wxId'].append(none_check.GetId())
01260                     else:
01261                         p['wxId'].append(None)
01262                 # file selector
01263                 elif p.get('prompt','') !=  'color' and p.get('element', '') ==  'file':
01264                     fbb = filebrowse.FileBrowseButton(parent = which_panel, id = wx.ID_ANY, fileMask = '*',
01265                                                       size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
01266                                                       dialogTitle = _('Choose %s') % \
01267                                                           p.get('description',_('File')),
01268                                                       buttonText = _('Browse'),
01269                                                       startDirectory = os.getcwd(), fileMode = 0,
01270                                                       changeCallback = self.OnSetValue)
01271                     value = self._getValue(p)
01272                     if value:
01273                         fbb.SetValue(value) # parameter previously set
01274                     which_sizer.Add(item = fbb, proportion = 0,
01275                                     flag = wx.EXPAND | wx.RIGHT, border = 5)
01276                     
01277                     # A file browse button is a combobox with two children:
01278                     # a textctl and a button;
01279                     # we have to target the button here
01280                     p['wxId'] = [ fbb.GetChildren()[1].GetId() ]
01281                     if p.get('age', 'new_file') == 'old_file' and \
01282                             UserSettings.Get(group='cmd', key='interactiveInput', subkey='enabled'):
01283                         # widget for interactive input
01284                         ifbb = wx.TextCtrl(parent = which_panel, id = wx.ID_ANY,
01285                                            style = wx.TE_MULTILINE,
01286                                            size = (-1, 75))
01287                         if p.get('value', '') and os.path.isfile(p['value']):
01288                             f = open(p['value'])
01289                             ifbb.SetValue(''.join(f.readlines()))
01290                             f.close()
01291                     
01292                         ifbb.Bind(wx.EVT_TEXT, self.OnFileText)
01293                         which_sizer.Add(item = wx.StaticText(parent = which_panel, id = wx.ID_ANY,
01294                                                              label = _('or enter values interactively')),
01295                                         proportion = 0,
01296                                         flag = wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM, border = 5)
01297                         which_sizer.Add(item = ifbb, proportion = 1,
01298                                         flag = wx.EXPAND | wx.RIGHT | wx.LEFT, border = 5)
01299                         p['wxId'].append(ifbb.GetId())
01300             
01301             if self.parent.GetName() ==  'MainFrame' and self.parent.modeler:
01302                 parChk = wx.CheckBox(parent = which_panel, id = wx.ID_ANY,
01303                                      label = _("Parameterized in model"))
01304                 parChk.SetName('ModelParam')
01305                 parChk.SetValue(p.get('parameterized', False))
01306                 if 'wxId' in p:
01307                     p['wxId'].append(parChk.GetId())
01308                 else:
01309                     p['wxId'] = [ parChk.GetId() ]
01310                 parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue)
01311                 which_sizer.Add(item = parChk, proportion = 0,
01312                                 flag = wx.LEFT, border = 20)
01313                 
01314             if title_txt is not None:
01315                 # create tooltip if given
01316                 if len(p['values_desc']) > 0:
01317                     if tooltip:
01318                         tooltip +=  2 * os.linesep
01319                     else:
01320                         tooltip = ''
01321                     if len(p['values']) ==  len(p['values_desc']):
01322                         for i in range(len(p['values'])):
01323                             tooltip +=  p['values'][i] + ': ' + p['values_desc'][i] + os.linesep
01324                     tooltip.strip(os.linesep)
01325                 if tooltip:
01326                     title_txt.SetToolTipString(tooltip)
01327 
01328             if p ==  first_param:
01329                 if 'wxId' in p and len(p['wxId']) > 0:
01330                     win = self.FindWindowById(p['wxId'][0])
01331                     win.SetFocus()
01332         
01333         #
01334         # set widget relations for OnUpdateSelection
01335         #
01336         pMap = None
01337         pLayer = []
01338         pDriver = None
01339         pDatabase = None
01340         pTable = None
01341         pColumn = []
01342         pGroup = None
01343         pSubGroup = None
01344         pDbase = None
01345         pLocation = None
01346         pMapset = None
01347         for p in self.task.params:
01348             if p.get('gisprompt', False) ==  False:
01349                 continue
01350             
01351             prompt = p.get('element', '')
01352             if prompt in ('cell', 'vector'):
01353                 name = p.get('name', '')
01354                 if name in ('map', 'input'):
01355                     pMap = p
01356             elif prompt ==  'layer':
01357                 pLayer.append(p)
01358             elif prompt ==  'dbcolumn':
01359                 pColumn.append(p)
01360             elif prompt ==  'dbdriver':
01361                 pDriver = p
01362             elif prompt ==  'dbname':
01363                 pDatabase = p
01364             elif prompt ==  'dbtable':
01365                 pTable = p
01366             elif prompt ==  'group':
01367                 pGroup = p
01368             elif prompt ==  'subgroup':
01369                 pSubGroup = p
01370             elif prompt ==  'dbase':
01371                 pDbase = p
01372             elif prompt ==  'location':
01373                 pLocation = p
01374             elif prompt ==  'mapset':
01375                 pMapset = p
01376         
01377         pColumnIds = []
01378         for p in pColumn:
01379             pColumnIds +=  p['wxId']
01380         pLayerIds = []
01381         for p in pLayer:
01382             pLayerIds +=  p['wxId']
01383         
01384         if pMap:
01385             pMap['wxId-bind'] = copy.copy(pColumnIds)
01386             if pLayer:
01387                 pMap['wxId-bind'] +=  pLayerIds
01388         if pLayer:
01389             for p in pLayer:
01390                 p['wxId-bind'] = copy.copy(pColumnIds)
01391         
01392         if pDriver and pTable:
01393             pDriver['wxId-bind'] = pTable['wxId']
01394 
01395         if pDatabase and pTable:
01396             pDatabase['wxId-bind'] = pTable['wxId']
01397 
01398         if pTable and pColumnIds:
01399             pTable['wxId-bind'] = pColumnIds
01400         
01401         if pGroup and pSubGroup:
01402             pGroup['wxId-bind'] = pSubGroup['wxId']
01403 
01404         if pDbase and pLocation:
01405             pDbase['wxId-bind'] = pLocation['wxId']
01406 
01407         if pLocation and pMapset:
01408             pLocation['wxId-bind'] = pMapset['wxId']
01409         
01410         if pLocation and pMapset and pMap:
01411             pLocation['wxId-bind'] +=  pMap['wxId']
01412             pMapset['wxId-bind'] = pMap['wxId']
01413         
01414         #
01415         # determine panel size
01416         #
01417         maxsizes = (0, 0)
01418         for section in sections:
01419             tab[section].SetSizer(tabsizer[section])
01420             tabsizer[section].Fit(tab[section])
01421             tab[section].Layout()
01422             minsecsizes = tabsizer[section].GetSize()
01423             maxsizes = map(lambda x: max(maxsizes[x], minsecsizes[x]), (0, 1))
01424 
01425         # TODO: be less arbitrary with these 600
01426         self.panelMinHeight = 100
01427         self.constrained_size = (min(600, maxsizes[0]) + 25, min(400, maxsizes[1]) + 25)
01428         for section in sections:
01429             tab[section].SetMinSize((self.constrained_size[0], self.panelMinHeight))
01430         
01431         if self.manual_tab.IsLoaded():
01432             self.manual_tab.SetMinSize((self.constrained_size[0], self.panelMinHeight))
01433         
01434         self.SetSizer(panelsizer)
01435         panelsizer.Fit(self.notebook)
01436         
01437         self.Bind(EVT_DIALOG_UPDATE, self.OnUpdateDialog)
01438 
01439     def _getValue(self, p):
01440         """!Get value or default value of given parameter
01441 
01442         @param p parameter directory
01443         """
01444         if p.get('value', '') !=  '':
01445             return p['value']
01446         return p.get('default', '')
01447         
01448     def OnFileText(self, event):
01449         """File input interactively entered"""
01450         text = event.GetString()
01451         p = self.task.get_param(value = event.GetId(), element = 'wxId', raiseError = False)
01452         if not p:
01453             return # should not happen
01454         win = self.FindWindowById(p['wxId'][0])
01455         if text:
01456             filename = win.GetValue()
01457             if not filename:
01458                 # outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
01459                 filename = grass.tempfile()
01460                 win.SetValue(filename)
01461                 
01462             f = open(filename, "w")
01463             try:
01464                 f.write(text)
01465             finally:
01466                 f.close()
01467         else:
01468             win.SetValue('')
01469         
01470     def OnUpdateDialog(self, event):
01471         for fn, kwargs in event.data.iteritems():
01472             fn(**kwargs)
01473         
01474         self.parent.updateValuesHook()
01475         
01476     def OnVerbosity(self, event):
01477         """!Verbosity level changed"""
01478         verbose = self.FindWindowById(self.task.get_flag('verbose')['wxId'][0])
01479         quiet = self.FindWindowById(self.task.get_flag('quiet')['wxId'][0])
01480         if event.IsChecked():
01481             if event.GetId() ==  verbose.GetId():
01482                 if quiet.IsChecked():
01483                     quiet.SetValue(False)
01484                     self.task.get_flag('quiet')['value'] = False
01485             else:
01486                 if verbose.IsChecked():
01487                     verbose.SetValue(False)
01488                     self.task.get_flag('verbose')['value'] = False
01489 
01490         event.Skip()
01491 
01492     def OnPageChange(self, event):
01493         if not event:
01494             sel = self.notebook.GetSelection()
01495         else:
01496             sel = event.GetSelection()
01497         
01498         idx = self.notebook.GetPageIndexByName('manual')
01499         if idx > -1 and sel ==  idx:
01500             # calling LoadPage() is strangely time-consuming (only first call)
01501             # FIXME: move to helpPage.__init__()
01502             if not self.manual_tab.IsLoaded():
01503                 wx.Yield()
01504                 self.manual_tab.LoadPage()
01505 
01506         self.Layout()
01507 
01508     def OnColorChange(self, event):
01509         myId = event.GetId()
01510         for p in self.task.params:
01511             if 'wxId' in p and myId in p['wxId']:
01512                 has_button = p['wxId'][1] is not None
01513                 if has_button and wx.FindWindowById(p['wxId'][1]).GetValue() ==  True:
01514                     p[ 'value' ] = 'none'
01515                 else:
01516                     colorchooser = wx.FindWindowById(p['wxId'][0])
01517                     new_color = colorchooser.GetValue()[:]
01518                     # This is weird: new_color is a 4-tuple and new_color[:] is a 3-tuple
01519                     # under wx2.8.1
01520                     new_label = rgb2str.get(new_color, ':'.join(map(str,new_color)))
01521                     colorchooser.SetLabel(new_label)
01522                     colorchooser.SetColour(new_color)
01523                     colorchooser.Refresh()
01524                     p[ 'value' ] = colorchooser.GetLabel()
01525         self.OnUpdateValues()
01526 
01527     def OnUpdateValues(self, event = None):
01528         """!If we were part of a richer interface, report back the
01529         current command being built.
01530 
01531         This method should be set by the parent of this panel if
01532         needed. It's a hook, actually.  Beware of what is 'self' in
01533         the method def, though. It will be called with no arguments.
01534         """
01535         pass
01536 
01537     def OnCheckBoxMulti(self, event):
01538         """!Fill the values as a ','-separated string according to
01539         current status of the checkboxes.
01540         """
01541         me = event.GetId()
01542         theParam = None
01543         for p in self.task.params:
01544             if 'wxId' in p and me in p['wxId']:
01545                 theParam = p
01546                 myIndex = p['wxId'].index(me)
01547 
01548         # Unpack current value list
01549         currentValues = {}
01550         for isThere in theParam.get('value', '').split(','):
01551             currentValues[isThere] = 1
01552         theValue = theParam['values'][myIndex]
01553 
01554         if event.Checked():
01555             currentValues[ theValue ] = 1
01556         else:
01557             del currentValues[ theValue ]
01558 
01559         # Keep the original order, so that some defaults may be recovered
01560         currentValueList = [] 
01561         for v in theParam['values']:
01562             if v in currentValues:
01563                 currentValueList.append(v)
01564 
01565         # Pack it back
01566         theParam['value'] = ','.join(currentValueList)
01567 
01568         self.OnUpdateValues()
01569 
01570     def OnSetValue(self, event):
01571         """!Retrieve the widget value and set the task value field
01572         accordingly.
01573         
01574         Use for widgets that have a proper GetValue() method, i.e. not
01575         for selectors.
01576         """
01577         myId = event.GetId()
01578         me = wx.FindWindowById(myId)
01579         name = me.GetName()
01580 
01581         found = False
01582         for porf in self.task.params + self.task.flags:
01583             if 'wxId' not in porf:
01584                 continue
01585             if myId in porf['wxId']:
01586                 found = True
01587                 break
01588         
01589         if not found:
01590             return
01591         
01592         if name in ('DriverSelect', 'TableSelect',
01593                     'LocationSelect', 'MapsetSelect', 'ProjSelect'):
01594             porf['value'] = me.GetStringSelection()
01595         elif name ==  'GdalSelect':
01596             porf['value'] = event.dsn
01597         elif name ==  'ModelParam':
01598             porf['parameterized'] = me.IsChecked()
01599         elif name ==  'LayerSelect':
01600             porf['value'] = me.GetValue()
01601         else:
01602             porf['value'] = me.GetValue()
01603         
01604         self.OnUpdateValues(event)
01605         
01606         event.Skip()
01607         
01608     def OnUpdateSelection(self, event):
01609         """!Update dialog (layers, tables, columns, etc.)
01610         """
01611         if not hasattr(self.parent, "updateThread"):
01612             if event:
01613                 event.Skip()
01614             return
01615         if event:
01616             self.parent.updateThread.Update(UpdateDialog,
01617                                             self,
01618                                             event,
01619                                             event.GetId(),
01620                                             self.task)
01621         else:
01622             self.parent.updateThread.Update(UpdateDialog,
01623                                             self,
01624                                             None,
01625                                             None,
01626                                             self.task)
01627             
01628     def createCmd(self, ignoreErrors = False, ignoreRequired = False):
01629         """!Produce a command line string (list) or feeding into GRASS.
01630 
01631         @param ignoreErrors True then it will return whatever has been
01632         built so far, even though it would not be a correct command
01633         for GRASS
01634         """
01635         try:
01636             cmd = self.task.getCmd(ignoreErrors = ignoreErrors,
01637                                    ignoreRequired = ignoreRequired)
01638         except ValueError, err:
01639             dlg = wx.MessageDialog(parent = self,
01640                                    message = unicode(err),
01641                                    caption = _("Error in %s") % self.task.name,
01642                                    style = wx.OK | wx.ICON_ERROR | wx.CENTRE)
01643             dlg.ShowModal()
01644             dlg.Destroy()
01645             cmd = None
01646         
01647         return cmd
01648     
01649     def OnSize(self, event):
01650         width = event.GetSize()[0]
01651         fontsize = self.GetFont().GetPointSize()
01652         text_width = max(width / (fontsize - 3), 70)
01653         
01654         for id in self.label_id:
01655             win = self.FindWindowById(id)
01656             label = win.GetLabel()
01657             label_new = '\n'.join(textwrap.wrap(label, text_width))
01658             win.SetLabel(label_new)
01659             
01660         event.Skip()
01661         
01662 class GrassGUIApp(wx.App):
01663     """!Stand-alone GRASS command GUI
01664     """
01665     def __init__(self, grass_task):
01666         self.grass_task = grass_task
01667         wx.App.__init__(self, False)
01668         
01669     def OnInit(self):
01670         msg = self.grass_task.get_error_msg()
01671         if msg:
01672             gcmd.GError(msg + '\n\nTry to set up GRASS_ADDON_PATH variable.')
01673             return True
01674         
01675         self.mf = mainFrame(parent = None, ID = wx.ID_ANY, task_description = self.grass_task)
01676         self.mf.CentreOnScreen()
01677         self.mf.Show(True)
01678         self.SetTopWindow(self.mf)
01679         
01680         return True
01681 
01682 class GUI:
01683     def __init__(self, parent = None, show = True, modal = False,
01684                  centreOnParent = False, checkError = False):
01685         """!Parses GRASS commands when module is imported and used from
01686         Layer Manager.
01687         """
01688         self.parent = parent
01689         self.show   = show
01690         self.modal  = modal
01691         self.centreOnParent = centreOnParent
01692         self.checkError     = checkError
01693         
01694         self.grass_task = None
01695         self.cmd = list()
01696         
01697         global _blackList
01698         if self.parent:
01699             _blackList['enabled'] = True
01700         else:
01701             _blackList['enabled'] = False
01702         
01703     def GetCmd(self):
01704         """!Get validated command"""
01705         return self.cmd
01706     
01707     def ParseCommand(self, cmd, gmpath = None, completed = None):
01708         """!Parse command
01709         
01710         Note: cmd is given as list
01711         
01712         If command is given with options, return validated cmd list:
01713          - add key name for first parameter if not given
01714          - change mapname to mapname@mapset
01715         """
01716         start = time.time()
01717         dcmd_params = {}
01718         if completed == None:
01719             get_dcmd = None
01720             layer = None
01721             dcmd_params = None
01722         else:
01723             get_dcmd = completed[0]
01724             layer = completed[1]
01725             if completed[2]:
01726                 dcmd_params.update(completed[2])
01727         
01728         # parse the interface decription
01729         try:
01730             global _blackList
01731             self.grass_task = gtask.parse_interface(cmd[0],
01732                                                     blackList = _blackList)
01733         except (grass.ScriptError, ValueError), e:
01734             raise gcmd.GException(e.value)
01735             return
01736         
01737         # if layer parameters previously set, re-insert them into dialog
01738         if completed is not None:
01739             if 'params' in dcmd_params:
01740                 self.grass_task.params = dcmd_params['params']
01741             if 'flags' in dcmd_params:
01742                 self.grass_task.flags = dcmd_params['flags']
01743         
01744         err = list()
01745         # update parameters if needed && validate command
01746         if len(cmd) > 1:
01747             i = 0
01748             cmd_validated = [cmd[0]]
01749             for option in cmd[1:]:
01750                 if option[0] ==  '-': # flag
01751                     if option[1] ==  '-':
01752                         self.grass_task.set_flag(option[2:], True)
01753                     else:
01754                         self.grass_task.set_flag(option[1], True)
01755                     cmd_validated.append(option)
01756                 else: # parameter
01757                     try:
01758                         key, value = option.split('=', 1)
01759                     except:
01760                         params = self.grass_task.get_options()['params']
01761                         if params:
01762                             if i == 0: # add key name of first parameter if not given
01763                                 key = params[0]['name']
01764                                 value = option
01765                             else:
01766                                 raise gcmd.GException, _("Unable to parse command '%s'") % ' '.join(cmd)
01767                         else:
01768                             continue
01769                     
01770                     element = self.grass_task.get_param(key, raiseError = False)
01771                     if not element:
01772                         err.append(_("%(cmd)s: parameter '%(key)s' not available") % \
01773                                        { 'cmd' : cmd[0],
01774                                          'key' : key })
01775                         continue
01776                     element = element['element']
01777                     
01778                     if element in ['cell', 'vector']:
01779                         # mapname -> mapname@mapset
01780                         if '@' not in value:
01781                             mapset = grass.find_file(value, element)['mapset']
01782                             curr_mapset = grass.gisenv()['MAPSET']
01783                             if mapset and mapset !=  curr_mapset:
01784                                 value = value + '@' + mapset
01785                     
01786                     self.grass_task.set_param(key, value)
01787                     cmd_validated.append(key + '=' + value)
01788                     i += 1
01789             
01790             # update original command list
01791             cmd = cmd_validated
01792         
01793         if self.show is not None:
01794             self.mf = mainFrame(parent = self.parent, ID = wx.ID_ANY,
01795                                 task_description = self.grass_task,
01796                                 get_dcmd = get_dcmd, layer = layer)
01797         else:
01798             self.mf = None
01799         
01800         if get_dcmd is not None:
01801             # update only propwin reference
01802             get_dcmd(dcmd = None, layer = layer, params = None,
01803                      propwin = self.mf)
01804         
01805         if self.show is not None:
01806             self.mf.notebookpanel.OnUpdateSelection(None)
01807             if self.show is True:
01808                 if self.parent and self.centreOnParent:
01809                     self.mf.CentreOnParent()
01810                 else:
01811                     self.mf.CenterOnScreen()
01812                 self.mf.Show(self.show)
01813                 self.mf.MakeModal(self.modal)
01814             else:
01815                 self.mf.OnApply(None)
01816         
01817         self.cmd = cmd
01818         
01819         if self.checkError:
01820             return self.grass_task, err
01821         else:
01822             return self.grass_task
01823     
01824     def GetCommandInputMapParamKey(self, cmd):
01825         """!Get parameter key for input raster/vector map
01826         
01827         @param cmd module name
01828         
01829         @return parameter key
01830         @return None on failure
01831         """
01832         # parse the interface decription
01833         if not self.grass_task:
01834             enc = locale.getdefaultlocale()[1]
01835             if enc and enc.lower() == "cp932":
01836                 p = re.compile('encoding="' + enc + '"', re.IGNORECASE)
01837                 tree = etree.fromstring(p.sub('encoding="utf-8"',
01838                                               gtask.get_interface_description(cmd).decode(enc).encode('utf-8')))
01839             else:
01840                 tree = etree.fromstring(gtask.get_interface_description(cmd))
01841             self.grass_task = gtask.processTask(tree).get_task()
01842             
01843             for p in self.grass_task.params:
01844                 if p.get('name', '') in ('input', 'map'):
01845                     age = p.get('age', '')
01846                     prompt = p.get('prompt', '')
01847                     element = p.get('element', '') 
01848                     if age ==  'old' and \
01849                             element in ('cell', 'grid3', 'vector') and \
01850                             prompt in ('raster', '3d-raster', 'vector'):
01851                         return p.get('name', None)
01852         return None
01853 
01854 class FloatValidator(wx.PyValidator):
01855     """!Validator for floating-point input"""
01856     def __init__(self):
01857         wx.PyValidator.__init__(self)
01858         
01859         self.Bind(wx.EVT_TEXT, self.OnText) 
01860         
01861     def Clone(self):
01862         """!Clone validator"""
01863         return FloatValidator()
01864 
01865     def Validate(self):
01866         """Validate input"""
01867         textCtrl = self.GetWindow()
01868         text = textCtrl.GetValue()
01869 
01870         if text:
01871             try:
01872                 float(text)
01873             except ValueError:
01874                 textCtrl.SetBackgroundColour("grey")
01875                 textCtrl.SetFocus()
01876                 textCtrl.Refresh()
01877                 return False
01878         
01879         sysColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
01880         textCtrl.SetBackgroundColour(sysColor)
01881         
01882         textCtrl.Refresh()
01883         
01884         return True
01885 
01886     def OnText(self, event):
01887         """!Do validation"""
01888         self.Validate()
01889         
01890         event.Skip()
01891         
01892     def TransferToWindow(self):
01893         return True # Prevent wxDialog from complaining.
01894     
01895     def TransferFromWindow(self):
01896         return True # Prevent wxDialog from complaining.
01897 
01898 class GNotebook(FN.FlatNotebook):
01899     """!Generic notebook widget
01900     """
01901     def __init__(self, parent, style, **kwargs):
01902         if globalvar.hasAgw:
01903             FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, agwStyle = style, **kwargs)
01904         else:
01905             FN.FlatNotebook.__init__(self, parent, id = wx.ID_ANY, style = style, **kwargs)
01906         
01907         self.notebookPages = {}
01908             
01909     def AddPage(self, **kwargs):
01910         """!Add a page
01911         """
01912         if 'name' in kwargs:
01913             self.notebookPages[kwargs['name']] = kwargs['page']
01914             del kwargs['name']
01915         super(GNotebook, self).AddPage(**kwargs)
01916 
01917     def SetSelectionByName(self, page):
01918         """!Set notebook
01919         
01920         @param page names, eg. 'layers', 'output', 'search', 'pyshell', 'nviz'
01921         """
01922         idx = self.GetPageIndexByName(page)
01923         if self.GetSelection() != idx:
01924             self.SetSelection(idx)
01925         
01926     def GetPageIndexByName(self, page):
01927         """!Get notebook page index
01928         
01929         @param page name
01930         """
01931         if page not in self.notebookPages:
01932             return -1
01933         
01934         return self.GetPageIndex(self.notebookPages[page])
01935     
01936 if __name__ ==  "__main__":
01937 
01938     if len(sys.argv) ==  1:
01939         sys.exit(_("usage: %s <grass command>") % sys.argv[0])
01940     if sys.argv[1] !=  'test':
01941         q = wx.LogNull()
01942         cmd = utils.split(sys.argv[1])
01943         if sys.platform == 'win32':
01944             if cmd[0] in globalvar.grassCmd['script']:
01945                 cmd[0] += globalvar.EXT_SCT
01946         task = gtask.grassTask(cmd[0], blackList = _blackList)
01947         task.set_options(cmd[1:])
01948         app = GrassGUIApp(task)
01949         app.MainLoop()
01950     else: #Test
01951         # Test grassTask from within a GRASS session
01952         if os.getenv("GISBASE") is not None:
01953             task = gtask.grassTask("d.vect")
01954             task.get_param('map')['value'] = "map_name"
01955             task.get_flag('v')['value'] = True
01956             task.get_param('layer')['value'] = 1
01957             task.get_param('bcolor')['value'] = "red"
01958             assert ' '.join(task.getCmd()) ==  "d.vect -v map = map_name layer = 1 bcolor = red"
01959         # Test interface building with handmade grassTask,
01960         # possibly outside of a GRASS session.
01961         task = gtask.grassTask()
01962         task.name = "TestTask"
01963         task.description = "This is an artificial grassTask() object intended for testing purposes."
01964         task.keywords = ["grass","test","task"]
01965         task.params = [
01966             {
01967             "name" : "text",
01968             "description" : "Descriptions go into tooltips if labels are present, like this one",
01969             "label" : "Enter some text",
01970             },{
01971             "name" : "hidden_text",
01972             "description" : "This text should not appear in the form",
01973             "hidden" : True
01974             },{
01975             "name" : "text_default",
01976             "description" : "Enter text to override the default",
01977             "default" : "default text"
01978             },{
01979             "name" : "text_prefilled",
01980             "description" : "You should see a friendly welcome message here",
01981             "value" : "hello, world"
01982             },{
01983             "name" : "plain_color",
01984             "description" : "This is a plain color, and it is a compulsory parameter",
01985             "required" : False,
01986             "gisprompt" : True,
01987             "prompt" : "color"
01988             },{
01989             "name" : "transparent_color",
01990             "description" : "This color becomes transparent when set to none",
01991             "guisection" : "tab",
01992             "gisprompt" : True,
01993             "prompt" : "color"
01994             },{
01995             "name" : "multi",
01996             "description" : "A multiple selection",
01997             'default': u'red,green,blue',
01998             'gisprompt': False,
01999             'guisection': 'tab',
02000             'multiple': u'yes',
02001             'type': u'string',
02002             'value': '',
02003             'values': ['red', 'green', u'yellow', u'blue', u'purple', u'other']
02004             },{
02005             "name" : "single",
02006             "description" : "A single multiple-choice selection",
02007             'values': ['red', 'green', u'yellow', u'blue', u'purple', u'other'],
02008             "guisection" : "tab"
02009             },{
02010             "name" : "large_multi",
02011             "description" : "A large multiple selection",
02012             "gisprompt" : False,
02013             "multiple" : "yes",
02014             # values must be an array of strings
02015             "values" : str2rgb.keys() + map(str, str2rgb.values())
02016             },{
02017             "name" : "a_file",
02018             "description" : "A file selector",
02019             "gisprompt" : True,
02020             "element" : "file"
02021             }
02022             ]
02023         task.flags = [
02024             {
02025             "name" : "a",
02026             "description" : "Some flag, will appear in Main since it is required",
02027             "required" : True
02028             },{
02029             "name" : "b",
02030             "description" : "pre-filled flag, will appear in options since it is not required",
02031             "value" : True
02032             },{
02033             "name" : "hidden_flag",
02034             "description" : "hidden flag, should not be changeable",
02035             "hidden" : "yes",
02036             "value" : True
02037             }
02038             ]
02039         q = wx.LogNull()
02040         GrassGUIApp(task).MainLoop()
02041 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines