class CartoDBLayerWorker(QObject): finished = pyqtSignal(CartoDBLayer) error = pyqtSignal(Exception, basestring) def __init__(self, iface, tableName, owner=None, dlg=None, sql=None, filterByExtent=False, readonly=False, multiuser=False): QObject.__init__(self) self.iface = iface self.owner = owner self.tableName = tableName self.readonly = readonly self.multiuser = multiuser self.dlg = dlg self.sql = sql self.filterByExtent = filterByExtent self.loop = None def load(self): worker = CartoDBPluginWorker(self, 'loadLayer') worker.error.connect(self.workerError) self.loop = QEventLoop() worker.finished.connect(self.workerFinished) worker.start() self.loop.exec_() @pyqtSlot(str) def _loadData(self, spatiaLite): layer = CartoDBLayer(self.iface, self.tableName, self.dlg.currentUser, self.dlg.currentApiKey, self.owner, self.sql, spatiaLite=spatiaLite, readonly=self.readonly, multiuser=self.multiuser) self.finished.emit(layer) @pyqtSlot() def loadLayer(self): if self.sql is None: sql = 'SELECT * FROM ' + ((self.owner + '.') if self.owner != self.dlg.currentUser else '') + self.tableName if self.filterByExtent: extent = self.iface.mapCanvas().extent() sql = sql + " WHERE ST_Intersects(ST_GeometryFromText('{}', 4326), the_geom)".format(extent.asWktPolygon()) else: sql = self.sql cartoDBApi = CartoDBApi(self.dlg.currentUser, self.dlg.currentApiKey) cartoDBApi.fetchContent.connect(self._loadData) cartoDBApi.download(sql) # cartoDBApi.getDataFromTable(sql, False) # geoJSON = self._loadData() """ On worker has finished """ def workerFinished(self, ret): QgsMessageLog.logMessage('Task finished:\n' + str(ret), 'CartoDB Plugin', QgsMessageLog.INFO) self.loop.exit() """ On worker error """ def workerError(self, e, exception_string): QgsMessageLog.logMessage('Worker thread raised an exception: {} - {}\n'.format(exception_string, str(e)), 'CartoDB Plugin', QgsMessageLog.CRITICAL)
def download_file(self, url, path_file): """ download function :param url: url path of file :param path_file: path to save file :return: """ self.file_install = path_file self.url = url loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(lambda: loop.exit(1)) timer.start(100000) # 10 second time-out # req = QNetworkRequest(QUrl('https://www.python.org/')) req = QNetworkRequest(QUrl(url)) result = self.manager.get(req) result.finished.connect(lambda: self.fin_req(loop, result)) self.print_('fetching request...', self.dbg) if loop.exec_() == 0: timer.stop() self.print_( '{} is received: {}'.format(os.path.basename(path_file), result.readAll().count()), self.dbg) else: self.print_('request timed-out')
class QuickMenu(FramelessWindow): """ A quick menu popup for the widgets. The widgets are set using :func:`QuickMenu.setModel` which must be a model as returned by :func:`QtWidgetRegistry.model` """ #: An action has been triggered in the menu. triggered = Signal(QAction) #: An action has been hovered in the menu hovered = Signal(QAction) def __init__(self, parent=None, **kwargs): FramelessWindow.__init__(self, parent, **kwargs) self.setWindowFlags(Qt.Popup) self.__filterFunc = None self.__setupUi() self.__loop = None self.__model = QStandardItemModel() self.__triggeredAction = None def __setupUi(self): self.setLayout(QVBoxLayout(self)) self.layout().setContentsMargins(6, 6, 6, 6) self.__search = SearchWidget(self, objectName="search-line") self.__search.setPlaceholderText( self.tr("Search for widget or select from the list.") ) self.layout().addWidget(self.__search) self.__frame = QFrame(self, objectName="menu-frame") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) self.__frame.setLayout(layout) self.layout().addWidget(self.__frame) self.__pages = PagedMenu(self, objectName="paged-menu") self.__pages.currentChanged.connect(self.setCurrentIndex) self.__pages.triggered.connect(self.triggered) self.__pages.hovered.connect(self.hovered) self.__frame.layout().addWidget(self.__pages) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__suggestPage = SuggestMenuPage(self, objectName="suggest-page") self.__suggestPage.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) self.__suggestPage.setIcon(icon_loader().get("icons/Search.svg")) if sys.platform == "darwin": view = self.__suggestPage.view() view.verticalScrollBar().setAttribute(Qt.WA_MacMiniSize, True) # Don't show the focus frame because it expands into the tab bar. view.setAttribute(Qt.WA_MacShowFocusRect, False) i = self.addPage(self.tr("Quick Search"), self.__suggestPage) button = self.__pages.tabButton(i) button.setObjectName("search-tab-button") button.setStyleSheet( "TabButton {\n" " qproperty-flat_: false;\n" " border: none;" "}\n") self.__search.textEdited.connect(self.__on_textEdited) self.__navigator = ItemViewKeyNavigator(self) self.__navigator.setView(self.__suggestPage.view()) self.__search.installEventFilter(self.__navigator) self.__grip = WindowSizeGrip(self) self.__grip.raise_() def setSizeGripEnabled(self, enabled): """ Enable the resizing of the menu with a size grip in a bottom right corner (enabled by default). """ if bool(enabled) != bool(self.__grip): if self.__grip: self.__grip.deleteLater() self.__grip = None else: self.__grip = WindowSizeGrip(self) self.__grip.raise_() def sizeGripEnabled(self): """ Is the size grip enabled. """ return bool(self.__grip) def addPage(self, name, page): """ Add the `page` (:class:`MenuPage`) with `name` and return it's index. The `page.icon()` will be used as the icon in the tab bar. """ icon = page.icon() tip = name if page.toolTip(): tip = page.toolTip() index = self.__pages.addPage(page, name, icon, tip) # Route the page's signals page.triggered.connect(self.__onTriggered) page.hovered.connect(self.hovered) # Install event filter to intercept key presses. page.view().installEventFilter(self) return index def createPage(self, index): """ Create a new page based on the contents of an index (:class:`QModeIndex`) item. """ page = MenuPage(self) page.setModel(index.model()) page.setRootIndex(index) view = page.view() if sys.platform == "darwin": view.verticalScrollBar().setAttribute(Qt.WA_MacMiniSize, True) # Don't show the focus frame because it expands into the tab # bar at the top. view.setAttribute(Qt.WA_MacShowFocusRect, False) name = unicode(index.data(Qt.DisplayRole)) page.setTitle(name) icon = index.data(Qt.DecorationRole).toPyObject() if isinstance(icon, QIcon): page.setIcon(icon) page.setToolTip(index.data(Qt.ToolTipRole).toPyObject()) return page def setModel(self, model): """ Set the model containing the actions. """ root = model.invisibleRootItem() for i in range(root.rowCount()): item = root.child(i) index = item.index() page = self.createPage(index) page.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) i = self.addPage(page.title(), page) brush = index.data(QtWidgetRegistry.BACKGROUND_ROLE) if brush.isValid(): brush = brush.toPyObject() base_color = brush.color() button = self.__pages.tabButton(i) button.setStyleSheet( TAB_BUTTON_STYLE_TEMPLATE % (create_css_gradient(base_color), create_css_gradient(base_color.darker(120))) ) self.__model = model self.__suggestPage.setModel(model) def setFilterFunc(self, func): """ Set a filter function. """ if func != self.__filterFunc: self.__filterFunc = func for i in range(0, self.__pages.count()): self.__pages.page(i).setFilterFunc(func) def popup(self, pos=None, searchText=""): """ Popup the menu at `pos` (in screen coordinates). 'Search' text field is initialized with `searchText` if provided. """ if pos is None: pos = QPoint() self.__clearCurrentItems() self.__search.setText(searchText) self.__suggestPage.setFilterFixedString(searchText) self.ensurePolished() if self.testAttribute(Qt.WA_Resized) and self.sizeGripEnabled(): size = self.size() else: size = self.sizeHint() desktop = QApplication.desktop() screen_geom = desktop.availableGeometry(pos) # Adjust the size to fit inside the screen. if size.height() > screen_geom.height(): size.setHeight(screen_geom.height()) if size.width() > screen_geom.width(): size.setWidth(screen_geom.width()) geom = QRect(pos, size) if geom.top() < screen_geom.top(): geom.setTop(screen_geom.top()) if geom.left() < screen_geom.left(): geom.setLeft(screen_geom.left()) bottom_margin = screen_geom.bottom() - geom.bottom() right_margin = screen_geom.right() - geom.right() if bottom_margin < 0: # Falls over the bottom of the screen, move it up. geom.translate(0, bottom_margin) # TODO: right to left locale if right_margin < 0: # Falls over the right screen edge, move the menu to the # other side of pos. geom.translate(-size.width(), 0) self.setGeometry(geom) self.show() if searchText: self.setFocusProxy(self.__search) else: self.setFocusProxy(None) def exec_(self, pos=None, searchText=""): """ Execute the menu at position `pos` (in global screen coordinates). Return the triggered :class:`QAction` or `None` if no action was triggered. 'Search' text field is initialized with `searchText` if provided. """ self.popup(pos, searchText) self.setFocus(Qt.PopupFocusReason) self.__triggeredAction = None self.__loop = QEventLoop() self.__loop.exec_() self.__loop.deleteLater() self.__loop = None action = self.__triggeredAction self.__triggeredAction = None return action def hideEvent(self, event): """ Reimplemented from :class:`QWidget` """ FramelessWindow.hideEvent(self, event) if self.__loop: self.__loop.exit() def setCurrentPage(self, page): """ Set the current shown page to `page`. """ self.__pages.setCurrentPage(page) def setCurrentIndex(self, index): """ Set the current page index. """ self.__pages.setCurrentIndex(index) def __clearCurrentItems(self): """ Clear any selected (or current) items in all the menus. """ for i in range(self.__pages.count()): self.__pages.page(i).view().selectionModel().clear() def __onTriggered(self, action): """ Re-emit the action from the page. """ self.__triggeredAction = action # Hide and exit the event loop if necessary. self.hide() self.triggered.emit(action) def __on_textEdited(self, text): self.__suggestPage.setFilterFixedString(text) self.__pages.setCurrentPage(self.__suggestPage) def triggerSearch(self): """ Trigger action search. This changes to current page to the 'Suggest' page and sets the keyboard focus to the search line edit. """ self.__pages.setCurrentPage(self.__suggestPage) self.__search.setFocus(Qt.ShortcutFocusReason) # Make sure that the first enabled item is set current. self.__suggestPage.ensureCurrent() def keyPressEvent(self, event): if event.text(): # Ignore modifiers, ... self.__search.setFocus(Qt.ShortcutFocusReason) self.setCurrentIndex(0) self.__search.keyPressEvent(event) FramelessWindow.keyPressEvent(self, event) event.accept() def event(self, event): if event.type() == QEvent.ShortcutOverride: log.debug("Overriding shortcuts") event.accept() return True return FramelessWindow.event(self, event) def eventFilter(self, obj, event): if isinstance(obj, QTreeView): etype = event.type() if etype == QEvent.KeyPress: # ignore modifiers non printable characters, Enter, ... if event.text() and event.key() not in \ [Qt.Key_Enter, Qt.Key_Return]: self.__search.setFocus(Qt.ShortcutFocusReason) self.setCurrentIndex(0) self.__search.keyPressEvent(event) return True return FramelessWindow.eventFilter(self, obj, event)
class CategoryPopupMenu(FramelessWindow): triggered = Signal(QAction) hovered = Signal(QAction) def __init__(self, parent=None, **kwargs): FramelessWindow.__init__(self, parent, **kwargs) self.setWindowFlags(self.windowFlags() | Qt.Popup) layout = QVBoxLayout() layout.setContentsMargins(6, 6, 6, 6) self.__menu = MenuPage() self.__menu.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) if sys.platform == "darwin": self.__menu.view().setAttribute(Qt.WA_MacShowFocusRect, False) self.__menu.triggered.connect(self.__onTriggered) self.__menu.hovered.connect(self.hovered) self.__dragListener = ItemViewDragStartEventListener(self) self.__dragListener.dragStarted.connect(self.__onDragStarted) self.__menu.view().viewport().installEventFilter(self.__dragListener) layout.addWidget(self.__menu) self.setLayout(layout) self.__action = None self.__loop = None self.__item = None def setCategoryItem(self, item): """ Set the category root item (:class:`QStandardItem`). """ self.__item = item model = item.model() self.__menu.setModel(model) self.__menu.setRootIndex(item.index()) def popup(self, pos=None): if pos is None: pos = self.pos() geom = widget_popup_geometry(pos, self) self.setGeometry(geom) self.show() def exec_(self, pos=None): self.popup(pos) self.__loop = QEventLoop() self.__action = None self.__loop.exec_() self.__loop = None if self.__action is not None: action = self.__action else: action = None return action def hideEvent(self, event): if self.__loop is not None: self.__loop.exit(0) return FramelessWindow.hideEvent(self, event) def __onTriggered(self, action): self.__action = action self.triggered.emit(action) self.hide() if self.__loop: self.__loop.exit(0) def __onDragStarted(self, index): desc = index.data(QtWidgetRegistry.WIDGET_DESC_ROLE) icon = index.data(Qt.DecorationRole) drag_data = QMimeData() drag_data.setData( "application/vnv.orange-canvas.registry.qualified-name", desc.qualified_name.encode('utf-8')) drag = QDrag(self) drag.setPixmap(icon.pixmap(38)) drag.setMimeData(drag_data) # TODO: Should animate (accept) hide. self.hide() # When a drag is started and the menu hidden the item's tool tip # can still show for a short time UNDER the cursor preventing a # drop. viewport = self.__menu.view().viewport() filter = ToolTipEventFilter() viewport.installEventFilter(filter) drag.exec_(Qt.CopyAction) viewport.removeEventFilter(filter)
class UserSettingsDialog(QMainWindow): """ A User Settings/Defaults dialog. """ MAC_UNIFIED = True def __init__(self, parent=None, **kwargs): QMainWindow.__init__(self, parent, **kwargs) self.setWindowFlags(Qt.Dialog) self.setWindowModality(Qt.ApplicationModal) self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize) self.__macUnified = sys.platform == "darwin" and self.MAC_UNIFIED self._manager = BindingManager(self, submitPolicy=BindingManager.AutoSubmit) self.__loop = None self.__settings = config.settings() self.__setupUi() def __setupUi(self): """Set up the UI. """ if self.__macUnified: self.tab = QToolBar() self.addToolBar(Qt.TopToolBarArea, self.tab) self.setUnifiedTitleAndToolBarOnMac(True) # This does not seem to work self.setWindowFlags(self.windowFlags() & \ ~Qt.MacWindowToolBarButtonHint) self.tab.actionTriggered[QAction].connect( self.__macOnToolBarAction ) central = QStackedWidget() central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: self.tab = central = QTabWidget(self) # Add a close button to the bottom of the dialog # (to satisfy GNOME 3 which shows the dialog without a title bar). container = container_widget_helper() container.layout().addWidget(central) buttonbox = QDialogButtonBox(QDialogButtonBox.Close) buttonbox.rejected.connect(self.close) container.layout().addWidget(buttonbox) self.setCentralWidget(container) self.stack = central # General Tab tab = QWidget() self.addTab(tab, self.tr("General"), toolTip=self.tr("General Options")) form = QFormLayout() tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) nodes = QWidget(self, objectName="nodes") nodes.setLayout(QVBoxLayout()) nodes.layout().setContentsMargins(0, 0, 0, 0) cb_anim = QCheckBox( self.tr("Enable node animations"), objectName="enable-node-animations", toolTip=self.tr("Enable shadow and ping animations for node " "items in the scheme.") ) self.bind(cb_anim, "checked", "schemeedit/enable-node-animations") nodes.layout().addWidget(cb_anim) form.addRow(self.tr("Nodes"), nodes) links = QWidget(self, objectName="links") links.setLayout(QVBoxLayout()) links.layout().setContentsMargins(0, 0, 0, 0) cb_show = QCheckBox( self.tr("Show channel names between widgets"), objectName="show-channel-names", toolTip=self.tr("Show source and sink channel names " "over the links.") ) self.bind(cb_show, "checked", "schemeedit/show-channel-names") links.layout().addWidget(cb_show) form.addRow(self.tr("Links"), links) quickmenu = QWidget(self, objectName="quickmenu-options") quickmenu.setLayout(QVBoxLayout()) quickmenu.layout().setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("On double click"), toolTip=self.tr("Open quick menu on a double click " "on an empty spot in the canvas")) cb2 = QCheckBox(self.tr("On right click"), toolTip=self.tr("Open quick menu on a right click " "on an empty spot in the canvas")) cb3 = QCheckBox(self.tr("On space key press"), toolTip=self.tr("On Space key press while the mouse" "is hovering over the canvas.")) cb4 = QCheckBox(self.tr("On any key press"), toolTip=self.tr("On any key press while the mouse" "is hovering over the canvas.")) self.bind(cb1, "checked", "quickmenu/trigger-on-double-click") self.bind(cb2, "checked", "quickmenu/trigger-on-right-click") self.bind(cb3, "checked", "quickmenu/trigger-on-space-key") self.bind(cb4, "checked", "quickmenu/trigger-on-any-key") quickmenu.layout().addWidget(cb1) quickmenu.layout().addWidget(cb2) quickmenu.layout().addWidget(cb3) quickmenu.layout().addWidget(cb4) form.addRow(self.tr("Open quick menu on"), quickmenu) startup = QWidget(self, objectName="startup-group") startup.setLayout(QVBoxLayout()) startup.layout().setContentsMargins(0, 0, 0, 0) cb_splash = QCheckBox(self.tr("Show splash screen"), self, objectName="show-splash-screen") cb_welcome = QCheckBox(self.tr("Show welcome screen"), self, objectName="show-welcome-screen") self.bind(cb_splash, "checked", "startup/show-splash-screen") self.bind(cb_welcome, "checked", "startup/show-welcome-screen") startup.layout().addWidget(cb_splash) startup.layout().addWidget(cb_welcome) form.addRow(self.tr("On startup"), startup) toolbox = QWidget(self, objectName="toolbox-group") toolbox.setLayout(QVBoxLayout()) toolbox.layout().setContentsMargins(0, 0, 0, 0) exclusive = QCheckBox(self.tr("Only one tab can be open at a time")) self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive") toolbox.layout().addWidget(exclusive) form.addRow(self.tr("Tool box"), toolbox) tab.setLayout(form) # Output Tab tab = QWidget() self.addTab(tab, self.tr("Output"), toolTip="Output Redirection") form = QFormLayout() box = QWidget(self, objectName="streams") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Standard output")) cb2 = QCheckBox(self.tr("Standard error")) self.bind(cb1, "checked", "output/redirect-stdout") self.bind(cb2, "checked", "output/redirect-stderr") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Redirect output"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) combo = QComboBox() combo.addItems([self.tr("Critical"), self.tr("Error"), self.tr("Warn"), self.tr("Info"), self.tr("Debug")]) cb = QCheckBox(self.tr("Show output on 'Error'"), objectName="focus-on-error") self.bind(combo, "currentIndex", "logging/level") self.bind(cb, "checked", "output/show-on-error") layout.addWidget(combo) layout.addWidget(cb) box.setLayout(layout) form.addRow(self.tr("Logging"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Stay on top"), objectName="stay-on-top") cb2 = QCheckBox(self.tr("Dockable"), objectName="output-dockable") self.bind(cb1, "checked", "output/stay-on-top") self.bind(cb2, "checked", "output/dockable") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Output window"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Open in external browser"), objectName="open-in-external-browser") cb2 = QCheckBox(self.tr("Stay on top"), objectName="help-stay-on-top") cb3 = QCheckBox(self.tr("Dockable"), objectName="help-dockable") self.bind(cb1, "checked", "help/open-in-external-browser") self.bind(cb2, "checked", "help/stay-on-top") self.bind(cb3, "checked", "help/dockable") layout.addWidget(cb1) layout.addWidget(cb2) layout.addWidget(cb3) box.setLayout(layout) form.addRow(self.tr("Help window"), box) tab.setLayout(form) # Categories Tab tab = QWidget() layout = QVBoxLayout() view = QListView() from .. import registry reg = registry.global_registry() model = QStandardItemModel() settings = QSettings() for cat in reg.categories(): item = QStandardItem() item.setText(cat.name) item.setCheckable(True) visible, _ = category_state(cat, settings) item.setCheckState(Qt.Checked if visible else Qt.Unchecked) model.appendRow([item]) view.setModel(model) layout.addWidget(view) tab.setLayout(layout) model.itemChanged.connect( lambda item: save_category_state( reg.category(str(item.text())), _State(item.checkState() == Qt.Checked, -1), settings ) ) self.addTab(tab, "Categories") if self.__macUnified: # Need some sensible size otherwise mac unified toolbar 'takes' # the space that should be used for layout of the contents self.adjustSize() def addTab(self, widget, text, toolTip=None, icon=None): if self.__macUnified: action = QAction(text, self) if toolTip: action.setToolTip(toolTip) if icon: action.setIcon(toolTip) action.setData(len(self.tab.actions())) self.tab.addAction(action) self.stack.addWidget(widget) else: i = self.tab.addTab(widget, text) if toolTip: self.tab.setTabToolTip(i, toolTip) if icon: self.tab.setTabIcon(i, icon) def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.hide() self.deleteLater() def bind(self, source, source_property, key, transformer=None): target = UserDefaultsPropertyBinding(self.__settings, key) source = PropertyBinding(source, source_property) source.set(target.get()) self._manager.bind(target, source) def commit(self): self._manager.commit() def revert(self): self._manager.revert() def reset(self): for target, source in self._manager.bindings(): try: source.reset() except NotImplementedError: # Cannot reset. pass except Exception: log.error("Error reseting %r", source.propertyName, exc_info=True) def exec_(self): self.__loop = QEventLoop() self.show() status = self.__loop.exec_() self.__loop = None return status def hideEvent(self, event): QMainWindow.hideEvent(self, event) if self.__loop is not None: self.__loop.exit(0) self.__loop = None def __macOnToolBarAction(self, action): index, _ = action.data().toInt() self.stack.setCurrentIndex(index)
class CartoDBLayerWorker(QObject): finished = pyqtSignal(CartoDBLayer) error = pyqtSignal(Exception, basestring) def __init__(self, iface, tableName, owner=None, dlg=None, sql=None, filterByExtent=False, readonly=False, multiuser=False): QObject.__init__(self) self.iface = iface self.owner = owner self.tableName = tableName self.readonly = readonly self.multiuser = multiuser self.dlg = dlg self.sql = sql self.filterByExtent = filterByExtent self.loop = None def load(self): worker = CartoDBPluginWorker(self, 'loadLayer') worker.error.connect(self.workerError) self.loop = QEventLoop() worker.finished.connect(self.workerFinished) worker.start() self.loop.exec_() @pyqtSlot(str) def _loadData(self, spatiaLite): layer = CartoDBLayer(self.iface, self.tableName, self.dlg.currentUser, self.dlg.currentApiKey, self.owner, self.sql, spatiaLite=spatiaLite, readonly=self.readonly, multiuser=self.multiuser) self.finished.emit(layer) @pyqtSlot() def loadLayer(self): if self.sql is None: sql = 'SELECT * FROM ' + ( ('"' + self.owner + '".') if self.owner != self.dlg.currentUser else '') + self.tableName if self.filterByExtent: extent = self.iface.mapCanvas().extent() sql = sql + " WHERE ST_Intersects(ST_GeometryFromText('{}', 4326), the_geom)".format( extent.asWktPolygon()) else: sql = self.sql cartoDBApi = CartoDBApi(self.dlg.currentUser, self.dlg.currentApiKey) cartoDBApi.fetchContent.connect(self._loadData) cartoDBApi.download(sql) # cartoDBApi.getDataFromTable(sql, False) # geoJSON = self._loadData() """ On worker has finished """ def workerFinished(self, ret): QgsMessageLog.logMessage('Task finished:\n' + str(ret), 'CartoDB Plugin', QgsMessageLog.INFO) self.loop.exit() """ On worker error """ def workerError(self, e, exception_string): QgsMessageLog.logMessage( 'Worker thread raised an exception: {} - {}\n'.format( exception_string, str(e)), 'CartoDB Plugin', QgsMessageLog.CRITICAL)
class ConverterThread(QThread): """Thread converts markdown to HTML. """ # This signal is emitted by the converter thread when a file has been # converted to HTML. htmlReady = pyqtSignal( # Path to the file which should be converted to / displayed as HTML. unicode, # HTML rendering of the file; empty if the HTML is provided in a file # specified by the URL below. unicode, # Error text resulting from the conversion process. unicode, # A reference to a file containing HTML rendering. Empty if the second # parameter above contains the HTML instead. QUrl) # This signal clears the context of the log window. logWindowClear = pyqtSignal() # This signal emits messages for the log window. logWindowText = pyqtSignal( # A string to append to the log window. unicode) _Task = collections.namedtuple("Task", ["filePath", "language", "text"]) def __init__(self): QThread.__init__(self) self._queue = Queue.Queue() self.start(QThread.LowPriority) self._ac = AsyncController('QThread', self) self._ac.defaultPriority = QThread.LowPriority self._SphinxInvocationCount = 1 def process(self, filePath, language, text): """Convert data and emit result. """ self._queue.put(self._Task(filePath, language, text)) def stop_async(self): self._queue.put(None) def _getHtml(self, language, text, filePath): """Get HTML for document """ if language == 'Markdown': return self._convertMarkdown(text), None, QUrl() # For ReST, use docutils only if Sphinx isn't available. elif language == 'Restructured Text' and not sphinxEnabledForFile(filePath): htmlUnicode, errString = self._convertReST(text) return htmlUnicode, errString, QUrl() elif filePath and sphinxEnabledForFile(filePath): # Use Sphinx to generate the HTML if possible. return self._convertSphinx(filePath) elif filePath and canUseCodeChat(filePath): # Otherwise, fall back to using CodeChat+docutils. return self._convertCodeChat(text, filePath) else: return 'No preview for this type of file', None, QUrl() def _convertMarkdown(self, text): """Convert Markdown to HTML """ try: import markdown except ImportError: return 'Markdown preview requires <i>python-markdown</i> package<br/>' \ 'Install it with your package manager or see ' \ '<a href="http://packages.python.org/Markdown/install.html">installation instructions</a>' extensions = ['fenced_code', 'nl2br', 'tables', 'enki.plugins.preview.mdx_math'] # version 2.0 supports only extension names, not instances if markdown.version_info[0] > 2 or \ (markdown.version_info[0] == 2 and markdown.version_info[1] > 0): class _StrikeThroughExtension(markdown.Extension): """http://achinghead.com/python-markdown-adding-insert-delete.html Class is placed here, because depends on imported markdown, and markdown import is lazy """ DEL_RE = r'(~~)(.*?)~~' def extendMarkdown(self, md, md_globals): # Create the del pattern delTag = markdown.inlinepatterns.SimpleTagPattern(self.DEL_RE, 'del') # Insert del pattern into markdown parser md.inlinePatterns.add('del', delTag, '>not_strong') extensions.append(_StrikeThroughExtension()) return markdown.markdown(text, extensions) def _convertReST(self, text): """Convert ReST """ try: import docutils.core import docutils.writers.html4css1 except ImportError: return 'Restructured Text preview requires the <i>python-docutils</i> package.<br/>' \ 'Install it with your package manager or see ' \ '<a href="http://pypi.python.org/pypi/docutils"/>this page.</a>', None errStream = StringIO.StringIO() settingsDict = { # Make sure to use Unicode everywhere. 'output_encoding': 'unicode', 'input_encoding' : 'unicode', # Don't stop processing, no matter what. 'halt_level' : 5, # Capture errors to a string and return it. 'warning_stream' : errStream } # Frozen-specific settings. if isFrozen: settingsDict['template'] = ( # The default docutils stylesheet and template uses a relative path, # which doesn't work when frozen ???. Under Unix when not frozen, # it produces: # ``IOError: [Errno 2] No such file or directory: # '/usr/lib/python2.7/dist-packages/docutils/writers/html4css1/template.txt'``. os.path.join(os.path.dirname(docutils.writers.html4css1.__file__), docutils.writers.html4css1.Writer.default_template) ) settingsDict['stylesheet_dirs'] = ['.', os.path.dirname(docutils.writers.html4css1.__file__)] htmlString = docutils.core.publish_string(text, writer_name='html', settings_overrides=settingsDict) errString = errStream.getvalue() errStream.close() return htmlString, errString def _convertSphinx(self, filePath): # Run the builder. errString = self._runHtmlBuilder() # Look for the HTML output. # # Get an absolute path to the output path, which could be relative. outputPath = core.config()['Sphinx']['OutputPath'] projectPath = core.config()['Sphinx']['ProjectPath'] if not os.path.isabs(outputPath): outputPath = os.path.join(projectPath, outputPath) # Create an htmlPath as OutputPath + remainder of filePath. htmlPath = os.path.join(outputPath + filePath[len(projectPath):]) html_file_suffix = u'.html' try: with codecs.open(os.path.join(projectPath, 'sphinx-enki-info.txt')) as f: hfs = f.read() # If the file is empty, then html_file_suffix wasn't defined # or is None. In this case, use the default extension. # Otherwise, use the extension read from the file. if hfs: html_file_suffix = hfs except: errString = "Warning: assuming .html extension. Use " + \ "the conf.py template to set the extension.\n" + errString pass # First place to look: file.html. For example, look for foo.py # in foo.py.html. htmlFile = htmlPath + html_file_suffix # Second place to look: file without extension.html. For # example, look for foo.html for foo.rst. htmlFileAlter = os.path.splitext(htmlPath)[0] + html_file_suffix # Check that the output file produced by Sphinx is newer than # the source file it was built from. if os.path.exists(htmlFile): return _checkModificationTime(filePath, htmlFile, errString) elif os.path.exists(htmlFileAlter): return _checkModificationTime(filePath, htmlFileAlter, errString) else: return ('No preview for this type of file.<br>Expected ' + htmlFile + " or " + htmlFileAlter, errString, QUrl()) def _convertCodeChat(self, text, filePath): # Use StringIO to pass CodeChat compilation information back to # the UI. errStream = StringIO.StringIO() try: htmlString = CodeToRest.code_to_html_string(text, errStream, filename=filePath) except KeyError: # Although the file extension may be in the list of supported # extensions, CodeChat may not support the lexer chosen by Pygments. # For example, a ``.v`` file may be Verilog (supported by CodeChat) # or Coq (not supported). In this case, provide an error messsage errStream.write('Error: this file is not supported by CodeChat.') htmlString = '' errString = errStream.getvalue() errStream.close() return htmlString, errString, QUrl() def _runHtmlBuilder(self): # Build the commond line for Sphinx. if core.config()['Sphinx']['AdvancedMode']: htmlBuilderCommandLine = core.config()['Sphinx']['Cmdline'] if sys.platform.startswith('linux'): # If Linux is used, then subprocess cannot take the whole # commandline as the name of an executable file. Module shlex # has to be used to parse commandline. htmlBuilderCommandLine = shlex.split(htmlBuilderCommandLine) else: # For available builder options, refer to: http://sphinx-doc.org/builders.html htmlBuilderCommandLine = [core.config()['Sphinx']['Executable'], # Place doctrees in the ``_build`` directory; by default, Sphinx # places this in _build/html/.doctrees. '-d', os.path.join('_build', 'doctrees'), # Source directory -- the current directory, since we'll chdir to # the project directory before executing this. '.', # Build directory core.config()['Sphinx']['OutputPath']] # Invoke it. try: # Clear the log at the beginning of a Sphinx build. self.logWindowClear.emit() cwd = core.config()['Sphinx']['ProjectPath'] # If the command line is already a string (advanced mode), just print it. # Otherwise, it's a list that should be transformed to a string. if isinstance(htmlBuilderCommandLine, str): htmlBuilderCommandLineStr = htmlBuilderCommandLine else: htmlBuilderCommandLineStr = ' '.join(htmlBuilderCommandLine) self.logWindowText.emit('{} : {}\n\n'.format(cwd, htmlBuilderCommandLineStr)) # Run Sphinx, reading stdout in a separate thread. self._qe = QEventLoop() # Sphinx will output just a carriage return (0x0D) to simulate a # single line being updated by build status and the build # progresses. Without universal newline support here, we'll wait # until the build is complete (with a \n\r) to report any build # progress! So, enable universal newlines, so that each \r will be # treated as a separate line, providing immediate feedback on build # progress. popen = open_console_output(htmlBuilderCommandLine, cwd=cwd, universal_newlines=True) # Perform reads in an event loop. The loop is exit when all reads # have completed. We can't simply start the _stderr_read thread # here, because calls to self._qe_exit() will be ignored until # we're inside the event loop. QTimer.singleShot(0, lambda: self._popen_read(popen)) self._qe.exec_() except OSError as ex: return ( 'Failed to execute HTML builder:\n' '{}\n'.format(str(ex)) + 'Go to Settings -> Settings -> CodeChat to set HTML' ' builder configurations.') return self._stderr # Read from stdout (in this thread) and stderr (in another thread), # so that the user sees output as the build progresses, rather than only # producing output after the build is complete. def _popen_read(self, popen): # Read are blocking; we can't read from both stdout and stderr in the # same thread without possible buffer overflows. So, use this thread to # read from and immediately report progress from stdout. In another # thread, read all stderr and report that after the build finishes. self._ac.start(None, self._stderr_read, popen.stderr) # Read a line of stdout then report it to the user immediately. s = popen.stdout.readline() while s: self.logWindowText.emit(s.rstrip('\n')) s = popen.stdout.readline() self._SphinxInvocationCount += 1 # I would expect the following code to do the same thing. It doesn't: # instead, it waits until Sphinx completes before returning anything. # ??? # # .. code-block: python # :linenos: # # for s in popen.stdout: # self.logWindowText.emit(s) # Runs in a separate thread to read stdout. It then exits the QEventLoop as # a way to signal that stderr reads have completed. def _stderr_read(self, stderr): self._stderr = stderr.read() self._qe.exit() def run(self): """Thread function """ while True: # exits with break # wait task task = self._queue.get() # take the last task while self._queue.qsize(): task = self._queue.get() if task is None: # None is a quit command self._ac.terminate() break # TODO: This is ugly. Should pass this exception back to the main # thread and re-raise it there, or use a QFuture like approach which # does this automaticlaly. try: html, errString, url = self._getHtml(task.language, task.text, task.filePath) except Exception: traceback.print_exc() self.htmlReady.emit(task.filePath, html, errString, url) # Free resources. self._ac.terminate()
class QuickMenu(FramelessWindow): """ A quick menu popup for the widgets. The widgets are set using :func:`QuickMenu.setModel` which must be a model as returned by :func:`QtWidgetRegistry.model` """ #: An action has been triggered in the menu. triggered = Signal(QAction) #: An action has been hovered in the menu hovered = Signal(QAction) def __init__(self, parent=None, **kwargs): FramelessWindow.__init__(self, parent, **kwargs) self.setWindowFlags(Qt.Popup) self.__filterFunc = None self.__setupUi() self.__loop = None self.__model = QStandardItemModel() self.__triggeredAction = None def __setupUi(self): self.setLayout(QVBoxLayout(self)) self.layout().setContentsMargins(6, 6, 6, 6) self.__search = SearchWidget(self, objectName="search-line") self.__search.setPlaceholderText( self.tr("Search for widget or select from the list.")) self.layout().addWidget(self.__search) self.__frame = QFrame(self, objectName="menu-frame") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) self.__frame.setLayout(layout) self.layout().addWidget(self.__frame) self.__pages = PagedMenu(self, objectName="paged-menu") self.__pages.currentChanged.connect(self.setCurrentIndex) self.__pages.triggered.connect(self.triggered) self.__pages.hovered.connect(self.hovered) self.__frame.layout().addWidget(self.__pages) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__suggestPage = SuggestMenuPage(self, objectName="suggest-page") self.__suggestPage.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) self.__suggestPage.setIcon(icon_loader().get("icons/Search.svg")) if sys.platform == "darwin": view = self.__suggestPage.view() view.verticalScrollBar().setAttribute(Qt.WA_MacMiniSize, True) # Don't show the focus frame because it expands into the tab bar. view.setAttribute(Qt.WA_MacShowFocusRect, False) i = self.addPage(self.tr("Quick Search"), self.__suggestPage) button = self.__pages.tabButton(i) button.setObjectName("search-tab-button") button.setStyleSheet("TabButton {\n" " qproperty-flat_: false;\n" " border: none;" "}\n") self.__search.textEdited.connect(self.__on_textEdited) self.__navigator = ItemViewKeyNavigator(self) self.__navigator.setView(self.__suggestPage.view()) self.__search.installEventFilter(self.__navigator) self.__grip = WindowSizeGrip(self) self.__grip.raise_() def setSizeGripEnabled(self, enabled): """ Enable the resizing of the menu with a size grip in a bottom right corner (enabled by default). """ if bool(enabled) != bool(self.__grip): if self.__grip: self.__grip.deleteLater() self.__grip = None else: self.__grip = WindowSizeGrip(self) self.__grip.raise_() def sizeGripEnabled(self): """ Is the size grip enabled. """ return bool(self.__grip) def addPage(self, name, page): """ Add the `page` (:class:`MenuPage`) with `name` and return it's index. The `page.icon()` will be used as the icon in the tab bar. """ icon = page.icon() tip = name if page.toolTip(): tip = page.toolTip() index = self.__pages.addPage(page, name, icon, tip) # Route the page's signals page.triggered.connect(self.__onTriggered) page.hovered.connect(self.hovered) # Install event filter to intercept key presses. page.view().installEventFilter(self) return index def createPage(self, index): """ Create a new page based on the contents of an index (:class:`QModeIndex`) item. """ page = MenuPage(self) page.setModel(index.model()) page.setRootIndex(index) view = page.view() if sys.platform == "darwin": view.verticalScrollBar().setAttribute(Qt.WA_MacMiniSize, True) # Don't show the focus frame because it expands into the tab # bar at the top. view.setAttribute(Qt.WA_MacShowFocusRect, False) name = unicode(index.data(Qt.DisplayRole)) page.setTitle(name) icon = index.data(Qt.DecorationRole).toPyObject() if isinstance(icon, QIcon): page.setIcon(icon) page.setToolTip(index.data(Qt.ToolTipRole).toPyObject()) return page def setModel(self, model): """ Set the model containing the actions. """ root = model.invisibleRootItem() for i in range(root.rowCount()): item = root.child(i) index = item.index() page = self.createPage(index) page.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) i = self.addPage(page.title(), page) brush = index.data(QtWidgetRegistry.BACKGROUND_ROLE) if brush.isValid(): brush = brush.toPyObject() base_color = brush.color() button = self.__pages.tabButton(i) button.setStyleSheet( TAB_BUTTON_STYLE_TEMPLATE % (create_css_gradient(base_color), create_css_gradient(base_color.darker(120)))) self.__model = model self.__suggestPage.setModel(model) def setFilterFunc(self, func): """ Set a filter function. """ if func != self.__filterFunc: self.__filterFunc = func for i in range(0, self.__pages.count()): self.__pages.page(i).setFilterFunc(func) def popup(self, pos=None, searchText=""): """ Popup the menu at `pos` (in screen coordinates). 'Search' text field is initialized with `searchText` if provided. """ if pos is None: pos = QPoint() self.__clearCurrentItems() self.__search.setText(searchText) self.__suggestPage.setFilterFixedString(searchText) self.ensurePolished() if self.testAttribute(Qt.WA_Resized) and self.sizeGripEnabled(): size = self.size() else: size = self.sizeHint() desktop = QApplication.desktop() screen_geom = desktop.availableGeometry(pos) # Adjust the size to fit inside the screen. if size.height() > screen_geom.height(): size.setHeight(screen_geom.height()) if size.width() > screen_geom.width(): size.setWidth(screen_geom.width()) geom = QRect(pos, size) if geom.top() < screen_geom.top(): geom.setTop(screen_geom.top()) if geom.left() < screen_geom.left(): geom.setLeft(screen_geom.left()) bottom_margin = screen_geom.bottom() - geom.bottom() right_margin = screen_geom.right() - geom.right() if bottom_margin < 0: # Falls over the bottom of the screen, move it up. geom.translate(0, bottom_margin) # TODO: right to left locale if right_margin < 0: # Falls over the right screen edge, move the menu to the # other side of pos. geom.translate(-size.width(), 0) self.setGeometry(geom) self.show() if searchText: self.setFocusProxy(self.__search) else: self.setFocusProxy(None) def exec_(self, pos=None, searchText=""): """ Execute the menu at position `pos` (in global screen coordinates). Return the triggered :class:`QAction` or `None` if no action was triggered. 'Search' text field is initialized with `searchText` if provided. """ self.popup(pos, searchText) self.setFocus(Qt.PopupFocusReason) self.__triggeredAction = None self.__loop = QEventLoop() self.__loop.exec_() self.__loop.deleteLater() self.__loop = None action = self.__triggeredAction self.__triggeredAction = None return action def hideEvent(self, event): """ Reimplemented from :class:`QWidget` """ FramelessWindow.hideEvent(self, event) if self.__loop: self.__loop.exit() def setCurrentPage(self, page): """ Set the current shown page to `page`. """ self.__pages.setCurrentPage(page) def setCurrentIndex(self, index): """ Set the current page index. """ self.__pages.setCurrentIndex(index) def __clearCurrentItems(self): """ Clear any selected (or current) items in all the menus. """ for i in range(self.__pages.count()): self.__pages.page(i).view().selectionModel().clear() def __onTriggered(self, action): """ Re-emit the action from the page. """ self.__triggeredAction = action # Hide and exit the event loop if necessary. self.hide() self.triggered.emit(action) def __on_textEdited(self, text): self.__suggestPage.setFilterFixedString(text) self.__pages.setCurrentPage(self.__suggestPage) def triggerSearch(self): """ Trigger action search. This changes to current page to the 'Suggest' page and sets the keyboard focus to the search line edit. """ self.__pages.setCurrentPage(self.__suggestPage) self.__search.setFocus(Qt.ShortcutFocusReason) # Make sure that the first enabled item is set current. self.__suggestPage.ensureCurrent() def keyPressEvent(self, event): if event.text(): # Ignore modifiers, ... self.__search.setFocus(Qt.ShortcutFocusReason) self.setCurrentIndex(0) self.__search.keyPressEvent(event) FramelessWindow.keyPressEvent(self, event) event.accept() def event(self, event): if event.type() == QEvent.ShortcutOverride: log.debug("Overriding shortcuts") event.accept() return True return FramelessWindow.event(self, event) def eventFilter(self, obj, event): if isinstance(obj, QTreeView): etype = event.type() if etype == QEvent.KeyPress: # ignore modifiers non printable characters, Enter, ... if event.text() and event.key() not in \ [Qt.Key_Enter, Qt.Key_Return]: self.__search.setFocus(Qt.ShortcutFocusReason) self.setCurrentIndex(0) self.__search.keyPressEvent(event) return True return FramelessWindow.eventFilter(self, obj, event)
class InternalShell(PythonShellWidget): """Shell base widget: link between PythonShellWidget and Interpreter""" def __init__(self, parent=None, namespace=None, commands=[], message="", max_line_count=300, font=None, exitfunc=None, profile=False, multithreaded=True, light_background=True): PythonShellWidget.__init__(self, parent, get_conf_path('.history_internal.py'), profile) self.set_light_background(light_background) self.multithreaded = multithreaded self.setMaximumBlockCount(max_line_count) if font is not None: self.set_font(font) # Allow raw_input support: self.input_loop = None self.input_mode = False # KeyboardInterrupt support self.interrupted = False # used only for not-multithreaded mode self.connect(self, SIGNAL("keyboard_interrupt()"), self.keyboard_interrupt) # Code completion / calltips getcfg = lambda option: CONF.get('internal_console', option) case_sensitive = getcfg('codecompletion/case_sensitive') show_single = getcfg('codecompletion/show_single') self.set_codecompletion_case(case_sensitive) self.set_codecompletion_single(show_single) # keyboard events management self.eventqueue = [] # Init interpreter self.exitfunc = exitfunc self.commands = commands self.message = message self.interpreter = None self.start_interpreter(namespace) # Clear status bar self.emit(SIGNAL("status(QString)"), '') # Embedded shell -- requires the monitor (which installs the # 'open_in_spyder' function in builtins) if hasattr(__builtin__, 'open_in_spyder'): self.connect(self, SIGNAL("go_to_error(QString)"), self.open_with_external_spyder) #------ Interpreter def start_interpreter(self, namespace): """Start Python interpreter""" self.clear() if self.interpreter is not None: self.interpreter.closing() self.interpreter = Interpreter(namespace, self.exitfunc, SysOutput, WidgetProxy, DEBUG) self.connect(self.interpreter.stdout_write, SIGNAL("void data_avail()"), self.stdout_avail) self.connect(self.interpreter.stderr_write, SIGNAL("void data_avail()"), self.stderr_avail) self.connect(self.interpreter.widget_proxy, SIGNAL("set_readonly(bool)"), self.setReadOnly) self.connect(self.interpreter.widget_proxy, SIGNAL("new_prompt(QString)"), self.new_prompt) self.connect(self.interpreter.widget_proxy, SIGNAL("edit(QString,bool)"), self.edit_script) self.connect(self.interpreter.widget_proxy, SIGNAL("wait_input(QString)"), self.wait_input) if self.multithreaded: self.interpreter.start() # Interpreter banner banner = create_banner(self.message) self.write(banner, prompt=True) # Initial commands for cmd in self.commands: self.run_command(cmd, history=False, new_prompt=False) # First prompt self.new_prompt(self.interpreter.p1) self.emit(SIGNAL("refresh()")) return self.interpreter def exit_interpreter(self): """Exit interpreter""" self.interpreter.exit_flag = True if self.multithreaded: self.interpreter.stdin_write.write('\n') self.interpreter.restore_stds() def edit_script(self, filename, external_editor): filename = unicode(filename) if external_editor: self.external_editor(filename) else: self.parent().edit_script(filename) def stdout_avail(self): """Data is available in stdout, let's empty the queue and write it!""" data = self.interpreter.stdout_write.empty_queue() if data: self.write(data) def stderr_avail(self): """Data is available in stderr, let's empty the queue and write it!""" data = self.interpreter.stderr_write.empty_queue() if data: self.write(data, error=True) self.flush(error=True) #------Raw input support def wait_input(self, prompt=''): """Wait for input (raw_input support)""" self.new_prompt(prompt) self.setFocus() self.input_mode = True self.input_loop = QEventLoop() self.input_loop.exec_() self.input_loop = None def end_input(self, cmd): """End of wait_input mode""" self.input_mode = False self.input_loop.exit() self.interpreter.widget_proxy.end_input(cmd) #----- Menus, actions, ... def setup_context_menu(self): """Reimplement PythonShellWidget method""" PythonShellWidget.setup_context_menu(self) self.help_action = create_action(self, _("Help..."), icon=get_std_icon('DialogHelpButton'), triggered=self.help) self.menu.addAction(self.help_action) def help(self): """Help on Spyder console""" QMessageBox.about( self, _("Help"), """<b>%s</b> <p><i>%s</i><br> edit foobar.py <p><i>%s</i><br> xedit foobar.py <p><i>%s</i><br> run foobar.py <p><i>%s</i><br> clear x, y <p><i>%s</i><br> !ls <p><i>%s</i><br> object? <p><i>%s</i><br> result = oedit(object) """ % (_('Shell special commands:'), _('Internal editor:'), _('External editor:'), _('Run script:'), _('Remove references:'), _('System commands:'), _('Python help:'), _('GUI-based editor:'))) #------ External editing def open_with_external_spyder(self, text): """Load file in external Spyder's editor, if available This method is used only for embedded consoles (could also be useful if we ever implement the magic %edit command)""" match = get_error_match(unicode(text)) if match: fname, lnb = match.groups() __builtin__.open_in_spyder(fname, int(lnb)) def external_editor(self, filename, goto=-1): """Edit in an external editor Recommended: SciTE (e.g. to go to line where an error did occur)""" editor_path = CONF.get('internal_console', 'external_editor/path') goto_option = CONF.get('internal_console', 'external_editor/gotoline') try: if goto > 0 and goto_option: Popen(r'%s "%s" %s%d' % (editor_path, filename, goto_option, goto)) else: Popen(r'%s "%s"' % (editor_path, filename)) except OSError: self.write_error("External editor was not found:" " %s\n" % editor_path) #------ I/O def flush(self, error=False, prompt=False): """Reimplement ShellBaseWidget method""" PythonShellWidget.flush(self, error=error, prompt=prompt) if self.interrupted: self.interrupted = False raise KeyboardInterrupt #------ Clear terminal def clear_terminal(self): """Reimplement ShellBaseWidget method""" self.clear() self.new_prompt(self.interpreter.p2 if self.interpreter.more else self. interpreter.p1) #------ Keyboard events def on_enter(self, command): """on_enter""" if self.profile: # Simple profiling test t0 = time() for _ in range(10): self.execute_command(command) self.insert_text(u"\n<Δt>=%dms\n" % (1e2 * (time() - t0))) self.new_prompt(self.interpreter.p1) else: self.execute_command(command) self.__flush_eventqueue() def keyPressEvent(self, event): """ Reimplement Qt Method Enhanced keypress event handler """ if self.preprocess_keyevent(event): # Event was accepted in self.preprocess_keyevent return self.postprocess_keyevent(event) def __flush_eventqueue(self): """Flush keyboard event queue""" while self.eventqueue: past_event = self.eventqueue.pop(0) self.postprocess_keyevent(past_event) #------ Command execution def keyboard_interrupt(self): """Simulate keyboard interrupt""" if self.multithreaded: self.interpreter.raise_keyboard_interrupt() else: if self.interpreter.more: self.write_error("\nKeyboardInterrupt\n") self.interpreter.more = False self.new_prompt(self.interpreter.p1) self.interpreter.resetbuffer() else: self.interrupted = True def execute_lines(self, lines): """ Execute a set of lines as multiple command lines: multiple lines of text to be executed as single commands """ for line in lines.splitlines(): stripped_line = line.strip() if stripped_line.startswith('#'): continue self.write(line + os.linesep, flush=True) self.execute_command(line + "\n") self.flush() def execute_command(self, cmd): """ Execute a command cmd: one-line command only, with '\n' at the end """ if self.input_mode: self.end_input(cmd) return if cmd.endswith('\n'): cmd = cmd[:-1] # cls command if cmd == 'cls': self.clear_terminal() return self.run_command(cmd) def run_command(self, cmd, history=True, new_prompt=True): """Run command in interpreter""" if not cmd: cmd = '' else: if history: self.add_to_history(cmd) self.interpreter.stdin_write.write(cmd.encode("utf-8") + '\n') if not self.multithreaded: self.interpreter.run_line() self.emit(SIGNAL("refresh()")) #------ Code completion / Calltips def _eval(self, text): """Is text a valid object?""" return self.interpreter.eval(text) def get_dir(self, objtxt): """Return dir(object)""" obj, valid = self._eval(objtxt) if valid: return getobjdir(obj) def get_globals_keys(self): """Return shell globals() keys""" return self.interpreter.namespace.keys() def get_cdlistdir(self): """Return shell current directory list dir""" return os.listdir(os.getcwdu()) def iscallable(self, objtxt): """Is object callable?""" obj, valid = self._eval(objtxt) if valid: return callable(obj) def get_arglist(self, objtxt): """Get func/method argument list""" obj, valid = self._eval(objtxt) if valid: return getargtxt(obj) def get__doc__(self, objtxt): """Get object __doc__""" obj, valid = self._eval(objtxt) if valid: return obj.__doc__ def get_doc(self, objtxt): """Get object documentation""" obj, valid = self._eval(objtxt) if valid: return getdoc(obj) def get_source(self, objtxt): """Get object source""" obj, valid = self._eval(objtxt) if valid: return getsource(obj) def is_defined(self, objtxt, force_import=False): """Return True if object is defined""" return self.interpreter.is_defined(objtxt, force_import)
class UserSettingsDialog(QMainWindow): """ A User Settings/Defaults dialog. """ MAC_UNIFIED = True def __init__(self, parent=None, **kwargs): QMainWindow.__init__(self, parent, **kwargs) self.setWindowFlags(Qt.Dialog) self.setWindowModality(Qt.ApplicationModal) self.layout().setSizeConstraint(QVBoxLayout.SetFixedSize) self.__macUnified = sys.platform == "darwin" and self.MAC_UNIFIED self._manager = BindingManager(self, submitPolicy=BindingManager.AutoSubmit) self.__loop = None self.__settings = config.settings() self.__setupUi() def __setupUi(self): """Set up the UI. """ if self.__macUnified: self.tab = QToolBar() self.addToolBar(Qt.TopToolBarArea, self.tab) self.setUnifiedTitleAndToolBarOnMac(True) # This does not seem to work self.setWindowFlags(self.windowFlags() & \ ~Qt.MacWindowToolBarButtonHint) self.tab.actionTriggered[QAction].connect( self.__macOnToolBarAction) central = QStackedWidget() central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: self.tab = central = QTabWidget(self) self.stack = central self.setCentralWidget(central) # General Tab tab = QWidget() self.addTab(tab, self.tr("General"), toolTip=self.tr("General Options")) form = QFormLayout() tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) nodes = QWidget(self, objectName="nodes") nodes.setLayout(QVBoxLayout()) nodes.layout().setContentsMargins(0, 0, 0, 0) cb_anim = QCheckBox(self.tr("Enable node animations"), objectName="enable-node-animations", toolTip=self.tr( "Enable shadow and ping animations for nodes " "in the workflow.")) self.bind(cb_anim, "checked", "schemeedit/enable-node-animations") nodes.layout().addWidget(cb_anim) form.addRow(self.tr("Nodes"), nodes) links = QWidget(self, objectName="links") links.setLayout(QVBoxLayout()) links.layout().setContentsMargins(0, 0, 0, 0) cb_show = QCheckBox(self.tr("Show channel names between widgets"), objectName="show-channel-names", toolTip=self.tr( "Show source and sink channel names " "over the links.")) self.bind(cb_show, "checked", "schemeedit/show-channel-names") links.layout().addWidget(cb_show) form.addRow(self.tr("Links"), links) quickmenu = QWidget(self, objectName="quickmenu-options") quickmenu.setLayout(QVBoxLayout()) quickmenu.layout().setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("On double click"), toolTip=self.tr("Open quick menu on a double click " "on an empty spot in the canvas")) cb2 = QCheckBox(self.tr("On right click"), toolTip=self.tr("Open quick menu on a right click " "on an empty spot in the canvas")) cb3 = QCheckBox(self.tr("On space key press"), toolTip=self.tr("On Space key press while the mouse" "is hovering over the canvas.")) cb4 = QCheckBox(self.tr("On any key press"), toolTip=self.tr("On any key press while the mouse" "is hovering over the canvas.")) self.bind(cb1, "checked", "quickmenu/trigger-on-double-click") self.bind(cb2, "checked", "quickmenu/trigger-on-right-click") self.bind(cb3, "checked", "quickmenu/trigger-on-space-key") self.bind(cb4, "checked", "quickmenu/trigger-on-any-key") quickmenu.layout().addWidget(cb1) quickmenu.layout().addWidget(cb2) quickmenu.layout().addWidget(cb3) quickmenu.layout().addWidget(cb4) form.addRow(self.tr("Open quick menu on"), quickmenu) startup = QWidget(self, objectName="startup-group") startup.setLayout(QVBoxLayout()) startup.layout().setContentsMargins(0, 0, 0, 0) cb_splash = QCheckBox(self.tr("Show splash screen"), self, objectName="show-splash-screen") cb_welcome = QCheckBox(self.tr("Show welcome screen"), self, objectName="show-welcome-screen") self.bind(cb_splash, "checked", "startup/show-splash-screen") self.bind(cb_welcome, "checked", "startup/show-welcome-screen") startup.layout().addWidget(cb_splash) startup.layout().addWidget(cb_welcome) form.addRow(self.tr("On startup"), startup) toolbox = QWidget(self, objectName="toolbox-group") toolbox.setLayout(QVBoxLayout()) toolbox.layout().setContentsMargins(0, 0, 0, 0) exclusive = QCheckBox(self.tr("Only one tab can be open at a time")) self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive") toolbox.layout().addWidget(exclusive) form.addRow(self.tr("Tool box"), toolbox) tab.setLayout(form) # Output Tab tab = QWidget() self.addTab(tab, self.tr("Output"), toolTip="Output Redirection") form = QFormLayout() box = QWidget(self, objectName="streams") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Standard output")) cb2 = QCheckBox(self.tr("Standard error")) self.bind(cb1, "checked", "output/redirect-stdout") self.bind(cb2, "checked", "output/redirect-stderr") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Redirect output"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) combo = QComboBox() combo.addItems([ self.tr("Critical"), self.tr("Error"), self.tr("Warn"), self.tr("Info"), self.tr("Debug") ]) cb = QCheckBox(self.tr("Show output on 'Error'"), objectName="focus-on-error") self.bind(combo, "currentIndex", "logging/level") self.bind(cb, "checked", "output/show-on-error") layout.addWidget(combo) layout.addWidget(cb) box.setLayout(layout) form.addRow(self.tr("Logging"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Stay on top"), objectName="stay-on-top") cb2 = QCheckBox(self.tr("Dockable"), objectName="output-dockable") self.bind(cb1, "checked", "output/stay-on-top") self.bind(cb2, "checked", "output/dockable") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Output window"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Open in external browser"), objectName="open-in-external-browser") cb2 = QCheckBox(self.tr("Stay on top"), objectName="help-stay-on-top") cb3 = QCheckBox(self.tr("Dockable"), objectName="help-dockable") self.bind(cb1, "checked", "help/open-in-external-browser") self.bind(cb2, "checked", "help/stay-on-top") self.bind(cb3, "checked", "help/dockable") layout.addWidget(cb1) layout.addWidget(cb2) layout.addWidget(cb3) box.setLayout(layout) form.addRow(self.tr("Help window"), box) tab.setLayout(form) if self.__macUnified: # Need some sensible size otherwise mac unified toolbar 'takes' # the space that should be used for layout of the contents self.adjustSize() def addTab(self, widget, text, toolTip=None, icon=None): if self.__macUnified: action = QAction(text, self) if toolTip: action.setToolTip(toolTip) if icon: action.setIcon(toolTip) action.setData(len(self.tab.actions())) self.tab.addAction(action) self.stack.addWidget(widget) else: i = self.tab.addTab(widget, text) if toolTip: self.tab.setTabToolTip(i, toolTip) if icon: self.tab.setTabIcon(i, icon) def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.hide() self.deleteLater() def bind(self, source, source_property, key, transformer=None): target = UserDefaultsPropertyBinding(self.__settings, key) source = PropertyBinding(source, source_property) source.set(target.get()) self._manager.bind(target, source) def commit(self): self._manager.commit() def revert(self): self._manager.revert() def reset(self): for target, source in self._manager.bindings(): try: source.reset() except NotImplementedError: # Cannot reset. pass except Exception: log.error("Error reseting %r", source.propertyName, exc_info=True) def exec_(self): self.__loop = QEventLoop() self.show() status = self.__loop.exec_() self.__loop = None return status def hideEvent(self, event): QMainWindow.hideEvent(self, event) if self.__loop is not None: self.__loop.exit(0) self.__loop = None def __macOnToolBarAction(self, action): self.stack.setCurrentIndex(action.data())
class InteractiveShell(PythonShellWidget): """Shell base widget: link between PythonShellWidget and Interpreter""" p1 = ">>> " p2 = "... " def __init__(self, parent=None, namespace=None, commands=None, message="", font=None, debug=False, exitfunc=None, profile=False): PythonShellWidget.__init__(self, parent, get_conf_path('.history_ic.py'), debug, profile) if font is not None: self.set_font(font) # Capture all interactive input/output self.initial_stdout = sys.stdout self.initial_stderr = sys.stderr self.initial_stdin = sys.stdin self.stdout = IOHandler('<spyder_stdout>', 'w', write_func=self.write, flush_func=lambda: self.flush(error=False)) self.stderr = IOHandler('<spyder_stderr>', 'w', write_func=self.write_error, flush_func=lambda: self.flush(error=True)) self.stdin = IOHandler('<spyder_stdin>', 'r', read_func=self.wait_input) self.redirect_stds() # KeyboardInterrupt support self.interrupted = False self.connect(self, SIGNAL("keyboard_interrupt()"), self.keyboard_interrupt) # Code completion / calltips getcfg = lambda option: CONF.get('shell', option) case_sensitive = getcfg('autocompletion/case-sensitivity') show_single = getcfg('autocompletion/select-single') from_document = getcfg('autocompletion/from-document') self.setup_code_completion(case_sensitive, show_single, from_document) # keyboard events management self.busy = False self.eventqueue = [] # Execution Status self.more = False # Init interpreter self.exitfunc = exitfunc self.commands = commands self.message = message self.interpreter = None self.start_interpreter(namespace) # Clear status bar self.emit(SIGNAL("status(QString)"), QString()) #------ Standard input/output def redirect_stds(self): """Redirects stds""" if not self.debug: sys.stdout = self.stdout sys.stderr = self.stderr sys.stdin = self.stdin def restore_stds(self): """Restore stds""" if not self.debug: sys.stdout = self.initial_stdout sys.stderr = self.initial_stderr sys.stdin = self.initial_stdin #------ Interpreter def start_interpreter(self, namespace): """Start Python interpreter""" self.clear() self.restore_stds() if self.interpreter is not None: self.interpreter.closing() self.interpreter = Interpreter(namespace, self.exitfunc, self.raw_input, self.help_replacement) self.redirect_stds() # interpreter banner banner = create_banner(self.tr('Type "copyright", "credits" or "license" for more information.'), self.message) self.write(banner, prompt=True) # Initial commands for cmd in self.commands: self.run_command(cmd, history=False, new_prompt=False) # First prompt self.new_prompt(self.p1) self.emit(SIGNAL("refresh()")) return self.interpreter #----- Menus, actions, ... def setup_context_menu(self): """Reimplement PythonShellWidget method""" PythonShellWidget.setup_context_menu(self) self.help_action = create_action(self, translate("InteractiveShell", "Help..."), icon=get_std_icon('DialogHelpButton'), triggered=self.help) self.menu.addAction(self.help_action) def help(self): """Help on Spyder console""" QMessageBox.about(self, translate("InteractiveShell", "Help"), self.tr("""<b>%1</b> <p><i>%2</i><br> edit foobar.py <p><i>%3</i><br> xedit foobar.py <p><i>%4</i><br> run foobar.py <p><i>%5</i><br> clear x, y <p><i>%6</i><br> !ls <p><i>%7</i><br> object? <p><i>%8</i><br> result = oedit(object) """) \ .arg(translate("InteractiveShell", 'Shell special commands:')) \ .arg(translate("InteractiveShell", 'Internal editor:')) \ .arg(translate("InteractiveShell", 'External editor:')) \ .arg(translate("InteractiveShell", 'Run script:')) \ .arg(translate("InteractiveShell", 'Remove references:')) \ .arg(translate("InteractiveShell", 'System commands:')) \ .arg(translate("InteractiveShell", 'Python help:')) \ .arg(translate("InteractiveShell", 'GUI-based editor:')) ) #------ External editing def external_editor(self, filename, goto=-1): """Edit in an external editor Recommended: SciTE (e.g. to go to line where an error did occur)""" editor_path = CONF.get('shell', 'external_editor/path') goto_option = CONF.get('shell', 'external_editor/gotoline') try: if goto > 0 and goto_option: Popen(r'%s "%s" %s%d' % (editor_path, filename, goto_option, goto)) else: Popen(r'%s "%s"' % (editor_path, filename)) except OSError: self.write_error("External editor was not found:" " %s\n" % editor_path) #------ I/O def raw_input(self, prompt=''): """Reimplementation of raw_input builtin""" self.new_prompt(prompt) self.setFocus() inp = self.wait_input() return inp def help_replacement(self, text=None, interactive=False): """For help() support (to be implemented...)""" if text is not None and not interactive: return pydoc.help(text) elif text is None: pyver = "%d.%d" % (sys.version_info[0], sys.version_info[1]) self.write(""" Welcome to Python %s! This is the online help utility. If this is your first time using Python, you should definitely check out the tutorial on the Internet at http://www.python.org/doc/tut/. Enter the name of any module, keyword, or topic to get help on writing Python programs and using Python modules. To quit this help utility and return to the interpreter, just type "quit". To get a list of available modules, keywords, or topics, type "modules", "keywords", or "topics". Each module also comes with a one-line summary of what it does; to list the modules whose summaries contain a given word such as "spam", type "modules spam". """ % pyver) else: text = text.strip() try: eval("pydoc.help(%s)" % text) except (NameError, SyntaxError): print "no Python documentation found for '%r'" % text self.write(os.linesep) self.new_prompt("help> ") inp = self.wait_input() if inp.strip(): self.help_replacement(inp, interactive=True) else: self.write(""" You are now leaving help and returning to the Python interpreter. If you want to ask for help on a particular object directly from the interpreter, you can type "help(object)". Executing "help('string')" has the same effect as typing a particular string at the help> prompt. """) def wait_input(self): """Wait for input (raw_input)""" self.input_data = None # If shell is closed, None will be returned self.input_mode = True self.input_loop = QEventLoop() self.input_loop.exec_() self.input_loop = None return self.input_data def end_input(self, cmd): """End of wait_input mode""" self.input_data = cmd self.input_mode = False self.input_loop.exit() def flush(self, error=False, prompt=False): """Reimplement PythonShellWidget method""" PythonShellWidget.flush(self, error=error, prompt=prompt) if self.interrupted: self.interrupted = False raise KeyboardInterrupt #------ Clear terminal def clear_terminal(self): """Reimplement ShellBaseWidget method""" self.clear() self.new_prompt(self.p2 if self.more else self.p1) #------ Keyboard events def on_enter(self, command): """on_enter""" self.busy = True if self.profile: # Simple profiling test t0 = time() for _ in range(10): self.execute_command(command) self.insert_text(u"\n<Δt>=%dms\n" % (1e2*(time()-t0))) self.new_prompt(self.p1) else: self.execute_command(command) self.busy = False self.__flush_eventqueue() def keyPressEvent(self, event): """ Reimplement Qt Method Enhanced keypress event handler """ if self.preprocess_keyevent(event): # Event was accepted in self.preprocess_keyevent return if self.busy and (not self.input_mode): #XXX: Until Issue 37 is fixed, disabling busy buffer # # Ignoring all events except KeyboardInterrupt # # Keep however these events in self.eventqueue # self.eventqueue.append(keyevent2tuple(event)) event.accept() else: self.postprocess_keyevent(event) def __flush_eventqueue(self): """Flush keyboard event queue""" while self.eventqueue: past_event = self.eventqueue.pop(0) self.postprocess_keyevent(past_event) #------ Command execution def keyboard_interrupt(self): """Simulate keyboard interrupt""" if self.busy: # Interrupt only if console is busy self.interrupted = True elif self.more: self.write_error("\nKeyboardInterrupt\n") self.more = False self.new_prompt(self.p1) self.interpreter.resetbuffer() def execute_lines(self, lines): """ Execute a set of lines as multiple command lines: multiple lines of text to be executed as single commands """ for line in lines.splitlines(): stripped_line = line.strip() if len(stripped_line) == 0 or stripped_line.startswith('#'): continue self.write(line+os.linesep, flush=True) self.execute_command(line+"\n") self.flush() def execute_command(self, cmd): """ Execute a command cmd: one-line command only, with '\n' at the end """ if self.input_mode: self.end_input(cmd) return if cmd.endswith('\n'): cmd = cmd[:-1] # cls command if cmd == 'cls': self.clear_terminal() return self.run_command(cmd) def run_command(self, cmd, history=True, new_prompt=True): """Run command in interpreter""" # Before running command self.emit(SIGNAL("status(QString)"), self.tr('Busy...')) self.emit(SIGNAL("executing_command(bool)"), True) if not cmd: cmd = '' else: if history: self.add_to_history(cmd) wd_before = os.getcwdu() # -- Special commands type I # (transformed into commands executed in the interpreter) # ? command special_pattern = r"^%s (?:r\')?(?:u\')?\"?\'?([a-zA-Z0-9_\.]+)" run_match = re.match(special_pattern % 'run', cmd) help_match = re.match(r'^([a-zA-Z0-9_\.]+)\?$', cmd) if help_match: cmd = 'help(%s)' % help_match.group(1) # run command elif run_match: filename = guess_filename(run_match.groups()[0]) cmd = 'execfile(r"%s")' % filename # -- End of Special commands type I # -- Special commands type II # (don't need code execution in interpreter) xedit_match = re.match(special_pattern % 'xedit', cmd) edit_match = re.match(special_pattern % 'edit', cmd) clear_match = re.match(r"^clear ([a-zA-Z0-9_, ]+)", cmd) # (external) edit command if xedit_match: filename = guess_filename(xedit_match.groups()[0]) self.external_editor(filename) # local edit command elif edit_match: filename = guess_filename(edit_match.groups()[0]) if osp.isfile(filename): self.parent().edit_script(filename) else: self.write_error("No such file or directory: %s\n" % filename) # remove reference (equivalent to MATLAB's clear command) elif clear_match: varnames = clear_match.groups()[0].replace(' ', '').split(',') for varname in varnames: try: self.interpreter.namespace.pop(varname) except KeyError: pass # Execute command elif cmd.startswith('!'): # System ! command pipe = Popen(cmd[1:], shell=True, stdin=PIPE, stderr=PIPE, stdout=PIPE) txt_out = encoding.transcode( pipe.stdout.read() ) txt_err = encoding.transcode( pipe.stderr.read().rstrip() ) if txt_err: self.write_error(txt_err) if txt_out: self.write(txt_out) self.write('\n') self.more = False # -- End of Special commands type II else: # Command executed in the interpreter self.more = self.interpreter.push(cmd) self.emit(SIGNAL("refresh()")) if os.getcwdu() != wd_before: # Force the explorer widget to change its current directory: self.emit(SIGNAL("refresh_explorer()")) # Refresh current directory contents in explorer widget: self.emit(SIGNAL("refresh_explorer(QString)"), os.getcwdu()) if new_prompt: self.new_prompt(self.p2 if self.more else self.p1) if not self.more: self.interpreter.resetbuffer() # After running command self.emit(SIGNAL("executing_command(bool)"), False) self.emit(SIGNAL("status(QString)"), QString()) #------ Code completion / Calltips def _eval(self, text): """Is text a valid object?""" return self.interpreter.eval(text) def get_dir(self, objtxt): """Return dir(object)""" obj, valid = self._eval(objtxt) if valid: return getobjdir(obj) def iscallable(self, objtxt): """Is object callable?""" obj, valid = self._eval(objtxt) if valid: return callable(obj) def get_arglist(self, objtxt): """Get func/method argument list""" obj, valid = self._eval(objtxt) if valid: return getargtxt(obj) def get__doc__(self, objtxt): """Get object __doc__""" obj, valid = self._eval(objtxt) if valid: return obj.__doc__ def get_doc(self, objtxt): """Get object documentation""" obj, valid = self._eval(objtxt) if valid: return getdoc(obj) def get_source(self, objtxt): """Get object source""" obj, valid = self._eval(objtxt) if valid: return getsource(obj)
class CategoryPopupMenu(FramelessWindow): triggered = Signal(QAction) hovered = Signal(QAction) def __init__(self, parent=None, **kwargs): FramelessWindow.__init__(self, parent, **kwargs) self.setWindowFlags(self.windowFlags() | Qt.Popup) layout = QVBoxLayout() layout.setContentsMargins(6, 6, 6, 6) self.__menu = MenuPage() self.__menu.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE) if sys.platform == "darwin": self.__menu.view().setAttribute(Qt.WA_MacShowFocusRect, False) self.__menu.triggered.connect(self.__onTriggered) self.__menu.hovered.connect(self.hovered) self.__dragListener = ItemViewDragStartEventListener(self) self.__dragListener.dragStarted.connect(self.__onDragStarted) self.__menu.view().viewport().installEventFilter(self.__dragListener) layout.addWidget(self.__menu) self.setLayout(layout) self.__action = None self.__loop = None self.__item = None def setCategoryItem(self, item): """ Set the category root item (:class:`QStandardItem`). """ self.__item = item model = item.model() self.__menu.setModel(model) self.__menu.setRootIndex(item.index()) def popup(self, pos=None): if pos is None: pos = self.pos() geom = widget_popup_geometry(pos, self) self.setGeometry(geom) self.show() def exec_(self, pos=None): self.popup(pos) self.__loop = QEventLoop() self.__action = None self.__loop.exec_() self.__loop = None if self.__action is not None: action = self.__action else: action = None return action def hideEvent(self, event): if self.__loop is not None: self.__loop.exit(0) return FramelessWindow.hideEvent(self, event) def __onTriggered(self, action): self.__action = action self.triggered.emit(action) self.hide() if self.__loop: self.__loop.exit(0) def __onDragStarted(self, index): desc = index.data(QtWidgetRegistry.WIDGET_DESC_ROLE) icon = index.data(Qt.DecorationRole) drag_data = QMimeData() drag_data.setData( "application/vnv.orange-canvas.registry.qualified-name", desc.qualified_name ) drag = QDrag(self) drag.setPixmap(icon.pixmap(38)) drag.setMimeData(drag_data) # TODO: Should animate (accept) hide. self.hide() # When a drag is started and the menu hidden the item's tool tip # can still show for a short time UNDER the cursor preventing a # drop. viewport = self.__menu.view().viewport() filter = ToolTipEventFilter() viewport.installEventFilter(filter) drag.exec_(Qt.CopyAction) viewport.removeEventFilter(filter)
class InternalShell(PythonShellWidget): """Shell base widget: link between PythonShellWidget and Interpreter""" def __init__(self, parent=None, namespace=None, commands=[], message="", max_line_count=300, font=None, exitfunc=None, profile=False, multithreaded=True, light_background=True): PythonShellWidget.__init__(self, parent, get_conf_path('.history_internal.py'), profile) self.set_light_background(light_background) self.multithreaded = multithreaded self.setMaximumBlockCount(max_line_count) if font is not None: self.set_font(font) # Allow raw_input support: self.input_loop = None self.input_mode = False # KeyboardInterrupt support self.interrupted = False # used only for not-multithreaded mode self.connect(self, SIGNAL("keyboard_interrupt()"), self.keyboard_interrupt) # Code completion / calltips getcfg = lambda option: CONF.get('internal_console', option) case_sensitive = getcfg('codecompletion/case_sensitive') show_single = getcfg('codecompletion/show_single') self.set_codecompletion_case(case_sensitive) self.set_codecompletion_single(show_single) # keyboard events management self.eventqueue = [] # Init interpreter self.exitfunc = exitfunc self.commands = commands self.message = message self.interpreter = None self.start_interpreter(namespace) # Clear status bar self.emit(SIGNAL("status(QString)"), '') # Embedded shell -- requires the monitor (which installs the # 'open_in_spyder' function in builtins) if hasattr(__builtin__, 'open_in_spyder'): self.connect(self, SIGNAL("go_to_error(QString)"), self.open_with_external_spyder) #------ Interpreter def start_interpreter(self, namespace): """Start Python interpreter""" self.clear() if self.interpreter is not None: self.interpreter.closing() self.interpreter = Interpreter(namespace, self.exitfunc, SysOutput, WidgetProxy, DEBUG) self.connect(self.interpreter.stdout_write, SIGNAL("void data_avail()"), self.stdout_avail) self.connect(self.interpreter.stderr_write, SIGNAL("void data_avail()"), self.stderr_avail) self.connect(self.interpreter.widget_proxy, SIGNAL("set_readonly(bool)"), self.setReadOnly) self.connect(self.interpreter.widget_proxy, SIGNAL("new_prompt(QString)"), self.new_prompt) self.connect(self.interpreter.widget_proxy, SIGNAL("edit(QString,bool)"), self.edit_script) self.connect(self.interpreter.widget_proxy, SIGNAL("wait_input(QString)"), self.wait_input) if self.multithreaded: self.interpreter.start() # Interpreter banner banner = create_banner(self.message) self.write(banner, prompt=True) # Initial commands for cmd in self.commands: self.run_command(cmd, history=False, new_prompt=False) # First prompt self.new_prompt(self.interpreter.p1) self.emit(SIGNAL("refresh()")) return self.interpreter def exit_interpreter(self): """Exit interpreter""" self.interpreter.exit_flag = True if self.multithreaded: self.interpreter.stdin_write.write('\n') self.interpreter.restore_stds() def edit_script(self, filename, external_editor): filename = unicode(filename) if external_editor: self.external_editor(filename) else: self.parent().edit_script(filename) def stdout_avail(self): """Data is available in stdout, let's empty the queue and write it!""" data = self.interpreter.stdout_write.empty_queue() if data: self.write(data) def stderr_avail(self): """Data is available in stderr, let's empty the queue and write it!""" data = self.interpreter.stderr_write.empty_queue() if data: self.write(data, error=True) self.flush(error=True) #------Raw input support def wait_input(self, prompt=''): """Wait for input (raw_input support)""" self.new_prompt(prompt) self.setFocus() self.input_mode = True self.input_loop = QEventLoop() self.input_loop.exec_() self.input_loop = None def end_input(self, cmd): """End of wait_input mode""" self.input_mode = False self.input_loop.exit() self.interpreter.widget_proxy.end_input(cmd) #----- Menus, actions, ... def setup_context_menu(self): """Reimplement PythonShellWidget method""" PythonShellWidget.setup_context_menu(self) self.help_action = create_action(self, _("Help..."), icon=get_std_icon('DialogHelpButton'), triggered=self.help) self.menu.addAction(self.help_action) def help(self): """Help on Spyder console""" QMessageBox.about(self, _("Help"), """<b>%s</b> <p><i>%s</i><br> edit foobar.py <p><i>%s</i><br> xedit foobar.py <p><i>%s</i><br> run foobar.py <p><i>%s</i><br> clear x, y <p><i>%s</i><br> !ls <p><i>%s</i><br> object? <p><i>%s</i><br> result = oedit(object) """ % (_('Shell special commands:'), _('Internal editor:'), _('External editor:'), _('Run script:'), _('Remove references:'), _('System commands:'), _('Python help:'), _('GUI-based editor:'))) #------ External editing def open_with_external_spyder(self, text): """Load file in external Spyder's editor, if available This method is used only for embedded consoles (could also be useful if we ever implement the magic %edit command)""" match = get_error_match(unicode(text)) if match: fname, lnb = match.groups() __builtin__.open_in_spyder(fname, int(lnb)) def external_editor(self, filename, goto=-1): """Edit in an external editor Recommended: SciTE (e.g. to go to line where an error did occur)""" editor_path = CONF.get('internal_console', 'external_editor/path') goto_option = CONF.get('internal_console', 'external_editor/gotoline') try: if goto > 0 and goto_option: Popen(r'%s "%s" %s%d' % (editor_path, filename, goto_option, goto)) else: Popen(r'%s "%s"' % (editor_path, filename)) except OSError: self.write_error("External editor was not found:" " %s\n" % editor_path) #------ I/O def flush(self, error=False, prompt=False): """Reimplement ShellBaseWidget method""" PythonShellWidget.flush(self, error=error, prompt=prompt) if self.interrupted: self.interrupted = False raise KeyboardInterrupt #------ Clear terminal def clear_terminal(self): """Reimplement ShellBaseWidget method""" self.clear() self.new_prompt(self.interpreter.p2 if self.interpreter.more else self.interpreter.p1) #------ Keyboard events def on_enter(self, command): """on_enter""" if self.profile: # Simple profiling test t0 = time() for _ in range(10): self.execute_command(command) self.insert_text(u"\n<Δt>=%dms\n" % (1e2*(time()-t0))) self.new_prompt(self.interpreter.p1) else: self.execute_command(command) self.__flush_eventqueue() def keyPressEvent(self, event): """ Reimplement Qt Method Enhanced keypress event handler """ if self.preprocess_keyevent(event): # Event was accepted in self.preprocess_keyevent return self.postprocess_keyevent(event) def __flush_eventqueue(self): """Flush keyboard event queue""" while self.eventqueue: past_event = self.eventqueue.pop(0) self.postprocess_keyevent(past_event) #------ Command execution def keyboard_interrupt(self): """Simulate keyboard interrupt""" if self.multithreaded: self.interpreter.raise_keyboard_interrupt() else: if self.interpreter.more: self.write_error("\nKeyboardInterrupt\n") self.interpreter.more = False self.new_prompt(self.interpreter.p1) self.interpreter.resetbuffer() else: self.interrupted = True def execute_lines(self, lines): """ Execute a set of lines as multiple command lines: multiple lines of text to be executed as single commands """ for line in lines.splitlines(): stripped_line = line.strip() if stripped_line.startswith('#'): continue self.write(line+os.linesep, flush=True) self.execute_command(line+"\n") self.flush() def execute_command(self, cmd): """ Execute a command cmd: one-line command only, with '\n' at the end """ if self.input_mode: self.end_input(cmd) return if cmd.endswith('\n'): cmd = cmd[:-1] # cls command if cmd == 'cls': self.clear_terminal() return self.run_command(cmd) def run_command(self, cmd, history=True, new_prompt=True): """Run command in interpreter""" if not cmd: cmd = '' else: if history: self.add_to_history(cmd) self.interpreter.stdin_write.write(cmd.encode("utf-8") + '\n') if not self.multithreaded: self.interpreter.run_line() self.emit(SIGNAL("refresh()")) #------ Code completion / Calltips def _eval(self, text): """Is text a valid object?""" return self.interpreter.eval(text) def get_dir(self, objtxt): """Return dir(object)""" obj, valid = self._eval(objtxt) if valid: return getobjdir(obj) def get_globals_keys(self): """Return shell globals() keys""" return self.interpreter.namespace.keys() def get_cdlistdir(self): """Return shell current directory list dir""" return os.listdir(os.getcwdu()) def iscallable(self, objtxt): """Is object callable?""" obj, valid = self._eval(objtxt) if valid: return callable(obj) def get_arglist(self, objtxt): """Get func/method argument list""" obj, valid = self._eval(objtxt) if valid: return getargtxt(obj) def get__doc__(self, objtxt): """Get object __doc__""" obj, valid = self._eval(objtxt) if valid: return obj.__doc__ def get_doc(self, objtxt): """Get object documentation""" obj, valid = self._eval(objtxt) if valid: return getdoc(obj) def get_source(self, objtxt): """Get object source""" obj, valid = self._eval(objtxt) if valid: return getsource(obj) def is_defined(self, objtxt, force_import=False): """Return True if object is defined""" return self.interpreter.is_defined(objtxt, force_import)
class WaitForSignal(unittest.TestCase): def __init__( self, # The signal to wait for. signal, # The maximum time to wait for the signal to be emitted, in ms. timeoutMs, # True to self.assertif the signal wasn't emitted. assertIfNotRaised=True, # Expected parameters which this signal must supply. expectedSignalParams=None, # True to print exceptions raised in the event loop printExcTraceback=True, # Number of times this signal must be emitted numEmittedExpected=1): self.signal = signal self.timeoutMs = timeoutMs self.expectedSignalParams = expectedSignalParams self.assertIfNotRaised = assertIfNotRaised self.printExcTraceback = printExcTraceback self.numEmittedExpected = numEmittedExpected # Stores the result of comparing self.expectedSignalParams with the # actual params. self.areSenderSignalArgsWrong = False # The number of times this signal was emitted. self.numEmitted = 0 # Create a slot which receives a senderSignal with any number # of arguments. Check the arguments against their expected # values, if requested, storing the result in senderSignalArgsWrong[0]. # (I can't use senderSignalArgsWrong = True/False, since # non-local variables cannot be assigned in another scope). def signalSlot(self, *args): # If the senderSignal args should be checked and they # don't match, then they're wrong. In all other cases, # they're right. if self.expectedSignalParams: self.areSenderSignalArgsWrong = (self.expectedSignalParams != args) self.numEmitted += 1 if self._gotSignal(): # We received the requested signal, so exit the event loop or never # enter it (exit won't exit an event loop that hasn't been run). When # this is nested inside other WaitForSignal clauses, signals may be # received in another QEventLoop, even before this object's QEventLoop # starts. self.qe.exit() # True of the signal was emitted the expected number of times. def _gotSignal(self): return self.numEmitted == self.numEmittedExpected def __enter__(self): # Create an event loop to run in. Otherwise, we need to use the papp # (QApplication) main loop, which may already be running and therefore # unusable. self.qe = QEventLoop() # Connect both signals to a slot which quits the event loop. self.signal.connect(self.signalSlot) return self def __exit__(self, exc_type, exc_value, traceback): # Create a single-shot timer. Could use QTimer.singleShot(), # but can't cancel this / disconnect it. self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.qe.quit) self.timer.start(self.timeoutMs) # Catch any exceptions which the EventLoop would otherwise catch # and not re-raise. self.exceptions = None def excepthook(type_, value, tracebackObj): self.exceptions = (value, tracebackObj) if self.printExcTraceback: oldExcHook(type_, value, tracebackObj) self.qe.exit() oldExcHook = sys.excepthook sys.excepthook = excepthook # Wait for an emitted signal, unless it already occurred. if not self._gotSignal(): self.qe.exec_() # Restore the old exception hook sys.excepthook = oldExcHook # Clean up: don't allow the timer to call qe.quit after this # function exits, which would produce "interesting" behavior. timerIsActive = self.timer.isActive() self.timer.stop() # Stopping the timer may not cancel timeout signals in the # event queue. Disconnect the signal to be sure that loop # will never receive a timeout after the function exits. # Likewise, disconnect the senderSignal for the same reason. self.signal.disconnect(self.signalSlot) self.timer.timeout.disconnect(self.qe.quit) # If an exception occurred in the event loop, re-raise it. if self.exceptions: value, tracebackObj = self.exceptions raise value, None, tracebackObj # Check that the signal occurred. self.sawSignal = timerIsActive and not self.areSenderSignalArgsWrong if self.assertIfNotRaised: self.assertTrue(self.sawSignal) # Don't mask exceptions. return False