def initGui(self): for keyseq, slot in ( (Qt.CTRL + Qt.ALT + Qt.Key_K, self.__create_group), (Qt.CTRL + Qt.ALT + Qt.Key_S, self.__select_next_group), (Qt.CTRL + Qt.ALT + Qt.Key_N, self.__next_section), (Qt.CTRL + Qt.ALT + Qt.Key_B, self.__previous_section), ): short = QShortcut(QKeySequence(keyseq), self.__iface.mainWindow()) short.setContext(Qt.ApplicationShortcut) short.activated.connect(slot) self.__shortcuts.append(short) self.__menu = QMenu("Albion") self.__menu.aboutToShow.connect(self.__create_menu_entries) self.__iface.mainWindow().menuBar().addMenu(self.__menu) self.__toolbar = QToolBar("Albion") self.__iface.addToolBar(self.__toolbar) self.__toolbar.addAction(icon("log_strati.svg"), "stratigraphic log").triggered.connect( self.__log_strati_clicked) self.__toolbar.addWidget(self.__current_graph) self.__current_graph.currentIndexChanged[unicode].connect( self.__current_graph_changed) self.__toolbar.addWidget(self.__current_section) self.__current_section.currentIndexChanged[unicode].connect( self.__current_section_changed) self.__toolbar.addAction( icon("previous_line.svg"), "previous section (Ctrl+Alt+b)").triggered.connect( self.__previous_section) self.__toolbar.addAction( icon("next_line.svg"), "next section (Ctrl+Alt+n)").triggered.connect(self.__next_section) self.__toolbar.addAction(icon("line_from_selected.svg"), "create temporary section").triggered.connect( self.__section_from_selection) self.__viewer3d = QDockWidget("3D") self.__viewer3d.setWidget(Viewer3d()) self.__iface.addDockWidget(Qt.LeftDockWidgetArea, self.__viewer3d) self.__iface.mainWindow().tabifyDockWidget( self.__iface.mainWindow().findChild(QDockWidget, "Layers"), self.__viewer3d) self.__viewer3d_ctrl = QToolBar("3D controls") self.__viewer3d_ctrl.addWidget(ViewerControls( self.__viewer3d.widget())) self.__iface.addToolBar(self.__viewer3d_ctrl) QgsProject.instance().readProject.connect(self.__qgis__project__loaded) self.__qgis__project__loaded() # case of reload
def _shortcutHelper(self, keySequence, group, description, parent, function, context = None, enabled = None): shortcut = QShortcut(QKeySequence(keySequence), parent, member=function, ambiguousMember=function) if context != None: shortcut.setContext(context) if enabled != None: shortcut.setEnabled(True) return shortcut, group, description
def install_shortcuts(obj, actions, ide): short = resources.get_shortcut for action in actions: short_key = action.get("shortcut", None) action_data = action.get("action", None) connect = action.get("connect", None) shortcut = None item_ui = None func = None if connect: func = getattr(obj, connect, None) if short_key and not action_data: if isinstance(short_key, QKeySequence): shortcut = QShortcut(short_key, ide) else: shortcut = QShortcut(short(short_key), ide) shortcut.setContext(Qt.ApplicationShortcut) if isinstance(func, collections.Callable): ide.connect(shortcut, SIGNAL("activated()"), func) if action_data: is_menu = action_data.get('is_menu', False) if is_menu: item_ui = QMenu(action_data['text'], ide) else: item_ui = QAction(action_data['text'], ide) object_name = "%s.%s" % (obj.__class__.__name__, connect) item_ui.setObjectName(object_name) image_name = action_data.get('image', None) section = action_data.get('section', None) weight = action_data.get('weight', None) keysequence = action_data.get('keysequence', None) if image_name: if isinstance(image_name, int): icon = ide.style().standardIcon(image_name) item_ui.setIcon(icon) elif isinstance(image_name, str): icon = QIcon(":img/" + image_name) item_ui.setIcon(icon) if short_key and not is_menu: item_ui.setShortcut(short(short_key)) item_ui.setShortcutContext(Qt.ApplicationShortcut) elif keysequence and not is_menu: item_ui.setShortcut(short(keysequence)) item_ui.setShortcutContext(Qt.ApplicationShortcut) if isinstance(func, collections.Callable) and not is_menu: ide.connect(item_ui, SIGNAL("triggered()"), func) if section and section[0] is not None and weight: ide.register_menuitem(item_ui, section, weight) if image_name and not is_menu: ide.register_toolbar(item_ui, section, weight) if short_key and shortcut: ide.register_shortcut(short_key, shortcut, item_ui)
def install_shortcuts(obj, actions, ide): short = resources.get_shortcut for action in actions: short_key = action.get("shortcut", None) action_data = action.get("action", None) connect = action.get("connect", None) shortcut = None item_ui = None func = None if connect: func = getattr(obj, connect, None) if short_key and not action_data: if isinstance(short_key, QKeySequence): shortcut = QShortcut(short_key, ide) else: shortcut = QShortcut(short(short_key), ide) shortcut.setContext(Qt.ApplicationShortcut) if isinstance(func, collections.Callable): ide.connect(shortcut, SIGNAL("activated()"), func) if action_data: is_menu = action_data.get('is_menu', False) if is_menu: item_ui = QMenu(action_data['text'], ide) else: item_ui = QAction(action_data['text'], ide) object_name = "%s.%s" % (obj.__class__.__name__, connect) item_ui.setObjectName(object_name) image_name = action_data.get('image', None) section = action_data.get('section', None) weight = action_data.get('weight', None) keysequence = action_data.get('keysequence', None) if image_name: if isinstance(image_name, int): icon = ide.style().standardIcon(image_name) item_ui.setIcon(icon) elif isinstance(image_name, str): icon = QIcon(":img/" + image_name) item_ui.setIcon(icon) if short_key and not is_menu: item_ui.setShortcut(short(short_key)) item_ui.setShortcutContext(Qt.ApplicationShortcut) elif keysequence and not is_menu: item_ui.setShortcut(short(keysequence)) item_ui.setShortcutContext(Qt.ApplicationShortcut) if isinstance(func, collections.Callable) and not is_menu: ide.connect(item_ui, SIGNAL("triggered()"), func) if section and section[0] is not None and weight: ide.register_menuitem(item_ui, section, weight) if image_name and not is_menu: ide.register_toolbar(item_ui, section, weight) if short_key and shortcut: ide.register_shortcut(short_key, shortcut, item_ui)
class Shortcut(object): def __init__(self, widget, *shortcut): self.qshortcut = QShortcut(QKeySequence(*shortcut), widget) if isinstance(widget, QMainWindow): self.qshortcut.setContext(Qt.ApplicationShortcut) else: self.qshortcut.setContext(Qt.WidgetShortcut) def __rshift__(self, functor): s = self.qshortcut s.connect(s, SIGNAL("activated()"), functor)
def __init__(self, parent, actions=None): QTabWidget.__init__(self, parent) tab_bar = TabsBase(self, parent) self.connect(tab_bar, SIGNAL('move_tab(int,int)'), self.move_tab) self.connect(tab_bar, SIGNAL('move_tab(long,int,int)'), self.move_tab_from_another_tabwidget) self.setTabBar(tab_bar) self.menu = QMenu(self) if actions: add_actions(self.menu, actions) self.index_history = [] self.connect(self, SIGNAL('currentChanged(int)'), self.__current_changed) tabsc = QShortcut(QKeySequence("Ctrl+Tab"), parent, self.tab_navigate) tabsc.setContext(Qt.WidgetWithChildrenShortcut)
def __init__(self, parent, actions=None): QTabWidget.__init__(self, parent) tab_bar = TabsBase(self, parent) self.connect(tab_bar, SIGNAL('move_tab(int,int)'), self.move_tab) self.connect(tab_bar, SIGNAL('move_tab(long,int,int)'), self.move_tab_from_another_tabwidget) self.setTabBar(tab_bar) self.menu = QMenu(self) if actions: add_actions(self.menu, actions) self.index_history = [] self.connect(self, SIGNAL('currentChanged(int)'), self.__current_changed) tabsc = QShortcut(QKeySequence("Ctrl+Tab"), parent, self.tab_navigate) tabsc.setContext(Qt.WidgetWithChildrenShortcut)
class DockWidget(QDockWidget): """Extended QDockWidget for Enki main window """ def __init__(self, *args): QDockWidget.__init__(self, *args) self._showAction = None self._titleBar = _TitleBar( self ) self.setTitleBarWidget( self._titleBar ) self._closeShortcut = QShortcut( QKeySequence( "Esc" ), self ) self._closeShortcut.setContext( Qt.WidgetWithChildrenShortcut ) self._closeShortcut.activated.connect(self._hide) def showAction(self): """Action shows the widget and set focus on it. Add this action to the main menu """ if not self._showAction : self._showAction = QAction(self.windowIcon(), self.windowTitle(), self) self._showAction.triggered.connect(self.show) self._showAction.triggered.connect(self._handleFocusProxy) return self._showAction def titleBarWidget(self): """QToolBar on the title. You may add own actions to this tool bar """ # method was added only for documenting return QDockWidget.titleBarWidget(self) def _handleFocusProxy(self): """Set focus to focus proxy. Called after widget has been shown """ if self.focusProxy() is not None: self.setFocus() def _hide(self): """Hide and return focus to MainWindow focus proxy """ self.hide() if self.parent() is not None and \ self.parent().focusProxy() is not None: self.parent().focusProxy().setFocus()
def __init__(self, parent, mainwin): QPlainTextEdit.__init__(self, parent) self.mainwin = mainwin self.setObjectName("restedit") self._formats = {} self.textChanged.connect(self._restedit_update) self.blockCountChanged.connect(self._blockcount_update) shortcut = QShortcut(QKeySequence("Ctrl+T"), self) shortcut.activated.connect(self._choose_font) shortcut = QShortcut(QKeySequence.ZoomIn, self) shortcut.activated.connect(self._zoom_in) shortcut.setContext(Qt.WidgetShortcut) shortcut = QShortcut(QKeySequence.ZoomOut, self) shortcut.activated.connect(self._zoom_out) shortcut.setContext(Qt.WidgetShortcut) self._doc = QApplication.instance().rest self._last_warnings = {} # should be moved to the document
def __init__(self, canvas, layer, iface, dockwidget): QApplication.restoreOverrideCursor() #QApplication.setOverrideCursor(Qt.CrossCursor) QgsMapToolEmitPoint.__init__(self, canvas) self.layer = layer self.start = None self.end = None self.ends = [] sample, geo_ref = get_whole_raster(self.layer) r = sample[0] g = sample[1] b = sample[2] where_are_NaNs = np.isnan(r) r[where_are_NaNs] = 0 where_are_NaNs = np.isnan(g) g[where_are_NaNs] = 0 where_are_NaNs = np.isnan(b) b[where_are_NaNs] = 0 self.sample = (r, g, b) h, s, v = rgb2hsv(r, g, b) self.sample_hsv = h, s, v self.geo_ref = geo_ref self.iface = iface self.layer_v = None self.points = [] self.double = False self.dockwidget = dockwidget self.linetype = "Line" shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self.iface.mainWindow()) shortcut.setContext(Qt.ApplicationShortcut) shortcut.activated.connect(self.fesq) self.esq = False self.layer_v = iface.activeLayer()
def _shortcutHelper(self, keySequence, group, description, parent, function, context=None, enabled=None, widget=None): shortcut = QShortcut(QKeySequence(keySequence), parent, member=function, ambiguousMember=function) if context != None: shortcut.setContext(context) if enabled != None: shortcut.setEnabled(True) ShortcutManager().register(group, description, shortcut, widget) return shortcut, group, description
def __init__(self, parent, actions=None): BaseTabs.__init__(self, parent, actions) tab_bar = TabBar(self, parent) self.connect(tab_bar, SIGNAL('move_tab(int,int)'), self.move_tab) self.connect(tab_bar, SIGNAL('move_tab(long,int,int)'), self.move_tab_from_another_tabwidget) self.setTabBar(tab_bar) self.index_history = [] self.connect(self, SIGNAL('currentChanged(int)'), self.__current_changed) tabsc = QShortcut(QKeySequence("Ctrl+Tab"), parent, self.tab_navigate) tabsc.setContext(Qt.WidgetWithChildrenShortcut) # Browsing tabs button browse_button = create_toolbutton(self, icon=get_icon("browse_tab.png"), tip=translate("Tabs", "Browse tabs")) self.browse_tabs_menu = QMenu(self) browse_button.setMenu(self.browse_tabs_menu) browse_button.setPopupMode(browse_button.InstantPopup) self.connect(self.browse_tabs_menu, SIGNAL("aboutToShow()"), self.update_browse_tabs_menu) self.setCornerWidget(browse_button)
def __init__(self, *args, **kw): # To override the Qt widget used by RichIPythonWidget self.custom_control = IPythonControlWidget self.custom_page_control = IPythonPageControlWidget super(SMIPythonWidget, self).__init__(*args, **kw) self.set_background_color() # --- Spyder variables --- self.ipyclient = None # --- Keyboard shortcuts --- inspectsc = QShortcut(QKeySequence("Ctrl+I"), self, self._control.inspect_current_object) inspectsc.setContext(Qt.WidgetWithChildrenShortcut) clear_consolesc = QShortcut(QKeySequence("Ctrl+L"), self, self.clear_console) clear_consolesc.setContext(Qt.WidgetWithChildrenShortcut) # --- IPython variables --- # To send an interrupt signal to the Spyder kernel self.custom_interrupt = True # To restart the Spyder kernel in case it dies self.custom_restart = True
def __init__(self, *args, **kw): # To override the Qt widget used by RichIPythonWidget self.custom_control = IPythonControlWidget self.custom_page_control = IPythonPageControlWidget super(SMIPythonWidget, self).__init__(*args, **kw) self.set_background_color() # --- Spyder variables --- self.ipyclient = None # --- Keyboard shortcuts --- inspectsc = QShortcut(QKeySequence("Ctrl+I"), self, self._control.inspect_current_object) inspectsc.setContext(Qt.WidgetWithChildrenShortcut) clear_consolesc = QShortcut(QKeySequence("Ctrl+L"), self, self.clear_console) clear_consolesc.setContext(Qt.WidgetWithChildrenShortcut) # --- IPython variables --- # To send an interrupt signal to the Spyder kernel self.custom_interrupt = True # To restart the Spyder kernel in case it dies self.custom_restart = True
def atalho_teclado(self): shortcut1 = QShortcut(QKeySequence(Qt.Key_1), self.iface.mainWindow()) shortcut1.setContext(Qt.ApplicationShortcut) shortcut1.activated.connect(self.setVerifica1) shortcut2 = QShortcut(QKeySequence(Qt.Key_2), self.iface.mainWindow()) shortcut2.setContext(Qt.ApplicationShortcut) shortcut2.activated.connect(self.setVerifica2) shortcut3 = QShortcut(QKeySequence(Qt.Key_3), self.iface.mainWindow()) shortcut3.setContext(Qt.ApplicationShortcut) shortcut3.activated.connect(self.setVerifica3)
def __init__(self, parent, mainwin): QWebView.__init__(self, parent) self.mainwin = mainwin self.setUrl(QUrl("about:blank")) self.setObjectName("webview") self.setTextSizeMultiplier(self.ZOOM_DEFAULT) shortcut = QShortcut(QKeySequence.ZoomIn, self) shortcut.activated.connect(self._zoom_in) shortcut.setContext(Qt.WidgetShortcut) shortcut = QShortcut(QKeySequence(Qt.CTRL | Qt.Key_Equal), self) shortcut.activated.connect(self._zoom_neutral) shortcut.setContext(Qt.WidgetShortcut) shortcut = QShortcut(QKeySequence.ZoomOut, self) shortcut.activated.connect(self._zoom_out) shortcut.setContext(Qt.WidgetShortcut)
class SqlEdit(QsciScintilla): LEXER_PYTHON = 0 LEXER_R = 1 def __init__(self, parent=None): QsciScintilla.__init__(self, parent) self.mylexer = None self.api = None self.setCommonOptions() self.initShortcuts() def setCommonOptions(self): # Enable non-ASCII characters self.setUtf8(True) # Default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setWrapVisualFlags(QsciScintilla.WrapFlagByText, QsciScintilla.WrapFlagNone, 4) self.setSelectionForegroundColor(QColor('#2e3436')) self.setSelectionBackgroundColor(QColor('#babdb6')) # Show line numbers self.setMarginWidth(1, '000') self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor('#2e3436')) self.setMarginsBackgroundColor(QColor('#babdb6')) # Highlight current line self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor('#d3d7cf')) # Folding self.setFolding(QsciScintilla.BoxedTreeFoldStyle) self.setFoldMarginColors(QColor('#d3d7cf'), QColor('#d3d7cf')) # Mark column 80 with vertical line self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QColor('#eeeeec')) # Indentation self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setTabIndents(True) self.setBackspaceUnindents(True) self.setTabWidth(4) # Autocomletion self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.setAutoCompletionCaseSensitivity(False) # Load font from Python console settings settings = QSettings() fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace') fontSize = int(settings.value('pythonConsole/fontsize', 10)) self.defaultFont = QFont(fontName) self.defaultFont.setFixedPitch(True) self.defaultFont.setPointSize(fontSize) self.defaultFont.setStyleHint(QFont.TypeWriter) self.defaultFont.setStretch(QFont.SemiCondensed) self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0) self.defaultFont.setBold(False) self.boldFont = QFont(self.defaultFont) self.boldFont.setBold(True) self.italicFont = QFont(self.defaultFont) self.italicFont.setItalic(True) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.initLexer() def initShortcuts(self): (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16) # Disable some shortcuts self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl) # Use Ctrl+Space for autocompletion self.shortcutAutocomplete = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.shortcutAutocomplete.setContext(Qt.WidgetShortcut) self.shortcutAutocomplete.activated.connect(self.autoComplete) def autoComplete(self): self.autoCompleteFromAll() def initLexer(self): self.mylexer = QsciLexerSQL() colorDefault = QColor('#2e3436') colorComment = QColor('#c00') colorCommentBlock = QColor('#3465a4') colorNumber = QColor('#4e9a06') colorType = QColor('#4e9a06') colorKeyword = QColor('#204a87') colorString = QColor('#ce5c00') self.mylexer.setDefaultFont(self.defaultFont) self.mylexer.setDefaultColor(colorDefault) self.mylexer.setColor(colorComment, 1) self.mylexer.setColor(colorNumber, 2) self.mylexer.setColor(colorString, 3) self.mylexer.setColor(colorString, 4) self.mylexer.setColor(colorKeyword, 5) self.mylexer.setColor(colorString, 6) self.mylexer.setColor(colorString, 7) self.mylexer.setColor(colorType, 8) self.mylexer.setColor(colorCommentBlock, 12) self.mylexer.setColor(colorString, 15) self.mylexer.setFont(self.italicFont, 1) self.mylexer.setFont(self.boldFont, 5) self.mylexer.setFont(self.boldFont, 8) self.mylexer.setFont(self.italicFont, 12) self.setLexer(self.mylexer) def lexer(self): return self.mylexer
class ShellScintilla(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): super(ShellScintilla, self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.parent = parent self.opening = ['(', '{', '[', "'", '"'] self.closing = [')', '}', ']', "'", '"'] self.settings = QSettings() # Enable non-ascii chars for editor self.setUtf8(True) self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = [] self.historyIndex = 0 # Read history command file self.readHistoryFile() self.historyDlg = HistoryDialog(self) # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setMatchedBraceBackgroundColor(QColor("#b7f907")) # Current line visible with special background color self.setCaretWidth(2) self.refreshSettingsShell() # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCSS = QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Space), self) self.newShortcutCAS = QShortcut(QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCSS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.activated.connect(self.autoCompleteKeyBinding) self.newShortcutCSS.activated.connect(self.showHistory) def _setMinimumHeight(self): fnt = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fntSize = self.settings.value("pythonConsole/fontsize", 10, type=int) fm = QFontMetrics(QFont(fnt, fntSize)) self.setMinimumHeight(fm.height() + 10) def refreshSettingsShell(self): # Set Python lexer self.setLexers() threshold = self.settings.value("pythonConsole/autoCompThreshold", 2, type=int) self.setAutoCompletionThreshold(threshold) radioButtonSource = self.settings.value("pythonConsole/autoCompleteSource", 'fromAPI') autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabled", True, type=bool) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.setAutoCompletionSource(self.AcsDocument) elif radioButtonSource == 'fromAPI': self.setAutoCompletionSource(self.AcsAPIs) elif radioButtonSource == 'fromDocAPI': self.setAutoCompletionSource(self.AcsAll) else: self.setAutoCompletionSource(self.AcsNone) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretForegroundColor(cursorColor) # Sets minimum height for input area based of font metric self._setMinimumHeight() def showHistory(self): if not self.historyDlg.isVisible(): self.historyDlg.show() self.historyDlg._reloadHistory() self.historyDlg.activateWindow() def autoCompleteKeyBinding(self): radioButtonSource = self.settings.value("pythonConsole/autoCompleteSource", 'fromAPI') autoCompEnabled = self.settings.value("pythonConsole/autoCompleteEnabled", True, type=bool) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.autoCompleteFromDocument() elif radioButtonSource == 'fromAPI': self.autoCompleteFromAPIs() elif radioButtonSource == 'fromDocAPI': self.autoCompleteFromAll() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() if command == "processing": # import Processing class self.append('import processing') elif command == "qtCore": # import QtCore class self.append('from PyQt4.QtCore import *') elif command == "qtGui": # import QtGui class self.append('from PyQt4.QtGui import *') self.entered() self.move_cursor_to_end() self.setFocus() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.api = QsciAPIs(self.lexer) chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True, type=bool) chekBoxPreparedAPI = self.settings.value("pythonConsole/usePreparedAPIFile", False, type=bool) if chekBoxAPI: pap = os.path.join(QgsApplication.pkgDataPath(), "python", "qsci_apis", "pyqgis.pap") self.api.loadPrepared(pap) elif chekBoxPreparedAPI: self.api.loadPrepared(self.settings.value("pythonConsole/preparedAPIFile")) else: apiPath = self.settings.value("pythonConsole/userAPI", []) for i in range(0, len(apiPath)): self.api.load(unicode(apiPath[i])) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH) + 1 bb = QByteArray(len, '0') self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, len(self.text(line))) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def is_cursor_on_edition_zone(self): """ Return True if the cursor is in the edition zone """ cline, cindex = self.getCursorPosition() return cline == self.lines() - 1 and cindex >= 4 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt line, index = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(line) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, list): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ command != self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self, fromCloseConsole=False): ok = False try: wH = codecs.open(_historyFile, 'w', encoding='utf-8') for s in self.history: wH.write(s + '\n') ok = True except: raise wH.close() if ok and not fromCloseConsole: msgText = QCoreApplication.translate('PythonConsole', 'History saved successfully.') self.parent.callWidgetMessageBar(msgText) def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = codecs.open(_historyFile, 'r', encoding='utf-8') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistory(self, clearSession=False): if clearSession: self.history = [] msgText = QCoreApplication.translate('PythonConsole', 'Session and file history cleared successfully.') self.parent.callWidgetMessageBar(msgText) return ok = False try: cH = codecs.open(_historyFile, 'w', encoding='utf-8') ok = True except: raise cH.close() if ok: msgText = QCoreApplication.translate('PythonConsole', 'History cleared successfully.') self.parent.callWidgetMessageBar(msgText) def clearHistorySession(self): self.clearHistory(True) def showPrevious(self): if self.historyIndex < len(self.history) and self.history: line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and self.history: line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): startLine, startPos, endLine, endPos = self.getSelection() # handle invalid cursor position and multiline selections if not self.is_cursor_on_edition_zone() or startLine < endLine: # allow to copy and select if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier): if e.key() in (Qt.Key_C, Qt.Key_A): QsciScintilla.keyPressEvent(self, e) return # allow selection if e.modifiers() & Qt.ShiftModifier: if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End): QsciScintilla.keyPressEvent(self, e) return # all other keystrokes get sent to the input line self.move_cursor_to_end() line, index = self.getCursorPosition() cmd = self.text(line) if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.key() in (Qt.Key_Left, Qt.Key_Home): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone newline, newindex = self.getCursorPosition() if newline < line or newindex < 4: # fix selection and the cursor position if self.hasSelectedText(): self.setSelection(line, self.getSelection()[3], line, 4) else: self.setCursorPosition(line, 4) elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone _, newindex = self.getCursorPosition() if newindex < 4: # restore the prompt chars (if removed) and # fix the cursor position self.insert(cmd[:3 - newindex] + " ") self.setCursorPosition(line, 4) self.recolor() elif (e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and e.key() == Qt.Key_V) or \ (e.modifiers() & Qt.ShiftModifier and e.key() == Qt.Key_Insert): self.paste() e.accept() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: t = unicode(e.text()) self.autoCloseBracket = self.settings.value("pythonConsole/autoCloseBracket", False, type=bool) self.autoImport = self.settings.value("pythonConsole/autoInsertionImport", True, type=bool) txt = cmd[:index].replace('>>> ', '').replace('... ', '') ## Close bracket automatically if t in self.opening and self.autoCloseBracket: i = self.opening.index(t) if self.hasSelectedText() and startPos != 0: selText = self.selectedText() self.removeSelectedText() self.insert(self.opening[i] + selText + self.closing[i]) self.setCursorPosition(endLine, endPos + 2) return elif t == '(' and (re.match(r'^[ \t]*def \w+$', txt) or re.match(r'^[ \t]*class \w+$', txt)): self.insert('):') else: self.insert(self.closing[i]) ## FIXES #8392 (automatically removes the redundant char ## when autoclosing brackets option is enabled) elif t in [')', ']', '}'] and self.autoCloseBracket: txt = self.text(line) try: if txt[index - 1] in self.opening and t == txt[index]: self.setCursorPosition(line, index + 1) self.SendScintilla(QsciScintilla.SCI_DELETEBACK) except IndexError: pass elif t == ' ' and self.autoImport: ptrn = r'^[ \t]*from [\w.]+$' if re.match(ptrn, txt): self.insert(' import') self.setCursorPosition(line, index + 7) QsciScintilla.keyPressEvent(self, e) def contextMenuEvent(self, e): menu = QMenu(self) subMenu = QMenu(menu) titleHistoryMenu = QCoreApplication.translate("PythonConsole", "Command History") subMenu.setTitle(titleHistoryMenu) subMenu.addAction( QCoreApplication.translate("PythonConsole", "Show"), self.showHistory, 'Ctrl+Shift+SPACE') subMenu.addSeparator() subMenu.addAction( QCoreApplication.translate("PythonConsole", "Save"), self.writeHistoryFile) subMenu.addSeparator() subMenu.addAction( QCoreApplication.translate("PythonConsole", "Clear File"), self.clearHistory) subMenu.addAction( QCoreApplication.translate("PythonConsole", "Clear Session"), self.clearHistorySession) menu.addMenu(subMenu) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) pasteAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Paste"), self.paste, QKeySequence.Paste) copyAction.setEnabled(False) pasteAction.setEnabled(False) if self.hasSelectedText(): copyAction.setEnabled(True) if QApplication.clipboard().text(): pasteAction.setEnabled(True) menu.exec_(self.mapToGlobal(e.pos())) def mousePressEvent(self, e): """ Re-implemented to handle the mouse press event. e: the mouse press event (QMouseEvent) """ self.setFocus() if e.button() == Qt.MidButton: stringSel = unicode(QApplication.clipboard().text(QClipboard.Selection)) if not self.is_cursor_on_last_line(): self.move_cursor_to_end() self.insertFromDropPaste(stringSel) e.accept() else: QsciScintilla.mousePressEvent(self, e) def paste(self): """ Method to display data from the clipboard. XXX: It should reimplement the virtual QScintilla.paste method, but it seems not used by QScintilla code. """ stringPaste = unicode(QApplication.clipboard().text()) if self.is_cursor_on_last_line(): if self.hasSelectedText(): self.removeSelectedText() else: self.move_cursor_to_end() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) self.setFocus() e.setDropAction(Qt.CopyAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = unicode(textDP).splitlines() if pasteList: for line in pasteList[:-1]: cleanLine = line.replace(">>> ", "").replace("... ", "") self.insert(unicode(cleanLine)) self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) if pasteList[-1] != "": line = pasteList[-1] cleanLine = line.replace(">>> ", "").replace("... ", "") self.insert(unicode(cleanLine)) self.move_cursor_to_end() def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) self.setFocus() self.move_cursor_to_end() def currentCommand(self): linenr, index = self.getCursorPosition() string = self.text() cmdLine = string[4:] cmd = unicode(cmdLine) return cmd def runCommand(self, cmd): self.writeCMD(cmd) import webbrowser self.updateHistory(cmd) if cmd in ('_pyqgis', '_api'): if cmd == '_pyqgis': webbrowser.open("http://qgis.org/pyqgis-cookbook/") elif cmd == '_api': webbrowser.open("http://qgis.org/api/") more = False else: self.buffer.append(cmd) src = u"\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] ## prevents to commands with more lines to break the console ## in the case they have a eol different from '\n' self.setText('') self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): sys.stderr.write(txt) def writeCMD(self, txt): if len(txt) > 0: getCmdString = self.text() prompt = getCmdString[0:4] sys.stdout.write(prompt + txt + '\n')
def newsc(keystr, triggered): sc = QShortcut(QKeySequence(keystr), parent, triggered) sc.setContext(Qt.WidgetWithChildrenShortcut) return sc
class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget, InspectObjectMixin): """Python shell widget""" QT_CLASS = ShellBaseWidget INITHISTORY = ['# -*- coding: utf-8 -*-', '# *** Spyder Python Console History Log ***',] SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) def __init__(self, parent, history_filename, profile=False): ShellBaseWidget.__init__(self, parent, history_filename, profile) TracebackLinksMixin.__init__(self) InspectObjectMixin.__init__(self) # Local shortcuts self.inspectsc = QShortcut(QKeySequence("Ctrl+I"), self, self.inspect_current_object) self.inspectsc.setContext(Qt.WidgetWithChildrenShortcut) def get_shortcut_data(self): """ Returns shortcut data, a list of tuples (shortcut, text, default) shortcut (QShortcut or QAction instance) text (string): action/shortcut description default (string): default key sequence """ return [ (self.inspectsc, "Inspect current object", "Ctrl+I"), ] #------ Context menu def setup_context_menu(self): """Reimplements ShellBaseWidget method""" ShellBaseWidget.setup_context_menu(self) self.copy_without_prompts_action = create_action(self, _("Copy without prompts"), icon=get_icon('copywop.png'), triggered=self.copy_without_prompts) clear_line_action = create_action(self, _("Clear line"), QKeySequence("Shift+Escape"), icon=get_icon('eraser.png'), tip=_("Clear line"), triggered=self.clear_line) clear_action = create_action(self, _("Clear shell"), QKeySequence("Ctrl+L"), icon=get_icon('clear.png'), tip=_("Clear shell contents " "('cls' command)"), triggered=self.clear_terminal) add_actions(self.menu, (self.copy_without_prompts_action, clear_line_action, clear_action)) def contextMenuEvent(self, event): """Reimplements ShellBaseWidget method""" state = self.has_selected_text() self.copy_without_prompts_action.setEnabled(state) ShellBaseWidget.contextMenuEvent(self, event) def copy_without_prompts(self): """Copy text to clipboard without prompts""" text = self.get_selected_text() lines = text.split(os.linesep) for index, line in enumerate(lines): if line.startswith('>>> ') or line.startswith('... '): lines[index] = line[4:] text = os.linesep.join(lines) QApplication.clipboard().setText(text) #------ Key handlers def postprocess_keyevent(self, event): """Process keypress event""" ShellBaseWidget.postprocess_keyevent(self, event) if QToolTip.isVisible(): _event, _text, key, _ctrl, _shift = restore_keyevent(event) self.hide_tooltip_if_necessary(key) def _key_other(self, text): """1 character key""" if self.is_completion_widget_visible(): self.completion_text += text def _key_backspace(self, cursor_position): """Action for Backspace key""" if self.has_selected_text(): self.check_selection() self.remove_selected_text() elif self.current_prompt_pos == cursor_position: # Avoid deleting prompt return elif self.is_cursor_on_last_line(): self.stdkey_backspace() if self.is_completion_widget_visible(): # Removing only last character because if there was a selection # the completion widget would have been canceled self.completion_text = self.completion_text[:-1] def _key_tab(self): """Action for TAB key""" if self.is_cursor_on_last_line(): empty_line = not self.get_current_line_to_cursor().strip() if empty_line: self.stdkey_tab() else: self.show_code_completion(automatic=False) def _key_ctrl_space(self): """Action for Ctrl+Space""" if not self.is_completion_widget_visible(): self.show_code_completion(automatic=False) def _key_pageup(self): """Action for PageUp key""" pass def _key_pagedown(self): """Action for PageDown key""" pass def _key_escape(self): """Action for ESCAPE key""" if self.is_completion_widget_visible(): self.hide_completion_widget() def _key_question(self, text): """Action for '?'""" if self.get_current_line_to_cursor(): last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_docstring(last_obj) self.insert_text(text) # In case calltip and completion are shown at the same time: if self.is_completion_widget_visible(): self.completion_text += '?' def _key_parenleft(self, text): """Action for '('""" self.hide_completion_widget() if self.get_current_line_to_cursor(): last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_docstring(last_obj, call=True) self.insert_text(text) def _key_period(self, text): """Action for '.'""" self.insert_text(text) if self.codecompletion_auto: # Enable auto-completion only if last token isn't a float last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_code_completion(automatic=True) #------ Paste def paste(self): """Reimplemented slot to handle multiline paste action""" text = unicode(QApplication.clipboard().text()) if len(text.splitlines()) > 1: # Multiline paste if self.new_input_line: self.on_new_line() self.remove_selected_text() # Remove selection, eventually end = self.get_current_line_from_cursor() lines = self.get_current_line_to_cursor() + text + end self.clear_line() self.execute_lines(lines) self.move_cursor(-len(end)) else: # Standard paste ShellBaseWidget.paste(self) #------ Code Completion / Calltips # Methods implemented in child class: # (e.g. InternalShell) def get_dir(self, objtxt): """Return dir(object)""" raise NotImplementedError def get_module_completion(self, objtxt): """Return module completion list associated to object name""" pass def get_globals_keys(self): """Return shell globals() keys""" raise NotImplementedError def get_cdlistdir(self): """Return shell current directory list dir""" raise NotImplementedError def iscallable(self, objtxt): """Is object callable?""" raise NotImplementedError def get_arglist(self, objtxt): """Get func/method argument list""" raise NotImplementedError def get__doc__(self, objtxt): """Get object __doc__""" raise NotImplementedError def get_doc(self, objtxt): """Get object documentation""" raise NotImplementedError def get_source(self, objtxt): """Get object source""" raise NotImplementedError def is_defined(self, objtxt, force_import=False): """Return True if object is defined""" raise NotImplementedError def show_code_completion(self, automatic): """Display a completion list based on the current line""" # Note: unicode conversion is needed only for ExternalShellBase text = unicode(self.get_current_line_to_cursor()) last_obj = self.get_last_obj() if text.startswith('import '): obj_list = self.get_module_completion(text) words = text.split(' ') if ',' in words[-1]: words = words[-1].split(',') self.show_completion_list(obj_list, completion_text=words[-1], automatic=automatic) return elif text.startswith('from '): obj_list = self.get_module_completion(text) if obj_list is None: return words = text.split(' ') if '(' in words[-1]: words = words[:-2] + words[-1].split('(') if ',' in words[-1]: words = words[:-2] + words[-1].split(',') self.show_completion_list(obj_list, completion_text=words[-1], automatic=automatic) return obj_dir = self.get_dir(last_obj) if last_obj and obj_dir and text.endswith('.'): self.show_completion_list(obj_dir, automatic=automatic) return # Builtins and globals import __builtin__, keyword if not text.endswith('.') and last_obj \ and re.match(r'[a-zA-Z_0-9]*$', last_obj): b_k_g = dir(__builtin__)+self.get_globals_keys()+keyword.kwlist for objname in b_k_g: if objname.startswith(last_obj) and objname != last_obj: self.show_completion_list(b_k_g, completion_text=last_obj, automatic=automatic) return else: return # Looking for an incomplete completion if last_obj is None: last_obj = text dot_pos = last_obj.rfind('.') if dot_pos != -1: if dot_pos == len(last_obj)-1: completion_text = "" else: completion_text = last_obj[dot_pos+1:] last_obj = last_obj[:dot_pos] completions = self.get_dir(last_obj) if completions is not None: self.show_completion_list(completions, completion_text=completion_text, automatic=automatic) return # Looking for ' or ": filename completion q_pos = max([text.rfind("'"), text.rfind('"')]) if q_pos != -1: completions = self.get_cdlistdir() if completions: self.show_completion_list(completions, completion_text=text[q_pos+1:], automatic=automatic) return #------ Drag'n Drop def drop_pathlist(self, pathlist): """Drop path list""" if pathlist: files = ["r'%s'" % path for path in pathlist] if len(files) == 1: text = files[0] else: text = "[" + ", ".join(files) + "]" if self.new_input_line: self.on_new_line() self.insert_text(text) self.setFocus()
class Tree(QTreeView): """File system tree """ _fileActivated = pyqtSignal() def __init__(self, fileBrowser): QTreeView.__init__(self, fileBrowser) self._fileBrowser = fileBrowser self.setAttribute( Qt.WA_MacShowFocusRect, False ) self.setAttribute( Qt.WA_MacSmallSize ) self.setContextMenuPolicy( Qt.ActionsContextMenu ) self.setHeaderHidden( True ) self.setUniformRowHeights( True ) self.setTextElideMode(Qt.ElideMiddle) # dir model self._dirsModel = _FileSystemModel( self ) self._dirsModel.setNameFilterDisables( False ) self._dirsModel.setFilter( QDir.AllDirs | QDir.AllEntries | QDir.CaseSensitive | QDir.NoDotAndDotDot ) # self._dirsModel.directoryLoaded.connect(self.setFocus) TODO don't have this signal in my Qt version # create proxy model self._filteredModel = FileBrowserFilteredModel( self ) self._filteredModel.setSourceModel( self._dirsModel ) self.setModel( self._filteredModel) if not sys.platform.startswith('win'): self._dirsModel.setRootPath( "/" ) else: self._dirsModel.setRootPath('') # shortcut accessible only when self._tree has focus self._upShortcut = QShortcut( QKeySequence( "BackSpace" ), self ) self._upShortcut.setContext( Qt.WidgetShortcut ) self._upShortcut.activated.connect(self.moveUp) # shortcut accessible only when self._tree has focus self._homeShortcut = QShortcut( QKeySequence( "`" ), self ) self._homeShortcut.setContext( Qt.WidgetShortcut ) self._homeShortcut.activated.connect(self._goUserHomeDir) # shortcut accessible only when self._tree has focus self._homeShortcut = QShortcut( QKeySequence( "." ), self ) self._homeShortcut.setContext( Qt.WidgetShortcut ) self._homeShortcut.activated.connect(self._goCurrentDir) self.activated.connect(self._onActivated) self._fileActivated.connect(fileBrowser.fileActivated) # QDirModel loads item asynchronously, therefore we need timer for setting focus to the first item self._setFocusTimer = QTimer() self._setFocusTimer.timeout.connect(self._setFirstItemAsCurrent) self._setFocusTimer.setInterval(50) self.destroyed.connect(self._setFocusTimer.stop) self._timerAttempts = 0 def _onActivated(self, idx ): """File or directory doubleClicked """ index = self._filteredModel.mapToSource( idx ) path = self._dirsModel.filePath( index ) if os.path.isdir( path ) : self._fileBrowser.setCurrentPath(path) else: self._fileActivated.emit() core.workspace().openFile(path) def moveUp(self): """User pressed Up key or button. Move focus and root up """ current = self.currentIndex() if not current.isValid(): current = self.rootIndex().child(0, 0) self.setCurrentIndex(current) if current.parent() == self.rootIndex() or \ current == self.rootIndex(): # need to move root up if self.rootIndex().parent().isValid(): # not reached root of the FS tree newRoot = self.rootIndex().parent() parentPath = self._filteredModelIndexToPath(current.parent()) self._fileBrowser.setCurrentPath(self._filteredModelIndexToPath(newRoot)) self.collapseAll() # if moving root up - collapse all items parentIndex = self._filteredModel.mapFromSource(self._dirsModel.index(parentPath)) self._setCurrentItem(parentIndex) else: # need to move selection up parentOfCurrent = self.currentIndex().parent() self._setCurrentItem(parentOfCurrent) # move selection up def _goUserHomeDir(self): """Go to home directory """ self._fileBrowser.setCurrentPath(os.path.expanduser("~")) self.collapseAll() def _goCurrentDir(self): """Go to current directory """ self._fileBrowser.setCurrentPath(_getCurDir()) self.collapseAll() def _filteredModelIndexToPath(self, index): """Map index to file path """ srcIndex = self._filteredModel.mapToSource( index ) return self._dirsModel.filePath( srcIndex ) def currentPath(self): """Get current path (root of the tree) """ index = self.rootIndex() index = self._filteredModel.mapToSource( index ) return self._dirsModel.filePath( index ) def _isDescendant(self, child, parent): """Check if child is descendant of parent """ while child.isValid(): if child.parent() == parent: return True child = child.parent() return False def _setFirstItemAsCurrent(self): """QDirModel loads items asynchronously. Therefore we select current item by timer """ if not self.currentIndex().isValid() or \ not self._isDescendant(self.currentIndex(), self.rootIndex()): firstChild = self.rootIndex().child(0, 0) if firstChild.isValid(): self._setFocusTimer.stop() self._setCurrentItem(self.rootIndex().child(0, 0)) else: self._timerAttempts -= 1 if not self._timerAttempts: self._setFocusTimer.stop() else: # nothing to do, have focus self._setFocusTimer.stop() def setCurrentPath(self, path): """Set current path (root of the tree) """ # get index index = self._dirsModel.index(path) # set current path self._filteredModel.invalidate() newRoot = self._filteredModel.mapFromSource( index ) self.setRootIndex(newRoot) self._timerAttempts = 10 self._setFocusTimer.start() def _setCurrentItem(self, index): """Make the item current and select it """ self.setCurrentIndex(index) self.selectionModel().select(index, QItemSelectionModel.SelectCurrent)
class DockWidget(QDockWidget): """Extended QDockWidget for Enki main window """ closed = pyqtSignal() """ closed() **Signal** emitted, when dock is closed """ shown = pyqtSignal() """ shown() **Signal** emitted, when dock is shown """ def __init__(self, parentObject, windowTitle, windowIcon = QIcon(), shortcut = None): QDockWidget.__init__(self, parentObject) self._showAction = None self.setObjectName(str(self.__class__)) self.setWindowTitle(windowTitle) if not windowIcon.isNull(): self.setWindowIcon(windowIcon) if shortcut is not None: self.showAction().setShortcut(shortcut) self._titleBar = _TitleBar( self ) self.setTitleBarWidget( self._titleBar ) if shortcut is not None: toolTip = "Move focus with <b>%s</b>,<br/>close with <b>Esc</b>" % shortcut else: toolTip = "Close with <b>Esc</b>" self._titleBar.setToolTip(toolTip) self._closeShortcut = QShortcut( QKeySequence( "Esc" ), self ) self._closeShortcut.setContext( Qt.WidgetWithChildrenShortcut ) self._closeShortcut.activated.connect(self._close) def keyPressEvent(self, event): """Catch Esc. Not using QShortcut, because dock shall be closed, only if child widgets haven't catched Esc event """ if event.key() == Qt.Key_Escape and \ event.modifiers() == Qt.NoModifier: self._hide() else: QDockWidget.keyPressEvent(self, event) def showAction(self): """Action shows the widget and set focus on it. Add this action to the main menu """ if not self._showAction : self._showAction = QAction(self.windowIcon(), self.windowTitle(), self) self._showAction.triggered.connect(self.show) self._showAction.triggered.connect(self._handleFocusProxy) return self._showAction def titleBarWidget(self): """QToolBar on the title. You may add own actions to this tool bar """ # method was added only for documenting return QDockWidget.titleBarWidget(self) def _handleFocusProxy(self): """Set focus to focus proxy. Called after widget has been shown """ if self.focusProxy() is not None: self.setFocus() def _close(self): """Hide and return focus to MainWindow focus proxy """ self.close() if self.parent() is not None and \ self.parent().focusProxy() is not None: self.parent().focusProxy().setFocus() def closeEvent(self, event): """Widget was closed""" self.closed.emit() def showEvent(self, event): """Widget was shown""" self.shown.emit()
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info") ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Settings"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class ScintillaPythonEditBox(QsciScintilla, BasePythonEditBox): def __init__(self, parent=None): QsciScintilla.__init__(self, parent) self.lexer = None self.api = None self.lexerType = -1 self.setCommonOptions() self.initShortcuts() def setCommonOptions(self): # Enable non-ASCII characters self.setUtf8(True) # Default font # Load font from Python console settings settings = QSettings() fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace') fontSize = int(settings.value('pythonConsole/fontsize', 10)) self.defaultFont = QFont(fontName) self.defaultFont.setFixedPitch(True) self.defaultFont.setPointSize(fontSize) self.defaultFont.setStyleHint(QFont.TypeWriter) self.defaultFont.setStretch(QFont.SemiCondensed) self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0) self.defaultFont.setBold(False) self.boldFont = QFont(self.defaultFont) self.boldFont.setBold(True) self.italicFont = QFont(self.defaultFont) self.italicFont.setItalic(True) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) self.initLexer() self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setWrapVisualFlags(QsciScintilla.WrapFlagByText, QsciScintilla.WrapFlagNone, 4) self.setSelectionForegroundColor(QColor('#2e3436')) self.setSelectionBackgroundColor(QColor('#babdb6')) # Show line numbers self.setMarginWidth(1, '000') self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor('#2e3436')) self.setMarginsBackgroundColor(QColor('#babdb6')) # Highlight current line self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor('#d3d7cf')) # Mark column 80 with vertical line self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QColor('#eeeeec')) # Indentation self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setTabIndents(True) self.setBackspaceUnindents(True) self.setTabWidth(4) # Autocomletion self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAll) def initShortcuts(self): (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16) # Disable some shortcuts self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl) # Use Ctrl+Space for autocompletion self.shortcutAutocomplete = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.shortcutAutocomplete.setContext(Qt.WidgetShortcut) self.shortcutAutocomplete.activated.connect(self.autoComplete) def autoComplete(self): self.autoCompleteFromAll() def setLexerType(self, lexerType): self.lexerType = lexerType self.initLexer() def initLexer(self): self.lexer = QsciLexerPython() colorDefault = QColor('#2e3436') colorComment = QColor('#c00') colorCommentBlock = QColor('#3465a4') colorNumber = QColor('#4e9a06') colorType = QColor('#4e9a06') colorKeyword = QColor('#204a87') colorString = QColor('#ce5c00') self.lexer.setDefaultFont(self.defaultFont) self.lexer.setDefaultColor(colorDefault) self.lexer.setColor(colorComment, 1) self.lexer.setColor(colorNumber, 2) self.lexer.setColor(colorString, 3) self.lexer.setColor(colorString, 4) self.lexer.setColor(colorKeyword, 5) self.lexer.setColor(colorString, 6) self.lexer.setColor(colorString, 7) self.lexer.setColor(colorType, 8) self.lexer.setColor(colorCommentBlock, 12) self.lexer.setColor(colorString, 15) self.lexer.setFont(self.italicFont, 1) self.lexer.setFont(self.boldFont, 5) self.lexer.setFont(self.boldFont, 8) self.lexer.setFont(self.italicFont, 12) self.api = QsciAPIs(self.lexer) settings = QSettings() useDefaultAPI = bool(settings.value('pythonConsole/preloadAPI', True)) if useDefaultAPI: # Load QGIS API shipped with Python console self.api.loadPrepared( os.path.join(QgsApplication.pkgDataPath(), 'python', 'qsci_apis', 'pyqgis.pap')) else: # Load user-defined API files apiPaths = settings.value('pythonConsole/userAPI', []) for path in apiPaths: self.api.load(path) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) #impliment base editor def insertPlainText(self, text): self.insert(text) pos = self.getCursorPosition() self.setCursorPosition(pos[0], pos[1] + len(text)) def toPlainText(self): return self.text() def get_font_size(self): return self.font().pointSize() def set_font_size(self, new_point_size): font = self.font() self.setFont(QFont(font.family(), new_point_size)) def wheelEvent(self, event): if event.modifiers() == Qt.ControlModifier: self.emit(SIGNAL("wheelEvent(QWheelEvent)"), event) else: super(ScintillaPythonEditBox, self).wheelEvent(event)
class ScriptEdit(QsciScintilla): LEXER_PYTHON = 0 LEXER_R = 1 def __init__(self, parent=None): QsciScintilla.__init__(self, parent) self.lexer = None self.api = None self.lexerType = -1 self.setCommonOptions() self.initShortcuts() def setCommonOptions(self): # Enable non-ASCII characters self.setUtf8(True) # Default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(20) self.setFont(font) self.setMarginsFont(font) self.initLexer() self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setWrapMode(QsciScintilla.WrapWord) self.setWrapVisualFlags(QsciScintilla.WrapFlagByText, QsciScintilla.WrapFlagNone, 4) self.setSelectionForegroundColor(QColor('#2e3436')) self.setSelectionBackgroundColor(QColor('#babdb6')) # Show line numbers self.setMarginWidth(1, '000') self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor('#2e3436')) self.setMarginsBackgroundColor(QColor('#babdb6')) # Highlight current line self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor('#d3d7cf')) # Folding self.setFolding(QsciScintilla.BoxedTreeFoldStyle) self.setFoldMarginColors(QColor('#d3d7cf'), QColor('#d3d7cf')) # Mark column 80 with vertical line self.setEdgeMode(QsciScintilla.EdgeLine) self.setEdgeColumn(80) self.setEdgeColor(QColor('#eeeeec')) # Indentation self.setAutoIndent(True) self.setIndentationsUseTabs(False) self.setIndentationWidth(4) self.setTabIndents(True) self.setBackspaceUnindents(True) self.setTabWidth(4) # Autocomletion self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.setFonts(10) def setFonts(self, size): # Load font from Python console settings settings = QSettings() fontName = settings.value('pythonConsole/fontfamilytext', 'Monospace') fontSize = int(settings.value('pythonConsole/fontsize', size)) self.defaultFont = QFont(fontName) self.defaultFont.setFixedPitch(True) self.defaultFont.setPointSize(fontSize) self.defaultFont.setStyleHint(QFont.TypeWriter) self.defaultFont.setStretch(QFont.SemiCondensed) self.defaultFont.setLetterSpacing(QFont.PercentageSpacing, 87.0) self.defaultFont.setBold(False) self.boldFont = QFont(self.defaultFont) self.boldFont.setBold(True) self.italicFont = QFont(self.defaultFont) self.italicFont.setItalic(True) self.setFont(self.defaultFont) self.setMarginsFont(self.defaultFont) def initShortcuts(self): (ctrl, shift) = (self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16) # Disable some shortcuts self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Z") + ctrl) #self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord("Y") + ctrl) # Use Ctrl+Space for autocompletion self.shortcutAutocomplete = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Space), self) self.shortcutAutocomplete.setContext(Qt.WidgetShortcut) self.shortcutAutocomplete.activated.connect(self.autoComplete) def autoComplete(self): self.autoCompleteFromAll() def setLexerType(self, lexerType): self.lexerType = lexerType self.initLexer() def initLexer(self): if self.lexerType == self.LEXER_PYTHON: self.lexer = QsciLexerPython() colorDefault = QColor('#2e3436') colorComment = QColor('#c00') colorCommentBlock = QColor('#3465a4') colorNumber = QColor('#4e9a06') colorType = QColor('#4e9a06') colorKeyword = QColor('#204a87') colorString = QColor('#ce5c00') self.lexer.setDefaultFont(self.defaultFont) self.lexer.setDefaultColor(colorDefault) self.lexer.setColor(colorComment, 1) self.lexer.setColor(colorNumber, 2) self.lexer.setColor(colorString, 3) self.lexer.setColor(colorString, 4) self.lexer.setColor(colorKeyword, 5) self.lexer.setColor(colorString, 6) self.lexer.setColor(colorString, 7) self.lexer.setColor(colorType, 8) self.lexer.setColor(colorCommentBlock, 12) self.lexer.setColor(colorString, 15) self.lexer.setFont(self.italicFont, 1) self.lexer.setFont(self.boldFont, 5) self.lexer.setFont(self.boldFont, 8) self.lexer.setFont(self.italicFont, 12) self.api = QsciAPIs(self.lexer) settings = QSettings() useDefaultAPI = bool(settings.value('pythonConsole/preloadAPI', True)) if useDefaultAPI: # Load QGIS API shipped with Python console self.api.loadPrepared( os.path.join(QgsApplication.pkgDataPath(), 'python', 'qsci_apis', 'pyqgis.pap')) else: # Load user-defined API files apiPaths = settings.value('pythonConsole/userAPI', []) for path in apiPaths: self.api.load(path) self.api.prepare() self.lexer.setAPIs(self.api) elif self.lexerType == self.LEXER_R: # R lexer self.lexer = LexerR() self.setLexer(self.lexer)
class DockWidget(QDockWidget): """Extended QDockWidget for Enki main window """ closed = pyqtSignal() """ closed() **Signal** emitted, when dock is closed """ shown = pyqtSignal() """ shown() **Signal** emitted, when dock is shown """ def __init__(self, parentObject, windowTitle, windowIcon=QIcon(), shortcut=None): QDockWidget.__init__(self, parentObject) self._showAction = None self.setObjectName(str(self.__class__)) self.setWindowTitle(windowTitle) self.setFeatures(self.features() & (~QDockWidget.DockWidgetFloatable)) if not windowIcon.isNull(): self.setWindowIcon(windowIcon) if shortcut is not None: self.showAction().setShortcut(shortcut) self._titleBar = _TitleBar(self) self.setTitleBarWidget(self._titleBar) if shortcut is not None: toolTip = "Move focus with <b>%s</b>,<br/>close with <b>Esc</b>" % shortcut else: toolTip = "Close with <b>Esc</b>" self._titleBar.setToolTip(toolTip) self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self._close) def keyPressEvent(self, event): """Catch Esc. Not using QShortcut, because dock shall be closed, only if child widgets haven't catched Esc event """ if event.key() == Qt.Key_Escape and \ event.modifiers() == Qt.NoModifier: self._hide() else: QDockWidget.keyPressEvent(self, event) def showAction(self): """Action shows the widget and set focus on it. Add this action to the main menu """ if not self._showAction: self._showAction = QAction(self.windowIcon(), self.windowTitle(), self) self._showAction.triggered.connect(self.show) self._showAction.triggered.connect(self._handleFocusProxy) return self._showAction def titleBarWidget(self): """QToolBar on the title. You may add own actions to this tool bar """ # method was added only for documenting return QDockWidget.titleBarWidget(self) def _handleFocusProxy(self): """Set focus to focus proxy. Called after widget has been shown """ if self.focusProxy() is not None: self.setFocus() def _close(self): """Hide and return focus to MainWindow focus proxy """ self.close() if self.parent() is not None and \ self.parent().focusProxy() is not None: self.parent().focusProxy().setFocus() def closeEvent(self, event): """Widget was closed""" self.closed.emit() def showEvent(self, event): """Widget was shown""" self.shown.emit()
class PythonShellWidget(TracebackLinksMixin, ShellBaseWidget, InspectObjectMixin): """Python shell widget""" QT_CLASS = ShellBaseWidget INITHISTORY = [ '# -*- coding: utf-8 -*-', '# *** Spyder Python Console History Log ***', ] SEPARATOR = '%s##---(%s)---' % (os.linesep * 2, time.ctime()) def __init__(self, parent, history_filename, profile=False): ShellBaseWidget.__init__(self, parent, history_filename, profile) TracebackLinksMixin.__init__(self) InspectObjectMixin.__init__(self) # Local shortcuts self.inspectsc = QShortcut(QKeySequence("Ctrl+I"), self, self.inspect_current_object) self.inspectsc.setContext(Qt.WidgetWithChildrenShortcut) def get_shortcut_data(self): """ Returns shortcut data, a list of tuples (shortcut, text, default) shortcut (QShortcut or QAction instance) text (string): action/shortcut description default (string): default key sequence """ return [ (self.inspectsc, "Inspect current object", "Ctrl+I"), ] #------ Context menu def setup_context_menu(self): """Reimplements ShellBaseWidget method""" ShellBaseWidget.setup_context_menu(self) self.copy_without_prompts_action = create_action( self, _("Copy without prompts"), icon=get_icon('copywop.png'), triggered=self.copy_without_prompts) clear_line_action = create_action(self, _("Clear line"), QKeySequence("Shift+Escape"), icon=get_icon('eraser.png'), tip=_("Clear line"), triggered=self.clear_line) clear_action = create_action(self, _("Clear shell"), QKeySequence("Ctrl+L"), icon=get_icon('clear.png'), tip=_("Clear shell contents " "('cls' command)"), triggered=self.clear_terminal) add_actions(self.menu, (self.copy_without_prompts_action, clear_line_action, clear_action)) def contextMenuEvent(self, event): """Reimplements ShellBaseWidget method""" state = self.has_selected_text() self.copy_without_prompts_action.setEnabled(state) ShellBaseWidget.contextMenuEvent(self, event) def copy_without_prompts(self): """Copy text to clipboard without prompts""" text = self.get_selected_text() lines = text.split(os.linesep) for index, line in enumerate(lines): if line.startswith('>>> ') or line.startswith('... '): lines[index] = line[4:] text = os.linesep.join(lines) QApplication.clipboard().setText(text) #------ Key handlers def postprocess_keyevent(self, event): """Process keypress event""" ShellBaseWidget.postprocess_keyevent(self, event) if QToolTip.isVisible(): _event, _text, key, _ctrl, _shift = restore_keyevent(event) self.hide_tooltip_if_necessary(key) def _key_other(self, text): """1 character key""" if self.is_completion_widget_visible(): self.completion_text += text def _key_backspace(self, cursor_position): """Action for Backspace key""" if self.has_selected_text(): self.check_selection() self.remove_selected_text() elif self.current_prompt_pos == cursor_position: # Avoid deleting prompt return elif self.is_cursor_on_last_line(): self.stdkey_backspace() if self.is_completion_widget_visible(): # Removing only last character because if there was a selection # the completion widget would have been canceled self.completion_text = self.completion_text[:-1] def _key_tab(self): """Action for TAB key""" if self.is_cursor_on_last_line(): empty_line = not self.get_current_line_to_cursor().strip() if empty_line: self.stdkey_tab() else: self.show_code_completion(automatic=False) def _key_ctrl_space(self): """Action for Ctrl+Space""" if not self.is_completion_widget_visible(): self.show_code_completion(automatic=False) def _key_pageup(self): """Action for PageUp key""" pass def _key_pagedown(self): """Action for PageDown key""" pass def _key_escape(self): """Action for ESCAPE key""" if self.is_completion_widget_visible(): self.hide_completion_widget() def _key_question(self, text): """Action for '?'""" if self.get_current_line_to_cursor(): last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_docstring(last_obj) self.insert_text(text) # In case calltip and completion are shown at the same time: if self.is_completion_widget_visible(): self.completion_text += '?' def _key_parenleft(self, text): """Action for '('""" self.hide_completion_widget() if self.get_current_line_to_cursor(): last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_docstring(last_obj, call=True) self.insert_text(text) def _key_period(self, text): """Action for '.'""" self.insert_text(text) if self.codecompletion_auto: # Enable auto-completion only if last token isn't a float last_obj = self.get_last_obj() if last_obj and not last_obj.isdigit(): self.show_code_completion(automatic=True) #------ Paste def paste(self): """Reimplemented slot to handle multiline paste action""" text = unicode(QApplication.clipboard().text()) if len(text.splitlines()) > 1: # Multiline paste if self.new_input_line: self.on_new_line() self.remove_selected_text() # Remove selection, eventually end = self.get_current_line_from_cursor() lines = self.get_current_line_to_cursor() + text + end self.clear_line() self.execute_lines(lines) self.move_cursor(-len(end)) else: # Standard paste ShellBaseWidget.paste(self) #------ Code Completion / Calltips # Methods implemented in child class: # (e.g. InternalShell) def get_dir(self, objtxt): """Return dir(object)""" raise NotImplementedError def get_module_completion(self, objtxt): """Return module completion list associated to object name""" pass def get_globals_keys(self): """Return shell globals() keys""" raise NotImplementedError def get_cdlistdir(self): """Return shell current directory list dir""" raise NotImplementedError def iscallable(self, objtxt): """Is object callable?""" raise NotImplementedError def get_arglist(self, objtxt): """Get func/method argument list""" raise NotImplementedError def get__doc__(self, objtxt): """Get object __doc__""" raise NotImplementedError def get_doc(self, objtxt): """Get object documentation""" raise NotImplementedError def get_source(self, objtxt): """Get object source""" raise NotImplementedError def is_defined(self, objtxt, force_import=False): """Return True if object is defined""" raise NotImplementedError def show_code_completion(self, automatic): """Display a completion list based on the current line""" # Note: unicode conversion is needed only for ExternalShellBase text = unicode(self.get_current_line_to_cursor()) last_obj = self.get_last_obj() if text.startswith('import '): obj_list = self.get_module_completion(text) words = text.split(' ') if ',' in words[-1]: words = words[-1].split(',') self.show_completion_list(obj_list, completion_text=words[-1], automatic=automatic) return elif text.startswith('from '): obj_list = self.get_module_completion(text) if obj_list is None: return words = text.split(' ') if '(' in words[-1]: words = words[:-2] + words[-1].split('(') if ',' in words[-1]: words = words[:-2] + words[-1].split(',') self.show_completion_list(obj_list, completion_text=words[-1], automatic=automatic) return obj_dir = self.get_dir(last_obj) if last_obj and obj_dir and text.endswith('.'): self.show_completion_list(obj_dir, automatic=automatic) return # Builtins and globals import __builtin__, keyword if not text.endswith('.') and last_obj \ and re.match(r'[a-zA-Z_0-9]*$', last_obj): b_k_g = dir(__builtin__) + self.get_globals_keys() + keyword.kwlist for objname in b_k_g: if objname.startswith(last_obj) and objname != last_obj: self.show_completion_list(b_k_g, completion_text=last_obj, automatic=automatic) return else: return # Looking for an incomplete completion if last_obj is None: last_obj = text dot_pos = last_obj.rfind('.') if dot_pos != -1: if dot_pos == len(last_obj) - 1: completion_text = "" else: completion_text = last_obj[dot_pos + 1:] last_obj = last_obj[:dot_pos] completions = self.get_dir(last_obj) if completions is not None: self.show_completion_list(completions, completion_text=completion_text, automatic=automatic) return # Looking for ' or ": filename completion q_pos = max([text.rfind("'"), text.rfind('"')]) if q_pos != -1: completions = self.get_cdlistdir() if completions: self.show_completion_list(completions, completion_text=text[q_pos + 1:], automatic=automatic) return #------ Drag'n Drop def drop_pathlist(self, pathlist): """Drop path list""" if pathlist: files = ["r'%s'" % path for path in pathlist] if len(files) == 1: text = files[0] else: text = "[" + ", ".join(files) + "]" if self.new_input_line: self.on_new_line() self.insert_text(text) self.setFocus()
def spaceBarPressed(): global myIt, activeLayerId aLayer = iface.activeLayer() if not aLayer or not aLayer.type() == 0: print "Please first select a vector layer in the ToC." return if activeLayerId != aLayer.id(): activeLayerId = aLayer.id() myIt = None if not myIt: myIt = aLayer.getFeatures() feat = next(myIt, None) if feat: aLayer.removeSelection() aLayer.select(feat.id()) iface.actionZoomToSelected().trigger() print "Selected feature:", str(feat.id()) else: print "We reached the last feature of this layer already.\n" + \ "If you want to restart press the Escape key." shortcutEscape = QShortcut(QKeySequence(Qt.Key_Escape), iface.mapCanvas()) shortcutEscape.setContext(Qt.ApplicationShortcut) shortcutEscape.activated.connect(escapePressed) shortcutSpaceBar = QShortcut(QKeySequence(Qt.Key_Space), iface.mapCanvas()) shortcutSpaceBar.setContext(Qt.ApplicationShortcut) shortcutSpaceBar.activated.connect(spaceBarPressed)
class FindReplace(QWidget): """ Find widget Signals: visibility_changed(bool) """ STYLE = {False: "background-color:rgb(255, 175, 90);", True: ""} def __init__(self, parent, enable_replace=False): QWidget.__init__(self, parent) self.enable_replace = enable_replace self.editor = None self.is_code_editor = None glayout = QGridLayout() glayout.setContentsMargins(0, 0, 0, 0) self.setLayout(glayout) self.close_button = create_toolbutton(self, triggered=self.hide, icon=get_std_icon("DialogCloseButton")) glayout.addWidget(self.close_button, 0, 0) # Find layout self.search_text = PatternComboBox(self, tip=_("Search string"), adjust_to_minimum=False) self.connect(self.search_text, SIGNAL('valid(bool)'), lambda state: self.find(changed=False, forward=True, rehighlight=False)) self.connect(self.search_text.lineEdit(), SIGNAL("textEdited(QString)"), self.text_has_been_edited) self.previous_button = create_toolbutton(self, triggered=self.find_previous, icon=get_std_icon("ArrowBack")) self.next_button = create_toolbutton(self, triggered=self.find_next, icon=get_std_icon("ArrowForward")) self.connect(self.next_button, SIGNAL('clicked()'), self.update_search_combo) self.connect(self.previous_button, SIGNAL('clicked()'), self.update_search_combo) self.re_button = create_toolbutton(self, icon=get_icon("advanced.png"), tip=_("Regular expression")) self.re_button.setCheckable(True) self.connect(self.re_button, SIGNAL("toggled(bool)"), lambda state: self.find()) self.case_button = create_toolbutton(self, icon=get_icon("upper_lower.png"), tip=_("Case Sensitive")) self.case_button.setCheckable(True) self.connect(self.case_button, SIGNAL("toggled(bool)"), lambda state: self.find()) self.words_button = create_toolbutton(self, icon=get_icon("whole_words.png"), tip=_("Whole words")) self.words_button.setCheckable(True) self.connect(self.words_button, SIGNAL("toggled(bool)"), lambda state: self.find()) self.highlight_button = create_toolbutton(self, icon=get_icon("highlight.png"), tip=_("Highlight matches")) self.highlight_button.setCheckable(True) self.connect(self.highlight_button, SIGNAL("toggled(bool)"), self.toggle_highlighting) hlayout = QHBoxLayout() self.widgets = [self.close_button, self.search_text, self.previous_button, self.next_button, self.re_button, self.case_button, self.words_button, self.highlight_button] for widget in self.widgets[1:]: hlayout.addWidget(widget) glayout.addLayout(hlayout, 0, 1) # Replace layout replace_with = QLabel(_("Replace with:")) self.replace_text = PatternComboBox(self, adjust_to_minimum=False, tip=_("Replace string")) self.replace_button = create_toolbutton(self, text=_("Replace/find"), icon=get_std_icon("DialogApplyButton"), triggered=self.replace_find, text_beside_icon=True) self.connect(self.replace_button, SIGNAL('clicked()'), self.update_replace_combo) self.connect(self.replace_button, SIGNAL('clicked()'), self.update_search_combo) self.all_check = QCheckBox(_("Replace all")) self.replace_layout = QHBoxLayout() widgets = [replace_with, self.replace_text, self.replace_button, self.all_check] for widget in widgets: self.replace_layout.addWidget(widget) glayout.addLayout(self.replace_layout, 1, 1) self.widgets.extend(widgets) self.replace_widgets = widgets self.hide_replace() self.search_text.setTabOrder(self.search_text, self.replace_text) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.findnext_sc = QShortcut(QKeySequence("F3"), parent, self.find_next) self.findnext_sc.setContext(Qt.WidgetWithChildrenShortcut) self.findprev_sc = QShortcut(QKeySequence("Shift+F3"), parent, self.find_previous) self.findprev_sc.setContext(Qt.WidgetWithChildrenShortcut) self.togglefind_sc = QShortcut(QKeySequence("Ctrl+F"), parent, self.show) self.togglefind_sc.setContext(Qt.WidgetWithChildrenShortcut) self.togglereplace_sc = QShortcut(QKeySequence("Ctrl+H"), parent, self.toggle_replace_widgets) self.togglereplace_sc.setContext(Qt.WidgetWithChildrenShortcut) escape_sc = QShortcut(QKeySequence("Escape"), parent, self.hide) escape_sc.setContext(Qt.WidgetWithChildrenShortcut) self.highlight_timer = QTimer(self) self.highlight_timer.setSingleShot(True) self.highlight_timer.setInterval(1000) self.connect(self.highlight_timer, SIGNAL("timeout()"), self.highlight_matches) def get_shortcut_data(self): """ Returns shortcut data, a list of tuples (shortcut, text, default) shortcut (QShortcut or QAction instance) text (string): action/shortcut description default (string): default key sequence """ return [(self.findnext_sc, "Find next", "F3"), (self.findprev_sc, "Find previous", "Shift+F3"), (self.togglefind_sc, "Find text", "Ctrl+F"), (self.togglereplace_sc, "Replace text", "Ctrl+H"),] def update_search_combo(self): self.search_text.lineEdit().emit(SIGNAL('returnPressed()')) def update_replace_combo(self): self.replace_text.lineEdit().emit(SIGNAL('returnPressed()')) def toggle_replace_widgets(self): if self.enable_replace: # Toggle replace widgets if self.replace_widgets[0].isVisible(): self.hide_replace() self.hide() else: self.show_replace() self.replace_text.setFocus() def toggle_highlighting(self, state): """Toggle the 'highlight all results' feature""" if self.editor is not None: if state: self.highlight_matches() else: self.clear_matches() def show(self): """Overrides Qt Method""" QWidget.show(self) self.emit(SIGNAL("visibility_changed(bool)"), True) if self.editor is not None: text = self.editor.get_selected_text() if len(text) > 0: self.search_text.setEditText(text) self.search_text.lineEdit().selectAll() self.refresh() else: self.search_text.lineEdit().selectAll() self.search_text.setFocus() def hide(self): """Overrides Qt Method""" for widget in self.replace_widgets: widget.hide() QWidget.hide(self) self.emit(SIGNAL("visibility_changed(bool)"), False) if self.editor is not None: self.editor.setFocus() self.clear_matches() def show_replace(self): """Show replace widgets""" self.show() for widget in self.replace_widgets: widget.show() def hide_replace(self): """Hide replace widgets""" for widget in self.replace_widgets: widget.hide() def refresh(self): """Refresh widget""" if self.isHidden(): if self.editor is not None: self.clear_matches() return state = self.editor is not None for widget in self.widgets: widget.setEnabled(state) if state: self.find() def set_editor(self, editor, refresh=True): """ Set associated editor/web page: codeeditor.base.TextEditBaseWidget browser.WebView """ self.editor = editor from PyQt4.QtWebKit import QWebView self.words_button.setVisible(not isinstance(editor, QWebView)) self.re_button.setVisible(not isinstance(editor, QWebView)) from SMlib.widgets.sourcecode.codeeditor import CodeEditor self.is_code_editor = isinstance(editor, CodeEditor) self.highlight_button.setVisible(self.is_code_editor) if refresh: self.refresh() if self.isHidden() and editor is not None: self.clear_matches() def find_next(self): """Find next occurence""" state = self.find(changed=False, forward=True, rehighlight=False) self.editor.setFocus() self.search_text.add_current_text() return state def find_previous(self): """Find previous occurence""" state = self.find(changed=False, forward=False, rehighlight=False) self.editor.setFocus() return state def text_has_been_edited(self, text): """Find text has been edited (this slot won't be triggered when setting the search pattern combo box text programmatically""" self.find(changed=True, forward=True, start_highlight_timer=True) def highlight_matches(self): """Highlight found results""" if self.is_code_editor and self.highlight_button.isChecked(): text = self.search_text.currentText() words = self.words_button.isChecked() regexp = self.re_button.isChecked() self.editor.highlight_found_results(text, words=words, regexp=regexp) def clear_matches(self): """Clear all highlighted matches""" if self.is_code_editor: self.editor.clear_found_results() def find(self, changed=True, forward=True, rehighlight=True, start_highlight_timer=False): """Call the find function""" text = self.search_text.currentText() if len(text) == 0: self.search_text.lineEdit().setStyleSheet("") return None else: case = self.case_button.isChecked() words = self.words_button.isChecked() regexp = self.re_button.isChecked() found = self.editor.find_text(text, changed, forward, case=case, words=words, regexp=regexp) self.search_text.lineEdit().setStyleSheet(self.STYLE[found]) if self.is_code_editor and found: if rehighlight or not self.editor.found_results: self.highlight_timer.stop() if start_highlight_timer: self.highlight_timer.start() else: self.highlight_matches() else: self.clear_matches() return found def replace_find(self): """Replace and find""" if (self.editor is not None): replace_text = unicode(self.replace_text.currentText()) search_text = unicode(self.search_text.currentText()) pattern = search_text if self.re_button.isChecked() else None case = self.case_button.isChecked() first = True cursor = None while True: if first: # First found seltxt = unicode(self.editor.get_selected_text()) cmptxt1 = search_text if case else search_text.lower() cmptxt2 = seltxt if case else seltxt.lower() if self.editor.has_selected_text() and cmptxt1 == cmptxt2: # Text was already found, do nothing pass else: if not self.find(changed=False, forward=True, rehighlight=False): break first = False wrapped = False position = self.editor.get_position('cursor') position0 = position cursor = self.editor.textCursor() cursor.beginEditBlock() else: position1 = self.editor.get_position('cursor') if wrapped: if position1 == position or \ is_position_sup(position1, position): # Avoid infinite loop: replace string includes # part of the search string break if position1 == position0: # Avoid infinite loop: single found occurence break if is_position_inf(position1, position0): wrapped = True position0 = position1 if pattern is None: cursor.removeSelectedText() cursor.insertText(replace_text) else: seltxt = unicode(cursor.selectedText()) cursor.removeSelectedText() cursor.insertText(re.sub(pattern, replace_text, seltxt)) if self.find_next(): found_cursor = self.editor.textCursor() cursor.setPosition(found_cursor.selectionStart(), QTextCursor.MoveAnchor) cursor.setPosition(found_cursor.selectionEnd(), QTextCursor.KeepAnchor) else: break if not self.all_check.isChecked(): break self.all_check.setCheckState(Qt.Unchecked) if cursor is not None: cursor.endEditBlock()
ui.browse_button.clicked.connect(container.browse) ui.vna_browse_button.clicked.connect(container.browse) ui.actionAbrir.triggered.connect(container.open_file) ui.actionGuardar.triggered.connect(container.save_file) ui.actionGuardar_como.triggered.connect(container.save_as_file) ui.actionSalir.triggered.connect(container.close) ui.actionCalibration.triggered.connect(container.launch_calibration) ui.actionCalibration_Presets.triggered.connect(container.launch_preset_calibration) ui.actionRI.triggered.connect(container.launch_ri) ui.actionContinuo_Alterno.triggered.connect(container.launch_conalt) ui.left_button.clicked.connect(container.move_left) left_shortcut = QShortcut(QKeySequence(QtCore.Qt.ControlModifier + QtCore.Qt.Key_Left), ui.centralwidget) left_shortcut.setContext(QtCore.Qt.ApplicationShortcut) left_shortcut.activated.connect(container.move_left) ui.right_button.clicked.connect(container.move_right) right_shortcut = QShortcut(QKeySequence(QtCore.Qt.ControlModifier + QtCore.Qt.Key_Right), ui.centralwidget) right_shortcut.setContext(QtCore.Qt.ApplicationShortcut) right_shortcut.activated.connect(container.move_right) ui.all_checkbox.stateChanged.connect(container.checked_all) app.aboutToQuit.connect(container.save_ui) restore_ui(ui) window.show() sys.exit(app.exec_())
class ShellScintilla(QsciScintilla, code.InteractiveInterpreter): def __init__(self, parent=None): super(ShellScintilla, self).__init__(parent) code.InteractiveInterpreter.__init__(self, locals=None) self.parent = parent self.opening = ['(', '{', '[', "'", '"'] self.closing = [')', '}', ']', "'", '"'] self.settings = QSettings() # Enable non-ascii chars for editor self.setUtf8(True) self.new_input_line = True self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) self.buffer = [] self.displayPrompt(False) for line in _init_commands: self.runsource(line) self.history = [] self.historyIndex = 0 # Read history command file self.readHistoryFile() self.historyDlg = HistoryDialog(self) # Brace matching: enable for a brace immediately before or after # the current position self.setBraceMatching(QsciScintilla.SloppyBraceMatch) self.setMatchedBraceBackgroundColor(QColor("#b7f907")) # Current line visible with special background color self.setCaretWidth(2) self.refreshSettingsShell() # Don't want to see the horizontal scrollbar at all # Use raw message to Scintilla here (all messages are documented # here: http://www.scintilla.org/ScintillaDoc.html) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) # not too small #self.setMinimumSize(500, 300) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_EMPTYUNDOBUFFER) ## Disable command key ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('T') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('D') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Z') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('Y') + ctrl) self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl + shift) ## New QShortcut = ctrl+space/ctrl+alt+space for Autocomplete self.newShortcutCSS = QShortcut( QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Space), self) self.newShortcutCAS = QShortcut( QKeySequence(Qt.CTRL + Qt.ALT + Qt.Key_Space), self) self.newShortcutCSS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.setContext(Qt.WidgetShortcut) self.newShortcutCAS.activated.connect(self.autoCompleteKeyBinding) self.newShortcutCSS.activated.connect(self.showHistory) def _setMinimumHeight(self): fnt = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fntSize = self.settings.value("pythonConsole/fontsize", 10, type=int) fm = QFontMetrics(QFont(fnt, fntSize)) self.setMinimumHeight(fm.height() + 10) def refreshSettingsShell(self): # Set Python lexer self.setLexers() threshold = self.settings.value("pythonConsole/autoCompThreshold", 2, type=int) self.setAutoCompletionThreshold(threshold) radioButtonSource = self.settings.value( "pythonConsole/autoCompleteSource", 'fromAPI') autoCompEnabled = self.settings.value( "pythonConsole/autoCompleteEnabled", True, type=bool) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.setAutoCompletionSource(self.AcsDocument) elif radioButtonSource == 'fromAPI': self.setAutoCompletionSource(self.AcsAPIs) elif radioButtonSource == 'fromDocAPI': self.setAutoCompletionSource(self.AcsAll) else: self.setAutoCompletionSource(self.AcsNone) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretForegroundColor(cursorColor) # Sets minimum height for input area based of font metric self._setMinimumHeight() def showHistory(self): if not self.historyDlg.isVisible(): self.historyDlg.show() self.historyDlg._reloadHistory() self.historyDlg.activateWindow() def autoCompleteKeyBinding(self): radioButtonSource = self.settings.value( "pythonConsole/autoCompleteSource", 'fromAPI') autoCompEnabled = self.settings.value( "pythonConsole/autoCompleteEnabled", True, type=bool) if autoCompEnabled: if radioButtonSource == 'fromDoc': self.autoCompleteFromDocument() elif radioButtonSource == 'fromAPI': self.autoCompleteFromAPIs() elif radioButtonSource == 'fromDocAPI': self.autoCompleteFromAll() def commandConsole(self, command): if not self.is_cursor_on_last_line(): self.move_cursor_to_end() line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() if command == "processing": # import Processing class self.append('import processing') elif command == "qtCore": # import QtCore class self.append('from PyQt4.QtCore import *') elif command == "qtGui": # import QtGui class self.append('from PyQt4.QtGui import *') self.entered() self.move_cursor_to_end() self.setFocus() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor( self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor( QColor( self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor( QColor( self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor( QColor( self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor( QColor( self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor( QColor( self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor( QColor( self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setFont(font, 1) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.api = QsciAPIs(self.lexer) chekBoxAPI = self.settings.value("pythonConsole/preloadAPI", True, type=bool) chekBoxPreparedAPI = self.settings.value( "pythonConsole/usePreparedAPIFile", False, type=bool) if chekBoxAPI: pap = os.path.join(QgsApplication.pkgDataPath(), "python", "qsci_apis", "pyqgis.pap") self.api.loadPrepared(pap) elif chekBoxPreparedAPI: self.api.loadPrepared( self.settings.value("pythonConsole/preparedAPIFile")) else: apiPath = self.settings.value("pythonConsole/userAPI", []) for i in range(0, len(apiPath)): self.api.load(unicode(apiPath[i])) self.api.prepare() self.lexer.setAPIs(self.api) self.setLexer(self.lexer) ## TODO: show completion list for file and directory def getText(self): """ Get the text as a unicode string. """ value = self.getBytes().decode('utf-8') # print (value) printing can give an error because the console font # may not have all unicode characters return value def getBytes(self): """ Get the text as bytes (utf-8 encoded). This is how the data is stored internally. """ len = self.SendScintilla(self.SCI_GETLENGTH) + 1 bb = QByteArray(len, '0') self.SendScintilla(self.SCI_GETTEXT, len, bb) return bytes(bb)[:-1] def getTextLength(self): return self.SendScintilla(QsciScintilla.SCI_GETLENGTH) def get_end_pos(self): """Return (line, index) position of the last character""" line = self.lines() - 1 return (line, len(self.text(line))) def is_cursor_at_end(self): """Return True if cursor is at the end of text""" cline, cindex = self.getCursorPosition() return (cline, cindex) == self.get_end_pos() def move_cursor_to_end(self): """Move cursor to end of text""" line, index = self.get_end_pos() self.setCursorPosition(line, index) self.ensureCursorVisible() self.ensureLineVisible(line) def is_cursor_on_last_line(self): """Return True if cursor is on the last line""" cline, _ = self.getCursorPosition() return cline == self.lines() - 1 def is_cursor_on_edition_zone(self): """ Return True if the cursor is in the edition zone """ cline, cindex = self.getCursorPosition() return cline == self.lines() - 1 and cindex >= 4 def new_prompt(self, prompt): """ Print a new prompt and save its (line, index) position """ self.write(prompt, prompt=True) # now we update our cursor giving end of prompt line, index = self.getCursorPosition() self.ensureCursorVisible() self.ensureLineVisible(line) def displayPrompt(self, more=False): self.append("... ") if more else self.append(">>> ") self.move_cursor_to_end() def updateHistory(self, command): if isinstance(command, list): for line in command: self.history.append(line) elif not command == "": if len(self.history) <= 0 or \ command != self.history[-1]: self.history.append(command) self.historyIndex = len(self.history) def writeHistoryFile(self, fromCloseConsole=False): ok = False try: wH = codecs.open(_historyFile, 'w', encoding='utf-8') for s in self.history: wH.write(s + '\n') ok = True except: raise wH.close() if ok and not fromCloseConsole: msgText = QCoreApplication.translate( 'PythonConsole', 'History saved successfully.') self.parent.callWidgetMessageBar(msgText) def readHistoryFile(self): fileExist = QFile.exists(_historyFile) if fileExist: rH = codecs.open(_historyFile, 'r', encoding='utf-8') for line in rH: if line != "\n": l = line.rstrip('\n') self.updateHistory(l) else: return def clearHistory(self, clearSession=False): if clearSession: self.history = [] msgText = QCoreApplication.translate( 'PythonConsole', 'Session and file history cleared successfully.') self.parent.callWidgetMessageBar(msgText) return ok = False try: cH = codecs.open(_historyFile, 'w', encoding='utf-8') ok = True except: raise cH.close() if ok: msgText = QCoreApplication.translate( 'PythonConsole', 'History cleared successfully.') self.parent.callWidgetMessageBar(msgText) def clearHistorySession(self): self.clearHistory(True) def showPrevious(self): if self.historyIndex < len(self.history) and self.history: line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex += 1 if self.historyIndex == len(self.history): self.insert("") pass else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def showNext(self): if self.historyIndex > 0 and self.history: line, pos = self.getCursorPosition() selCmdLenght = len(self.text(line)) self.setSelection(line, 4, line, selCmdLenght) self.removeSelectedText() self.historyIndex -= 1 if self.historyIndex == len(self.history): self.insert("") else: self.insert(self.history[self.historyIndex]) self.move_cursor_to_end() #self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def keyPressEvent(self, e): startLine, startPos, endLine, endPos = self.getSelection() # handle invalid cursor position and multiline selections if not self.is_cursor_on_edition_zone() or startLine < endLine: # allow copying and selecting if e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier): if e.key() in (Qt.Key_C, Qt.Key_A): QsciScintilla.keyPressEvent(self, e) return # allow selection if e.modifiers() & Qt.ShiftModifier: if e.key() in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Home, Qt.Key_End): QsciScintilla.keyPressEvent(self, e) return # all other keystrokes get sent to the input line self.move_cursor_to_end() line, index = self.getCursorPosition() cmd = self.text(line) if e.key() in (Qt.Key_Return, Qt.Key_Enter) and not self.isListActive(): self.entered() elif e.key() in (Qt.Key_Left, Qt.Key_Home): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone newline, newindex = self.getCursorPosition() if newline < line or newindex < 4: # fix selection and the cursor position if self.hasSelectedText(): self.setSelection(line, self.getSelection()[3], line, 4) else: self.setCursorPosition(line, 4) elif e.key() in (Qt.Key_Backspace, Qt.Key_Delete): QsciScintilla.keyPressEvent(self, e) # check whether the cursor is moved out of the edition zone _, newindex = self.getCursorPosition() if newindex < 4: # restore the prompt chars (if removed) and # fix the cursor position self.insert(cmd[:3 - newindex] + " ") self.setCursorPosition(line, 4) self.recolor() elif (e.modifiers() & (Qt.ControlModifier | Qt.MetaModifier) and e.key() == Qt.Key_V) or \ (e.modifiers() & Qt.ShiftModifier and e.key() == Qt.Key_Insert): self.paste() e.accept() elif e.key() == Qt.Key_Down and not self.isListActive(): self.showPrevious() elif e.key() == Qt.Key_Up and not self.isListActive(): self.showNext() ## TODO: press event for auto-completion file directory else: t = unicode(e.text()) self.autoCloseBracket = self.settings.value( "pythonConsole/autoCloseBracket", False, type=bool) self.autoImport = self.settings.value( "pythonConsole/autoInsertionImport", True, type=bool) txt = cmd[:index].replace('>>> ', '').replace('... ', '') ## Close bracket automatically if t in self.opening and self.autoCloseBracket: i = self.opening.index(t) if self.hasSelectedText() and startPos != 0: selText = self.selectedText() self.removeSelectedText() self.insert(self.opening[i] + selText + self.closing[i]) self.setCursorPosition(endLine, endPos + 2) return elif t == '(' and (re.match(r'^[ \t]*def \w+$', txt) or re.match(r'^[ \t]*class \w+$', txt)): self.insert('):') else: self.insert(self.closing[i]) ## FIXES #8392 (automatically removes the redundant char ## when autoclosing brackets option is enabled) elif t in [')', ']', '}'] and self.autoCloseBracket: txt = self.text(line) try: if txt[index - 1] in self.opening and t == txt[index]: self.setCursorPosition(line, index + 1) self.SendScintilla(QsciScintilla.SCI_DELETEBACK) except IndexError: pass elif t == ' ' and self.autoImport: ptrn = r'^[ \t]*from [\w.]+$' if re.match(ptrn, txt): self.insert(' import') self.setCursorPosition(line, index + 7) QsciScintilla.keyPressEvent(self, e) def contextMenuEvent(self, e): menu = QMenu(self) subMenu = QMenu(menu) titleHistoryMenu = QCoreApplication.translate("PythonConsole", "Command History") subMenu.setTitle(titleHistoryMenu) subMenu.addAction(QCoreApplication.translate("PythonConsole", "Show"), self.showHistory, 'Ctrl+Shift+SPACE') subMenu.addSeparator() subMenu.addAction(QCoreApplication.translate("PythonConsole", "Save"), self.writeHistoryFile) subMenu.addSeparator() subMenu.addAction( QCoreApplication.translate("PythonConsole", "Clear File"), self.clearHistory) subMenu.addAction( QCoreApplication.translate("PythonConsole", "Clear Session"), self.clearHistorySession) menu.addMenu(subMenu) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) pasteAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Paste"), self.paste, QKeySequence.Paste) copyAction.setEnabled(False) pasteAction.setEnabled(False) if self.hasSelectedText(): copyAction.setEnabled(True) if QApplication.clipboard().text(): pasteAction.setEnabled(True) menu.exec_(self.mapToGlobal(e.pos())) def mousePressEvent(self, e): """ Re-implemented to handle the mouse press event. e: the mouse press event (QMouseEvent) """ self.setFocus() if e.button() == Qt.MidButton: stringSel = unicode(QApplication.clipboard().text( QClipboard.Selection)) if not self.is_cursor_on_last_line(): self.move_cursor_to_end() self.insertFromDropPaste(stringSel) e.accept() else: QsciScintilla.mousePressEvent(self, e) def paste(self): """ Method to display data from the clipboard. XXX: It should reimplement the virtual QScintilla.paste method, but it seems not used by QScintilla code. """ stringPaste = unicode(QApplication.clipboard().text()) if self.is_cursor_on_last_line(): if self.hasSelectedText(): self.removeSelectedText() else: self.move_cursor_to_end() self.insertFromDropPaste(stringPaste) ## Drag and drop def dropEvent(self, e): if e.mimeData().hasText(): stringDrag = e.mimeData().text() self.insertFromDropPaste(stringDrag) self.setFocus() e.setDropAction(Qt.CopyAction) e.accept() else: QsciScintillaCompat.dropEvent(self, e) def insertFromDropPaste(self, textDP): pasteList = unicode(textDP).splitlines() if pasteList: for line in pasteList[:-1]: cleanLine = line.replace(">>> ", "").replace("... ", "") self.insert(unicode(cleanLine)) self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) if pasteList[-1] != "": line = pasteList[-1] cleanLine = line.replace(">>> ", "").replace("... ", "") self.insert(unicode(cleanLine)) self.move_cursor_to_end() def insertTextFromFile(self, listOpenFile): for line in listOpenFile[:-1]: self.append(line) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) self.runCommand(unicode(self.currentCommand())) self.append(unicode(listOpenFile[-1])) self.move_cursor_to_end() self.SendScintilla(QsciScintilla.SCI_DELETEBACK) def entered(self): self.move_cursor_to_end() self.runCommand(unicode(self.currentCommand())) self.setFocus() self.move_cursor_to_end() def currentCommand(self): linenr, index = self.getCursorPosition() string = self.text() cmdLine = string[4:] cmd = unicode(cmdLine) return cmd def runCommand(self, cmd): self.writeCMD(cmd) import webbrowser self.updateHistory(cmd) if cmd in ('_pyqgis', '_api'): if cmd == '_pyqgis': webbrowser.open("http://qgis.org/pyqgis-cookbook/") elif cmd == '_api': webbrowser.open("http://qgis.org/api/") more = False else: self.buffer.append(cmd) src = u"\n".join(self.buffer) more = self.runsource(src, "<input>") if not more: self.buffer = [] ## prevents to commands with more lines to break the console ## in the case they have a eol different from '\n' self.setText('') self.move_cursor_to_end() self.displayPrompt(more) def write(self, txt): sys.stderr.write(txt) def writeCMD(self, txt): if len(txt) > 0: getCmdString = self.text() prompt = getCmdString[0:4] sys.stdout.write(prompt + txt + '\n')
class DockWidget(QDockWidget): """Extended QDockWidget for Enki main window """ def __init__(self, parentObject, windowTitle, windowIcon = QIcon(), shortcut = None): QDockWidget.__init__(self, parentObject) self._showAction = None self.setObjectName(str(self.__class__)) self.setWindowTitle(windowTitle) if not windowIcon.isNull(): self.setWindowIcon(windowIcon) if shortcut is not None: self.showAction().setShortcut(shortcut) self._titleBar = _TitleBar( self ) self.setTitleBarWidget( self._titleBar ) if shortcut is not None: toolTip = "Move focus with <b>%s</b>,<br/>close with <b>Esc</b>" % shortcut else: toolTip = "Close with <b>Esc</b>" self._titleBar.setToolTip(toolTip) self._closeShortcut = QShortcut( QKeySequence( "Esc" ), self ) self._closeShortcut.setContext( Qt.WidgetWithChildrenShortcut ) self._closeShortcut.activated.connect(self._hide) def showAction(self): """Action shows the widget and set focus on it. Add this action to the main menu """ if not self._showAction : self._showAction = QAction(self.windowIcon(), self.windowTitle(), self) self._showAction.triggered.connect(self.show) self._showAction.triggered.connect(self._handleFocusProxy) return self._showAction def titleBarWidget(self): """QToolBar on the title. You may add own actions to this tool bar """ # method was added only for documenting return QDockWidget.titleBarWidget(self) def _handleFocusProxy(self): """Set focus to focus proxy. Called after widget has been shown """ if self.focusProxy() is not None: self.setFocus() def _hide(self): """Hide and return focus to MainWindow focus proxy """ self.hide() if self.parent() is not None and \ self.parent().focusProxy() is not None: self.parent().focusProxy().setFocus()
class Tree(QTreeView): """File system tree """ _fileActivated = pyqtSignal() def __init__(self, fileBrowser): QTreeView.__init__(self, fileBrowser) self._fileBrowser = fileBrowser self.setAttribute(Qt.WA_MacShowFocusRect, False) self.setAttribute(Qt.WA_MacSmallSize) self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setHeaderHidden(True) self.setUniformRowHeights(True) self.setTextElideMode(Qt.ElideMiddle) # dir model self._dirsModel = _FileSystemModel(self) self._dirsModel.setNameFilterDisables(False) self._dirsModel.setFilter(QDir.AllDirs | QDir.AllEntries | QDir.CaseSensitive | QDir.NoDotAndDotDot) # self._dirsModel.directoryLoaded.connect(self.setFocus) TODO don't have this signal in my Qt version # create proxy model self._filteredModel = FileBrowserFilteredModel(self) self._filteredModel.setSourceModel(self._dirsModel) self.setModel(self._filteredModel) if not sys.platform.startswith('win'): self._dirsModel.setRootPath("/") else: self._dirsModel.setRootPath('') # shortcut accessible only when self._tree has focus self._upShortcut = QShortcut(QKeySequence("BackSpace"), self) self._upShortcut.setContext(Qt.WidgetShortcut) self._upShortcut.activated.connect(self.moveUp) # shortcut accessible only when self._tree has focus self._homeShortcut = QShortcut(QKeySequence("`"), self) self._homeShortcut.setContext(Qt.WidgetShortcut) self._homeShortcut.activated.connect(self._goUserHomeDir) # shortcut accessible only when self._tree has focus self._homeShortcut = QShortcut(QKeySequence("."), self) self._homeShortcut.setContext(Qt.WidgetShortcut) self._homeShortcut.activated.connect(self._goCurrentDir) self.activated.connect(self._onActivated) self._fileActivated.connect(fileBrowser.fileActivated) # QDirModel loads item asynchronously, therefore we need timer for setting focus to the first item self._setFocusTimer = QTimer() self._setFocusTimer.timeout.connect(self._setFirstItemAsCurrent) self._setFocusTimer.setInterval(50) self._timerAttempts = 0 def _onActivated(self, idx): """File or directory doubleClicked """ index = self._filteredModel.mapToSource(idx) path = self._dirsModel.filePath(index) if os.path.isdir(path): self._fileBrowser.setCurrentPath(path) else: self._fileActivated.emit() core.workspace().openFile(path) def moveUp(self): """User pressed Up key or button. Move focus and root up """ current = self.currentIndex() if not current.isValid(): current = self.rootIndex().child(0, 0) self.setCurrentIndex(current) if current.parent() == self.rootIndex() or \ current == self.rootIndex(): # need to move root up if self.rootIndex().parent().isValid( ): # not reached root of the FS tree newRoot = self.rootIndex().parent() parentPath = self._filteredModelIndexToPath(current.parent()) self._fileBrowser.setCurrentPath( self._filteredModelIndexToPath(newRoot)) self.collapseAll() # if moving root up - collapse all items parentIndex = self._filteredModel.mapFromSource( self._dirsModel.index(parentPath)) self._setCurrentItem(parentIndex) else: # need to move selection up parentOfCurrent = self.currentIndex().parent() self._setCurrentItem(parentOfCurrent) # move selection up def _goUserHomeDir(self): """Go to home directory """ self._fileBrowser.setCurrentPath(os.path.expanduser("~")) self.collapseAll() def _goCurrentDir(self): """Go to current directory """ self._fileBrowser.setCurrentPath(_getCurDir()) self.collapseAll() def _filteredModelIndexToPath(self, index): """Map index to file path """ srcIndex = self._filteredModel.mapToSource(index) return self._dirsModel.filePath(srcIndex) def currentPath(self): """Get current path (root of the tree) """ index = self.rootIndex() index = self._filteredModel.mapToSource(index) return self._dirsModel.filePath(index) def _isDescendant(self, child, parent): """Check if child is descendant of parent """ while child.isValid(): if child.parent() == parent: return True child = child.parent() return False def _setFirstItemAsCurrent(self): """QDirModel loads items asynchronously. Therefore we select current item by timer """ if not self.currentIndex().isValid() or \ not self._isDescendant(self.currentIndex(), self.rootIndex()): firstChild = self.rootIndex().child(0, 0) if firstChild.isValid(): self._setFocusTimer.stop() self._setCurrentItem(self.rootIndex().child(0, 0)) else: self._timerAttempts -= 1 if not self._timerAttempts: self._setFocusTimer.stop() else: # nothing to do, have focus self._setFocusTimer.stop() def setCurrentPath(self, path): """Set current path (root of the tree) """ # get index index = self._dirsModel.index(path) # set current path self._filteredModel.invalidate() newRoot = self._filteredModel.mapFromSource(index) self.setRootIndex(newRoot) self._timerAttempts = 10 self._setFocusTimer.start() def _setCurrentItem(self, index): """Make the item current and select it """ self.setCurrentIndex(index) self.selectionModel().select(index, QItemSelectionModel.SelectCurrent)
class SearchWidget(QFrame): """Widget, appeared, when Ctrl+F pressed. Has different forms for different search modes """ Normal = "normal" Good = "good" Bad = "bad" Incorrect = "incorrect" visibilityChanged = pyqtSignal(bool) """ visibilityChanged(visible) **Signal** emitted, when widget has been shown or hidden """ # pylint: disable=W0105 searchInDirectoryStartPressed = pyqtSignal(type(re.compile("")), list, unicode) """ searchInDirectoryStartPressed(regEx, mask, path) **Signal** emitted, when 'search in directory' button had been pressed """ # pylint: disable=W0105 searchInDirectoryStopPressed = pyqtSignal() """ searchInDirectoryStopPressed() **Signal** emitted, when 'stop search in directory' button had been pressed """ # pylint: disable=W0105 replaceCheckedStartPressed = pyqtSignal(unicode) """ replaceCheckedStartPressed(replText) **Signal** emitted, when 'replace checked' button had been pressed """ # pylint: disable=W0105 replaceCheckedStopPressed = pyqtSignal() """ replaceCheckedStartPressed() **Signal** emitted, when 'stop replacing checked' button had been pressed """ # pylint: disable=W0105 searchRegExpChanged = pyqtSignal(type(re.compile(""))) """ searchRegExpValidStateChanged(regEx) **Signal** emitted, when search regexp has been changed. If reg exp is invalid - regEx object contains empty pattern """ # pylint: disable=W0105 searchNext = pyqtSignal() """ searchNext() **Signal** emitted, when 'Search Next' had been pressed """ # pylint: disable=W0105 searchPrevious = pyqtSignal() """ searchPrevious() **Signal** emitted, when 'Search Previous' had been pressed """ # pylint: disable=W0105 replaceFileOne = pyqtSignal(unicode) """ replaceFileOne(replText) **Signal** emitted, when 'Replace' had been pressed """ # pylint: disable=W0105 replaceFileAll = pyqtSignal(unicode) """ replaceFileAll(replText) **Signal** emitted, when 'Replace All' had been pressed """ # pylint: disable=W0105 def __init__(self, plugin): QFrame.__init__(self, core.workspace()) self._mode = None self.plugin = plugin from PyQt4 import uic # lazy import for better startup performance uic.loadUi(os.path.join(os.path.dirname(__file__), "SearchWidget.ui"), self) self.cbSearch.setCompleter(None) self.cbReplace.setCompleter(None) self.cbMask.setCompleter(None) self.fsModel = QDirModel(self.cbPath.lineEdit()) self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.cbPath.lineEdit().setCompleter(QCompleter(self.fsModel, self.cbPath.lineEdit())) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemodel - please update when possible.""" self.cbSearch.setCompleter(None) self.pbSearchStop.setVisible(False) self.pbReplaceCheckedStop.setVisible(False) self._progress = QProgressBar(self) self._progress.setAlignment(Qt.AlignCenter) self._progress.setToolTip(self.tr("Search in progress...")) self._progress.setMaximumSize(QSize(80, 16)) core.mainWindow().statusBar().insertPermanentWidget(1, self._progress) self._progress.setVisible(False) # cd up action self.tbCdUp = QToolButton(self.cbPath.lineEdit()) self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png")) self.tbCdUp.setCursor(Qt.ArrowCursor) self.tbCdUp.installEventFilter(self) # for drawing button self.cbSearch.installEventFilter(self) # for catching Tab and Shift+Tab self.cbReplace.installEventFilter(self) # for catching Tab and Shift+Tab self.cbPath.installEventFilter(self) # for catching Tab and Shift+Tab self.cbMask.installEventFilter(self) # for catching Tab and Shift+Tab self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self.hide) # connections self.cbSearch.lineEdit().textChanged.connect(self._onSearchRegExpChanged) self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbRegularExpression.stateChanged.connect(self._onSearchRegExpChanged) self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged) self.tbCdUp.clicked.connect(self._onCdUpPressed) core.mainWindow().hideAllWindows.connect(self.hide) core.workspace().currentDocumentChanged.connect( lambda old, new: self.setVisible(self.isVisible() and new is not None) ) def show(self): """Reimplemented function. Sends signal """ super(SearchWidget, self).show() self.visibilityChanged.emit(self.isVisible()) def hide(self): """Reimplemented function. Sends signal, returns focus to workspace """ super(SearchWidget, self).hide() core.workspace().focusCurrentDocument() self.visibilityChanged.emit(self.isVisible()) def setVisible(self, visible): """Reimplemented function. Sends signal """ super(SearchWidget, self).setVisible(visible) self.visibilityChanged.emit(self.isVisible()) def _regExEscape(self, text): """Improved version of re.escape() Doesn't escape space, comma, underscore. Escapes \n and \t """ text = re.escape(text) # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable for symbol in " ,_='\"/:@#%&": text = text.replace("\\" + symbol, symbol) text = text.replace("\\\n", "\\n") text = text.replace("\\\t", "\\t") return text def _makeEscapeSeqsVisible(self, text): """Replace invisible \n and \t with escape sequences """ text = text.replace("\t", "\\t") text = text.replace("\n", "\\n") return text def setMode(self, mode): """Change search mode. i.e. from "Search file" to "Replace directory" """ if self._mode == mode and self.isVisible(): if core.workspace().currentDocument() is not None and not core.workspace().currentDocument().hasFocus(): self.cbSearch.lineEdit().selectAll() self.cbSearch.setFocus() self._mode = mode # Set Search and Replace text if ( core.workspace().currentDocument() is not None and core.workspace().currentDocument().hasFocus() and core.workspace().currentDocument().selectedText() ): searchText = core.workspace().currentDocument().selectedText() self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText)) if self.cbRegularExpression.checkState() == Qt.Checked: searchText = self._regExEscape(searchText) self.cbSearch.setEditText(searchText) if ( not self.cbReplace.lineEdit().text() and self.cbSearch.lineEdit().text() and not self.cbRegularExpression.checkState() == Qt.Checked ): self.cbReplace.setEditText(self.cbSearch.lineEdit().text()) # Move focus to Search edit self.cbSearch.setFocus() self.cbSearch.lineEdit().selectAll() # Set search path if mode & ModeFlagDirectory and not (self.isVisible() and self.cbPath.isVisible()): try: searchPath = os.path.abspath(unicode(os.path.curdir)) self.cbPath.setEditText(searchPath) except OSError: # current directory might have been deleted pass # Set widgets visibility flag according to state widgets = ( self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath, self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask, ) # wSear pbPrev pbNext pbSear wRepl wPath pbRep pbRAll pbRCHK wOpti wMask visible = { ModeSearch: (1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0), ModeReplace: (1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0), ModeSearchDirectory: (1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1), ModeReplaceDirectory: (1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1), ModeSearchOpenedFiles: (1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1), ModeReplaceOpenedFiles: (1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1), } for i, widget in enumerate(widgets): widget.setVisible(visible[mode][i]) # Search next button text if mode == ModeReplace: self.pbNext.setText("Next") else: self.pbNext.setText(u"Next↵") # Finaly show all with valid size self.show() # show before updating widgets and labels self._updateLabels() self._updateWidgets() def eventFilter(self, object_, event): """ Event filter for mode switch tool button Draws icons in the search and path lineEdits """ if event.type() == QEvent.Paint and object_ is self.tbCdUp: # draw CdUp button in search path QLineEdit toolButton = object_ lineEdit = self.cbPath.lineEdit() lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0) height = lineEdit.height() availableRect = QRect(0, 0, height, height) if toolButton.rect() != availableRect: toolButton.setGeometry(availableRect) painter = QPainter(toolButton) toolButton.icon().paint(painter, availableRect) return True elif event.type() == QEvent.KeyPress: # Tab and Shift+Tab in QLineEdits if event.key() == Qt.Key_Tab: self._moveFocus(1) return True elif event.key() == Qt.Key_Backtab: self._moveFocus(-1) return True return QFrame.eventFilter(self, object_, event) def _onReturnPressed(self): """Return or Enter pressed on widget. Search next or Replace next """ if self.pbReplace.isVisible(): self.pbReplace.click() elif self.pbNext.isVisible(): self.pbNext.click() elif self.pbSearch.isVisible(): self.pbSearch.click() elif self.pbSearchStop.isVisible(): self.pbSearchStop.click() def _moveFocus(self, step): """Move focus forward or backward according to step. Standard Qt Keyboard focus algorithm doesn't allow circular navigation """ allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask) visibleWidgets = [widget for widget in allFocusableWidgets if widget.isVisible()] try: focusedIndex = visibleWidgets.index(QApplication.focusWidget()) except ValueError: print >>sys.stderr, "Invalid focused widget in Search Widget" return nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets) visibleWidgets[nextFocusedIndex].setFocus() visibleWidgets[nextFocusedIndex].lineEdit().selectAll() def _updateLabels(self): """Update 'Search' 'Replace' 'Path' labels geometry """ width = 0 if self.lSearch.isVisible(): width = max(width, self.lSearch.minimumSizeHint().width()) if self.lReplace.isVisible(): width = max(width, self.lReplace.minimumSizeHint().width()) if self.lPath.isVisible(): width = max(width, self.lPath.minimumSizeHint().width()) self.lSearch.setMinimumWidth(width) self.lReplace.setMinimumWidth(width) self.lPath.setMinimumWidth(width) def _updateWidgets(self): """Update geometry of widgets with buttons """ width = 0 if self.wSearchRight.isVisible(): width = max(width, self.wSearchRight.minimumSizeHint().width()) if self.wReplaceRight.isVisible(): width = max(width, self.wReplaceRight.minimumSizeHint().width()) if self.wPathRight.isVisible(): width = max(width, self.wPathRight.minimumSizeHint().width()) self.wSearchRight.setMinimumWidth(width) self.wReplaceRight.setMinimumWidth(width) self.wPathRight.setMinimumWidth(width) def _updateComboBoxes(self): """Update comboboxes with last used texts """ searchText = self.cbSearch.currentText() replaceText = self.cbReplace.currentText() maskText = self.cbMask.currentText() # search if searchText: index = self.cbSearch.findText(searchText) if index == -1: self.cbSearch.addItem(searchText) # replace if replaceText: index = self.cbReplace.findText(replaceText) if index == -1: self.cbReplace.addItem(replaceText) # mask if maskText: index = self.cbMask.findText(maskText) if index == -1: self.cbMask.addItem(maskText) def _searchPatternTextAndFlags(self): """Get search pattern and flags """ pattern = self.cbSearch.currentText() if not self.cbRegularExpression.checkState() == Qt.Checked: pattern = re.escape(pattern) flags = 0 if not self.cbCaseSensitive.checkState() == Qt.Checked: flags = re.IGNORECASE return pattern, flags def getRegExp(self): """Read search parameters from controls and present it as a regular expression """ pattern, flags = self._searchPatternTextAndFlags() return re.compile(pattern, flags) def isSearchRegExpValid(self): """Try to compile search pattern to check if it is valid Returns bool result and text error """ pattern, flags = self._searchPatternTextAndFlags() try: re.compile(pattern, flags) except re.error, ex: return False, unicode(ex) return True, None
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info" ) ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor( self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor( QColor( self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor( QColor( self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor( QColor( self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor( QColor( self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor( QColor( self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor( QColor( self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon( "console/iconSettingsConsole.png") menu.addAction( iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction( iconSettings, QCoreApplication.translate("PythonConsole", "Options..."), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
def __init__(self, parent, enable_replace=False): QWidget.__init__(self, parent) self.enable_replace = enable_replace self.editor = None self.is_code_editor = None glayout = QGridLayout() glayout.setContentsMargins(0, 0, 0, 0) self.setLayout(glayout) self.close_button = create_toolbutton(self, triggered=self.hide, icon=get_std_icon("DialogCloseButton")) glayout.addWidget(self.close_button, 0, 0) # Find layout self.search_text = PatternComboBox(self, tip=_("Search string"), adjust_to_minimum=False) self.connect(self.search_text, SIGNAL('valid(bool)'), lambda state: self.find(changed=False, forward=True, rehighlight=False)) self.connect(self.search_text.lineEdit(), SIGNAL("textEdited(QString)"), self.text_has_been_edited) self.previous_button = create_toolbutton(self, triggered=self.find_previous, icon=get_std_icon("ArrowBack")) self.next_button = create_toolbutton(self, triggered=self.find_next, icon=get_std_icon("ArrowForward")) self.connect(self.next_button, SIGNAL('clicked()'), self.update_search_combo) self.connect(self.previous_button, SIGNAL('clicked()'), self.update_search_combo) self.re_button = create_toolbutton(self, icon=get_icon("advanced.png"), tip=_("Regular expression")) self.re_button.setCheckable(True) self.connect(self.re_button, SIGNAL("toggled(bool)"), lambda state: self.find()) self.case_button = create_toolbutton(self, icon=get_icon("upper_lower.png"), tip=_("Case Sensitive")) self.case_button.setCheckable(True) self.connect(self.case_button, SIGNAL("toggled(bool)"), lambda state: self.find()) self.words_button = create_toolbutton(self, icon=get_icon("whole_words.png"), tip=_("Whole words")) self.words_button.setCheckable(True) self.connect(self.words_button, SIGNAL("toggled(bool)"), lambda state: self.find()) self.highlight_button = create_toolbutton(self, icon=get_icon("highlight.png"), tip=_("Highlight matches")) self.highlight_button.setCheckable(True) self.connect(self.highlight_button, SIGNAL("toggled(bool)"), self.toggle_highlighting) hlayout = QHBoxLayout() self.widgets = [self.close_button, self.search_text, self.previous_button, self.next_button, self.re_button, self.case_button, self.words_button, self.highlight_button] for widget in self.widgets[1:]: hlayout.addWidget(widget) glayout.addLayout(hlayout, 0, 1) # Replace layout replace_with = QLabel(_("Replace with:")) self.replace_text = PatternComboBox(self, adjust_to_minimum=False, tip=_("Replace string")) self.replace_button = create_toolbutton(self, text=_("Replace/find"), icon=get_std_icon("DialogApplyButton"), triggered=self.replace_find, text_beside_icon=True) self.connect(self.replace_button, SIGNAL('clicked()'), self.update_replace_combo) self.connect(self.replace_button, SIGNAL('clicked()'), self.update_search_combo) self.all_check = QCheckBox(_("Replace all")) self.replace_layout = QHBoxLayout() widgets = [replace_with, self.replace_text, self.replace_button, self.all_check] for widget in widgets: self.replace_layout.addWidget(widget) glayout.addLayout(self.replace_layout, 1, 1) self.widgets.extend(widgets) self.replace_widgets = widgets self.hide_replace() self.search_text.setTabOrder(self.search_text, self.replace_text) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.findnext_sc = QShortcut(QKeySequence("F3"), parent, self.find_next) self.findnext_sc.setContext(Qt.WidgetWithChildrenShortcut) self.findprev_sc = QShortcut(QKeySequence("Shift+F3"), parent, self.find_previous) self.findprev_sc.setContext(Qt.WidgetWithChildrenShortcut) self.togglefind_sc = QShortcut(QKeySequence("Ctrl+F"), parent, self.show) self.togglefind_sc.setContext(Qt.WidgetWithChildrenShortcut) self.togglereplace_sc = QShortcut(QKeySequence("Ctrl+H"), parent, self.toggle_replace_widgets) self.togglereplace_sc.setContext(Qt.WidgetWithChildrenShortcut) escape_sc = QShortcut(QKeySequence("Escape"), parent, self.hide) escape_sc.setContext(Qt.WidgetWithChildrenShortcut) self.highlight_timer = QTimer(self) self.highlight_timer.setSingleShot(True) self.highlight_timer.setInterval(1000) self.connect(self.highlight_timer, SIGNAL("timeout()"), self.highlight_matches)
class SearchWidget(QFrame): """Widget, appeared, when Ctrl+F pressed. Has different forms for different search modes """ Normal = 'normal' Good = 'good' Bad = 'bad' Incorrect = 'incorrect' visibilityChanged = pyqtSignal(bool) """ visibilityChanged(visible) **Signal** emitted, when widget has been shown or hidden """ # pylint: disable=W0105 searchInDirectoryStartPressed = pyqtSignal(type(re.compile('')), list, unicode) """ searchInDirectoryStartPressed(regEx, mask, path) **Signal** emitted, when 'search in directory' button had been pressed """ # pylint: disable=W0105 searchInDirectoryStopPressed = pyqtSignal() """ searchInDirectoryStopPressed() **Signal** emitted, when 'stop search in directory' button had been pressed """ # pylint: disable=W0105 replaceCheckedStartPressed = pyqtSignal(unicode) """ replaceCheckedStartPressed(replText) **Signal** emitted, when 'replace checked' button had been pressed """ # pylint: disable=W0105 replaceCheckedStopPressed = pyqtSignal() """ replaceCheckedStartPressed() **Signal** emitted, when 'stop replacing checked' button had been pressed """ # pylint: disable=W0105 searchRegExpChanged = pyqtSignal(type(re.compile(''))) """ searchRegExpValidStateChanged(regEx) **Signal** emitted, when search regexp has been changed. If reg exp is invalid - regEx object contains empty pattern """ # pylint: disable=W0105 searchNext = pyqtSignal() """ searchNext() **Signal** emitted, when 'Search Next' had been pressed """ # pylint: disable=W0105 searchPrevious = pyqtSignal() """ searchPrevious() **Signal** emitted, when 'Search Previous' had been pressed """ # pylint: disable=W0105 replaceFileOne = pyqtSignal(unicode) """ replaceFileOne(replText) **Signal** emitted, when 'Replace' had been pressed """ # pylint: disable=W0105 replaceFileAll = pyqtSignal(unicode) """ replaceFileAll(replText) **Signal** emitted, when 'Replace All' had been pressed """ # pylint: disable=W0105 def __init__(self, plugin): QFrame.__init__(self, core.workspace()) self._mode = None self.plugin = plugin from PyQt4 import uic # lazy import for better startup performance uic.loadUi(os.path.join(os.path.dirname(__file__), 'SearchWidget.ui'), self) self.cbSearch.setCompleter(None) self.cbReplace.setCompleter(None) self.cbMask.setCompleter(None) self.fsModel = QDirModel(self.cbPath.lineEdit()) self.fsModel.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.cbPath.lineEdit().setCompleter( QCompleter(self.fsModel, self.cbPath.lineEdit())) # TODO QDirModel is deprecated but QCompleter does not yet handle # QFileSystemodel - please update when possible.""" self.cbSearch.setCompleter(None) self.pbSearchStop.setVisible(False) self.pbReplaceCheckedStop.setVisible(False) self._progress = QProgressBar(self) self._progress.setAlignment(Qt.AlignCenter) self._progress.setToolTip(self.tr("Search in progress...")) self._progress.setMaximumSize(QSize(80, 16)) core.mainWindow().statusBar().insertPermanentWidget(1, self._progress) self._progress.setVisible(False) # cd up action self.tbCdUp = QToolButton(self.cbPath.lineEdit()) self.tbCdUp.setIcon(QIcon(":/enkiicons/go-up.png")) self.tbCdUp.setCursor(Qt.ArrowCursor) self.tbCdUp.installEventFilter(self) # for drawing button self.cbSearch.installEventFilter( self) # for catching Tab and Shift+Tab self.cbReplace.installEventFilter( self) # for catching Tab and Shift+Tab self.cbPath.installEventFilter(self) # for catching Tab and Shift+Tab self.cbMask.installEventFilter(self) # for catching Tab and Shift+Tab self._closeShortcut = QShortcut(QKeySequence("Esc"), self) self._closeShortcut.setContext(Qt.WidgetWithChildrenShortcut) self._closeShortcut.activated.connect(self.hide) # connections self.cbSearch.lineEdit().textChanged.connect( self._onSearchRegExpChanged) self.cbSearch.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbReplace.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbPath.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbMask.lineEdit().returnPressed.connect(self._onReturnPressed) self.cbRegularExpression.stateChanged.connect( self._onSearchRegExpChanged) self.cbCaseSensitive.stateChanged.connect(self._onSearchRegExpChanged) self.cbWholeWord.stateChanged.connect(self._onSearchRegExpChanged) self.tbCdUp.clicked.connect(self._onCdUpPressed) self.pbNext.pressed.connect(self.searchNext) self.pbPrevious.pressed.connect(self.searchPrevious) self.pbSearchStop.pressed.connect(self.searchInDirectoryStopPressed) self.pbReplaceCheckedStop.pressed.connect( self.replaceCheckedStopPressed) core.mainWindow().hideAllWindows.connect(self.hide) core.workspace().escPressed.connect(self.hide) core.workspace().currentDocumentChanged.connect( \ lambda old, new: self.setVisible(self.isVisible() and new is not None)) def show(self): """Reimplemented function. Sends signal """ super(SearchWidget, self).show() self.visibilityChanged.emit(self.isVisible()) def hide(self): """Reimplemented function. Sends signal, returns focus to workspace """ super(SearchWidget, self).hide() core.workspace().focusCurrentDocument() self.visibilityChanged.emit(self.isVisible()) def setVisible(self, visible): """Reimplemented function. Sends signal """ super(SearchWidget, self).setVisible(visible) self.visibilityChanged.emit(self.isVisible()) def _regExEscape(self, text): """Improved version of re.escape() Doesn't escape space, comma, underscore. Escapes \n and \t """ text = re.escape(text) # re.escape escapes space, comma, underscore, but, it is not necessary and makes text not readable for symbol in (' ,_=\'"/:@#%&'): text = text.replace('\\' + symbol, symbol) text = text.replace('\\\n', '\\n') text = text.replace('\\\t', '\\t') return text def _makeEscapeSeqsVisible(self, text): """Replace invisible \n and \t with escape sequences """ text = text.replace('\\', '\\\\') text = text.replace('\t', '\\t') text = text.replace('\n', '\\n') return text def setMode(self, mode): """Change search mode. i.e. from "Search file" to "Replace directory" """ if self._mode == mode and self.isVisible(): if core.workspace().currentDocument() is not None and \ not core.workspace().currentDocument().hasFocus(): self.cbSearch.lineEdit().selectAll() self.cbSearch.setFocus() self._mode = mode # Set Search and Replace text document = core.workspace().currentDocument() if document is not None and \ document.hasFocus() and \ document.qutepart.selectedText: searchText = document.qutepart.selectedText self.cbReplace.setEditText(self._makeEscapeSeqsVisible(searchText)) if self.cbRegularExpression.checkState() == Qt.Checked: searchText = self._regExEscape(searchText) self.cbSearch.setEditText(searchText) if not self.cbReplace.lineEdit().text() and \ self.cbSearch.lineEdit().text() and \ not self.cbRegularExpression.checkState() == Qt.Checked: replaceText = self.cbSearch.lineEdit().text().replace('\\', '\\\\') self.cbReplace.setEditText(replaceText) # Move focus to Search edit self.cbSearch.setFocus() self.cbSearch.lineEdit().selectAll() # Set search path if mode & MODE_FLAG_DIRECTORY and \ not (self.isVisible() and self.cbPath.isVisible()): try: searchPath = os.path.abspath(unicode(os.path.curdir)) self.cbPath.setEditText(searchPath) except OSError: # current directory might have been deleted pass # Set widgets visibility flag according to state widgets = (self.wSearch, self.pbPrevious, self.pbNext, self.pbSearch, self.wReplace, self.wPath, \ self.pbReplace, self.pbReplaceAll, self.pbReplaceChecked, self.wOptions, self.wMask) # wSear pbPrev pbNext pbSear wRepl wPath pbRep pbRAll pbRCHK wOpti wMask visible = \ {MODE_SEARCH : (1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,), MODE_REPLACE: (1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,), MODE_SEARCH_DIRECTORY: (1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1,), MODE_REPLACE_DIRECTORY: (1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,), MODE_SEARCH_OPENED_FILES: (1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1,), MODE_REPLACE_OPENED_FILES: (1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1,)} for i, widget in enumerate(widgets): widget.setVisible(visible[mode][i]) # Search next button text if mode == MODE_REPLACE: self.pbNext.setText('Next') else: self.pbNext.setText(u'Next↵') # Finaly show all with valid size self.show() # show before updating widgets and labels self._updateLabels() self._updateWidgets() def eventFilter(self, object_, event): """ Event filter for mode switch tool button Draws icons in the search and path lineEdits """ if event.type( ) == QEvent.Paint and object_ is self.tbCdUp: # draw CdUp button in search path QLineEdit toolButton = object_ lineEdit = self.cbPath.lineEdit() lineEdit.setContentsMargins(lineEdit.height(), 0, 0, 0) height = lineEdit.height() availableRect = QRect(0, 0, height, height) if toolButton.rect() != availableRect: toolButton.setGeometry(availableRect) painter = QPainter(toolButton) toolButton.icon().paint(painter, availableRect) return True elif event.type( ) == QEvent.KeyPress: # Tab and Shift+Tab in QLineEdits if event.key() == Qt.Key_Tab: self._moveFocus(1) return True elif event.key() == Qt.Key_Backtab: self._moveFocus(-1) return True return QFrame.eventFilter(self, object_, event) def _onReturnPressed(self): """Return or Enter pressed on widget. Search next or Replace next """ if self.pbReplace.isVisible(): self.pbReplace.click() elif self.pbNext.isVisible(): self.pbNext.click() elif self.pbSearch.isVisible(): self.pbSearch.click() elif self.pbSearchStop.isVisible(): self.pbSearchStop.click() def _moveFocus(self, step): """Move focus forward or backward according to step. Standard Qt Keyboard focus algorithm doesn't allow circular navigation """ allFocusableWidgets = (self.cbSearch, self.cbReplace, self.cbPath, self.cbMask) visibleWidgets = [widget for widget in allFocusableWidgets \ if widget.isVisible()] try: focusedIndex = visibleWidgets.index(QApplication.focusWidget()) except ValueError: print >> sys.stderr, 'Invalid focused widget in Search Widget' return nextFocusedIndex = (focusedIndex + step) % len(visibleWidgets) visibleWidgets[nextFocusedIndex].setFocus() visibleWidgets[nextFocusedIndex].lineEdit().selectAll() def _updateLabels(self): """Update 'Search' 'Replace' 'Path' labels geometry """ width = 0 if self.lSearch.isVisible(): width = max(width, self.lSearch.minimumSizeHint().width()) if self.lReplace.isVisible(): width = max(width, self.lReplace.minimumSizeHint().width()) if self.lPath.isVisible(): width = max(width, self.lPath.minimumSizeHint().width()) self.lSearch.setMinimumWidth(width) self.lReplace.setMinimumWidth(width) self.lPath.setMinimumWidth(width) def _updateWidgets(self): """Update geometry of widgets with buttons """ width = 0 if self.wSearchRight.isVisible(): width = max(width, self.wSearchRight.minimumSizeHint().width()) if self.wReplaceRight.isVisible(): width = max(width, self.wReplaceRight.minimumSizeHint().width()) if self.wPathRight.isVisible(): width = max(width, self.wPathRight.minimumSizeHint().width()) self.wSearchRight.setMinimumWidth(width) self.wReplaceRight.setMinimumWidth(width) self.wPathRight.setMinimumWidth(width) def updateComboBoxes(self): """Update comboboxes with last used texts """ searchText = self.cbSearch.currentText() replaceText = self.cbReplace.currentText() maskText = self.cbMask.currentText() # search if searchText: index = self.cbSearch.findText(searchText) if index == -1: self.cbSearch.addItem(searchText) # replace if replaceText: index = self.cbReplace.findText(replaceText) if index == -1: self.cbReplace.addItem(replaceText) # mask if maskText: index = self.cbMask.findText(maskText) if index == -1: self.cbMask.addItem(maskText) def _searchPatternTextAndFlags(self): """Get search pattern and flags """ pattern = self.cbSearch.currentText() pattern = pattern.replace( u'\u2029', '\n') # replace unicode paragraph separator with habitual \n if not self.cbRegularExpression.checkState() == Qt.Checked: pattern = re.escape(pattern) if self.cbWholeWord.checkState() == Qt.Checked: pattern = r'\b' + pattern + r'\b' flags = 0 if not self.cbCaseSensitive.checkState() == Qt.Checked: flags = re.IGNORECASE return pattern, flags def getRegExp(self): """Read search parameters from controls and present it as a regular expression """ pattern, flags = self._searchPatternTextAndFlags() return re.compile(pattern, flags) def isSearchRegExpValid(self): """Try to compile search pattern to check if it is valid Returns bool result and text error """ pattern, flags = self._searchPatternTextAndFlags() try: re.compile(pattern, flags) except re.error, ex: return False, unicode(ex) return True, None