goutput.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. """!
  2. @package goutput
  3. @brief Command output log widget
  4. Classes:
  5. - GMConsole
  6. - GMStc
  7. - GMStdout
  8. - GMStderr
  9. (C) 2007-2009 by the GRASS Development Team
  10. This program is free software under the GNU General Public
  11. License (>=v2). Read the file COPYING that comes with GRASS
  12. for details.
  13. @author Michael Barton (Arizona State University)
  14. @author Martin Landa <landa.martin gmail.com>
  15. """
  16. import os
  17. import sys
  18. import textwrap
  19. import time
  20. import threading
  21. import Queue
  22. import wx
  23. import wx.stc
  24. from wx.lib.newevent import NewEvent
  25. import grass.script as grass
  26. import globalvar
  27. import gcmd
  28. import utils
  29. import preferences
  30. import menuform
  31. import prompt
  32. from debug import Debug as Debug
  33. from preferences import globalSettings as UserSettings
  34. wxCmdOutput, EVT_CMD_OUTPUT = NewEvent()
  35. wxCmdProgress, EVT_CMD_PROGRESS = NewEvent()
  36. wxCmdRun, EVT_CMD_RUN = NewEvent()
  37. wxCmdDone, EVT_CMD_DONE = NewEvent()
  38. wxCmdAbort, EVT_CMD_ABORT = NewEvent()
  39. def GrassCmd(cmd, stdout, stderr):
  40. """!Return GRASS command thread"""
  41. return gcmd.CommandThread(cmd,
  42. stdout=stdout, stderr=stderr)
  43. class CmdThread(threading.Thread):
  44. """!Thread for GRASS commands"""
  45. requestId = 0
  46. def __init__(self, parent, requestQ, resultQ, **kwds):
  47. threading.Thread.__init__(self, **kwds)
  48. self.setDaemon(True)
  49. self.parent = parent # GMConsole
  50. self.requestQ = requestQ
  51. self.resultQ = resultQ
  52. self.start()
  53. def RunCmd(self, callable, onDone, *args, **kwds):
  54. CmdThread.requestId += 1
  55. self.requestCmd = None
  56. self.requestQ.put((CmdThread.requestId, callable, onDone, args, kwds))
  57. return CmdThread.requestId
  58. def run(self):
  59. while True:
  60. requestId, callable, onDone, args, kwds = self.requestQ.get()
  61. requestTime = time.time()
  62. event = wxCmdRun(cmd=args[0],
  63. pid=requestId)
  64. wx.PostEvent(self.parent, event)
  65. time.sleep(.1)
  66. self.requestCmd = callable(*args, **kwds)
  67. self.resultQ.put((requestId, self.requestCmd.run()))
  68. try:
  69. returncode = self.requestCmd.module.returncode
  70. except AttributeError:
  71. returncode = 0 # being optimistic
  72. try:
  73. aborted = self.requestCmd.aborted
  74. except AttributeError:
  75. aborted = False
  76. time.sleep(.1)
  77. # set default color table for raster data
  78. if UserSettings.Get(group='cmd', key='rasterColorTable', subkey='enabled') and \
  79. args[0][0][:2] == 'r.':
  80. moduleInterface = menuform.GUI().ParseCommand(args[0], show = None)
  81. outputParam = moduleInterface.get_param(value = 'output', raiseError = False)
  82. colorTable = UserSettings.Get(group='cmd', key='rasterColorTable', subkey='selection')
  83. if outputParam and outputParam['prompt'] == 'raster':
  84. argsColor = list(args)
  85. argsColor[0] = [ 'r.colors',
  86. 'map=%s' % outputParam['value'],
  87. 'color=%s' % colorTable]
  88. self.requestCmdColor = callable(*argsColor, **kwds)
  89. self.resultQ.put((requestId, self.requestCmdColor.run()))
  90. event = wxCmdDone(aborted = aborted,
  91. returncode = returncode,
  92. time = requestTime,
  93. pid = requestId,
  94. onDone = onDone)
  95. # send event
  96. wx.PostEvent(self.parent, event)
  97. def abort(self):
  98. self.requestCmd.abort()
  99. class GMConsole(wx.SplitterWindow):
  100. """!Create and manage output console for commands run by GUI.
  101. """
  102. def __init__(self, parent, id=wx.ID_ANY, margin=False, pageid=0,
  103. notebook = None,
  104. style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
  105. **kwargs):
  106. wx.SplitterWindow.__init__(self, parent, id, style = style, *kwargs)
  107. self.SetName("GMConsole")
  108. self.panel = wx.Panel(parent = self, id = wx.ID_ANY)
  109. # initialize variables
  110. self.Map = None
  111. self.parent = parent # GMFrame | CmdPanel | ?
  112. if notebook:
  113. self._notebook = notebook
  114. else:
  115. self._notebook = self.parent.notebook
  116. self.lineWidth = 80
  117. self.pageid = pageid
  118. # remember position of line begining (used for '\r')
  119. self.linePos = -1
  120. #
  121. # create queues
  122. #
  123. self.requestQ = Queue.Queue()
  124. self.resultQ = Queue.Queue()
  125. #
  126. # progress bar
  127. #
  128. self.console_progressbar = wx.Gauge(parent=self.panel, id=wx.ID_ANY,
  129. range=100, pos=(110, 50), size=(-1, 25),
  130. style=wx.GA_HORIZONTAL)
  131. self.console_progressbar.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
  132. #
  133. # text control for command output
  134. #
  135. self.cmd_output = GMStc(parent=self, id=wx.ID_ANY, margin=margin,
  136. wrap=None)
  137. self.cmd_output_timer = wx.Timer(self.cmd_output, id=wx.ID_ANY)
  138. self.cmd_output.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
  139. self.cmd_output.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents)
  140. self.Bind(EVT_CMD_RUN, self.OnCmdRun)
  141. self.Bind(EVT_CMD_DONE, self.OnCmdDone)
  142. #
  143. # command prompt
  144. #
  145. self.cmd_prompt = prompt.GPromptSTC(parent = self.panel, id=wx.ID_ANY,
  146. onRun = self.RunCmd)
  147. if self.parent.GetName() != 'LayerManager':
  148. self.cmd_prompt.Hide()
  149. #
  150. # stream redirection
  151. #
  152. self.cmd_stdout = GMStdout(self)
  153. self.cmd_stderr = GMStderr(self)
  154. #
  155. # thread
  156. #
  157. self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
  158. #
  159. # buttons
  160. #
  161. self.btn_console_clear = wx.Button(parent = self.panel, id = wx.ID_ANY,
  162. label = _("C&lear output"), size=(125,-1))
  163. self.btn_cmd_clear = wx.Button(parent = self.panel, id = wx.ID_ANY,
  164. label = _("Cl&ear command"), size=(125,-1))
  165. if self.parent.GetName() != 'LayerManager':
  166. self.btn_cmd_clear.Hide()
  167. self.btn_console_save = wx.Button(parent = self.panel, id = wx.ID_ANY,
  168. label = _("&Save output"), size=(125,-1))
  169. # abort
  170. self.btn_abort = wx.Button(parent = self.panel, id = wx.ID_ANY, label = _("&Abort command"),
  171. size=(125,-1))
  172. self.btn_abort.SetToolTipString(_("Abort the running command"))
  173. self.btn_abort.Enable(False)
  174. self.btn_cmd_clear.Bind(wx.EVT_BUTTON, self.cmd_prompt.OnCmdErase)
  175. self.btn_console_clear.Bind(wx.EVT_BUTTON, self.ClearHistory)
  176. self.btn_console_save.Bind(wx.EVT_BUTTON, self.SaveHistory)
  177. self.btn_abort.Bind(wx.EVT_BUTTON, self.OnCmdAbort)
  178. self.btn_abort.Bind(EVT_CMD_ABORT, self.OnCmdAbort)
  179. self.__layout()
  180. def __layout(self):
  181. """!Do layout"""
  182. boxsizer = wx.BoxSizer(wx.VERTICAL)
  183. buttonsizer = wx.BoxSizer(wx.HORIZONTAL)
  184. boxsizer.Add(item=self.cmd_prompt, proportion=1,
  185. flag=wx.EXPAND | wx.ALL, border=1)
  186. buttonsizer.Add(item=self.btn_console_clear, proportion=0,
  187. flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
  188. buttonsizer.Add(item=self.btn_console_save, proportion=0,
  189. flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
  190. buttonsizer.Add(item=self.btn_cmd_clear, proportion=0,
  191. flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
  192. buttonsizer.Add(item=self.btn_abort, proportion=0,
  193. flag=wx.ALIGN_CENTER | wx.FIXED_MINSIZE | wx.ALL, border=5)
  194. boxsizer.Add(item=buttonsizer, proportion=0,
  195. flag=wx.ALIGN_CENTER)
  196. boxsizer.Add(item=self.console_progressbar, proportion=1,
  197. flag=wx.EXPAND | wx.ALIGN_CENTRE_VERTICAL | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=3)
  198. boxsizer.Fit(self)
  199. boxsizer.SetSizeHints(self)
  200. self.panel.SetSizer(boxsizer)
  201. # split window
  202. if self.parent.GetName() == 'LayerManager':
  203. self.SplitHorizontally(self.cmd_output, self.panel, -75)
  204. self.SetMinimumPaneSize(100)
  205. else:
  206. self.SplitHorizontally(self.cmd_output, self.panel, -10)
  207. self.SetMinimumPaneSize(65)
  208. self.Fit()
  209. # layout
  210. self.SetAutoLayout(True)
  211. self.Layout()
  212. def Redirect(self):
  213. """!Redirect stderr
  214. @return True redirected
  215. @return False failed
  216. """
  217. if Debug.get_level() == 0:
  218. # don't redirect when debugging is enabled
  219. sys.stdout = self.cmd_stdout
  220. sys.stderr = self.cmd_stderr
  221. return True
  222. return False
  223. def WriteLog(self, text, style = None, wrap = None,
  224. switchPage = False):
  225. """!Generic method for writing log message in
  226. given style
  227. @param line text line
  228. @param style text style (see GMStc)
  229. @param stdout write to stdout or stderr
  230. """
  231. self.cmd_output.SetStyle()
  232. if switchPage and \
  233. self._notebook.GetSelection() != self.parent.goutput.pageid:
  234. self._notebook.SetSelection(self.parent.goutput.pageid)
  235. if not style:
  236. style = self.cmd_output.StyleDefault
  237. # p1 = self.cmd_output.GetCurrentPos()
  238. p1 = self.cmd_output.GetEndStyled()
  239. # self.cmd_output.GotoPos(p1)
  240. self.cmd_output.DocumentEnd()
  241. for line in text.splitlines():
  242. # fill space
  243. if len(line) < self.lineWidth:
  244. diff = self.lineWidth - len(line)
  245. line += diff * ' '
  246. self.cmd_output.AddTextWrapped(line, wrap=wrap) # adds '\n'
  247. p2 = self.cmd_output.GetCurrentPos()
  248. self.cmd_output.StartStyling(p1, 0xff)
  249. self.cmd_output.SetStyling(p2 - p1, style)
  250. self.cmd_output.EnsureCaretVisible()
  251. def WriteCmdLog(self, line, pid=None):
  252. """!Write message in selected style"""
  253. if pid:
  254. line = '(' + str(pid) + ') ' + line
  255. self.WriteLog(line, style=self.cmd_output.StyleCommand, switchPage = True)
  256. def WriteWarning(self, line):
  257. """!Write message in warning style"""
  258. self.WriteLog(line, style=self.cmd_output.StyleWarning, switchPage = True)
  259. def WriteError(self, line):
  260. """!Write message in error style"""
  261. self.WriteLog(line, style=self.cmd_output.StyleError, switchPage = True)
  262. def RunCmd(self, command, compReg=True, switchPage=False,
  263. onDone = None):
  264. """
  265. Run in GUI GRASS (or other) commands typed into
  266. console command text widget, and send stdout output to output
  267. text widget.
  268. Command is transformed into a list for processing.
  269. TODO: Display commands (*.d) are captured and
  270. processed separately by mapdisp.py. Display commands are
  271. rendered in map display widget that currently has
  272. the focus (as indicted by mdidx).
  273. @param command command (list)
  274. @param compReg if true use computation region
  275. @param switchPage switch to output page
  276. @param onDone function to be called when command is finished
  277. """
  278. # map display window available ?
  279. try:
  280. curr_disp = self.parent.curr_page.maptree.mapdisplay
  281. self.Map = curr_disp.GetRender()
  282. except:
  283. curr_disp = None
  284. # command given as a string ?
  285. try:
  286. cmdlist = command.strip().split(' ')
  287. except:
  288. cmdlist = command
  289. # update history file
  290. env = grass.gisenv()
  291. fileHistory = open(os.path.join(env['GISDBASE'], env['LOCATION_NAME'], env['MAPSET'],
  292. '.bash_history'), 'a')
  293. cmdString = ' '.join(cmdlist)
  294. try:
  295. fileHistory.write(cmdString + '\n')
  296. finally:
  297. fileHistory.close()
  298. # update history items
  299. if self.parent.GetName() == 'LayerManager':
  300. try:
  301. self.parent.cmdinput.SetHistoryItems()
  302. except AttributeError:
  303. pass
  304. # allow writing to output window
  305. self.cmd_output.SetReadOnly(False)
  306. if cmdlist[0] in globalvar.grassCmd['all']:
  307. # send GRASS command without arguments to GUI command interface
  308. # except display commands (they are handled differently)
  309. if cmdlist[0][0:2] == "d.":
  310. #
  311. # display GRASS commands
  312. #
  313. try:
  314. layertype = {'d.rast' : 'raster',
  315. 'd.rgb' : 'rgb',
  316. 'd.his' : 'his',
  317. 'd.shaded' : 'shaded',
  318. 'd.legend' : 'rastleg',
  319. 'd.rast.arrow' : 'rastarrow',
  320. 'd.rast.num' : 'rastnum',
  321. 'd.vect' : 'vector',
  322. 'd.vect.thematic': 'thememap',
  323. 'd.vect.chart' : 'themechart',
  324. 'd.grid' : 'grid',
  325. 'd.geodesic' : 'geodesic',
  326. 'd.rhumbline' : 'rhumb',
  327. 'd.labels' : 'labels'}[cmdlist[0]]
  328. except KeyError:
  329. wx.MessageBox(message=_("Command '%s' not yet implemented.") % cmdlist[0])
  330. return None
  331. # add layer into layer tree
  332. if cmdlist[0] == 'd.rast':
  333. lname = utils.GetLayerNameFromCmd(cmdlist, fullyQualified = True,
  334. layerType = 'raster')
  335. elif cmdlist[0] == 'd.vect':
  336. lname = utils.GetLayerNameFromCmd(cmdlist, fullyQualified = True,
  337. layerType = 'vector')
  338. else:
  339. lname = None
  340. self.parent.curr_page.maptree.AddLayer(ltype=layertype,
  341. lname=lname,
  342. lcmd=cmdlist)
  343. else:
  344. #
  345. # other GRASS commands (r|v|g|...)
  346. #
  347. # switch to 'Command output'
  348. if switchPage:
  349. if self._notebook.GetSelection() != self.parent.goutput.pageid:
  350. self._notebook.SetSelection(self.parent.goutput.pageid)
  351. self.parent.SetFocus() # -> set focus
  352. self.parent.Raise()
  353. # activate computational region (set with g.region)
  354. # for all non-display commands.
  355. if compReg:
  356. tmpreg = os.getenv("GRASS_REGION")
  357. if os.environ.has_key("GRASS_REGION"):
  358. del os.environ["GRASS_REGION"]
  359. if len(cmdlist) == 1:
  360. import menuform
  361. # process GRASS command without argument
  362. menuform.GUI().ParseCommand(cmdlist, parentframe=self)
  363. else:
  364. # process GRASS command with argument
  365. self.cmdThread.RunCmd(GrassCmd,
  366. onDone,
  367. cmdlist,
  368. self.cmd_stdout, self.cmd_stderr)
  369. self.btn_abort.Enable()
  370. self.cmd_output_timer.Start(50)
  371. return None
  372. # deactivate computational region and return to display settings
  373. if compReg and tmpreg:
  374. os.environ["GRASS_REGION"] = tmpreg
  375. else:
  376. # Send any other command to the shell. Send output to
  377. # console output window
  378. self.cmdThread.RunCmd(GrassCmd,
  379. onDone,
  380. cmdlist,
  381. self.cmd_stdout, self.cmd_stderr)
  382. self.btn_abort.Enable()
  383. self.cmd_output_timer.Start(50)
  384. # reset output window to read only
  385. self.cmd_output.SetReadOnly(True)
  386. return None
  387. def ClearHistory(self, event):
  388. """!Clear history of commands"""
  389. self.cmd_output.ClearAll()
  390. self.console_progressbar.SetValue(0)
  391. def SaveHistory(self, event):
  392. """!Save history of commands"""
  393. self.history = self.cmd_output.GetSelectedText()
  394. if self.history == '':
  395. self.history = self.cmd_output.GetText()
  396. # add newline if needed
  397. if len(self.history) > 0 and self.history[-1] != '\n':
  398. self.history += '\n'
  399. wildcard = "Text file (*.txt)|*.txt"
  400. dlg = wx.FileDialog(
  401. self, message=_("Save file as..."), defaultDir=os.getcwd(),
  402. defaultFile="grass_cmd_history.txt", wildcard=wildcard,
  403. style=wx.SAVE|wx.FD_OVERWRITE_PROMPT)
  404. # Show the dialog and retrieve the user response. If it is the OK response,
  405. # process the data.
  406. if dlg.ShowModal() == wx.ID_OK:
  407. path = dlg.GetPath()
  408. output = open(path, "w")
  409. output.write(self.history)
  410. output.close()
  411. dlg.Destroy()
  412. def GetCmd(self):
  413. """!Get running command or None"""
  414. return self.requestQ.get()
  415. def OnCmdOutput(self, event):
  416. """!Print command output"""
  417. message = event.text
  418. type = event.type
  419. if self._notebook.GetSelection() != self.parent.goutput.pageid:
  420. textP = self._notebook.GetPageText(self.parent.goutput.pageid)
  421. if textP[-1] != ')':
  422. textP += ' (...)'
  423. self._notebook.SetPageText(self.parent.goutput.pageid,
  424. textP)
  425. # message prefix
  426. if type == 'warning':
  427. messege = 'WARNING: ' + message
  428. elif type == 'error':
  429. message = 'ERROR: ' + message
  430. p1 = self.cmd_output.GetEndStyled()
  431. self.cmd_output.GotoPos(p1)
  432. if '\b' in message:
  433. if self.linepos < 0:
  434. self.linepos = p1
  435. last_c = ''
  436. for c in message:
  437. if c == '\b':
  438. self.linepos -= 1
  439. else:
  440. if c == '\r':
  441. pos = self.cmd_output.GetCurLine()[1]
  442. # self.cmd_output.SetCurrentPos(pos)
  443. else:
  444. self.cmd_output.SetCurrentPos(self.linepos)
  445. self.cmd_output.ReplaceSelection(c)
  446. self.linepos = self.cmd_output.GetCurrentPos()
  447. if c != ' ':
  448. last_c = c
  449. if last_c not in ('0123456789'):
  450. self.cmd_output.AddTextWrapped('\n', wrap=None)
  451. self.linepos = -1
  452. else:
  453. self.linepos = -1 # don't force position
  454. if '\n' not in message:
  455. self.cmd_output.AddTextWrapped(message, wrap=60)
  456. else:
  457. self.cmd_output.AddTextWrapped(message, wrap=None)
  458. p2 = self.cmd_output.GetCurrentPos()
  459. if p2 >= p1:
  460. self.cmd_output.StartStyling(p1, 0xff)
  461. if type == 'error':
  462. self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleError)
  463. elif type == 'warning':
  464. self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleWarning)
  465. elif type == 'message':
  466. self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleMessage)
  467. else: # unknown
  468. self.cmd_output.SetStyling(p2 - p1, self.cmd_output.StyleUnknown)
  469. self.cmd_output.EnsureCaretVisible()
  470. def OnCmdProgress(self, event):
  471. """!Update progress message info"""
  472. self.console_progressbar.SetValue(event.value)
  473. def OnCmdAbort(self, event):
  474. """!Abort running command"""
  475. self.cmdThread.abort()
  476. def OnCmdRun(self, event):
  477. """!Run command"""
  478. self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)))
  479. def OnCmdDone(self, event):
  480. """!Command done (or aborted)"""
  481. if event.aborted:
  482. # Thread aborted (using our convention of None return)
  483. self.WriteLog(_('Please note that the data are left in incosistent stage '
  484. 'and can be corrupted'), self.cmd_output.StyleWarning)
  485. self.WriteCmdLog('(%s) %s (%d sec)' % (str(time.ctime()),
  486. _('Command aborted'),
  487. (time.time() - event.time)))
  488. # pid=self.cmdThread.requestId)
  489. self.btn_abort.Enable(False)
  490. else:
  491. try:
  492. # Process results here
  493. self.WriteCmdLog('(%s) %s (%d sec)' % (str(time.ctime()),
  494. _('Command finished'),
  495. (time.time() - event.time)))
  496. except KeyError:
  497. # stopped deamon
  498. pass
  499. self.btn_abort.Enable(False)
  500. if event.onDone:
  501. event.onDone(returncode = event.returncode)
  502. self.console_progressbar.SetValue(0) # reset progress bar on '0%'
  503. self.cmd_output_timer.Stop()
  504. # set focus on prompt
  505. if self.parent.GetName() == "LayerManager":
  506. self.cmd_prompt.SetFocus()
  507. self.btn_abort.Enable(False)
  508. else:
  509. # updated command dialog
  510. dialog = self.parent.parent
  511. if hasattr(self.parent.parent, "btn_abort"):
  512. dialog.btn_abort.Enable(False)
  513. if hasattr(self.parent.parent, "btn_cancel"):
  514. dialog.btn_cancel.Enable(True)
  515. if hasattr(self.parent.parent, "btn_clipboard"):
  516. dialog.btn_clipboard.Enable(True)
  517. if hasattr(self.parent.parent, "btn_help"):
  518. dialog.btn_help.Enable(True)
  519. if hasattr(self.parent.parent, "btn_run"):
  520. dialog.btn_run.Enable(True)
  521. if event.returncode == 0 and \
  522. not event.aborted and hasattr(dialog, "addbox") and \
  523. dialog.addbox.IsChecked():
  524. # add new map into layer tree
  525. if dialog.outputType in ('raster', 'vector'):
  526. # add layer into layer tree
  527. cmd = dialog.notebookpanel.createCmd(ignoreErrors = True)
  528. name = utils.GetLayerNameFromCmd(cmd, fullyQualified=True, param='output')
  529. winName = self.parent.parent.parent.GetName()
  530. if winName == 'LayerManager':
  531. mapTree = self.parent.parent.parent.curr_page.maptree
  532. else: # GMConsole
  533. mapTree = self.parent.parent.parent.parent.curr_page.maptree
  534. if not mapTree.GetMap().GetListOfLayers(l_name = name):
  535. if dialog.outputType == 'raster':
  536. lcmd = ['d.rast',
  537. 'map=%s' % name]
  538. else:
  539. lcmd = ['d.vect',
  540. 'map=%s' % name]
  541. mapTree.AddLayer(ltype=dialog.outputType,
  542. lcmd=lcmd,
  543. lname=name)
  544. if hasattr(dialog, "get_dcmd") and \
  545. dialog.get_dcmd is None and \
  546. dialog.closebox.IsChecked():
  547. time.sleep(1)
  548. dialog.Close()
  549. event.Skip()
  550. def OnProcessPendingOutputWindowEvents(self, event):
  551. self.ProcessPendingEvents()
  552. class GMStdout:
  553. """!GMConsole standard output
  554. Based on FrameOutErr.py
  555. Name: FrameOutErr.py
  556. Purpose: Redirecting stdout / stderr
  557. Author: Jean-Michel Fauth, Switzerland
  558. Copyright: (c) 2005-2007 Jean-Michel Fauth
  559. Licence: GPL
  560. """
  561. def __init__(self, parent):
  562. self.parent = parent # GMConsole
  563. def write(self, s):
  564. if len(s) == 0 or s == '\n':
  565. return
  566. for line in s.splitlines():
  567. if len(line) == 0:
  568. continue
  569. evt = wxCmdOutput(text=line + '\n',
  570. type='')
  571. wx.PostEvent(self.parent.cmd_output, evt)
  572. class GMStderr:
  573. """!GMConsole standard error output
  574. Based on FrameOutErr.py
  575. Name: FrameOutErr.py
  576. Purpose: Redirecting stdout / stderr
  577. Author: Jean-Michel Fauth, Switzerland
  578. Copyright: (c) 2005-2007 Jean-Michel Fauth
  579. Licence: GPL
  580. """
  581. def __init__(self, parent):
  582. self.parent = parent # GMConsole
  583. self.type = ''
  584. self.message = ''
  585. self.printMessage = False
  586. def write(self, s):
  587. if "GtkPizza" in s:
  588. return
  589. # remove/replace escape sequences '\b' or '\r' from stream
  590. progressValue = -1
  591. for line in s.splitlines():
  592. if len(line) == 0:
  593. continue
  594. if 'GRASS_INFO_PERCENT' in line:
  595. value = int(line.rsplit(':', 1)[1].strip())
  596. if value >= 0 and value < 100:
  597. progressValue = value
  598. else:
  599. progressValue = 0
  600. elif 'GRASS_INFO_MESSAGE' in line:
  601. self.type = 'message'
  602. self.message += line.split(':', 1)[1].strip() + '\n'
  603. elif 'GRASS_INFO_WARNING' in line:
  604. self.type = 'warning'
  605. self.message += line.split(':', 1)[1].strip() + '\n'
  606. elif 'GRASS_INFO_ERROR' in line:
  607. self.type = 'error'
  608. self.message += line.split(':', 1)[1].strip() + '\n'
  609. elif 'GRASS_INFO_END' in line:
  610. self.printMessage = True
  611. elif self.type == '':
  612. if len(line) == 0:
  613. continue
  614. evt = wxCmdOutput(text=line,
  615. type='')
  616. wx.PostEvent(self.parent.cmd_output, evt)
  617. elif len(line) > 0:
  618. self.message += line.strip() + '\n'
  619. if self.printMessage and len(self.message) > 0:
  620. evt = wxCmdOutput(text=self.message,
  621. type=self.type)
  622. wx.PostEvent(self.parent.cmd_output, evt)
  623. self.type = ''
  624. self.message = ''
  625. self.printMessage = False
  626. # update progress message
  627. if progressValue > -1:
  628. # self.gmgauge.SetValue(progressValue)
  629. evt = wxCmdProgress(value=progressValue)
  630. wx.PostEvent(self.parent.console_progressbar, evt)
  631. class GMStc(wx.stc.StyledTextCtrl):
  632. """!Styled GMConsole
  633. Based on FrameOutErr.py
  634. Name: FrameOutErr.py
  635. Purpose: Redirecting stdout / stderr
  636. Author: Jean-Michel Fauth, Switzerland
  637. Copyright: (c) 2005-2007 Jean-Michel Fauth
  638. Licence: GPL
  639. """
  640. def __init__(self, parent, id, margin=False, wrap=None):
  641. wx.stc.StyledTextCtrl.__init__(self, parent, id)
  642. self.parent = parent
  643. self.SetUndoCollection(True)
  644. self.SetReadOnly(True)
  645. #
  646. # styles
  647. #
  648. self.SetStyle()
  649. #
  650. # line margins
  651. #
  652. # TODO print number only from cmdlog
  653. self.SetMarginWidth(1, 0)
  654. self.SetMarginWidth(2, 0)
  655. if margin:
  656. self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
  657. self.SetMarginWidth(0, 30)
  658. else:
  659. self.SetMarginWidth(0, 0)
  660. #
  661. # miscellaneous
  662. #
  663. self.SetViewWhiteSpace(False)
  664. self.SetTabWidth(4)
  665. self.SetUseTabs(False)
  666. self.UsePopUp(True)
  667. self.SetSelBackground(True, "#FFFF00")
  668. self.SetUseHorizontalScrollBar(True)
  669. #
  670. # bindings
  671. #
  672. self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
  673. def SetStyle(self):
  674. """!Set styles for styled text output windows with type face
  675. and point size selected by user (Courier New 10 is default)"""
  676. settings = preferences.Settings()
  677. typeface = settings.Get(group='display', key='outputfont', subkey='type')
  678. if typeface == "": typeface = "Courier New"
  679. typesize = settings.Get(group='display', key='outputfont', subkey='size')
  680. if typesize == None or typesize <= 0: typesize = 10
  681. typesize = float(typesize)
  682. self.StyleDefault = 0
  683. self.StyleDefaultSpec = "face:%s,size:%d,fore:#000000,back:#FFFFFF" % (typeface, typesize)
  684. self.StyleCommand = 1
  685. self.StyleCommandSpec = "face:%s,size:%d,,fore:#000000,back:#bcbcbc" % (typeface, typesize)
  686. self.StyleOutput = 2
  687. self.StyleOutputSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
  688. # fatal error
  689. self.StyleError = 3
  690. self.StyleErrorSpec = "face:%s,size:%d,,fore:#7F0000,back:#FFFFFF" % (typeface, typesize)
  691. # warning
  692. self.StyleWarning = 4
  693. self.StyleWarningSpec = "face:%s,size:%d,,fore:#0000FF,back:#FFFFFF" % (typeface, typesize)
  694. # message
  695. self.StyleMessage = 5
  696. self.StyleMessageSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
  697. # unknown
  698. self.StyleUnknown = 6
  699. self.StyleUnknownSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (typeface, typesize)
  700. # default and clear => init
  701. self.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
  702. self.StyleClearAll()
  703. self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec)
  704. self.StyleSetSpec(self.StyleOutput, self.StyleOutputSpec)
  705. self.StyleSetSpec(self.StyleError, self.StyleErrorSpec)
  706. self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec)
  707. self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec)
  708. self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)
  709. def OnDestroy(self, evt):
  710. """!The clipboard contents can be preserved after
  711. the app has exited"""
  712. wx.TheClipboard.Flush()
  713. evt.Skip()
  714. def AddTextWrapped(self, txt, wrap=None):
  715. """!Add string to text area.
  716. String is wrapped and linesep is also added to the end
  717. of the string"""
  718. if wrap:
  719. txt = textwrap.fill(txt, wrap) + '\n'
  720. else:
  721. if txt[-1] != '\n':
  722. txt += '\n'
  723. if '\r' in txt:
  724. self.parent.linePos = -1
  725. for seg in txt.split('\r'):
  726. if self.parent.linePos > -1:
  727. self.SetCurrentPos(self.parent.linePos)
  728. self.ReplaceSelection(seg)
  729. else:
  730. self.parent.linePos = self.GetCurrentPos()
  731. self.AddText(seg)
  732. else:
  733. self.parent.linePos = self.GetCurrentPos()
  734. try:
  735. self.AddText(txt)
  736. except UnicodeDecodeError:
  737. enc = UserSettings.Get(group='atm', key='encoding', subkey='value')
  738. if enc:
  739. txt = unicode(txt, enc)
  740. elif os.environ.has_key('GRASS_DB_ENCODING'):
  741. txt = unicode(txt, os.environ['GRASS_DB_ENCODING'])
  742. else:
  743. txt = _('Unable to encode text. Please set encoding in GUI preferences.') + '\n'
  744. self.AddText(txt)