|
GRASS Programmer's Manual
6.5.svn(2012)-r51648
|
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