source: grass/trunk/gui/wxpython/gui_core/goutput.py

Last change on this file was 74307, checked in by neteler, 5 years ago

i18N: cleanup gettext usage for Python code (fixes #3790) (contributed by pmav99)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id
  • Property svn:mime-type set to text/x-python
File size: 27.9 KB
Line 
1"""
2@package gui_core.goutput
3
4@brief Command output widgets
5
6Classes:
7 - goutput::GConsoleWindow
8 - goutput::GStc
9 - goutput::GConsoleFrame
10
11(C) 2007-2012 by the GRASS Development Team
12
13This program is free software under the GNU General Public License
14(>=v2). Read the file COPYING that comes with GRASS for details.
15
16@author Michael Barton (Arizona State University)
17@author Martin Landa <landa.martin gmail.com>
18@author Vaclav Petras <wenzeslaus gmail.com> (refactoring)
19@author Anna Kratochvilova <kratochanna gmail.com> (refactoring)
20"""
21
22import os
23import textwrap
24
25import wx
26from wx import stc
27
28from grass.pydispatch.signal import Signal
29
30# needed just for testing
31if __name__ == '__main__':
32 from grass.script.setup import set_gui_path
33 set_gui_path()
34
35from core.gcmd import GError, EncodeString
36from core.gconsole import GConsole, \
37 EVT_CMD_OUTPUT, EVT_CMD_PROGRESS, EVT_CMD_RUN, EVT_CMD_DONE, \
38 Notification
39from gui_core.prompt import GPromptSTC
40from gui_core.wrap import Button, ToggleButton, StaticText, \
41 StaticBox
42from core.settings import UserSettings
43from gui_core.widgets import SearchModuleWidget
44
45
46GC_EMPTY = 0
47GC_SEARCH = 1
48GC_PROMPT = 2
49
50
51class GConsoleWindow(wx.SplitterWindow):
52 """Create and manage output console for commands run by GUI.
53 """
54
55 def __init__(self, parent, gconsole, menuModel=None, margin=False,
56 style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
57 gcstyle=GC_EMPTY,
58 **kwargs):
59 """
60 :param parent: gui parent
61 :param gconsole: console logic
62 :param menuModel: tree model of modules (from menu)
63 :param margin: use margin in output pane (GStc)
64 :param style: wx.SplitterWindow style
65 :param gcstyle: GConsole style
66 (GC_EMPTY, GC_PROMPT to show command prompt,
67 GC_SEARCH to show search widget)
68 """
69 wx.SplitterWindow.__init__(
70 self, parent, id=wx.ID_ANY, style=style, **kwargs)
71 self.SetName("GConsole")
72
73 self.panelOutput = wx.Panel(parent=self, id=wx.ID_ANY)
74 self.panelProgress = wx.Panel(
75 parent=self.panelOutput,
76 id=wx.ID_ANY,
77 name='progressPanel')
78 self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY)
79 # initialize variables
80 self.parent = parent # GMFrame | CmdPanel | ?
81 self._gconsole = gconsole
82 self._menuModel = menuModel
83
84 self._gcstyle = gcstyle
85 self.lineWidth = 80
86
87 # signal which requests showing of a notification
88 self.showNotification = Signal("GConsoleWindow.showNotification")
89 # signal emitted when text appears in the console
90 # parameter 'notification' suggests form of notification (according to
91 # core.giface.Notification)
92 self.contentChanged = Signal("GConsoleWindow.contentChanged")
93
94 # progress bar
95 self.progressbar = wx.Gauge(parent=self.panelProgress, id=wx.ID_ANY,
96 range=100, pos=(110, 50), size=(-1, 25),
97 style=wx.GA_HORIZONTAL)
98 self._gconsole.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
99 self._gconsole.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
100 self._gconsole.Bind(EVT_CMD_RUN, self.OnCmdRun)
101 self._gconsole.Bind(EVT_CMD_DONE, self.OnCmdDone)
102
103 self._gconsole.writeLog.connect(self.WriteLog)
104 self._gconsole.writeCmdLog.connect(self.WriteCmdLog)
105 self._gconsole.writeWarning.connect(self.WriteWarning)
106 self._gconsole.writeError.connect(self.WriteError)
107
108 # text control for command output
109 self.cmdOutput = GStc(
110 parent=self.panelOutput,
111 id=wx.ID_ANY,
112 margin=margin,
113 wrap=None)
114
115 # search & command prompt
116 # move to the if below
117 # search depends on cmd prompt
118 self.cmdPrompt = GPromptSTC(parent=self, menuModel=self._menuModel)
119 self.cmdPrompt.promptRunCmd.connect(lambda cmd:
120 self._gconsole.RunCmd(command=cmd))
121 self.cmdPrompt.showNotification.connect(self.showNotification)
122
123 if not self._gcstyle & GC_PROMPT:
124 self.cmdPrompt.Hide()
125
126 if self._gcstyle & GC_SEARCH:
127 self.infoCollapseLabelExp = _(
128 "Click here to show search module engine")
129 self.infoCollapseLabelCol = _(
130 "Click here to hide search module engine")
131 self.searchPane = wx.CollapsiblePane(
132 parent=self.panelOutput, label=self.infoCollapseLabelExp,
133 style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE | wx.EXPAND)
134 self.MakeSearchPaneContent(
135 self.searchPane.GetPane(), self._menuModel)
136 self.searchPane.Collapse(True)
137 self.Bind(
138 wx.EVT_COLLAPSIBLEPANE_CHANGED,
139 self.OnSearchPaneChanged,
140 self.searchPane)
141 self.search.moduleSelected.connect(
142 lambda name: self.cmdPrompt.SetTextAndFocus(name + ' '))
143 else:
144 self.search = None
145
146 if self._gcstyle & GC_PROMPT:
147 cmdLabel = _("Command prompt")
148 self.outputBox = StaticBox(
149 parent=self.panelOutput,
150 id=wx.ID_ANY,
151 label=" %s " %
152 _("Output window"))
153
154 self.cmdBox = StaticBox(parent=self.panelOutput, id=wx.ID_ANY,
155 label=" %s " % cmdLabel)
156
157 # buttons
158 self.btnOutputClear = Button(
159 parent=self.panelOutput, id=wx.ID_CLEAR)
160 self.btnOutputClear.SetToolTip(_("Clear output window content"))
161 self.btnCmdClear = Button(parent=self.panelOutput, id=wx.ID_CLEAR)
162 self.btnCmdClear.SetToolTip(_("Clear command prompt content"))
163 self.btnOutputSave = Button(parent=self.panelOutput, id=wx.ID_SAVE)
164 self.btnOutputSave.SetToolTip(
165 _("Save output window content to the file"))
166 self.btnCmdAbort = Button(parent=self.panelProgress, id=wx.ID_STOP)
167 self.btnCmdAbort.SetToolTip(_("Abort running command"))
168 self.btnCmdProtocol = ToggleButton(
169 parent=self.panelOutput,
170 id=wx.ID_ANY,
171 label=_("&Log file"),
172 size=self.btnCmdClear.GetSize())
173 self.btnCmdProtocol.SetToolTip(_("Toggle to save list of executed commands into "
174 "a file; content saved when switching off."))
175 self.cmdFileProtocol = None
176
177 if not self._gcstyle & GC_PROMPT:
178 self.btnCmdClear.Hide()
179 self.btnCmdProtocol.Hide()
180
181 self.btnCmdClear.Bind(wx.EVT_BUTTON, self.cmdPrompt.OnCmdErase)
182 self.btnOutputClear.Bind(wx.EVT_BUTTON, self.OnOutputClear)
183 self.btnOutputSave.Bind(wx.EVT_BUTTON, self.OnOutputSave)
184 self.btnCmdAbort.Bind(wx.EVT_BUTTON, self._gconsole.OnCmdAbort)
185 self.btnCmdProtocol.Bind(wx.EVT_TOGGLEBUTTON, self.OnCmdProtocol)
186
187 self._layout()
188
189 def _layout(self):
190 """Do layout"""
191 self.outputSizer = wx.BoxSizer(wx.VERTICAL)
192 progressSizer = wx.BoxSizer(wx.HORIZONTAL)
193 btnSizer = wx.BoxSizer(wx.HORIZONTAL)
194 if self._gcstyle & GC_PROMPT:
195 outBtnSizer = wx.StaticBoxSizer(self.outputBox, wx.HORIZONTAL)
196 cmdBtnSizer = wx.StaticBoxSizer(self.cmdBox, wx.HORIZONTAL)
197 else:
198 outBtnSizer = wx.BoxSizer(wx.HORIZONTAL)
199 cmdBtnSizer = wx.BoxSizer(wx.HORIZONTAL)
200
201 if self._gcstyle & GC_PROMPT:
202 promptSizer = wx.BoxSizer(wx.VERTICAL)
203 promptSizer.Add(self.cmdPrompt, proportion=1,
204 flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
205 border=3)
206 helpText = StaticText(
207 self.panelPrompt, id=wx.ID_ANY,
208 label="Press Tab to display command help, Ctrl+Space to autocomplete")
209 helpText.SetForegroundColour(
210 wx.SystemSettings.GetColour(
211 wx.SYS_COLOUR_GRAYTEXT))
212 promptSizer.Add(helpText,
213 proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)
214
215 if self._gcstyle & GC_SEARCH:
216 self.outputSizer.Add(self.searchPane, proportion=0,
217 flag=wx.EXPAND | wx.ALL, border=3)
218 self.outputSizer.Add(self.cmdOutput, proportion=1,
219 flag=wx.EXPAND | wx.ALL, border=3)
220 if self._gcstyle & GC_PROMPT:
221 proportion = 1
222 else:
223 proportion = 0
224 outBtnSizer.AddStretchSpacer()
225
226 outBtnSizer.Add(
227 self.btnOutputClear,
228 proportion=proportion,
229 flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.BOTTOM,
230 border=5)
231
232 outBtnSizer.Add(self.btnOutputSave, proportion=proportion,
233 flag=wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, border=5)
234
235 cmdBtnSizer.Add(
236 self.btnCmdProtocol,
237 proportion=1,
238 flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.BOTTOM,
239 border=5)
240 cmdBtnSizer.Add(self.btnCmdClear, proportion=1,
241 flag=wx.ALIGN_CENTER | wx.RIGHT | wx.BOTTOM, border=5)
242 progressSizer.Add(self.btnCmdAbort, proportion=0,
243 flag=wx.ALL | wx.ALIGN_CENTER, border=5)
244 progressSizer.Add(
245 self.progressbar,
246 proportion=1,
247 flag=wx.ALIGN_CENTER | wx.RIGHT | wx.TOP | wx.BOTTOM,
248 border=5)
249
250 self.panelProgress.SetSizer(progressSizer)
251 progressSizer.Fit(self.panelProgress)
252
253 btnSizer.Add(outBtnSizer, proportion=1,
254 flag=wx.ALL | wx.ALIGN_CENTER, border=5)
255 btnSizer.Add(
256 cmdBtnSizer,
257 proportion=1,
258 flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT,
259 border=5)
260 self.outputSizer.Add(self.panelProgress, proportion=0,
261 flag=wx.EXPAND)
262 self.outputSizer.Add(btnSizer, proportion=0,
263 flag=wx.EXPAND)
264
265 self.outputSizer.Fit(self)
266 self.outputSizer.SetSizeHints(self)
267 self.panelOutput.SetSizer(self.outputSizer)
268 # eliminate gtk_widget_size_allocate() warnings
269 # avoid to use a deprecated method in wxPython >= 2.9
270 getattr(self.outputSizer, 'FitInside',
271 self.outputSizer.SetVirtualSizeHints)(self.panelOutput)
272
273 if self._gcstyle & GC_PROMPT:
274 promptSizer.Fit(self)
275 promptSizer.SetSizeHints(self)
276 self.panelPrompt.SetSizer(promptSizer)
277
278 # split window
279 if self._gcstyle & GC_PROMPT:
280 self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50)
281 else:
282 self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45)
283 self.Unsplit()
284 self.SetMinimumPaneSize(self.btnCmdClear.GetSize()[1] + 25)
285
286 self.SetSashGravity(1.0)
287
288 self.outputSizer.Hide(self.panelProgress)
289 # layout
290 self.SetAutoLayout(True)
291 self.Layout()
292
293 def MakeSearchPaneContent(self, pane, model):
294 """Create search pane"""
295 border = wx.BoxSizer(wx.VERTICAL)
296
297 self.search = SearchModuleWidget(parent=pane,
298 model=model)
299
300 self.search.showNotification.connect(self.showNotification)
301
302 border.Add(self.search, proportion=0,
303 flag=wx.EXPAND | wx.ALL, border=1)
304
305 pane.SetSizer(border)
306 border.Fit(pane)
307
308 def OnSearchPaneChanged(self, event):
309 """Collapse search module box"""
310 if self.searchPane.IsExpanded():
311 self.searchPane.SetLabel(self.infoCollapseLabelCol)
312 else:
313 self.searchPane.SetLabel(self.infoCollapseLabelExp)
314
315 self.panelOutput.Layout()
316 self.panelOutput.SendSizeEvent()
317
318 def GetPanel(self, prompt=True):
319 """Get panel
320
321 :param prompt: get prompt / output panel
322
323 :return: wx.Panel reference
324 """
325 if prompt:
326 return self.panelPrompt
327
328 return self.panelOutput
329
330 def WriteLog(self, text, style=None, wrap=None,
331 notification=Notification.HIGHLIGHT):
332 """Generic method for writing log message in
333 given style.
334
335 Emits contentChanged signal.
336
337 :param line: text line
338 :param style: text style (see GStc)
339 :param stdout: write to stdout or stderr
340 :param notification: form of notification
341 """
342
343 self.cmdOutput.SetStyle()
344
345 # documenting old behavior/implementation:
346 # switch notebook if required
347 # now, let user to bind to the old event
348
349 if not style:
350 style = self.cmdOutput.StyleDefault
351
352 # p1 = self.cmdOutput.GetCurrentPos()
353 p1 = self.cmdOutput.GetEndStyled()
354 # self.cmdOutput.GotoPos(p1)
355 self.cmdOutput.DocumentEnd()
356
357 for line in text.splitlines():
358 # fill space
359 if len(line) < self.lineWidth:
360 diff = self.lineWidth - len(line)
361 line += diff * ' '
362
363 self.cmdOutput.AddTextWrapped(line, wrap=wrap) # adds '\n'
364
365 p2 = self.cmdOutput.GetCurrentPos()
366
367 self.cmdOutput.StartStyling(p1, 0xff)
368 self.cmdOutput.SetStyling(p2 - p1, style)
369
370 self.cmdOutput.EnsureCaretVisible()
371
372 self.contentChanged.emit(notification=notification)
373
374 def WriteCmdLog(self, text, pid=None,
375 notification=Notification.MAKE_VISIBLE):
376 """Write message in selected style
377
378 :param text: message to be printed
379 :param pid: process pid or None
380 :param switchPage: True to switch page
381 """
382 if pid:
383 text = '(' + str(pid) + ') ' + text
384 self.WriteLog(
385 text,
386 style=self.cmdOutput.StyleCommand,
387 notification=notification)
388
389 def WriteWarning(self, text):
390 """Write message in warning style"""
391 self.WriteLog(text, style=self.cmdOutput.StyleWarning,
392 notification=Notification.MAKE_VISIBLE)
393
394 def WriteError(self, text):
395 """Write message in error style"""
396 self.WriteLog(text, style=self.cmdOutput.StyleError,
397 notification=Notification.MAKE_VISIBLE)
398
399 def OnOutputClear(self, event):
400 """Clear content of output window"""
401 self.cmdOutput.SetReadOnly(False)
402 self.cmdOutput.ClearAll()
403 self.cmdOutput.SetReadOnly(True)
404 self.progressbar.SetValue(0)
405
406 def GetProgressBar(self):
407 """Return progress bar widget"""
408 return self.progressbar
409
410 def OnOutputSave(self, event):
411 """Save (selected) text from output window to the file"""
412 text = self.cmdOutput.GetSelectedText()
413 if not text:
414 text = self.cmdOutput.GetText()
415
416 # add newline if needed
417 if len(text) > 0 and text[-1] != '\n':
418 text += '\n'
419
420 dlg = wx.FileDialog(
421 self, message=_("Save file as..."),
422 defaultFile="grass_cmd_output.txt",
423 wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") %
424 {'txt': _("Text files"),
425 'files': _("Files")},
426 style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
427
428 # Show the dialog and retrieve the user response. If it is the OK response,
429 # process the data.
430 if dlg.ShowModal() == wx.ID_OK:
431 path = dlg.GetPath()
432
433 try:
434 output = open(path, "w")
435 output.write(EncodeString(text))
436 except IOError as e:
437 GError(
438 _("Unable to write file '%(path)s'.\n\nDetails: %(error)s") % {
439 'path': path,
440 'error': e})
441 finally:
442 output.close()
443 message = _("Command output saved into '%s'") % path
444 self.showNotification.emit(message=message)
445
446 dlg.Destroy()
447
448 def SetCopyingOfSelectedText(self, copy):
449 """Enable or disable copying of selected text in to clipboard.
450 Effects prompt and output.
451
452 :param bool copy: True for enable, False for disable
453 """
454 if copy:
455 self.cmdPrompt.Bind(
456 stc.EVT_STC_PAINTED,
457 self.cmdPrompt.OnTextSelectionChanged)
458 self.cmdOutput.Bind(
459 stc.EVT_STC_PAINTED,
460 self.cmdOutput.OnTextSelectionChanged)
461 else:
462 self.cmdPrompt.Unbind(stc.EVT_STC_PAINTED)
463 self.cmdOutput.Unbind(stc.EVT_STC_PAINTED)
464
465 def OnCmdOutput(self, event):
466 """Prints command output.
467
468 Emits contentChanged signal.
469 """
470 message = event.text
471 type = event.type
472
473 self.cmdOutput.AddStyledMessage(message, type)
474
475 if event.type in ('warning', 'error'):
476 self.contentChanged.emit(notification=Notification.MAKE_VISIBLE)
477 else:
478 self.contentChanged.emit(notification=Notification.HIGHLIGHT)
479
480 def OnCmdProgress(self, event):
481 """Update progress message info"""
482 self.progressbar.SetValue(event.value)
483 event.Skip()
484
485 def CmdProtocolSave(self):
486 """Save list of manually entered commands into a text log file"""
487 if self.cmdFileProtocol is None:
488 return # it should not happen
489
490 try:
491 with open(self.cmdFileProtocol, "a") as output:
492 cmds = self.cmdPrompt.GetCommands()
493 output.write(os.linesep.join(cmds))
494 if len(cmds) > 0:
495 output.write(os.linesep)
496 except IOError as e:
497 GError(_("Unable to write file '{filePath}'.\n\nDetails: {error}").format(
498 filePath=self.cmdFileProtocol, error=e))
499
500 self.showNotification.emit(
501 message=_("Command log saved to '{}'".format(self.cmdFileProtocol))
502 )
503 self.cmdFileProtocol = None
504
505 def OnCmdProtocol(self, event=None):
506 """Save commands into file"""
507 if not event.IsChecked():
508 # stop capturing commands, save list of commands to the
509 # protocol file
510 self.CmdProtocolSave()
511 else:
512 # start capturing commands
513 self.cmdPrompt.ClearCommands()
514 # ask for the file
515 dlg = wx.FileDialog(
516 self, message=_("Save file as..."),
517 defaultFile="grass_cmd_log.txt",
518 wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") %
519 {'txt': _("Text files"),
520 'files': _("Files")},
521 style=wx.FD_SAVE)
522 if dlg.ShowModal() == wx.ID_OK:
523 self.cmdFileProtocol = dlg.GetPath()
524 else:
525 wx.CallAfter(self.btnCmdProtocol.SetValue, False)
526
527 dlg.Destroy()
528
529 event.Skip()
530
531 def OnCmdRun(self, event):
532 """Run command"""
533 self.outputSizer.Show(self.panelProgress)
534 self.outputSizer.Layout()
535 event.Skip()
536
537 def OnCmdDone(self, event):
538 """Command done (or aborted)
539 """
540 self.progressbar.SetValue(0) # reset progress bar on '0%'
541 wx.CallLater(100, self._hideProgress)
542 event.Skip()
543
544 def _hideProgress(self):
545 self.outputSizer.Hide(self.panelProgress)
546 self.outputSizer.Layout()
547
548 def ResetFocus(self):
549 """Reset focus"""
550 self.cmdPrompt.SetFocus()
551
552 def GetPrompt(self):
553 """Get prompt"""
554 return self.cmdPrompt
555
556
557class GStc(stc.StyledTextCtrl):
558 """Styled text control for GRASS stdout and stderr.
559
560 Based on FrameOutErr.py
561
562 Name: FrameOutErr.py
563 Purpose: Redirecting stdout / stderr
564 Author: Jean-Michel Fauth, Switzerland
565 Copyright: (c) 2005-2007 Jean-Michel Fauth
566 Licence: GPL
567 """
568
569 def __init__(self, parent, id, margin=False, wrap=None):
570 stc.StyledTextCtrl.__init__(self, parent, id)
571 self.parent = parent
572 self.SetUndoCollection(True)
573 self.SetReadOnly(True)
574
575 # remember position of line beginning (used for '\r')
576 self.linePos = -1
577
578 #
579 # styles
580 #
581 self.SetStyle()
582
583 #
584 # line margins
585 #
586 # TODO print number only from cmdlog
587 self.SetMarginWidth(1, 0)
588 self.SetMarginWidth(2, 0)
589 if margin:
590 self.SetMarginType(0, stc.STC_MARGIN_NUMBER)
591 self.SetMarginWidth(0, 30)
592 else:
593 self.SetMarginWidth(0, 0)
594
595 #
596 # miscellaneous
597 #
598 self.SetViewWhiteSpace(False)
599 self.SetTabWidth(4)
600 self.SetUseTabs(False)
601 self.UsePopUp(True)
602 self.SetSelBackground(True, "#FFFF00")
603 self.SetUseHorizontalScrollBar(True)
604
605 #
606 # bindings
607 #
608 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
609
610 def OnTextSelectionChanged(self, event):
611 """Copy selected text to clipboard and skip event.
612 The same function is in TextCtrlAutoComplete class (prompt.py).
613 """
614 wx.CallAfter(self.Copy)
615 event.Skip()
616
617 def SetStyle(self):
618 """Set styles for styled text output windows with type face
619 and point size selected by user (Courier New 10 is default)"""
620
621 typeface = UserSettings.Get(
622 group='appearance',
623 key='outputfont',
624 subkey='type')
625 if typeface == "":
626 typeface = "Courier New"
627
628 typesize = UserSettings.Get(
629 group='appearance',
630 key='outputfont',
631 subkey='size')
632 if typesize is None or int(typesize) <= 0:
633 typesize = 10
634 typesize = float(typesize)
635
636 self.StyleDefault = 0
637 self.StyleDefaultSpec = "face:%s,size:%d,fore:#000000,back:#FFFFFF" % (
638 typeface,
639 typesize)
640 self.StyleCommand = 1
641 self.StyleCommandSpec = "face:%s,size:%d,,fore:#000000,back:#bcbcbc" % (
642 typeface, typesize)
643 self.StyleOutput = 2
644 self.StyleOutputSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (
645 typeface,
646 typesize)
647 # fatal error
648 self.StyleError = 3
649 self.StyleErrorSpec = "face:%s,size:%d,,fore:#7F0000,back:#FFFFFF" % (
650 typeface,
651 typesize)
652 # warning
653 self.StyleWarning = 4
654 self.StyleWarningSpec = "face:%s,size:%d,,fore:#0000FF,back:#FFFFFF" % (
655 typeface, typesize)
656 # message
657 self.StyleMessage = 5
658 self.StyleMessageSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (
659 typeface, typesize)
660 # unknown
661 self.StyleUnknown = 6
662 self.StyleUnknownSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (
663 typeface, typesize)
664
665 # default and clear => init
666 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
667 self.StyleClearAll()
668 self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec)
669 self.StyleSetSpec(self.StyleOutput, self.StyleOutputSpec)
670 self.StyleSetSpec(self.StyleError, self.StyleErrorSpec)
671 self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec)
672 self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec)
673 self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)
674
675 def OnDestroy(self, evt):
676 """The clipboard contents can be preserved after
677 the app has exited"""
678
679 wx.TheClipboard.Flush()
680 evt.Skip()
681
682 def AddTextWrapped(self, txt, wrap=None):
683 """Add string to text area.
684
685 String is wrapped and linesep is also added to the end
686 of the string"""
687 # allow writing to output window
688 self.SetReadOnly(False)
689
690 if wrap:
691 txt = textwrap.fill(txt, wrap) + '\n'
692 else:
693 if txt[-1] != '\n':
694 txt += '\n'
695
696 if '\r' in txt:
697 self.linePos = -1
698 for seg in txt.split('\r'):
699 if self.linePos > -1:
700 self.SetCurrentPos(self.linePos)
701 self.ReplaceSelection(seg)
702 else:
703 self.linePos = self.GetCurrentPos()
704 self.AddText(seg)
705 else:
706 self.linePos = self.GetCurrentPos()
707
708 try:
709 self.AddText(txt)
710 except UnicodeDecodeError:
711 enc = UserSettings.Get(
712 group='atm', key='encoding', subkey='value')
713 if enc:
714 txt = unicode(txt, enc, errors='replace')
715 elif 'GRASS_DB_ENCODING' in os.environ:
716 txt = unicode(
717 txt, os.environ['GRASS_DB_ENCODING'],
718 errors='replace')
719 else:
720 txt = EncodeString(txt)
721
722 self.AddText(txt)
723
724 # reset output window to read only
725 self.SetReadOnly(True)
726
727 def AddStyledMessage(self, message, style=None):
728 """Add message to text area.
729
730 Handles messages with progress percentages.
731
732 :param message: message to be added
733 :param style: style of message, allowed values: 'message',
734 'warning', 'error' or None
735 """
736 # message prefix
737 if style == 'warning':
738 message = 'WARNING: ' + message
739 elif style == 'error':
740 message = 'ERROR: ' + message
741
742 p1 = self.GetEndStyled()
743 self.GotoPos(p1)
744
745 # is this still needed?
746 if '\b' in message:
747 if self.linePos < 0:
748 self.linePos = p1
749 last_c = ''
750 for c in message:
751 if c == '\b':
752 self.linePos -= 1
753 else:
754 if c == '\r':
755 pos = self.GetCurLine()[1]
756 # self.SetCurrentPos(pos)
757 else:
758 self.SetCurrentPos(self.linePos)
759 self.ReplaceSelection(c)
760 self.linePos = self.GetCurrentPos()
761 if c != ' ':
762 last_c = c
763 if last_c not in ('0123456789'):
764 self.AddTextWrapped('\n', wrap=None)
765 self.linePos = -1
766 else:
767 self.linePos = -1 # don't force position
768 if '\n' not in message:
769 self.AddTextWrapped(message, wrap=60)
770 else:
771 self.AddTextWrapped(message, wrap=None)
772 p2 = self.GetCurrentPos()
773
774 if p2 >= p1:
775 self.StartStyling(p1, 0xff)
776
777 if style == 'error':
778 self.SetStyling(p2 - p1, self.StyleError)
779 elif style == 'warning':
780 self.SetStyling(p2 - p1, self.StyleWarning)
781 elif style == 'message':
782 self.SetStyling(p2 - p1, self.StyleMessage)
783 else: # unknown
784 self.SetStyling(p2 - p1, self.StyleUnknown)
785
786 self.EnsureCaretVisible()
787
788
789class GConsoleFrame(wx.Frame):
790 """Standalone GConsole for testing only"""
791
792 def __init__(self, parent, id=wx.ID_ANY, title="GConsole Test Frame",
793 style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, **kwargs):
794 wx.Frame.__init__(self, parent=parent, id=id, title=title, style=style)
795
796 panel = wx.Panel(self, id=wx.ID_ANY)
797
798 from lmgr.menudata import LayerManagerMenuData
799 menuTreeBuilder = LayerManagerMenuData()
800 self.gconsole = GConsole(guiparent=self)
801 self.goutput = GConsoleWindow(parent=panel, gconsole=self.gconsole,
802 menuModel=menuTreeBuilder.GetModel(),
803 gcstyle=GC_SEARCH | GC_PROMPT)
804
805 mainSizer = wx.BoxSizer(wx.VERTICAL)
806 mainSizer.Add(
807 self.goutput,
808 proportion=1,
809 flag=wx.EXPAND,
810 border=0)
811
812 panel.SetSizer(mainSizer)
813 mainSizer.Fit(panel)
814 self.SetMinSize((550, 500))
815
816
817def testGConsole():
818 app = wx.App()
819 frame = GConsoleFrame(parent=None)
820 frame.Show()
821 app.MainLoop()
822
823if __name__ == '__main__':
824 testGConsole()
Note: See TracBrowser for help on using the repository browser.