~ubuntu-branches/ubuntu/wily/grass/wily

« back to all changes in this revision

Viewing changes to gui/wxpython/core/gconsole.py

Tags: 7.0.0~rc1+ds1-1~exp1
* New upstream release candidate.
* Repack upstream tarball, remove precompiled Python objects.
* Add upstream metadata.
* Update gbp.conf and Vcs-Git URL to use the experimental branch.
* Update watch file for GRASS 7.0.
* Drop build dependencies for Tcl/Tk, add build dependencies:
  python-numpy, libnetcdf-dev, netcdf-bin, libblas-dev, liblapack-dev
* Update Vcs-Browser URL to use cgit instead of gitweb.
* Update paths to use grass70.
* Add configure options: --with-netcdf, --with-blas, --with-lapack,
  remove --with-tcltk-includes.
* Update patches for GRASS 7.
* Update copyright file, changes:
  - Update copyright years
  - Group files by license
  - Remove unused license sections
* Add patches for various typos.
* Fix desktop file with patch instead of d/rules.
* Use minimal dh rules.
* Bump Standards-Version to 3.9.6, no changes.
* Use dpkg-maintscript-helper to replace directories with symlinks.
  (closes: #776349)
* Update my email to use @debian.org address.

Show diffs side-by-side

added added

removed removed

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