gconsole.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. """
  2. @package core.gconsole
  3. @brief Command output widgets
  4. Classes:
  5. - goutput::CmdThread
  6. - goutput::GStdout
  7. - goutput::GStderr
  8. - goutput::GConsole
  9. (C) 2007-2015 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Michael Barton (Arizona State University)
  13. @author Martin Landa <landa.martin gmail.com>
  14. @author Vaclav Petras <wenzeslaus gmail.com> (refactoring)
  15. @author Anna Kratochvilova <kratochanna gmail.com> (refactoring)
  16. """
  17. import os
  18. import sys
  19. import re
  20. import time
  21. import threading
  22. import Queue
  23. import codecs
  24. import locale
  25. import wx
  26. from wx.lib.newevent import NewEvent
  27. import grass.script as grass
  28. from grass.script import task as gtask
  29. from grass.pydispatch.signal import Signal
  30. from core import globalvar
  31. from core.gcmd import CommandThread, GError, GException
  32. from core.utils import _
  33. from gui_core.forms import GUI
  34. from core.debug import Debug
  35. from core.settings import UserSettings
  36. from core.giface import Notification
  37. from gui_core.widgets import FormNotebook
  38. wxCmdOutput, EVT_CMD_OUTPUT = NewEvent()
  39. wxCmdProgress, EVT_CMD_PROGRESS = NewEvent()
  40. wxCmdRun, EVT_CMD_RUN = NewEvent()
  41. wxCmdDone, EVT_CMD_DONE = NewEvent()
  42. wxCmdAbort, EVT_CMD_ABORT = NewEvent()
  43. wxCmdPrepare, EVT_CMD_PREPARE = NewEvent()
  44. def GrassCmd(cmd, env=None, stdout=None, stderr=None):
  45. """Return GRASS command thread"""
  46. return CommandThread(cmd, env=env,
  47. stdout=stdout, stderr=stderr)
  48. class CmdThread(threading.Thread):
  49. """Thread for GRASS commands"""
  50. requestId = 0
  51. def __init__(self, receiver, requestQ=None, resultQ=None, **kwds):
  52. """
  53. :param receiver: event receiver (used in PostEvent)
  54. """
  55. threading.Thread.__init__(self, **kwds)
  56. if requestQ is None:
  57. self.requestQ = Queue.Queue()
  58. else:
  59. self.requestQ = requestQ
  60. if resultQ is None:
  61. self.resultQ = Queue.Queue()
  62. else:
  63. self.resultQ = resultQ
  64. self.setDaemon(True)
  65. self.requestCmd = None
  66. self.receiver = receiver
  67. self._want_abort_all = False
  68. self.start()
  69. def RunCmd(self, *args, **kwds):
  70. """Run command in queue
  71. :param args: unnamed command arguments
  72. :param kwds: named command arguments
  73. :return: request id in queue
  74. """
  75. CmdThread.requestId += 1
  76. self.requestCmd = None
  77. self.requestQ.put((CmdThread.requestId, args, kwds))
  78. return CmdThread.requestId
  79. def GetId(self):
  80. """Get id for next command"""
  81. return CmdThread.requestId + 1
  82. def SetId(self, id):
  83. """Set starting id"""
  84. CmdThread.requestId = id
  85. def run(self):
  86. os.environ['GRASS_MESSAGE_FORMAT'] = 'gui'
  87. while True:
  88. requestId, args, kwds = self.requestQ.get()
  89. for key in ('callable', 'onDone', 'onPrepare',
  90. 'userData', 'addLayer', 'notification'):
  91. if key in kwds:
  92. vars()[key] = kwds[key]
  93. del kwds[key]
  94. else:
  95. vars()[key] = None
  96. if not vars()['callable']:
  97. vars()['callable'] = GrassCmd
  98. requestTime = time.time()
  99. # prepare
  100. if self.receiver:
  101. event = wxCmdPrepare(cmd=args[0],
  102. time=requestTime,
  103. pid=requestId,
  104. onPrepare=vars()['onPrepare'],
  105. userData=vars()['userData'])
  106. wx.PostEvent(self.receiver, event)
  107. # run command
  108. event = wxCmdRun(cmd=args[0],
  109. pid=requestId,
  110. notification=vars()['notification'])
  111. wx.PostEvent(self.receiver, event)
  112. time.sleep(.1)
  113. self.requestCmd = vars()['callable'](*args, **kwds)
  114. if self._want_abort_all and self.requestCmd is not None:
  115. self.requestCmd.abort()
  116. if self.requestQ.empty():
  117. self._want_abort_all = False
  118. self.resultQ.put((requestId, self.requestCmd.run()))
  119. try:
  120. returncode = self.requestCmd.module.returncode
  121. except AttributeError:
  122. returncode = 0 # being optimistic
  123. try:
  124. aborted = self.requestCmd.aborted
  125. except AttributeError:
  126. aborted = False
  127. time.sleep(.1)
  128. # set default color table for raster data
  129. if UserSettings.Get(group='rasterLayer',
  130. key='colorTable', subkey='enabled') and \
  131. args[0][0][:2] == 'r.':
  132. colorTable = UserSettings.Get(group='rasterLayer',
  133. key='colorTable',
  134. subkey='selection')
  135. mapName = None
  136. if args[0][0] == 'r.mapcalc':
  137. try:
  138. mapName = args[0][1].split('=', 1)[0].strip()
  139. except KeyError:
  140. pass
  141. else:
  142. moduleInterface = GUI(show=None).ParseCommand(args[0])
  143. outputParam = moduleInterface.get_param(value='output',
  144. raiseError=False)
  145. if outputParam and outputParam['prompt'] == 'raster':
  146. mapName = outputParam['value']
  147. if mapName:
  148. argsColor = list(args)
  149. argsColor[0] = ['r.colors',
  150. 'map=%s' % mapName,
  151. 'color=%s' % colorTable]
  152. self.requestCmdColor = vars()['callable'](
  153. *argsColor, **kwds)
  154. self.resultQ.put((requestId, self.requestCmdColor.run()))
  155. if self.receiver:
  156. event = wxCmdDone(cmd=args[0],
  157. aborted=aborted,
  158. returncode=returncode,
  159. time=requestTime,
  160. pid=requestId,
  161. onDone=vars()['onDone'],
  162. userData=vars()['userData'],
  163. addLayer=vars()['addLayer'],
  164. notification=vars()['notification'])
  165. # send event
  166. wx.PostEvent(self.receiver, event)
  167. def abort(self, abortall=True):
  168. """Abort command(s)"""
  169. if abortall:
  170. self._want_abort_all = True
  171. if self.requestCmd is not None:
  172. self.requestCmd.abort()
  173. if self.requestQ.empty():
  174. self._want_abort_all = False
  175. class GStdout:
  176. """GConsole standard output
  177. Based on FrameOutErr.py
  178. Name: FrameOutErr.py
  179. Purpose: Redirecting stdout / stderr
  180. Author: Jean-Michel Fauth, Switzerland
  181. Copyright: (c) 2005-2007 Jean-Michel Fauth
  182. Licence: GPL
  183. """
  184. def __init__(self, receiver):
  185. """
  186. :param receiver: event receiver (used in PostEvent)
  187. """
  188. self.receiver = receiver
  189. def flush(self):
  190. pass
  191. def write(self, s):
  192. if len(s) == 0 or s == '\n':
  193. return
  194. for line in s.splitlines():
  195. if len(line) == 0:
  196. continue
  197. evt = wxCmdOutput(text=line + '\n',
  198. type='')
  199. wx.PostEvent(self.receiver, evt)
  200. class GStderr:
  201. """GConsole standard error output
  202. Based on FrameOutErr.py
  203. Name: FrameOutErr.py
  204. Purpose: Redirecting stdout / stderr
  205. Author: Jean-Michel Fauth, Switzerland
  206. Copyright: (c) 2005-2007 Jean-Michel Fauth
  207. Licence: GPL
  208. """
  209. def __init__(self, receiver):
  210. """
  211. :param receiver: event receiver (used in PostEvent)
  212. """
  213. self.receiver = receiver
  214. self.type = ''
  215. self.message = ''
  216. self.printMessage = False
  217. def flush(self):
  218. pass
  219. def write(self, s):
  220. if "GtkPizza" in s:
  221. return
  222. # remove/replace escape sequences '\b' or '\r' from stream
  223. progressValue = -1
  224. for line in s.splitlines():
  225. if len(line) == 0:
  226. continue
  227. if 'GRASS_INFO_PERCENT' in line:
  228. value = int(line.rsplit(':', 1)[1].strip())
  229. if value >= 0 and value < 100:
  230. progressValue = value
  231. else:
  232. progressValue = 0
  233. elif 'GRASS_INFO_MESSAGE' in line:
  234. self.type = 'message'
  235. self.message += line.split(':', 1)[1].strip() + '\n'
  236. elif 'GRASS_INFO_WARNING' in line:
  237. self.type = 'warning'
  238. self.message += line.split(':', 1)[1].strip() + '\n'
  239. elif 'GRASS_INFO_ERROR' in line:
  240. self.type = 'error'
  241. self.message += line.split(':', 1)[1].strip() + '\n'
  242. elif 'GRASS_INFO_END' in line:
  243. self.printMessage = True
  244. elif self.type == '':
  245. if len(line) == 0:
  246. continue
  247. evt = wxCmdOutput(text=line,
  248. type='')
  249. wx.PostEvent(self.receiver, evt)
  250. elif len(line) > 0:
  251. self.message += line.strip() + '\n'
  252. if self.printMessage and len(self.message) > 0:
  253. evt = wxCmdOutput(text=self.message,
  254. type=self.type)
  255. wx.PostEvent(self.receiver, evt)
  256. self.type = ''
  257. self.message = ''
  258. self.printMessage = False
  259. # update progress message
  260. if progressValue > -1:
  261. # self.gmgauge.SetValue(progressValue)
  262. evt = wxCmdProgress(value=progressValue)
  263. wx.PostEvent(self.receiver, evt)
  264. # Occurs when an ignored command is called.
  265. # Attribute cmd contains command (as a list).
  266. gIgnoredCmdRun, EVT_IGNORED_CMD_RUN = NewEvent()
  267. class GConsole(wx.EvtHandler):
  268. """
  269. """
  270. def __init__(self, guiparent=None, giface=None, ignoredCmdPattern=None):
  271. """
  272. :param guiparent: parent window for created GUI objects
  273. :param lmgr: layer manager window (TODO: replace by giface)
  274. :param ignoredCmdPattern: regular expression specifying commads
  275. to be ignored (e.g. @c '^d\..*' for
  276. display commands)
  277. """
  278. wx.EvtHandler.__init__(self)
  279. # Signal when some map is created or updated by a module.
  280. # attributes: name: map name, ltype: map type,
  281. self.mapCreated = Signal('GConsole.mapCreated')
  282. # emitted when map display should be re-render
  283. self.updateMap = Signal('GConsole.updateMap')
  284. # emitted when log message should be written
  285. self.writeLog = Signal('GConsole.writeLog')
  286. # emitted when command log message should be written
  287. self.writeCmdLog = Signal('GConsole.writeCmdLog')
  288. # emitted when warning message should be written
  289. self.writeWarning = Signal('GConsole.writeWarning')
  290. # emitted when error message should be written
  291. self.writeError = Signal('GConsole.writeError')
  292. self._guiparent = guiparent
  293. self._giface = giface
  294. self._ignoredCmdPattern = ignoredCmdPattern
  295. # create queues
  296. self.requestQ = Queue.Queue()
  297. self.resultQ = Queue.Queue()
  298. self.cmdOutputTimer = wx.Timer(self)
  299. self.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents)
  300. self.Bind(EVT_CMD_RUN, self.OnCmdRun)
  301. self.Bind(EVT_CMD_DONE, self.OnCmdDone)
  302. self.Bind(EVT_CMD_ABORT, self.OnCmdAbort)
  303. # stream redirection
  304. self.cmdStdOut = GStdout(receiver=self)
  305. self.cmdStdErr = GStderr(receiver=self)
  306. # thread
  307. self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)
  308. def Redirect(self):
  309. """Redirect stdout/stderr
  310. """
  311. if Debug.GetLevel() == 0 and grass.debug_level(force=True) == 0:
  312. # don't redirect when debugging is enabled
  313. sys.stdout = self.cmdStdOut
  314. sys.stderr = self.cmdStdErr
  315. else:
  316. enc = locale.getdefaultlocale()[1]
  317. if enc:
  318. sys.stdout = codecs.getwriter(enc)(sys.__stdout__)
  319. sys.stderr = codecs.getwriter(enc)(sys.__stderr__)
  320. else:
  321. sys.stdout = sys.__stdout__
  322. sys.stderr = sys.__stderr__
  323. def WriteLog(self, text, style=None, wrap=None,
  324. notification=Notification.HIGHLIGHT):
  325. """Generic method for writing log message in
  326. given style
  327. :param text: text line
  328. :param notification: form of notification
  329. """
  330. self.writeLog.emit(text=text, wrap=wrap,
  331. notification=notification)
  332. def WriteCmdLog(self, text, pid=None,
  333. notification=Notification.MAKE_VISIBLE):
  334. """Write message in selected style
  335. :param text: message to be printed
  336. :param pid: process pid or None
  337. :param notification: form of notification
  338. """
  339. self.writeCmdLog.emit(text=text, pid=pid,
  340. notification=notification)
  341. def WriteWarning(self, text):
  342. """Write message in warning style"""
  343. self.writeWarning.emit(text=text)
  344. def WriteError(self, text):
  345. """Write message in error style"""
  346. self.writeError.emit(text=text)
  347. def RunCmd(self, command, compReg=True, env=None, skipInterface=False,
  348. onDone=None, onPrepare=None, userData=None, addLayer=None,
  349. notification=Notification.MAKE_VISIBLE):
  350. """Run command typed into console command prompt (GPrompt).
  351. .. todo::
  352. Document the other event.
  353. .. todo::
  354. Solve problem with the other event (now uses gOutputText
  355. event but there is no text, use onPrepare handler instead?)
  356. .. todo::
  357. Skip interface is ignored and determined always automatically.
  358. Posts event EVT_IGNORED_CMD_RUN when command which should be ignored
  359. (according to ignoredCmdPattern) is run.
  360. For example, see layer manager which handles d.* on its own.
  361. :param command: command given as a list (produced e.g. by utils.split())
  362. :param compReg: True use computation region
  363. :param notification: form of notification
  364. :param bool skipInterface: True to do not launch GRASS interface
  365. parser when command has no arguments
  366. given
  367. :param onDone: function to be called when command is finished
  368. :param onPrepare: function to be called before command is launched
  369. :param addLayer: to be passed in the mapCreated signal
  370. :param userData: data defined for the command
  371. """
  372. if len(command) == 0:
  373. Debug.msg(2, "GPrompt:RunCmd(): empty command")
  374. return
  375. # update history file
  376. self.UpdateHistoryFile(' '.join(command))
  377. if command[0] in globalvar.grassCmd:
  378. # send GRASS command without arguments to GUI command interface
  379. # except ignored commands (event is emitted)
  380. if self._ignoredCmdPattern and \
  381. re.compile(self._ignoredCmdPattern).search(' '.join(command)) and \
  382. '--help' not in command and '--ui' not in command:
  383. event = gIgnoredCmdRun(cmd=command)
  384. wx.PostEvent(self, event)
  385. return
  386. else:
  387. # other GRASS commands (r|v|g|...)
  388. try:
  389. task = GUI(show=None).ParseCommand(command)
  390. except GException as e:
  391. GError(parent=self._guiparent,
  392. message=unicode(e),
  393. showTraceback=False)
  394. return
  395. hasParams = False
  396. if task:
  397. options = task.get_options()
  398. hasParams = options['params'] and options['flags']
  399. # check for <input>=-
  400. for p in options['params']:
  401. if p.get('prompt', '') == 'input' and \
  402. p.get('element', '') == 'file' and \
  403. p.get('age', 'new') == 'old' and \
  404. p.get('value', '') == '-':
  405. GError(
  406. parent=self._guiparent,
  407. message=_(
  408. "Unable to run command:\n%(cmd)s\n\n"
  409. "Option <%(opt)s>: read from standard input is not "
  410. "supported by wxGUI") % {
  411. 'cmd': ' '.join(command),
  412. 'opt': p.get(
  413. 'name',
  414. '')})
  415. return
  416. if len(command) == 1:
  417. if command[0].startswith('g.gui.'):
  418. import imp
  419. import inspect
  420. pyFile = command[0]
  421. if sys.platform == 'win32':
  422. pyFile += '.py'
  423. pyPath = os.path.join(
  424. os.environ['GISBASE'], 'scripts', pyFile)
  425. if not os.path.exists(pyPath):
  426. pyPath = os.path.join(
  427. os.environ['GRASS_ADDON_BASE'], 'scripts', pyFile)
  428. if not os.path.exists(pyPath):
  429. GError(
  430. parent=self._guiparent,
  431. message=_("Module <%s> not found.") %
  432. command[0])
  433. pymodule = imp.load_source(
  434. command[0].replace('.', '_'), pyPath)
  435. pymain = inspect.getargspec(pymodule.main)
  436. if pymain and 'giface' in pymain.args:
  437. pymodule.main(self._giface)
  438. return
  439. # no arguments given
  440. if hasParams and \
  441. not isinstance(self._guiparent, FormNotebook):
  442. # also parent must be checked, see #3135 for details
  443. try:
  444. GUI(parent=self._guiparent,
  445. giface=self._giface).ParseCommand(command)
  446. except GException as e:
  447. print >> sys.stderr, e
  448. return
  449. if env:
  450. env = env.copy()
  451. else:
  452. env = os.environ.copy()
  453. # activate computational region (set with g.region)
  454. # for all non-display commands.
  455. if compReg and "GRASS_REGION" in env:
  456. del env["GRASS_REGION"]
  457. # process GRASS command with argument
  458. self.cmdThread.RunCmd(command,
  459. stdout=self.cmdStdOut,
  460. stderr=self.cmdStdErr,
  461. onDone=onDone, onPrepare=onPrepare,
  462. userData=userData, addLayer=addLayer,
  463. env=env,
  464. notification=notification)
  465. self.cmdOutputTimer.Start(50)
  466. # we don't need to change computational region settings
  467. # because we work on a copy
  468. else:
  469. # Send any other command to the shell. Send output to
  470. # console output window
  471. #
  472. # Check if the script has an interface (avoid double-launching
  473. # of the script)
  474. # check if we ignore the command (similar to grass commands part)
  475. if self._ignoredCmdPattern and \
  476. re.compile(self._ignoredCmdPattern).search(' '.join(command)):
  477. event = gIgnoredCmdRun(cmd=command)
  478. wx.PostEvent(self, event)
  479. return
  480. skipInterface = True
  481. if os.path.splitext(command[0])[1] in ('.py', '.sh'):
  482. try:
  483. sfile = open(command[0], "r")
  484. for line in sfile.readlines():
  485. if len(line) < 2:
  486. continue
  487. if line[0] is '#' and line[1] is '%':
  488. skipInterface = False
  489. break
  490. sfile.close()
  491. except IOError:
  492. pass
  493. if len(command) == 1 and not skipInterface:
  494. try:
  495. task = gtask.parse_interface(command[0])
  496. except:
  497. task = None
  498. else:
  499. task = None
  500. if task:
  501. # process GRASS command without argument
  502. GUI(parent=self._guiparent,
  503. giface=self._giface).ParseCommand(command)
  504. else:
  505. self.cmdThread.RunCmd(command,
  506. stdout=self.cmdStdOut,
  507. stderr=self.cmdStdErr,
  508. onDone=onDone, onPrepare=onPrepare,
  509. userData=userData, addLayer=addLayer,
  510. env=env,
  511. notification=notification)
  512. self.cmdOutputTimer.Start(50)
  513. def GetLog(self, err=False):
  514. """Get widget used for logging
  515. .. todo::
  516. what's this?
  517. :param bool err: True to get stderr widget
  518. """
  519. if err:
  520. return self.cmdStdErr
  521. return self.cmdStdOut
  522. def GetCmd(self):
  523. """Get running command or None"""
  524. return self.requestQ.get()
  525. def OnCmdAbort(self, event):
  526. """Abort running command"""
  527. self.cmdThread.abort()
  528. event.Skip()
  529. def OnCmdRun(self, event):
  530. """Run command"""
  531. self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)),
  532. notification=event.notification)
  533. event.Skip()
  534. def OnCmdDone(self, event):
  535. """Command done (or aborted)
  536. Sends signal mapCreated if map is recognized in output
  537. parameters or for specific modules (as r.colors).
  538. """
  539. # Process results here
  540. try:
  541. ctime = time.time() - event.time
  542. if ctime < 60:
  543. stime = _("%d sec") % int(ctime)
  544. else:
  545. mtime = int(ctime / 60)
  546. stime = _("%(min)d min %(sec)d sec") % {
  547. 'min': mtime, 'sec': int(ctime - (mtime * 60))}
  548. except KeyError:
  549. # stopped deamon
  550. stime = _("unknown")
  551. if event.aborted:
  552. # Thread aborted (using our convention of None return)
  553. self.WriteWarning(_('Please note that the data are left in'
  554. ' inconsistent state and may be corrupted'))
  555. msg = _('Command aborted')
  556. else:
  557. msg = _('Command finished')
  558. self.WriteCmdLog('(%s) %s (%s)' % (str(time.ctime()), msg, stime),
  559. notification=event.notification)
  560. if event.onDone:
  561. event.onDone(event)
  562. self.cmdOutputTimer.Stop()
  563. if event.cmd[0] == 'g.gisenv':
  564. Debug.SetLevel()
  565. self.Redirect()
  566. # do nothing when no map added
  567. if event.returncode != 0 or event.aborted:
  568. event.Skip()
  569. return
  570. if event.cmd[0] not in globalvar.grassCmd:
  571. return
  572. # find which maps were created
  573. try:
  574. task = GUI(show=None).ParseCommand(event.cmd)
  575. except GException as e:
  576. print >> sys.stderr, e
  577. task = None
  578. return
  579. name = task.get_name()
  580. for p in task.get_options()['params']:
  581. prompt = p.get('prompt', '')
  582. if prompt in (
  583. 'raster', 'vector', 'raster_3d') and p.get(
  584. 'value', None):
  585. if p.get('age', 'old') == 'new' or name in (
  586. 'r.colors', 'r3.colors', 'v.colors', 'v.proj', 'r.proj'):
  587. # if multiple maps (e.g. r.series.interp), we need add each
  588. if p.get('multiple', False):
  589. lnames = p.get('value').split(',')
  590. # in case multiple input (old) maps in r.colors
  591. # we don't want to rerender it multiple times! just
  592. # once
  593. if p.get('age', 'old') == 'old':
  594. lnames = lnames[0:1]
  595. else:
  596. lnames = [p.get('value')]
  597. for lname in lnames:
  598. if '@' not in lname:
  599. lname += '@' + grass.gisenv()['MAPSET']
  600. if grass.find_file(lname, element=p.get('element'))[
  601. 'fullname']:
  602. self.mapCreated.emit(
  603. name=lname, ltype=prompt, add=event.addLayer)
  604. if name == 'r.mask':
  605. self.updateMap.emit()
  606. event.Skip()
  607. def OnProcessPendingOutputWindowEvents(self, event):
  608. wx.GetApp().ProcessPendingEvents()
  609. def UpdateHistoryFile(self, command):
  610. """Update history file
  611. :param command: the command given as a string
  612. """
  613. env = grass.gisenv()
  614. try:
  615. filePath = os.path.join(env['GISDBASE'],
  616. env['LOCATION_NAME'],
  617. env['MAPSET'],
  618. '.bash_history')
  619. fileHistory = codecs.open(filePath, encoding='utf-8', mode='a')
  620. except IOError as e:
  621. GError(_("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s") %
  622. {'filePath': filePath, 'error': e},
  623. parent=self._guiparent)
  624. return
  625. try:
  626. fileHistory.write(command + os.linesep)
  627. finally:
  628. fileHistory.close()
  629. # update wxGUI prompt
  630. if self._giface:
  631. self._giface.UpdateCmdHistory(command)