class SbMask(SbItem): """Button to show whether mask is activated and remove mask with left mouse click """ def __init__(self, mapframe, statusbar, position=0): SbItem.__init__(self, mapframe, statusbar, position) self.name = "mask" self.widget = Button(parent=self.statusbar, id=wx.ID_ANY, label=_("MASK"), style=wx.NO_BORDER) self.widget.Bind(wx.EVT_BUTTON, self.OnRemoveMask) self.widget.SetForegroundColour(wx.Colour(255, 0, 0)) self.widget.SetToolTip(tip=_("Left mouse click to remove the MASK")) self.widget.Hide() def Update(self): if grass.find_file(name="MASK", element="cell", mapset=grass.gisenv()["MAPSET"])["name"]: self.Show() else: self.Hide() def OnRemoveMask(self, event): if grass.find_file(name="MASK", element="cell", mapset=grass.gisenv()["MAPSET"])["name"]: dlg = wx.MessageDialog( self.mapFrame, message=_("Are you sure that you want to remove the MASK?"), caption=_("Remove MASK"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) if dlg.ShowModal() != wx.ID_YES: dlg.Destroy() return RunCommand("r.mask", flags="r") self.Hide() self.mapFrame.OnRender(event=None)
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.ALIGN_RIGHT | 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) # eliminate gtk_widget_size_allocate() warnings # avoid to use a deprecated method in wxPython >= 2.9 getattr(self.outputSizer, 'FitInside', self.outputSizer.SetVirtualSizeHints)(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() 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(os.linesep.join(cmds)) if len(cmds) > 0: output.write(os.linesep) 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 SbMask: """Button to show whether mask is activated and remove mask with left mouse click """ def __init__(self, parent, giface): self.name = "mask" self.mask_layer = "MASK" self.parent = parent self.giface = giface self.widget = Button( parent=parent, id=wx.ID_ANY, label=_(self.mask_layer), style=wx.NO_BORDER ) self.widget.Bind(wx.EVT_BUTTON, self.OnRemoveMask) self.widget.SetForegroundColour(wx.Colour(255, 0, 0)) self.widget.SetToolTip(tip=_("Left mouse click to remove the MASK")) self.giface.currentMapsetChanged.connect(self.Refresh) self.giface.grassdbChanged.connect(self._dbChanged) self.Refresh() def _dbChanged(self, map=None, newname=None): if map == self.mask_layer or newname == self.mask_layer: self.Refresh() self.giface.updateMap.emit() def Show(self): """Invokes showing of underlying widget. In derived classes it can do what is appropriate for it, e.g. showing text on statusbar (only). """ self.widget.Show() def Hide(self): self.widget.Hide() def SetValue(self, value): self.widget.SetValue(value) def GetValue(self): return self.widget.GetValue() def GetWidget(self): """Returns underlying widget. :return: widget or None if doesn't exist """ return self.widget def Refresh(self): """Show mask in the statusbar if mask file found""" if gs.find_file( name=self.mask_layer, element="cell", mapset=gs.gisenv()["MAPSET"] )["name"]: self.Show() else: self.Hide() def OnRemoveMask(self, event): dlg = wx.MessageDialog( self.parent, message=_("Are you sure that you want to remove the MASK?"), caption=_("Remove MASK"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) if dlg.ShowModal() != wx.ID_YES: dlg.Destroy() return RunCommand("r.mask", flags="r") gisenv = gs.gisenv() self.giface.grassdbChanged.emit( grassdb=gisenv["GISDBASE"], location=gisenv["LOCATION_NAME"], mapset=gisenv["MAPSET"], map=self.mask_layer, action="delete", element="raster", )