gconsole.py 27 KB

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