gconsole.py 27 KB

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