GRASS Programmer's Manual
6.4.2(2012)
|
00001 """! 00002 @package goutput 00003 00004 @brief Command output log widget 00005 00006 Classes: 00007 - GMConsole 00008 - GMStc 00009 - GMStdout 00010 - GMStderr 00011 00012 (C) 2007-2011 by the GRASS Development Team 00013 This program is free software under the GNU General Public 00014 License (>=v2). Read the file COPYING that comes with GRASS 00015 for details. 00016 00017 @author Michael Barton (Arizona State University) 00018 @author Martin Landa <landa.martin gmail.com> 00019 @author Vaclav Petras <wenzeslaus gmail.com> (copy&paste customization) 00020 """ 00021 00022 import os 00023 import sys 00024 import textwrap 00025 import time 00026 import threading 00027 import Queue 00028 import codecs 00029 import locale 00030 00031 import wx 00032 import wx.stc 00033 from wx.lib.newevent import NewEvent 00034 00035 import grass.script as grass 00036 from grass.script import task as gtask 00037 00038 import globalvar 00039 import gcmd 00040 import utils 00041 import preferences 00042 import menuform 00043 import prompt 00044 00045 from debug import Debug 00046 from preferences import globalSettings as UserSettings 00047 from ghelp import SearchModuleWindow 00048 00049 wxCmdOutput, EVT_CMD_OUTPUT = NewEvent() 00050 wxCmdProgress, EVT_CMD_PROGRESS = NewEvent() 00051 wxCmdRun, EVT_CMD_RUN = NewEvent() 00052 wxCmdDone, EVT_CMD_DONE = NewEvent() 00053 wxCmdAbort, EVT_CMD_ABORT = NewEvent() 00054 00055 def GrassCmd(cmd, stdout = None, stderr = None): 00056 """!Return GRASS command thread""" 00057 return gcmd.CommandThread(cmd, 00058 stdout = stdout, stderr = stderr) 00059 00060 class CmdThread(threading.Thread): 00061 """!Thread for GRASS commands""" 00062 requestId = 0 00063 def __init__(self, parent, requestQ, resultQ, **kwds): 00064 threading.Thread.__init__(self, **kwds) 00065 00066 self.setDaemon(True) 00067 00068 self.parent = parent # GMConsole 00069 self._want_abort_all = False 00070 00071 self.requestQ = requestQ 00072 self.resultQ = resultQ 00073 00074 self.start() 00075 00076 def RunCmd(self, *args, **kwds): 00077 CmdThread.requestId += 1 00078 00079 self.requestCmd = None 00080 self.requestQ.put((CmdThread.requestId, args, kwds)) 00081 00082 return CmdThread.requestId 00083 00084 def SetId(self, id): 00085 """!Set starting id""" 00086 CmdThread.requestId = id 00087 00088 def run(self): 00089 os.environ['GRASS_MESSAGE_FORMAT'] = 'gui' 00090 while True: 00091 requestId, args, kwds = self.requestQ.get() 00092 for key in ('callable', 'onDone', 'userData'): 00093 if key in kwds: 00094 vars()[key] = kwds[key] 00095 del kwds[key] 00096 else: 00097 vars()[key] = None 00098 00099 if not vars()['callable']: 00100 vars()['callable'] = GrassCmd 00101 00102 requestTime = time.time() 00103 event = wxCmdRun(cmd = args[0], 00104 pid = requestId) 00105 wx.PostEvent(self.parent, event) 00106 00107 time.sleep(.1) 00108 self.requestCmd = vars()['callable'](*args, **kwds) 00109 if self._want_abort_all: 00110 self.requestCmd.abort() 00111 if self.requestQ.empty(): 00112 self._want_abort_all = False 00113 00114 self.resultQ.put((requestId, self.requestCmd.run())) 00115 00116 try: 00117 returncode = self.requestCmd.module.returncode 00118 except AttributeError: 00119 returncode = 0 # being optimistic 00120 00121 try: 00122 aborted = self.requestCmd.aborted 00123 except AttributeError: 00124 aborted = False 00125 00126 time.sleep(.1) 00127 00128 # set default color table for raster data 00129 if UserSettings.Get(group='cmd', key='rasterColorTable', subkey='enabled') and \ 00130 args[0][0][:2] == 'r.': 00131 colorTable = UserSettings.Get(group='cmd', key='rasterColorTable', subkey='selection') 00132 mapName = None 00133 if args[0][0] == 'r.mapcalc': 00134 try: 00135 mapName = args[0][1].split('=', 1)[0].strip() 00136 except KeyError: 00137 pass 00138 else: 00139 moduleInterface = menuform.GUI(show = None).ParseCommand(args[0]) 00140 outputParam = moduleInterface.get_param(value = 'output', raiseError = False) 00141 if outputParam and outputParam['prompt'] == 'raster': 00142 mapName = outputParam['value'] 00143 00144 if mapName: 00145 argsColor = list(args) 00146 argsColor[0] = [ 'r.colors', 00147 'map=%s' % mapName, 00148 'color=%s' % colorTable ] 00149 self.requestCmdColor = vars()['callable'](*argsColor, **kwds) 00150 self.resultQ.put((requestId, self.requestCmdColor.run())) 00151 00152 event = wxCmdDone(cmd = args[0], 00153 aborted = aborted, 00154 returncode = returncode, 00155 time = requestTime, 00156 pid = requestId, 00157 onDone = vars()['onDone'], 00158 userData = vars()['userData']) 00159 00160 # send event 00161 wx.PostEvent(self.parent, event) 00162 00163 def abort(self, abortall = True): 00164 """!Abort command(s)""" 00165 if abortall: 00166 self._want_abort_all = True 00167 self.requestCmd.abort() 00168 if self.requestQ.empty(): 00169 self._want_abort_all = False 00170 00171 class GMConsole(wx.SplitterWindow): 00172 """!Create and manage output console for commands run by GUI. 00173 """ 00174 def __init__(self, parent, id = wx.ID_ANY, margin = False, 00175 notebook = None, 00176 style = wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE, 00177 **kwargs): 00178 wx.SplitterWindow.__init__(self, parent, id, style = style, *kwargs) 00179 self.SetName("GMConsole") 00180 00181 self.panelOutput = wx.Panel(parent = self, id = wx.ID_ANY) 00182 self.panelPrompt = wx.Panel(parent = self, id = wx.ID_ANY) 00183 00184 # initialize variables 00185 self.parent = parent # GMFrame | CmdPanel | ? 00186 if notebook: 00187 self._notebook = notebook 00188 else: 00189 self._notebook = self.parent.notebook 00190 self.lineWidth = 80 00191 00192 # remember position of line begining (used for '\r') 00193 self.linePos = -1 00194 00195 # 00196 # create queues 00197 # 00198 self.requestQ = Queue.Queue() 00199 self.resultQ = Queue.Queue() 00200 00201 # 00202 # progress bar 00203 # 00204 self.console_progressbar = wx.Gauge(parent=self.panelOutput, id=wx.ID_ANY, 00205 range=100, pos=(110, 50), size=(-1, 25), 00206 style=wx.GA_HORIZONTAL) 00207 self.console_progressbar.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress) 00208 00209 # 00210 # text control for command output 00211 # 00212 self.cmd_output = GMStc(parent=self.panelOutput, id=wx.ID_ANY, margin=margin, 00213 wrap=None) 00214 self.cmd_output_timer = wx.Timer(self.cmd_output, id=wx.ID_ANY) 00215 self.cmd_output.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput) 00216 self.cmd_output.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents) 00217 self.Bind(EVT_CMD_RUN, self.OnCmdRun) 00218 self.Bind(EVT_CMD_DONE, self.OnCmdDone) 00219 00220 # search & command prompt 00221 self.cmd_prompt = prompt.GPromptSTC(parent = self) 00222 00223 if self.parent.GetName() != 'LayerManager': 00224 self.search = None 00225 self.cmd_prompt.Hide() 00226 else: 00227 self.infoCollapseLabelExp = _("Click here to show search module engine") 00228 self.infoCollapseLabelCol = _("Click here to hide search module engine") 00229 self.searchPane = wx.CollapsiblePane(parent = self.panelOutput, 00230 label = self.infoCollapseLabelExp, 00231 style = wx.CP_DEFAULT_STYLE | 00232 wx.CP_NO_TLW_RESIZE | wx.EXPAND) 00233 self.MakeSearchPaneContent(self.searchPane.GetPane()) 00234 self.searchPane.Collapse(True) 00235 self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnSearchPaneChanged, self.searchPane) 00236 self.search.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar) 00237 00238 # 00239 # stream redirection 00240 # 00241 self.cmd_stdout = GMStdout(self) 00242 self.cmd_stderr = GMStderr(self) 00243 00244 # 00245 # thread 00246 # 00247 self.cmdThread = CmdThread(self, self.requestQ, self.resultQ) 00248 00249 # 00250 # buttons 00251 # 00252 self.btn_console_clear = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY, 00253 label = _("&Clear output"), size=(100,-1)) 00254 self.btn_cmd_clear = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY, 00255 label = _("C&lear cmd"), size=(100,-1)) 00256 if self.parent.GetName() != 'LayerManager': 00257 self.btn_cmd_clear.Hide() 00258 self.btn_console_save = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY, 00259 label = _("&Save output"), size=(100,-1)) 00260 # abort 00261 self.btn_abort = wx.Button(parent = self.panelPrompt, id = wx.ID_ANY, label = _("&Abort cmd"), 00262 size=(100,-1)) 00263 self.btn_abort.SetToolTipString(_("Abort the running command")) 00264 self.btn_abort.Enable(False) 00265 00266 self.btn_cmd_clear.Bind(wx.EVT_BUTTON, self.cmd_prompt.OnCmdErase) 00267 self.btn_console_clear.Bind(wx.EVT_BUTTON, self.ClearHistory) 00268 self.btn_console_save.Bind(wx.EVT_BUTTON, self.SaveHistory) 00269 self.btn_abort.Bind(wx.EVT_BUTTON, self.OnCmdAbort) 00270 self.btn_abort.Bind(EVT_CMD_ABORT, self.OnCmdAbort) 00271 00272 self.__layout() 00273 00274 def __layout(self): 00275 """!Do layout""" 00276 OutputSizer = wx.BoxSizer(wx.VERTICAL) 00277 PromptSizer = wx.BoxSizer(wx.VERTICAL) 00278 ButtonSizer = wx.BoxSizer(wx.HORIZONTAL) 00279 00280 if self.search and self.search.IsShown(): 00281 OutputSizer.Add(item=self.searchPane, proportion=0, 00282 flag=wx.EXPAND | wx.ALL, border=3) 00283 OutputSizer.Add(item=self.cmd_output, proportion=1, 00284 flag=wx.EXPAND | wx.ALL, border=3) 00285 OutputSizer.Add(item=self.console_progressbar, proportion=0, 00286 flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=3) 00287 00288 PromptSizer.Add(item=self.cmd_prompt, proportion=1, 00289 flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=3) 00290 00291 ButtonSizer.Add(item=self.btn_console_clear, proportion=0, 00292 flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5) 00293 ButtonSizer.Add(item=self.btn_console_save, proportion=0, 00294 flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5) 00295 ButtonSizer.Add(item=self.btn_cmd_clear, proportion=0, 00296 flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5) 00297 ButtonSizer.Add(item=self.btn_abort, proportion=0, 00298 flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5) 00299 PromptSizer.Add(item=ButtonSizer, proportion=0, 00300 flag=wx.ALIGN_CENTER) 00301 00302 OutputSizer.Fit(self) 00303 OutputSizer.SetSizeHints(self) 00304 00305 PromptSizer.Fit(self) 00306 PromptSizer.SetSizeHints(self) 00307 00308 self.panelOutput.SetSizer(OutputSizer) 00309 self.panelPrompt.SetSizer(PromptSizer) 00310 00311 # split window 00312 if self.parent.GetName() == 'LayerManager': 00313 self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50) 00314 self.SetMinimumPaneSize(self.btn_cmd_clear.GetSize()[1] + 50) 00315 else: 00316 self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45) 00317 self.SetMinimumPaneSize(self.btn_cmd_clear.GetSize()[1] + 10) 00318 00319 self.SetSashGravity(1.0) 00320 00321 # layout 00322 self.SetAutoLayout(True) 00323 self.Layout() 00324 00325 def MakeSearchPaneContent(self, pane): 00326 """!Create search pane""" 00327 border = wx.BoxSizer(wx.VERTICAL) 00328 00329 self.search = SearchModuleWindow(parent = pane, cmdPrompt = self.cmd_prompt) 00330 00331 border.Add(item = self.search, proportion = 0, 00332 flag = wx.EXPAND | wx.ALL, border = 1) 00333 00334 pane.SetSizer(border) 00335 border.Fit(pane) 00336 00337 def OnSearchPaneChanged(self, event): 00338 """!Collapse search module box""" 00339 if self.searchPane.IsExpanded(): 00340 self.searchPane.SetLabel(self.infoCollapseLabelCol) 00341 else: 00342 self.searchPane.SetLabel(self.infoCollapseLabelExp) 00343 00344 self.panelOutput.Layout() 00345 self.panelOutput.SendSizeEvent() 00346 00347 def GetPanel(self, prompt = True): 00348 """!Get panel 00349 00350 @param prompt get prompt / output panel 00351 00352 @return wx.Panel reference 00353 """ 00354 if prompt: 00355 return self.panelPrompt 00356 00357 return self.panelOutput 00358 00359 def Redirect(self): 00360 """!Redirect stdout/stderr 00361 """ 00362 if Debug.GetLevel() == 0 and int(grass.gisenv().get('DEBUG', 0)) == 0: 00363 # don't redirect when debugging is enabled 00364 sys.stdout = self.cmd_stdout 00365 sys.stderr = self.cmd_stderr 00366 else: 00367 enc = locale.getdefaultlocale()[1] 00368 if enc: 00369 sys.stdout = codecs.getwriter(enc)(sys.__stdout__) 00370 sys.stderr = codecs.getwriter(enc)(sys.__stderr__) 00371 else: 00372 sys.stdout = sys.__stdout__ 00373 sys.stderr = sys.__stderr__ 00374 00375 def WriteLog(self, text, style = None, wrap = None, 00376 switchPage = False): 00377 """!Generic method for writing log message in 00378 given style 00379 00380 @param line text line 00381 @param style text style (see GMStc) 00382 @param stdout write to stdout or stderr 00383 """ 00384 00385 self.cmd_output.SetStyle() 00386 00387 if switchPage: 00388 self._notebook.SetSelectionByName('output') 00389 00390 if not style: 00391 style = self.cmd_output.StyleDefault 00392 00393 # p1 = self.cmd_output.GetCurrentPos() 00394 p1 = self.cmd_output.GetEndStyled() 00395 # self.cmd_output.GotoPos(p1) 00396 self.cmd_output.DocumentEnd() 00397 00398 for line in text.splitlines(): 00399 # fill space 00400 if len(line) < self.lineWidth: 00401 diff = self.lineWidth - len(line) 00402 line += diff * ' ' 00403 00404 self.cmd_output.AddTextWrapped(line, wrap=wrap) # adds '\n' 00405 00406 p2 = self.cmd_output.GetCurrentPos() 00407 00408 self.cmd_output.StartStyling(p1, 0xff) 00409 self.cmd_output.SetStyling(p2 - p1, style) 00410 00411 self.cmd_output.EnsureCaretVisible() 00412 00413 def WriteCmdLog(self, line, pid = None, switchPage = True): 00414 """!Write message in selected style""" 00415 if pid: 00416 line = '(' + str(pid) + ') ' + line 00417 self.WriteLog(line, style=self.cmd_output.StyleCommand, switchPage = switchPage) 00418 00419 def WriteWarning(self, line): 00420 """!Write message in warning style""" 00421 self.WriteLog(line, style=self.cmd_output.StyleWarning, switchPage = True) 00422 00423 def WriteError(self, line): 00424 """!Write message in error style""" 00425 self.WriteLog(line, style = self.cmd_output.StyleError, switchPage = True) 00426 00427 def RunCmd(self, command, compReg = True, switchPage = False, 00428 onDone = None): 00429 """!Run command typed into console command prompt (GPrompt). 00430 00431 @todo Display commands (*.d) are captured and processed 00432 separately by mapdisp.py. Display commands are rendered in map 00433 display widget that currently has the focus (as indicted by 00434 mdidx). 00435 00436 @param command command given as a list (produced e.g. by utils.split()) 00437 @param compReg True use computation region 00438 @param switchPage switch to output page 00439 @param onDone function to be called when command is finished 00440 """ 00441 if len(command) == 0: 00442 Debug.msg(2, "GPrompt:RunCmd(): empty command") 00443 return 00444 00445 # update history file 00446 env = grass.gisenv() 00447 try: 00448 fileHistory = codecs.open(os.path.join(env['GISDBASE'], 00449 env['LOCATION_NAME'], 00450 env['MAPSET'], 00451 '.bash_history'), 00452 encoding = 'utf-8', mode = 'a') 00453 except IOError, e: 00454 self.WriteError(e) 00455 fileHistory = None 00456 00457 if fileHistory: 00458 try: 00459 fileHistory.write(' '.join(command) + os.linesep) 00460 finally: 00461 fileHistory.close() 00462 00463 # update history items 00464 if self.parent.GetName() == 'LayerManager': 00465 try: 00466 self.parent.cmdinput.SetHistoryItems() 00467 except AttributeError: 00468 pass 00469 00470 if command[0] in globalvar.grassCmd['all']: 00471 # send GRASS command without arguments to GUI command interface 00472 # except display commands (they are handled differently) 00473 if self.parent.GetName() == "LayerManager" and \ 00474 command[0][0:2] == "d." and \ 00475 'help' not in ' '.join(command[1:]): 00476 # display GRASS commands 00477 try: 00478 layertype = {'d.rast' : 'raster', 00479 'd.rast3d' : '3d-raster', 00480 'd.rgb' : 'rgb', 00481 'd.his' : 'his', 00482 'd.shaded' : 'shaded', 00483 'd.legend' : 'rastleg', 00484 'd.rast.arrow' : 'rastarrow', 00485 'd.rast.num' : 'rastnum', 00486 'd.vect' : 'vector', 00487 'd.vect.thematic': 'thememap', 00488 'd.vect.chart' : 'themechart', 00489 'd.grid' : 'grid', 00490 'd.geodesic' : 'geodesic', 00491 'd.rhumbline' : 'rhumb', 00492 'd.labels' : 'labels', 00493 'd.barscale' : 'barscale', 00494 'd.redraw' : 'redraw'}[command[0]] 00495 except KeyError: 00496 gcmd.GMessage(parent = self.parent, 00497 message = _("Command '%s' not yet implemented in the WxGUI. " 00498 "Try adding it as a command layer instead.") % command[0]) 00499 return None 00500 00501 if layertype == 'barscale': 00502 self.parent.curr_page.maptree.GetMapDisplay().OnAddBarscale(None) 00503 elif layertype == 'rastleg': 00504 self.parent.curr_page.maptree.GetMapDisplay().OnAddLegend(None) 00505 elif layertype == 'redraw': 00506 self.parent.curr_page.maptree.GetMapDisplay().OnRender(None) 00507 else: 00508 # add layer into layer tree 00509 lname, found = utils.GetLayerNameFromCmd(command, fullyQualified = True, 00510 layerType = layertype) 00511 if self.parent.GetName() == "LayerManager": 00512 self.parent.curr_page.maptree.AddLayer(ltype = layertype, 00513 lname = lname, 00514 lcmd = command) 00515 00516 else: 00517 # other GRASS commands (r|v|g|...) 00518 if sys.platform == 'win32': 00519 if command[0] in globalvar.grassCmd['script']: 00520 command[0] += globalvar.EXT_SCT 00521 hasParams = False 00522 if command[0] != 'r.mapcalc': 00523 task = menuform.GUI(show = None).ParseCommand(command) 00524 if task: 00525 options = task.get_options() 00526 hasParams = options['params'] and options['flags'] 00527 # check for <input>=- 00528 for p in options['params']: 00529 if p.get('prompt', '') == 'input' and \ 00530 p.get('element', '') == 'file' and \ 00531 p.get('age', 'new') == 'old_file' and \ 00532 p.get('value', '') == '-': 00533 gcmd.GError(parent = self, 00534 message = _("Unable to run command:\n%(cmd)s\n\n" 00535 "Option <%(opt)s>: read from standard input is not " 00536 "supported by wxGUI") % { 'cmd': ' '.join(command), 00537 'opt': p.get('name', '') }) 00538 return None 00539 else: 00540 task = None 00541 00542 if len(command) == 1 and hasParams: 00543 # no arguments given 00544 try: 00545 menuform.GUI(parent = self).ParseCommand(command) 00546 except gcmd.GException, e: 00547 print >> sys.stderr, e 00548 return 0 00549 00550 # switch to 'Command output' if required 00551 if switchPage: 00552 self._notebook.SetSelectionByName('output') 00553 00554 self.parent.SetFocus() 00555 self.parent.Raise() 00556 00557 # activate computational region (set with g.region) 00558 # for all non-display commands. 00559 if compReg: 00560 tmpreg = os.getenv("GRASS_REGION") 00561 if "GRASS_REGION" in os.environ: 00562 del os.environ["GRASS_REGION"] 00563 00564 # process GRASS command with argument 00565 self.cmdThread.RunCmd(command, stdout = self.cmd_stdout, stderr = self.cmd_stderr, 00566 onDone = onDone) 00567 self.cmd_output_timer.Start(50) 00568 00569 # deactivate computational region and return to display settings 00570 if compReg and tmpreg: 00571 os.environ["GRASS_REGION"] = tmpreg 00572 else: 00573 # Send any other command to the shell. Send output to 00574 # console output window 00575 if len(command) == 1: 00576 try: 00577 task = gtask.parse_interface(command[0]) 00578 except: 00579 task = None 00580 else: 00581 task = None 00582 00583 if task: 00584 # process GRASS command without argument 00585 menuform.GUI(parent = self).ParseCommand(command) 00586 else: 00587 self.cmdThread.RunCmd(command, stdout = self.cmd_stdout, stderr = self.cmd_stderr, 00588 onDone = onDone) 00589 self.cmd_output_timer.Start(50) 00590 00591 return None 00592 00593 def ClearHistory(self, event): 00594 """!Clear history of commands""" 00595 self.cmd_output.SetReadOnly(False) 00596 self.cmd_output.ClearAll() 00597 self.cmd_output.SetReadOnly(True) 00598 self.console_progressbar.SetValue(0) 00599 00600 def GetProgressBar(self): 00601 """!Return progress bar widget""" 00602 return self.console_progressbar 00603 00604 def GetLog(self, err = False): 00605 """!Get widget used for logging 00606 00607 @param err True to get stderr widget 00608 """ 00609 if err: 00610 return self.cmd_stderr 00611 00612 return self.cmd_stdout 00613 00614 def SaveHistory(self, event): 00615 """!Save history of commands""" 00616 self.history = self.cmd_output.GetSelectedText() 00617 if self.history == '': 00618 self.history = self.cmd_output.GetText() 00619 00620 # add newline if needed 00621 if len(self.history) > 0 and self.history[-1] != '\n': 00622 self.history += '\n' 00623 00624 wildcard = "Text file (*.txt)|*.txt" 00625 dlg = wx.FileDialog(self, message = _("Save file as..."), defaultDir = os.getcwd(), 00626 defaultFile = "grass_cmd_history.txt", wildcard = wildcard, 00627 style = wx.SAVE | wx.FD_OVERWRITE_PROMPT) 00628 00629 # Show the dialog and retrieve the user response. If it is the OK response, 00630 # process the data. 00631 if dlg.ShowModal() == wx.ID_OK: 00632 path = dlg.GetPath() 00633 00634 output = open(path, "w") 00635 output.write(self.history) 00636 output.close() 00637 00638 dlg.Destroy() 00639 00640 def GetCmd(self): 00641 """!Get running command or None""" 00642 return self.requestQ.get() 00643 00644 def SetCopyingOfSelectedText(self, copy): 00645 """!Enable or disable copying of selected text in to clipboard. 00646 Effects prompt and output. 00647 00648 @param copy True for enable, False for disable 00649 """ 00650 if copy: 00651 self.cmd_prompt.Bind(wx.stc.EVT_STC_PAINTED, self.cmd_prompt.OnTextSelectionChanged) 00652 self.cmd_output.Bind(wx.stc.EVT_STC_PAINTED, self.cmd_output.OnTextSelectionChanged) 00653 else: 00654 self.cmd_prompt.Unbind(wx.stc.EVT_STC_PAINTED) 00655 self.cmd_output.Unbind(wx.stc.EVT_STC_PAINTED) 00656 00657 def OnUpdateStatusBar(self, event): 00658 """!Update statusbar text""" 00659 if event.GetString(): 00660 nItems = len(self.cmd_prompt.GetCommandItems()) 00661 self.parent.SetStatusText(_('%d modules match') % nItems, 0) 00662 else: 00663 self.parent.SetStatusText('', 0) 00664 00665 event.Skip() 00666 00667 def OnCmdOutput(self, event): 00668 """!Print command output""" 00669 message = event.text 00670 type = event.type 00671 if self._notebook.GetSelection() != self._notebook.GetPageIndexByName('output'): 00672 page = self._notebook.GetPageIndexByName('output') 00673 textP = self._notebook.GetPageText(page) 00674 if textP[-1] != ')': 00675 textP += ' (...)' 00676 self._notebook.SetPageText(page, textP) 00677 00678 # message prefix 00679 if type == 'warning': 00680 messege = 'WARNING: ' + message 00681 elif type == 'error': 00682 message = 'ERROR: ' + message 00683 00684 p1 = self.cmd_output.GetEndStyled() 00685 self.cmd_output.GotoPos(p1) 00686 00687 if '\b' in message: 00688 if self.linepos < 0: 00689 self.linepos = p1 00690 last_c = '' 00691 for c in message: 00692 if c == '\b': 00693 self.linepos -= 1 00694 else: 00695 if c == '\r': 00696 pos = self.cmd_output.GetCurLine()[1] 00697 # self.cmd_output.SetCurrentPos(pos) 00698 else: 00699 self.cmd_output.SetCurrentPos(self.linepos) 00700 self.cmd_output.ReplaceSelection(c) 00701 self.linepos = self.cmd_output.GetCurrentPos() 00702 if c != ' ': 00703 last_c = c 00704 if last_c not in ('0123456789'): 00705 self.cmd_output.AddTextWrapped('\n', wrap=None) 00706 self.linepos = -1 00707 else: 00708 self.linepos = -1 # don't force position 00709 if '\n' not in message: 00710 self.cmd_output.AddTextWrapped(message, wrap=60) 00711 else: 00712 self.cmd_output.AddTextWrapped(message, wrap=None) 00713 00714 p2 = self.cmd_output.GetCurrentPos() 00715 00716 if p2 >= p1: 00717 self.cmd_output.StartStyling(p1, 0xff) 00718 00719 if type == 'error': 00720 self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleError) 00721 elif type == 'warning': 00722 self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleWarning) 00723 elif type == 'message': 00724 self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleMessage) 00725 else: # unknown 00726 self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleUnknown) 00727 00728 self.cmd_output.EnsureCaretVisible() 00729 00730 def OnCmdProgress(self, event): 00731 """!Update progress message info""" 00732 self.console_progressbar.SetValue(event.value) 00733 00734 def OnCmdAbort(self, event): 00735 """!Abort running command""" 00736 self.cmdThread.abort() 00737 00738 def OnCmdRun(self, event): 00739 """!Run command""" 00740 if self.parent.GetName() == 'Modeler': 00741 self.parent.OnCmdRun(event) 00742 00743 self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd))) 00744 self.btn_abort.Enable() 00745 00746 def OnCmdDone(self, event): 00747 """!Command done (or aborted)""" 00748 if self.parent.GetName() == 'Modeler': 00749 self.parent.OnCmdDone(event) 00750 00751 if event.aborted: 00752 # Thread aborted (using our convention of None return) 00753 self.WriteLog(_('Please note that the data are left in inconsistent state ' 00754 'and may be corrupted'), self.cmd_output.StyleWarning) 00755 self.WriteCmdLog('(%s) %s (%d sec)' % (str(time.ctime()), 00756 _('Command aborted'), 00757 (time.time() - event.time))) 00758 # pid=self.cmdThread.requestId) 00759 self.btn_abort.Enable(False) 00760 else: 00761 try: 00762 # Process results here 00763 self.WriteCmdLog('(%s) %s (%d sec)' % (str(time.ctime()), 00764 _('Command finished'), 00765 (time.time() - event.time))) 00766 except KeyError: 00767 # stopped deamon 00768 pass 00769 00770 self.btn_abort.Enable(False) 00771 00772 if event.onDone: 00773 event.onDone(cmd = event.cmd, returncode = event.returncode) 00774 00775 self.console_progressbar.SetValue(0) # reset progress bar on '0%' 00776 00777 self.cmd_output_timer.Stop() 00778 00779 if event.cmd[0] == 'g.gisenv': 00780 Debug.SetLevel() 00781 self.Redirect() 00782 00783 if self.parent.GetName() == "LayerManager": 00784 self.btn_abort.Enable(False) 00785 if event.cmd[0] not in globalvar.grassCmd['all'] or \ 00786 event.cmd[0] == 'r.mapcalc': 00787 return 00788 00789 display = self.parent.GetLayerTree().GetMapDisplay() 00790 if not display or not display.IsAutoRendered(): 00791 return 00792 mapLayers = map(lambda x: x.GetName(), 00793 display.GetRender().GetListOfLayers(l_type = 'raster') + 00794 display.GetRender().GetListOfLayers(l_type = 'vector')) 00795 00796 try: 00797 task = menuform.GUI(show = None).ParseCommand(event.cmd) 00798 except gcmd.GException, e: 00799 print >> sys.stderr, e 00800 task = None 00801 return 00802 00803 for p in task.get_options()['params']: 00804 if p.get('prompt', '') not in ('raster', 'vector'): 00805 continue 00806 mapName = p.get('value', '') 00807 if '@' not in mapName: 00808 mapName = mapName + '@' + grass.gisenv()['MAPSET'] 00809 if mapName in mapLayers: 00810 display.GetWindow().UpdateMap(render = True) 00811 return 00812 elif self.parent.GetName() == 'Modeler': 00813 pass 00814 else: # standalone dialogs 00815 dialog = self.parent.parent 00816 if hasattr(self.parent.parent, "btn_abort"): 00817 dialog.btn_abort.Enable(False) 00818 00819 if hasattr(self.parent.parent, "btn_cancel"): 00820 dialog.btn_cancel.Enable(True) 00821 00822 if hasattr(self.parent.parent, "btn_clipboard"): 00823 dialog.btn_clipboard.Enable(True) 00824 00825 if hasattr(self.parent.parent, "btn_help"): 00826 dialog.btn_help.Enable(True) 00827 00828 if hasattr(self.parent.parent, "btn_run"): 00829 dialog.btn_run.Enable(True) 00830 00831 if event.returncode == 0 and not event.aborted: 00832 try: 00833 winName = self.parent.parent.parent.GetName() 00834 except AttributeError: 00835 winName = '' 00836 00837 if winName == 'LayerManager': 00838 mapTree = self.parent.parent.parent.GetLayerTree() 00839 elif winName == 'LayerTree': 00840 mapTree = self.parent.parent.parent 00841 elif winName: # GMConsole 00842 mapTree = self.parent.parent.parent.parent.GetLayerTree() 00843 else: 00844 mapTree = None 00845 00846 cmd = dialog.notebookpanel.createCmd(ignoreErrors = True) 00847 if hasattr(dialog, "addbox") and dialog.addbox.IsChecked(): 00848 # add created maps into layer tree 00849 for p in dialog.task.get_options()['params']: 00850 prompt = p.get('prompt', '') 00851 if prompt in ('raster', 'vector', '3d-raster') and \ 00852 p.get('age', 'old') == 'new' and \ 00853 p.get('value', None): 00854 name, found = utils.GetLayerNameFromCmd(cmd, fullyQualified = True, 00855 param = p.get('name', '')) 00856 00857 if mapTree.GetMap().GetListOfLayers(l_name = name): 00858 continue 00859 00860 if prompt == 'raster': 00861 lcmd = ['d.rast', 00862 'map=%s' % name] 00863 else: 00864 lcmd = ['d.vect', 00865 'map=%s' % name] 00866 mapTree.AddLayer(ltype = prompt, 00867 lcmd = lcmd, 00868 lname = name) 00869 00870 if hasattr(dialog, "get_dcmd") and \ 00871 dialog.get_dcmd is None and \ 00872 hasattr(dialog, "closebox") and \ 00873 dialog.closebox.IsChecked() and \ 00874 (event.returncode == 0 or event.aborted): 00875 self.cmd_output.Update() 00876 time.sleep(2) 00877 dialog.Close() 00878 00879 def OnProcessPendingOutputWindowEvents(self, event): 00880 self.ProcessPendingEvents() 00881 00882 class GMStdout: 00883 """!GMConsole standard output 00884 00885 Based on FrameOutErr.py 00886 00887 Name: FrameOutErr.py 00888 Purpose: Redirecting stdout / stderr 00889 Author: Jean-Michel Fauth, Switzerland 00890 Copyright: (c) 2005-2007 Jean-Michel Fauth 00891 Licence: GPL 00892 """ 00893 def __init__(self, parent): 00894 self.parent = parent # GMConsole 00895 00896 def write(self, s): 00897 if len(s) == 0 or s == '\n': 00898 return 00899 00900 for line in s.splitlines(): 00901 if len(line) == 0: 00902 continue 00903 00904 evt = wxCmdOutput(text=line + '\n', 00905 type='') 00906 wx.PostEvent(self.parent.cmd_output, evt) 00907 00908 class GMStderr: 00909 """!GMConsole standard error output 00910 00911 Based on FrameOutErr.py 00912 00913 Name: FrameOutErr.py 00914 Purpose: Redirecting stdout / stderr 00915 Author: Jean-Michel Fauth, Switzerland 00916 Copyright: (c) 2005-2007 Jean-Michel Fauth 00917 Licence: GPL 00918 """ 00919 def __init__(self, parent): 00920 self.parent = parent # GMConsole 00921 00922 self.type = '' 00923 self.message = '' 00924 self.printMessage = False 00925 00926 def flush(self): 00927 pass 00928 00929 def write(self, s): 00930 if "GtkPizza" in s: 00931 return 00932 00933 # remove/replace escape sequences '\b' or '\r' from stream 00934 progressValue = -1 00935 00936 for line in s.splitlines(): 00937 if len(line) == 0: 00938 continue 00939 00940 if 'GRASS_INFO_PERCENT' in line: 00941 value = int(line.rsplit(':', 1)[1].strip()) 00942 if value >= 0 and value < 100: 00943 progressValue = value 00944 else: 00945 progressValue = 0 00946 elif 'GRASS_INFO_MESSAGE' in line: 00947 self.type = 'message' 00948 self.message += line.split(':', 1)[1].strip() + '\n' 00949 elif 'GRASS_INFO_WARNING' in line: 00950 self.type = 'warning' 00951 self.message += line.split(':', 1)[1].strip() + '\n' 00952 elif 'GRASS_INFO_ERROR' in line: 00953 self.type = 'error' 00954 self.message += line.split(':', 1)[1].strip() + '\n' 00955 elif 'GRASS_INFO_END' in line: 00956 self.printMessage = True 00957 elif self.type == '': 00958 if len(line) == 0: 00959 continue 00960 evt = wxCmdOutput(text=line, 00961 type='') 00962 wx.PostEvent(self.parent.cmd_output, evt) 00963 elif len(line) > 0: 00964 self.message += line.strip() + '\n' 00965 00966 if self.printMessage and len(self.message) > 0: 00967 evt = wxCmdOutput(text=self.message, 00968 type=self.type) 00969 wx.PostEvent(self.parent.cmd_output, evt) 00970 00971 self.type = '' 00972 self.message = '' 00973 self.printMessage = False 00974 00975 # update progress message 00976 if progressValue > -1: 00977 # self.gmgauge.SetValue(progressValue) 00978 evt = wxCmdProgress(value=progressValue) 00979 wx.PostEvent(self.parent.console_progressbar, evt) 00980 00981 class GMStc(wx.stc.StyledTextCtrl): 00982 """!Styled GMConsole 00983 00984 Based on FrameOutErr.py 00985 00986 Name: FrameOutErr.py 00987 Purpose: Redirecting stdout / stderr 00988 Author: Jean-Michel Fauth, Switzerland 00989 Copyright: (c) 2005-2007 Jean-Michel Fauth 00990 Licence: GPL 00991 """ 00992 def __init__(self, parent, id, margin=False, wrap=None): 00993 wx.stc.StyledTextCtrl.__init__(self, parent, id) 00994 self.parent = parent 00995 self.SetUndoCollection(True) 00996 self.SetReadOnly(True) 00997 00998 # 00999 # styles 01000 # 01001 self.SetStyle() 01002 01003 # 01004 # line margins 01005 # 01006 # TODO print number only from cmdlog 01007 self.SetMarginWidth(1, 0) 01008 self.SetMarginWidth(2, 0) 01009 if margin: 01010 self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER) 01011 self.SetMarginWidth(0, 30) 01012 else: 01013 self.SetMarginWidth(0, 0) 01014 01015 # 01016 # miscellaneous 01017 # 01018 self.SetViewWhiteSpace(False) 01019 self.SetTabWidth(4) 01020 self.SetUseTabs(False) 01021 self.UsePopUp(True) 01022 self.SetSelBackground(True, "#FFFF00") 01023 self.SetUseHorizontalScrollBar(True) 01024 01025 # 01026 # bindings 01027 # 01028 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) 01029 01030 def OnTextSelectionChanged(self, event): 01031 """!Copy selected text to clipboard and skip event. 01032 The same function is in TextCtrlAutoComplete class (prompt.py). 01033 """ 01034 self.Copy() 01035 event.Skip() 01036 01037 def SetStyle(self): 01038 """!Set styles for styled text output windows with type face 01039 and point size selected by user (Courier New 10 is default)""" 01040 01041 settings = preferences.Settings() 01042 01043 typeface = settings.Get(group = 'appearance', key = 'outputfont', subkey = 'type') 01044 if typeface == "": 01045 typeface = "Courier New" 01046 01047 typesize = settings.Get(group = 'appearance', key = 'outputfont', subkey = 'size') 01048 if typesize == None or typesize <= 0: 01049 typesize = 10 01050 typesize = float(typesize) 01051 01052 self.StyleDefault = 0 01053 self.StyleDefaultSpec = "face:%s,size:%d,fore:#000000,back:#FFFFFF" % (typeface, typesize) 01054 self.StyleCommand = 1 01055 self.StyleCommandSpec = "face:%s,size:%d,,fore:#000000,back:#bcbcbc" % (typeface, typesize) 01056 self.StyleOutput = 2 01057 self.StyleOutputSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize) 01058 # fatal error 01059 self.StyleError = 3 01060 self.StyleErrorSpec = "face:%s,size:%d,,fore:#7F0000,back:#FFFFFF" % (typeface, typesize) 01061 # warning 01062 self.StyleWarning = 4 01063 self.StyleWarningSpec = "face:%s,size:%d,,fore:#0000FF,back:#FFFFFF" % (typeface, typesize) 01064 # message 01065 self.StyleMessage = 5 01066 self.StyleMessageSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize) 01067 # unknown 01068 self.StyleUnknown = 6 01069 self.StyleUnknownSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize) 01070 01071 # default and clear => init 01072 self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec) 01073 self.StyleClearAll() 01074 self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec) 01075 self.StyleSetSpec(self.StyleOutput, self.StyleOutputSpec) 01076 self.StyleSetSpec(self.StyleError, self.StyleErrorSpec) 01077 self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec) 01078 self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec) 01079 self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec) 01080 01081 def OnDestroy(self, evt): 01082 """!The clipboard contents can be preserved after 01083 the app has exited""" 01084 01085 wx.TheClipboard.Flush() 01086 evt.Skip() 01087 01088 def AddTextWrapped(self, txt, wrap=None): 01089 """!Add string to text area. 01090 01091 String is wrapped and linesep is also added to the end 01092 of the string""" 01093 # allow writing to output window 01094 self.SetReadOnly(False) 01095 01096 if wrap: 01097 txt = textwrap.fill(txt, wrap) + '\n' 01098 else: 01099 if txt[-1] != '\n': 01100 txt += '\n' 01101 01102 if '\r' in txt: 01103 self.parent.linePos = -1 01104 for seg in txt.split('\r'): 01105 if self.parent.linePos > -1: 01106 self.SetCurrentPos(self.parent.linePos) 01107 self.ReplaceSelection(seg) 01108 else: 01109 self.parent.linePos = self.GetCurrentPos() 01110 self.AddText(seg) 01111 else: 01112 self.parent.linePos = self.GetCurrentPos() 01113 try: 01114 self.AddText(txt) 01115 except UnicodeDecodeError: 01116 enc = UserSettings.Get(group='atm', key='encoding', subkey='value') 01117 if enc: 01118 txt = unicode(txt, enc) 01119 elif 'GRASS_DB_ENCODING' in os.environ: 01120 txt = unicode(txt, os.environ['GRASS_DB_ENCODING']) 01121 else: 01122 txt = utils.EncodeString(txt) 01123 01124 self.AddText(txt) 01125 01126 # reset output window to read only 01127 self.SetReadOnly(True) 01128