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