123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- """
- @package gui_core.goutput
- @brief Command output widgets
- Classes:
- - goutput::GConsoleWindow
- - goutput::GStc
- - goutput::GConsoleFrame
- (C) 2007-2012 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- @author Michael Barton (Arizona State University)
- @author Martin Landa <landa.martin gmail.com>
- @author Vaclav Petras <wenzeslaus gmail.com> (refactoring)
- @author Anna Kratochvilova <kratochanna gmail.com> (refactoring)
- """
- import os
- import textwrap
- import wx
- from wx import stc
- from grass.pydispatch.signal import Signal
- # needed just for testing
- if __name__ == '__main__':
- from grass.script.setup import set_gui_path
- set_gui_path()
- from core.gcmd import GError, EncodeString
- from core.gconsole import GConsole, \
- EVT_CMD_OUTPUT, EVT_CMD_PROGRESS, EVT_CMD_RUN, EVT_CMD_DONE, \
- Notification
- from gui_core.prompt import GPromptSTC
- from gui_core.wrap import Button, ToggleButton, StaticText, \
- StaticBox
- from core.settings import UserSettings
- from gui_core.widgets import SearchModuleWidget
- GC_EMPTY = 0
- GC_SEARCH = 1
- GC_PROMPT = 2
- class GConsoleWindow(wx.SplitterWindow):
- """Create and manage output console for commands run by GUI.
- """
- def __init__(self, parent, gconsole, menuModel=None, margin=False,
- style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
- gcstyle=GC_EMPTY,
- **kwargs):
- """
- :param parent: gui parent
- :param gconsole: console logic
- :param menuModel: tree model of modules (from menu)
- :param margin: use margin in output pane (GStc)
- :param style: wx.SplitterWindow style
- :param gcstyle: GConsole style
- (GC_EMPTY, GC_PROMPT to show command prompt,
- GC_SEARCH to show search widget)
- """
- wx.SplitterWindow.__init__(
- self, parent, id=wx.ID_ANY, style=style, **kwargs)
- self.SetName("GConsole")
- self.panelOutput = wx.Panel(parent=self, id=wx.ID_ANY)
- self.panelProgress = wx.Panel(
- parent=self.panelOutput,
- id=wx.ID_ANY,
- name='progressPanel')
- self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY)
- # initialize variables
- self.parent = parent # GMFrame | CmdPanel | ?
- self._gconsole = gconsole
- self._menuModel = menuModel
- self._gcstyle = gcstyle
- self.lineWidth = 80
- # signal which requests showing of a notification
- self.showNotification = Signal("GConsoleWindow.showNotification")
- # signal emitted when text appears in the console
- # parameter 'notification' suggests form of notification (according to
- # core.giface.Notification)
- self.contentChanged = Signal("GConsoleWindow.contentChanged")
- # progress bar
- self.progressbar = wx.Gauge(parent=self.panelProgress, id=wx.ID_ANY,
- range=100, pos=(110, 50), size=(-1, 25),
- style=wx.GA_HORIZONTAL)
- self._gconsole.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
- self._gconsole.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
- self._gconsole.Bind(EVT_CMD_RUN, self.OnCmdRun)
- self._gconsole.Bind(EVT_CMD_DONE, self.OnCmdDone)
- self._gconsole.writeLog.connect(self.WriteLog)
- self._gconsole.writeCmdLog.connect(self.WriteCmdLog)
- self._gconsole.writeWarning.connect(self.WriteWarning)
- self._gconsole.writeError.connect(self.WriteError)
- # text control for command output
- self.cmdOutput = GStc(
- parent=self.panelOutput,
- id=wx.ID_ANY,
- margin=margin,
- wrap=None)
- # search & command prompt
- # move to the if below
- # search depends on cmd prompt
- self.cmdPrompt = GPromptSTC(parent=self, menuModel=self._menuModel)
- self.cmdPrompt.promptRunCmd.connect(lambda cmd:
- self._gconsole.RunCmd(command=cmd))
- self.cmdPrompt.showNotification.connect(self.showNotification)
- if not self._gcstyle & GC_PROMPT:
- self.cmdPrompt.Hide()
- if self._gcstyle & GC_SEARCH:
- self.infoCollapseLabelExp = _(
- "Click here to show search module engine")
- self.infoCollapseLabelCol = _(
- "Click here to hide search module engine")
- self.searchPane = wx.CollapsiblePane(
- parent=self.panelOutput, label=self.infoCollapseLabelExp,
- style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE | wx.EXPAND)
- self.MakeSearchPaneContent(
- self.searchPane.GetPane(), self._menuModel)
- self.searchPane.Collapse(True)
- self.Bind(
- wx.EVT_COLLAPSIBLEPANE_CHANGED,
- self.OnSearchPaneChanged,
- self.searchPane)
- self.search.moduleSelected.connect(
- lambda name: self.cmdPrompt.SetTextAndFocus(name + ' '))
- else:
- self.search = None
- if self._gcstyle & GC_PROMPT:
- cmdLabel = _("Command prompt")
- self.outputBox = StaticBox(
- parent=self.panelOutput,
- id=wx.ID_ANY,
- label=" %s " %
- _("Output window"))
- self.cmdBox = StaticBox(parent=self.panelOutput, id=wx.ID_ANY,
- label=" %s " % cmdLabel)
- # buttons
- self.btnOutputClear = Button(
- parent=self.panelOutput, id=wx.ID_CLEAR)
- self.btnOutputClear.SetToolTip(_("Clear output window content"))
- self.btnCmdClear = Button(parent=self.panelOutput, id=wx.ID_CLEAR)
- self.btnCmdClear.SetToolTip(_("Clear command prompt content"))
- self.btnOutputSave = Button(parent=self.panelOutput, id=wx.ID_SAVE)
- self.btnOutputSave.SetToolTip(
- _("Save output window content to the file"))
- self.btnCmdAbort = Button(parent=self.panelProgress, id=wx.ID_STOP)
- self.btnCmdAbort.SetToolTip(_("Abort running command"))
- self.btnCmdProtocol = ToggleButton(
- parent=self.panelOutput,
- id=wx.ID_ANY,
- label=_("&Log file"),
- size=self.btnCmdClear.GetSize())
- self.btnCmdProtocol.SetToolTip(_("Toggle to save list of executed commands into "
- "a file; content saved when switching off."))
- self.cmdFileProtocol = None
- if not self._gcstyle & GC_PROMPT:
- self.btnCmdClear.Hide()
- self.btnCmdProtocol.Hide()
- self.btnCmdClear.Bind(wx.EVT_BUTTON, self.cmdPrompt.OnCmdErase)
- self.btnOutputClear.Bind(wx.EVT_BUTTON, self.OnOutputClear)
- self.btnOutputSave.Bind(wx.EVT_BUTTON, self.OnOutputSave)
- self.btnCmdAbort.Bind(wx.EVT_BUTTON, self._gconsole.OnCmdAbort)
- self.btnCmdProtocol.Bind(wx.EVT_TOGGLEBUTTON, self.OnCmdProtocol)
- self._layout()
- def _layout(self):
- """Do layout"""
- self.outputSizer = wx.BoxSizer(wx.VERTICAL)
- progressSizer = wx.BoxSizer(wx.HORIZONTAL)
- btnSizer = wx.BoxSizer(wx.HORIZONTAL)
- if self._gcstyle & GC_PROMPT:
- outBtnSizer = wx.StaticBoxSizer(self.outputBox, wx.HORIZONTAL)
- cmdBtnSizer = wx.StaticBoxSizer(self.cmdBox, wx.HORIZONTAL)
- else:
- outBtnSizer = wx.BoxSizer(wx.HORIZONTAL)
- cmdBtnSizer = wx.BoxSizer(wx.HORIZONTAL)
- if self._gcstyle & GC_PROMPT:
- promptSizer = wx.BoxSizer(wx.VERTICAL)
- promptSizer.Add(self.cmdPrompt, proportion=1,
- flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
- border=3)
- helpText = StaticText(
- self.panelPrompt, id=wx.ID_ANY,
- label="Press Tab to display command help, Ctrl+Space to autocomplete")
- helpText.SetForegroundColour(
- wx.SystemSettings.GetColour(
- wx.SYS_COLOUR_GRAYTEXT))
- promptSizer.Add(helpText,
- proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)
- if self._gcstyle & GC_SEARCH:
- self.outputSizer.Add(self.searchPane, proportion=0,
- flag=wx.EXPAND | wx.ALL, border=3)
- self.outputSizer.Add(self.cmdOutput, proportion=1,
- flag=wx.EXPAND | wx.ALL, border=3)
- if self._gcstyle & GC_PROMPT:
- proportion = 1
- else:
- proportion = 0
- outBtnSizer.AddStretchSpacer()
- outBtnSizer.Add(
- self.btnOutputClear,
- proportion=proportion,
- flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.BOTTOM,
- border=5)
- outBtnSizer.Add(self.btnOutputSave, proportion=proportion,
- flag=wx.RIGHT | wx.BOTTOM, border=5)
- cmdBtnSizer.Add(
- self.btnCmdProtocol,
- proportion=1,
- flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.BOTTOM,
- border=5)
- cmdBtnSizer.Add(self.btnCmdClear, proportion=1,
- flag=wx.ALIGN_CENTER | wx.RIGHT | wx.BOTTOM, border=5)
- progressSizer.Add(self.btnCmdAbort, proportion=0,
- flag=wx.ALL | wx.ALIGN_CENTER, border=5)
- progressSizer.Add(
- self.progressbar,
- proportion=1,
- flag=wx.ALIGN_CENTER | wx.RIGHT | wx.TOP | wx.BOTTOM,
- border=5)
- self.panelProgress.SetSizer(progressSizer)
- progressSizer.Fit(self.panelProgress)
- btnSizer.Add(outBtnSizer, proportion=1,
- flag=wx.ALL | wx.ALIGN_CENTER, border=5)
- btnSizer.Add(
- cmdBtnSizer,
- proportion=1,
- flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT,
- border=5)
- self.outputSizer.Add(self.panelProgress, proportion=0,
- flag=wx.EXPAND)
- self.outputSizer.Add(btnSizer, proportion=0,
- flag=wx.EXPAND)
- self.outputSizer.Fit(self)
- self.outputSizer.SetSizeHints(self)
- self.panelOutput.SetSizer(self.outputSizer)
- self.outputSizer.FitInside(self.panelOutput)
- if self._gcstyle & GC_PROMPT:
- promptSizer.Fit(self)
- promptSizer.SetSizeHints(self)
- self.panelPrompt.SetSizer(promptSizer)
- # split window
- if self._gcstyle & GC_PROMPT:
- self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50)
- else:
- self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45)
- self.Unsplit()
- self.SetMinimumPaneSize(self.btnCmdClear.GetSize()[1] + 25)
- self.SetSashGravity(1.0)
- self.outputSizer.Hide(self.panelProgress)
- # layout
- self.SetAutoLayout(True)
- self.Layout()
- def MakeSearchPaneContent(self, pane, model):
- """Create search pane"""
- border = wx.BoxSizer(wx.VERTICAL)
- self.search = SearchModuleWidget(parent=pane,
- model=model)
- self.search.showNotification.connect(self.showNotification)
- border.Add(self.search, proportion=0,
- flag=wx.EXPAND | wx.ALL, border=1)
- pane.SetSizer(border)
- border.Fit(pane)
- def OnSearchPaneChanged(self, event):
- """Collapse search module box"""
- if self.searchPane.IsExpanded():
- self.searchPane.SetLabel(self.infoCollapseLabelCol)
- else:
- self.searchPane.SetLabel(self.infoCollapseLabelExp)
- self.panelOutput.Layout()
- self.panelOutput.SendSizeEvent()
- def GetPanel(self, prompt=True):
- """Get panel
- :param prompt: get prompt / output panel
- :return: wx.Panel reference
- """
- if prompt:
- return self.panelPrompt
- return self.panelOutput
- def WriteLog(self, text, style=None, wrap=None,
- notification=Notification.HIGHLIGHT):
- """Generic method for writing log message in
- given style.
- Emits contentChanged signal.
- :param line: text line
- :param style: text style (see GStc)
- :param stdout: write to stdout or stderr
- :param notification: form of notification
- """
- self.cmdOutput.SetStyle()
- # documenting old behavior/implementation:
- # switch notebook if required
- # now, let user to bind to the old event
- if not style:
- style = self.cmdOutput.StyleDefault
- # p1 = self.cmdOutput.GetCurrentPos()
- p1 = self.cmdOutput.GetEndStyled()
- # self.cmdOutput.GotoPos(p1)
- self.cmdOutput.DocumentEnd()
- for line in text.splitlines():
- # fill space
- if len(line) < self.lineWidth:
- diff = self.lineWidth - len(line)
- line += diff * ' '
- self.cmdOutput.AddTextWrapped(line, wrap=wrap) # adds '\n'
- p2 = self.cmdOutput.GetCurrentPos()
- # between wxWidgets 3.0 and 3.1 they dropped mask param
- try:
- self.cmdOutput.StartStyling(p1)
- except TypeError:
- self.cmdOutput.StartStyling(p1, 0xff)
- self.cmdOutput.SetStyling(p2 - p1, style)
- self.cmdOutput.EnsureCaretVisible()
- self.contentChanged.emit(notification=notification)
- def WriteCmdLog(self, text, pid=None,
- notification=Notification.MAKE_VISIBLE):
- """Write message in selected style
- :param text: message to be printed
- :param pid: process pid or None
- :param switchPage: True to switch page
- """
- if pid:
- text = '(' + str(pid) + ') ' + text
- self.WriteLog(
- text,
- style=self.cmdOutput.StyleCommand,
- notification=notification)
- def WriteWarning(self, text):
- """Write message in warning style"""
- self.WriteLog(text, style=self.cmdOutput.StyleWarning,
- notification=Notification.MAKE_VISIBLE)
- def WriteError(self, text):
- """Write message in error style"""
- self.WriteLog(text, style=self.cmdOutput.StyleError,
- notification=Notification.MAKE_VISIBLE)
- def OnOutputClear(self, event):
- """Clear content of output window"""
- self.cmdOutput.SetReadOnly(False)
- self.cmdOutput.ClearAll()
- self.cmdOutput.SetReadOnly(True)
- self.progressbar.SetValue(0)
- def GetProgressBar(self):
- """Return progress bar widget"""
- return self.progressbar
- def OnOutputSave(self, event):
- """Save (selected) text from output window to the file"""
- text = self.cmdOutput.GetSelectedText()
- if not text:
- text = self.cmdOutput.GetText()
- # add newline if needed
- if len(text) > 0 and text[-1] != '\n':
- text += '\n'
- dlg = wx.FileDialog(
- self, message=_("Save file as..."),
- defaultFile="grass_cmd_output.txt",
- wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") %
- {'txt': _("Text files"),
- 'files': _("Files")},
- style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
- # Show the dialog and retrieve the user response. If it is the OK response,
- # process the data.
- if dlg.ShowModal() == wx.ID_OK:
- path = dlg.GetPath()
- try:
- output = open(path, "w")
- output.write(text)
- except IOError as e:
- GError(
- _("Unable to write file '%(path)s'.\n\nDetails: %(error)s") % {
- 'path': path,
- 'error': e})
- finally:
- output.close()
- message = _("Command output saved into '%s'") % path
- self.showNotification.emit(message=message)
- dlg.Destroy()
- def SetCopyingOfSelectedText(self, copy):
- """Enable or disable copying of selected text in to clipboard.
- Effects prompt and output.
- :param bool copy: True for enable, False for disable
- """
- if copy:
- self.cmdPrompt.Bind(
- stc.EVT_STC_PAINTED,
- self.cmdPrompt.OnTextSelectionChanged)
- self.cmdOutput.Bind(
- stc.EVT_STC_PAINTED,
- self.cmdOutput.OnTextSelectionChanged)
- else:
- self.cmdPrompt.Unbind(stc.EVT_STC_PAINTED)
- self.cmdOutput.Unbind(stc.EVT_STC_PAINTED)
- def OnCmdOutput(self, event):
- """Prints command output.
- Emits contentChanged signal.
- """
- message = event.text
- type = event.type
- self.cmdOutput.AddStyledMessage(message, type)
- if event.type in ('warning', 'error'):
- self.contentChanged.emit(notification=Notification.MAKE_VISIBLE)
- else:
- self.contentChanged.emit(notification=Notification.HIGHLIGHT)
- def OnCmdProgress(self, event):
- """Update progress message info"""
- self.progressbar.SetValue(event.value)
- event.Skip()
- def CmdProtocolSave(self):
- """Save list of manually entered commands into a text log file"""
- if self.cmdFileProtocol is None:
- return # it should not happen
- try:
- with open(self.cmdFileProtocol, "a") as output:
- cmds = self.cmdPrompt.GetCommands()
- output.write('\n'.join(cmds))
- if len(cmds) > 0:
- output.write('\n')
- except IOError as e:
- GError(_("Unable to write file '{filePath}'.\n\nDetails: {error}").format(
- filePath=self.cmdFileProtocol, error=e))
- self.showNotification.emit(
- message=_("Command log saved to '{}'".format(self.cmdFileProtocol))
- )
- self.cmdFileProtocol = None
- def OnCmdProtocol(self, event=None):
- """Save commands into file"""
- if not event.IsChecked():
- # stop capturing commands, save list of commands to the
- # protocol file
- self.CmdProtocolSave()
- else:
- # start capturing commands
- self.cmdPrompt.ClearCommands()
- # ask for the file
- dlg = wx.FileDialog(
- self, message=_("Save file as..."),
- defaultFile="grass_cmd_log.txt",
- wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") %
- {'txt': _("Text files"),
- 'files': _("Files")},
- style=wx.FD_SAVE)
- if dlg.ShowModal() == wx.ID_OK:
- self.cmdFileProtocol = dlg.GetPath()
- else:
- wx.CallAfter(self.btnCmdProtocol.SetValue, False)
- dlg.Destroy()
- event.Skip()
- def OnCmdRun(self, event):
- """Run command"""
- self.outputSizer.Show(self.panelProgress)
- self.outputSizer.Layout()
- event.Skip()
- def OnCmdDone(self, event):
- """Command done (or aborted)
- """
- self.progressbar.SetValue(0) # reset progress bar on '0%'
- wx.CallLater(100, self._hideProgress)
- event.Skip()
- def _hideProgress(self):
- self.outputSizer.Hide(self.panelProgress)
- self.outputSizer.Layout()
- def ResetFocus(self):
- """Reset focus"""
- self.cmdPrompt.SetFocus()
- def GetPrompt(self):
- """Get prompt"""
- return self.cmdPrompt
- class GStc(stc.StyledTextCtrl):
- """Styled text control for GRASS stdout and stderr.
- Based on FrameOutErr.py
- Name: FrameOutErr.py
- Purpose: Redirecting stdout / stderr
- Author: Jean-Michel Fauth, Switzerland
- Copyright: (c) 2005-2007 Jean-Michel Fauth
- Licence: GPL
- """
- def __init__(self, parent, id, margin=False, wrap=None):
- stc.StyledTextCtrl.__init__(self, parent, id)
- self.parent = parent
- self.SetUndoCollection(True)
- self.SetReadOnly(True)
- # remember position of line beginning (used for '\r')
- self.linePos = -1
- #
- # styles
- #
- self.SetStyle()
- #
- # line margins
- #
- # TODO print number only from cmdlog
- self.SetMarginWidth(1, 0)
- self.SetMarginWidth(2, 0)
- if margin:
- self.SetMarginType(0, stc.STC_MARGIN_NUMBER)
- self.SetMarginWidth(0, 30)
- else:
- self.SetMarginWidth(0, 0)
- #
- # miscellaneous
- #
- self.SetViewWhiteSpace(False)
- self.SetTabWidth(4)
- self.SetUseTabs(False)
- self.UsePopUp(True)
- self.SetSelBackground(True, "#FFFF00")
- self.SetUseHorizontalScrollBar(True)
- #
- # bindings
- #
- self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
- def OnTextSelectionChanged(self, event):
- """Copy selected text to clipboard and skip event.
- The same function is in TextCtrlAutoComplete class (prompt.py).
- """
- wx.CallAfter(self.Copy)
- event.Skip()
- def SetStyle(self):
- """Set styles for styled text output windows with type face
- and point size selected by user (Courier New 10 is default)"""
- typeface = UserSettings.Get(
- group='appearance',
- key='outputfont',
- subkey='type')
- if typeface == "":
- typeface = "Courier New"
- typesize = UserSettings.Get(
- group='appearance',
- key='outputfont',
- subkey='size')
- if typesize is None or int(typesize) <= 0:
- typesize = 10
- typesize = float(typesize)
- self.StyleDefault = 0
- self.StyleDefaultSpec = "face:%s,size:%d,fore:#000000,back:#FFFFFF" % (
- typeface,
- typesize)
- self.StyleCommand = 1
- self.StyleCommandSpec = "face:%s,size:%d,,fore:#000000,back:#bcbcbc" % (
- typeface, typesize)
- self.StyleOutput = 2
- self.StyleOutputSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (
- typeface,
- typesize)
- # fatal error
- self.StyleError = 3
- self.StyleErrorSpec = "face:%s,size:%d,,fore:#7F0000,back:#FFFFFF" % (
- typeface,
- typesize)
- # warning
- self.StyleWarning = 4
- self.StyleWarningSpec = "face:%s,size:%d,,fore:#0000FF,back:#FFFFFF" % (
- typeface, typesize)
- # message
- self.StyleMessage = 5
- self.StyleMessageSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (
- typeface, typesize)
- # unknown
- self.StyleUnknown = 6
- self.StyleUnknownSpec = "face:%s,size:%d,,fore:#000000,back:#FFFFFF" % (
- typeface, typesize)
- # default and clear => init
- self.StyleSetSpec(stc.STC_STYLE_DEFAULT, self.StyleDefaultSpec)
- self.StyleClearAll()
- self.StyleSetSpec(self.StyleCommand, self.StyleCommandSpec)
- self.StyleSetSpec(self.StyleOutput, self.StyleOutputSpec)
- self.StyleSetSpec(self.StyleError, self.StyleErrorSpec)
- self.StyleSetSpec(self.StyleWarning, self.StyleWarningSpec)
- self.StyleSetSpec(self.StyleMessage, self.StyleMessageSpec)
- self.StyleSetSpec(self.StyleUnknown, self.StyleUnknownSpec)
- def OnDestroy(self, evt):
- """The clipboard contents can be preserved after
- the app has exited"""
- wx.TheClipboard.Flush()
- evt.Skip()
- def AddTextWrapped(self, txt, wrap=None):
- """Add string to text area.
- String is wrapped and linesep is also added to the end
- of the string"""
- # allow writing to output window
- self.SetReadOnly(False)
- if wrap:
- txt = textwrap.fill(txt, wrap) + '\n'
- else:
- if txt[-1] != '\n':
- txt += '\n'
- if '\r' in txt:
- self.linePos = -1
- for seg in txt.split('\r'):
- if self.linePos > -1:
- self.SetCurrentPos(self.linePos)
- self.ReplaceSelection(seg)
- else:
- self.linePos = self.GetCurrentPos()
- self.AddText(seg)
- else:
- self.linePos = self.GetCurrentPos()
- try:
- self.AddText(txt)
- except UnicodeDecodeError:
- # TODO: this might be dead code for Py3, txt is already unicode?
- enc = UserSettings.Get(
- group='atm', key='encoding', subkey='value')
- if enc:
- txt = unicode(txt, enc, errors='replace')
- elif 'GRASS_DB_ENCODING' in os.environ:
- txt = unicode(
- txt, os.environ['GRASS_DB_ENCODING'],
- errors='replace')
- else:
- txt = EncodeString(txt)
- self.AddText(txt)
- # reset output window to read only
- self.SetReadOnly(True)
- def AddStyledMessage(self, message, style=None):
- """Add message to text area.
- Handles messages with progress percentages.
- :param message: message to be added
- :param style: style of message, allowed values: 'message',
- 'warning', 'error' or None
- """
- # message prefix
- if style == 'warning':
- message = 'WARNING: ' + message
- elif style == 'error':
- message = 'ERROR: ' + message
- p1 = self.GetEndStyled()
- self.GotoPos(p1)
- # is this still needed?
- if '\b' in message:
- if self.linePos < 0:
- self.linePos = p1
- last_c = ''
- for c in message:
- if c == '\b':
- self.linePos -= 1
- else:
- if c == '\r':
- pos = self.GetCurLine()[1]
- # self.SetCurrentPos(pos)
- else:
- self.SetCurrentPos(self.linePos)
- self.ReplaceSelection(c)
- self.linePos = self.GetCurrentPos()
- if c != ' ':
- last_c = c
- if last_c not in ('0123456789'):
- self.AddTextWrapped('\n', wrap=None)
- self.linePos = -1
- else:
- self.linePos = -1 # don't force position
- if '\n' not in message:
- self.AddTextWrapped(message, wrap=60)
- else:
- self.AddTextWrapped(message, wrap=None)
- p2 = self.GetCurrentPos()
- if p2 >= p1:
- try:
- self.StartStyling(p1)
- except TypeError:
- self.StartStyling(p1, 0xff)
- if style == 'error':
- self.SetStyling(p2 - p1, self.StyleError)
- elif style == 'warning':
- self.SetStyling(p2 - p1, self.StyleWarning)
- elif style == 'message':
- self.SetStyling(p2 - p1, self.StyleMessage)
- else: # unknown
- self.SetStyling(p2 - p1, self.StyleUnknown)
- self.EnsureCaretVisible()
- class GConsoleFrame(wx.Frame):
- """Standalone GConsole for testing only"""
- def __init__(self, parent, id=wx.ID_ANY, title="GConsole Test Frame",
- style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL, **kwargs):
- wx.Frame.__init__(self, parent=parent, id=id, title=title, style=style)
- panel = wx.Panel(self, id=wx.ID_ANY)
- from lmgr.menudata import LayerManagerMenuData
- menuTreeBuilder = LayerManagerMenuData()
- self.gconsole = GConsole(guiparent=self)
- self.goutput = GConsoleWindow(parent=panel, gconsole=self.gconsole,
- menuModel=menuTreeBuilder.GetModel(),
- gcstyle=GC_SEARCH | GC_PROMPT)
- mainSizer = wx.BoxSizer(wx.VERTICAL)
- mainSizer.Add(
- self.goutput,
- proportion=1,
- flag=wx.EXPAND,
- border=0)
- panel.SetSizer(mainSizer)
- mainSizer.Fit(panel)
- self.SetMinSize((550, 500))
- def testGConsole():
- app = wx.App()
- frame = GConsoleFrame(parent=None)
- frame.Show()
- app.MainLoop()
- if __name__ == '__main__':
- testGConsole()
|