gconsole.py 28 KB

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