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