def update_widget_state(self, condition=None): # Update the state of the menu bar state = not self.gui_vars["gui_state"]["running_computations"] self.menuBar().setEnabled(state) state_computations = self.gui_vars["gui_state"]["running_computations"] if state_computations: if not self._cursor_set: QGuiApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self._cursor_set = True else: if self._cursor_set: QGuiApplication.restoreOverrideCursor() self._cursor_set = False # Forward to children self.central_widget.update_widget_state(condition) # Forward the updates to open windows self.wnd_manage_emission_lines.update_widget_state(condition) self.wnd_compute_roi_maps.update_widget_state(condition) self.wnd_image_wizard.update_widget_state(condition) self.wnd_load_quantitative_calibration.update_widget_state(condition) self.wnd_general_fitting_settings.update_widget_state(condition) self.wnd_fitting_parameters_shared.update_widget_state(condition) self.wnd_fitting_parameters_lines.update_widget_state(condition)
def switchToCli(self): if config.isHtmlTextInstalled: config.pluginContext = self.name QGuiApplication.instance().setApplicationName( "UniqueBible.app CLI") config.pluginContext = "" else: self.displayMessage( "CLI feature is not enabled! \n Install module 'html-text' first, by running 'pip3 install html-text'!" )
def _get_splash_image(): # gets the width of the screen where the main window was initialised width = QGuiApplication.primaryScreen().size().width() height = QGuiApplication.primaryScreen().size().height() # the proportion of the whole window size for the splash screen splash_screen_scaling = 0.25 return QPixmap(':/images/MantidSplashScreen_4k.jpg').scaled( width * splash_screen_scaling, height * splash_screen_scaling, Qt.KeepAspectRatio, Qt.SmoothTransformation)
class Client(QWebEnginePage): def __init__(self, url): self.app = QGuiApplication(sys.argv) QWebEnginePage.__init__(self) self.loadFinished.connect(self.on_page_load) self.mainFrame().load(QUrl(url)) self.app.exec_() def on_page_load(self): self.app.quit()
def main(): import sys app = QGuiApplication(sys.argv) w = PaintedWindow() w.create() w.show() sys.exit(app.exec_())
def test_qt_viewer_clipboard_without_flash(make_napari_viewer): viewer = make_napari_viewer() # make sure clipboard is empty QGuiApplication.clipboard().clear() clipboard_image = QGuiApplication.clipboard().image() assert clipboard_image.isNull() # capture screenshot with pytest.warns(FutureWarning): viewer.window.qt_viewer.clipboard(flash=False) viewer.window.clipboard(flash=False, canvas_only=True) clipboard_image = QGuiApplication.clipboard().image() assert not clipboard_image.isNull() # ensure the flash effect is not applied assert viewer.window._qt_viewer._canvas_overlay.graphicsEffect() is None assert not hasattr(viewer.window._qt_viewer._canvas_overlay, "_flash_animation") # clear clipboard and grab image from application view QGuiApplication.clipboard().clear() clipboard_image = QGuiApplication.clipboard().image() assert clipboard_image.isNull() # capture screenshot of the entire window viewer.window.clipboard(flash=False) clipboard_image = QGuiApplication.clipboard().image() assert not clipboard_image.isNull() # ensure the flash effect is not applied assert viewer.window._qt_window.graphicsEffect() is None assert not hasattr(viewer.window._qt_window, "_flash_animation")
def dropEvent(self, event): """Add local files and web URLS with drag and drop. For each file, attempt to open with existing associated reader (if available). If no reader is associated or opening fails, and more than one reader is available, open dialog and ask user to choose among available readers. User can choose to persist this choice. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ shift_down = QGuiApplication.keyboardModifiers() & Qt.ShiftModifier filenames = [] for url in event.mimeData().urls(): if url.isLocalFile(): filenames.append(url.toLocalFile()) else: filenames.append(url.toString()) # if trying to open as a stack, open with any available reader if shift_down: self.viewer.open(filenames, stack=bool(shift_down)) return for filename in filenames: # get available readers for this file from all registered plugins readers = get_potential_readers(filename) if not readers: warnings.warn( trans._( 'No readers found to try reading {filename}.', deferred=True, filename=filename, ) ) continue # see whether an existing setting can be used _, extension = os.path.splitext(filename) error_message = self._try_reader_from_settings( readers, extension, filename ) # we've successfully opened file, move to the next one if error_message is None: continue # there is no existing setting, or it failed, get choice from user readerDialog = QtReaderDialog( parent=self, pth=filename, extension=extension, error_message=error_message, readers=readers, ) self._get_and_try_preferred_reader( readerDialog, readers, error_message )
def __init__(self, items=[], checkedItems=[], toolTips=None, *args, **kwargs): super().__init__(*args, **kwargs) # Set up initial checked items self.checkItems = checkedItems # Make the combo editable to set a custom text, but readonly self.setEditable(True) self.lineEdit().setReadOnly(True) # Make the lineedit the same color as QPushButton palette = QGuiApplication.instance().palette() palette.setBrush(QPalette.Base, palette.button()) self.lineEdit().setPalette(palette) # Use custom delegate self.setItemDelegate(CheckableComboBox.Delegate()) # Update the text when an item is toggled self.model().dataChanged.connect(self.updateText) # Hide and show popup when clicking the line edit self.lineEdit().installEventFilter(self) self.closeOnLineEditClick = False # Prevent popup from closing when clicking on an item self.view().viewport().installEventFilter(self) # Fill in items self.addItems(items, toolTips=toolTips)
def create_screenshots(app, window, is_darkstyle): """Save screenshots for different application views and quit.""" from qtpy.QtCore import QCoreApplication from qtpy.QtGui import QGuiApplication from qtpy.QtWidgets import QDockWidget, QTabWidget theme = 'dark' if is_darkstyle else 'normal' print('\nCreating {} screenshots'.format(theme)) docks = window.findChildren(QDockWidget) tabs = window.findChildren(QTabWidget) widget_data = { 'containers_buttons.png': [ 'Containers - No Tabs', 'Buttons', ], 'containers_tabs_displays.png': [ 'Containers - Tabs', 'Displays', ], 'views_inputs_no_fields.png': [ 'Views', 'Inputs - No Fields', ], 'widgets_inputs_fields.png': [ 'Widgets', 'Inputs - Fields', ], } prefix = 'qdarkstyle_' if is_darkstyle else 'no_dark_' screen = QGuiApplication.primaryScreen() QCoreApplication.processEvents() tab = [tab for tab in tabs if tab.count() >= 12][0] tab.setCurrentIndex(11) QCoreApplication.processEvents() for fname_suffix, widgets in widget_data.items(): QCoreApplication.processEvents() png_path = os.path.join(SCREENSHOTS_PATH, prefix + fname_suffix) print('\t' + png_path) for dockwidget_name in widgets: dockwidget = [dw for dw in docks if dw.windowTitle() == dockwidget_name] if dockwidget: dockwidget = dockwidget[0] dockwidget.show() dockwidget.raise_() QCoreApplication.processEvents() dockwidget = None QCoreApplication.processEvents() pixmap = screen.grabWindow(window.winId()) img = pixmap.toImage() img.save(png_path) QCoreApplication.processEvents() window.showNormal() QCoreApplication.processEvents() print('\n') app.exit()
def _setup_window_size(self, fraction): """Setup default window dimensions""" screen = QGuiApplication.primaryScreen() geometry = screen.geometry() width = geometry.width() * fraction height = geometry.height() * fraction self.resize(width, height)
def dropEvent(self, event): """Add local files and web URLS with drag and drop. For each file, attempt to open with existing associated reader (if available). If no reader is associated or opening fails, and more than one reader is available, open dialog and ask user to choose among available readers. User can choose to persist this choice. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ shift_down = QGuiApplication.keyboardModifiers() & Qt.ShiftModifier filenames = [] for url in event.mimeData().urls(): if url.isLocalFile(): # directories get a trailing "/", Path conversion removes it filenames.append(str(Path(url.toLocalFile()))) else: filenames.append(url.toString()) # if trying to open as a stack, open with any available reader if shift_down: self._qt_open(filenames, stack=bool(shift_down)) return for filename in filenames: self._qt_open([filename], stack=bool(shift_down))
def show_dpi_change_message(self, dpi): """Show message to restart Spyder since the DPI scale changed.""" if not self.get_conf('show_dpi_message'): return if self.current_dpi != dpi: # Check the window state to not show the message if the window # is in fullscreen mode. window = self._window.windowHandle() if (window.windowState() == Qt.WindowFullScreen and sys.platform == 'darwin'): return if self.get_conf('high_dpi_scaling'): return if self.dpi_messagebox is not None: self.dpi_messagebox.activateWindow() self.dpi_messagebox.raise_() return self.dpi_messagebox = MessageCheckBox(icon=QMessageBox.Warning, parent=self) self.dpi_messagebox.set_checkbox_text(_("Don't show again.")) self.dpi_messagebox.set_checked(False) self.dpi_messagebox.set_check_visible(True) self.dpi_messagebox.setText( _("A monitor scale change was detected. <br><br>" "We recommend restarting Spyder to ensure that it's properly " "displayed. If you don't want to do that, please be sure to " "activate the option<br><br><tt>Enable auto high DPI scaling" "</tt><br><br>in <tt>Preferences > Application > " "Interface</tt>, in case Spyder is not displayed " "correctly.<br><br>" "Do you want to restart Spyder?")) self.dpi_messagebox.addButton(_('Restart now'), QMessageBox.NoRole) dismiss_button = self.dpi_messagebox.addButton( _('Dismiss'), QMessageBox.NoRole) self.dpi_messagebox.setDefaultButton(dismiss_button) self.dpi_messagebox.finished.connect( lambda result: self.handle_dpi_change_response(result, dpi)) self.dpi_messagebox.open() # Show dialog always in the primary screen to prevent not being # able to see it if a screen gets disconnected while # in suspended state. See spyder-ide/spyder#16390 dpi_messagebox_width = self.dpi_messagebox.rect().width() dpi_messagebox_height = self.dpi_messagebox.rect().height() screen_geometry = QGuiApplication.primaryScreen().geometry() x = (screen_geometry.width() - dpi_messagebox_width) / 2 y = (screen_geometry.height() - dpi_messagebox_height) / 2 # Convert coordinates to int to avoid a TypeError in Python 3.10 # Fixes spyder-ide/spyder#17677 self.dpi_messagebox.move(int(x), int(y)) self.dpi_messagebox.adjustSize()
def _get_splash_image_name(): # gets the width of the screen where the main window was initialised width = QGuiApplication.primaryScreen().size().width() if width > 2048: return ':/images/MantidSplashScreen_4k.jpg' else: return ':/images/MantidSplashScreen.png'
def main(config): # Set default style to "fusion" # https://doc.qt.io/qt-5/qtquickcontrols2-styles.html#using-styles-in-qt-quick-controls-2 #os.environ.setdefault('QT_QUICK_CONTROLS_STYLE', 'fusion') qt_app = QGuiApplication(sys.argv) qt_app.setOrganizationName("EPFL") qt_app.setOrganizationDomain("ch") # Init QML qml_engine = QQmlApplicationEngine() # tell it the location of qml files qml_engine.addImportPath(str(DIR_RESOURCES)) # Register backend classes backend = LabelBackend() backend.load_config(Path(config)) backend.set_image_path(DIR_RESOURCES / 'images' / 'test.jpg') qml_engine.rootContext().setContextProperty('backend', backend) # QML loads image from the backend using an image provider qml_engine.addImageProvider('backend', backend.image_provider) # Load main window qml_engine.load(QUrl.fromLocalFile(str(DIR_RESOURCES / 'main.qml'))) if qml_engine.rootObjects(): exit_code = qt_app.exec_() del qml_engine sys.exit(exit_code) else: print('QML failed to load') sys.exit(1)
def run(config=None, dir=None): try: config, start_dir = get_config_and_start_dir(config=config, dir=dir) config = config or CONFIG_DEFAULT # Set default style to "fusion" # https://doc.qt.io/qt-5/qtquickcontrols2-styles.html#using-styles-in-qt-quick-controls-2 os.environ.setdefault('QT_QUICK_CONTROLS_STYLE', 'Fusion') qt_app = QGuiApplication(sys.argv) qt_app.setOrganizationName("EPFL") qt_app.setOrganizationDomain("ch") qt_app.setWindowIcon(QIcon(str(DIR_RESOURCES / 'label-grab-icon.svg'))) # Init QML qml_engine = QQmlApplicationEngine() # tell it the location of qml files qml_engine.addImportPath(str(DIR_RESOURCES)) # Register backend classes backend = LabelBackend() backend.load_config(Path(config)) backend.set_starting_directory(start_dir) backend.set_image_path(DIR_RESOURCES / 'images' / 'test.jpg') qml_engine.rootContext().setContextProperty('backend', backend) qtutils = QtUtils() qml_engine.rootContext().setContextProperty('utils', qtutils) # QML loads image from the backend using an image provider qml_engine.addImageProvider('backend', backend.image_provider) # Load main window qml_engine.load( QUrl.fromLocalFile(str(DIR_RESOURCES / 'qml' / 'main.qml'))) #qml_engine.load('qml/main.qml') if qml_engine.rootObjects(): exit_code = qt_app.exec_() del qml_engine sys.exit(exit_code) else: log.error('QML failed to load') sys.exit(1) except Exception as e: log.exception('Exception in main application') sys.exit(1)
def save_result(self): if self.settings.image_path is not None and QMessageBox.Yes == QMessageBox.question( self, "Copy", "Copy name to clipboard?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes): clipboard = QGuiApplication.clipboard() clipboard.setText( os.path.splitext(os.path.basename( self.settings.image_path))[0]) if self.settings.roi is None or len(self.settings.sizes) == 1: QMessageBox.warning(self, "No components", "No components to save") return dial = PSaveDialog( io_functions.save_components_dict, system_widget=False, settings=self.settings, file_mode=PSaveDialog.Directory, path="io.save_components_directory", ) dial.selectFile( os.path.splitext(os.path.basename(self.settings.image_path))[0]) if not dial.exec_(): return res = dial.get_result() potential_names = self.settings.get_file_names_for_save_result( res.save_destination) conflict = [] for el in potential_names: if os.path.exists(el): conflict.append(el) if len(conflict) > 0: # TODO modify because of long lists conflict_str = "\n".join(conflict) if QMessageBox.No == QMessageBox.warning( self, "Overwrite", f"Overwrite files:\n {conflict_str}", QMessageBox.Yes | QMessageBox.No, QMessageBox.No, ): self.save_result() def exception_hook(exception): QMessageBox.critical( self, "Save error", f"Error on disc operation. Text: {exception}", QMessageBox.Ok) dial = ExecuteFunctionDialog( res.save_class.save, [ res.save_destination, self.settings.get_project_info(), res.parameters ], text="Save components", exception_hook=exception_hook, ) dial.exec_()
def determine_dialog_dimensions(self): width = REFERENCE_WIDTH height = REFERENCE_HEIGHT screen = None try: if hasattr(QGuiApplication, "screenAt"): screen = QGuiApplication.screenAt( self.parent().parent().geometry().center()) else: # get the screen from the last top level window windows = QGuiApplication.topLevelWindows() screen = windows[-1].screen() except Exception: # something failed just take the primary screen screen = QGuiApplication.primaryScreen() if screen is not None: screen_width = screen.availableSize().width() screen_height = screen.availableSize().height() # the proportion of the whole window size for the about screen window_scaling = 0.4 width = int(screen_width * window_scaling) # also calculate the intended width but using the hieght and a standard screen aspect ratio width_by_height = int(screen_height * WIDESCREEN_ASPECT_RATIO * window_scaling) # take the smaller of the width from the screen width and height if width_by_height < width: width = width_by_height # set a minimum size if width < REFERENCE_WIDTH: width = REFERENCE_WIDTH # calculate height from the width and aspect ratio height = int(width / REFERENCE_ASPECT_RATIO) if width > screen_width or height > screen_height: self.is_widget_too_big = True return width, height
def clipboard(self, flash=True): """Take a screenshot of the currently displayed screen and copy the image to the clipboard. Parameters ---------- flash : bool Flag to indicate whether flash animation should be shown after the screenshot was captured. """ cb = QGuiApplication.clipboard() cb.setImage(self._screenshot(flash))
def pasteShape(self): from qtpy.QtGui import QGuiApplication clipboard = QGuiApplication.clipboard() if not clipboard.ownsClipboard(): return shapes_json = clipboard.text() added_shapes = self.canvas.pasteShapes(shapes_json) self.labelList.clearSelection() for shape in added_shapes: self.addLabel(shape) self.setDirty()
def event(self, e: QEvent) -> bool: ''' Event Parameters ---------- e : QEvent Returns ------- value : bool ''' state = self.d.dragging_state if state == DragState.inactive: # Normally we would check here, if the left mouse button is pressed. # But from QT version 5.12.2 on the mouse events from # QEvent.NonClientAreaMouseButtonPress return the wrong mouse # button The event always returns Qt.RightButton even if the left # button is clicked. if e.type() == QEvent.NonClientAreaMouseButtonPress: if QT_VERSION_TUPLE >= (5, 12, 2): # and QGuiApplication.mouseButtons().testFlag(Qt.LeftButton ... logger.debug('FloatingWidget.event Event.NonClientAreaMouseButtonPress %s', e.type()) self.d.set_state(DragState.mouse_pressed) elif QGuiApplication.mouseButtons() == Qt.LeftButton: logger.debug('FloatingWidget.event Event.NonClientAreaMouseButtonPress %s', e.type()) self.d.set_state(DragState.mouse_pressed) elif state == DragState.mouse_pressed: if e.type() == QEvent.NonClientAreaMouseButtonDblClick: logger.debug('FloatingWidget.event QEvent.NonClientAreaMouseButtonDblClick') self.d.set_state(DragState.inactive) elif e.type() == QEvent.Resize: # If the first event after the mouse press is a resize event, then # the user resizes the window instead of dragging it around. # But there is one exception. If the window is maximized, # then dragging the window via title bar will cause the widget to # leave the maximized state. This in turn will trigger a resize event. # To know, if the resize event was triggered by user via moving a # corner of the window frame or if it was caused by a windows state # change, we check, if we are not in maximized state. if not self.isMaximized(): self.d.set_state(DragState.inactive) elif state == DragState.floating_widget: if e.type() == QEvent.NonClientAreaMouseButtonRelease: logger.debug('FloatingWidget.event QEvent.NonClientAreaMouseButtonRelease') self.d.title_mouse_release_event() return super().event(e)
def test_error_reporter(qtbot, monkeypatch): """test that QtPluginErrReporter shows any instantiated PluginErrors.""" monkeypatch.setattr( qt_plugin_report, 'standard_metadata', lambda x: {'url': 'https://github.com/example/example'}, ) error_message = 'my special error' try: # we need to raise to make sure a __traceback__ is attached to the error. raise PluginError(error_message, plugin_name='test_plugin', plugin="mock") except PluginError: pass report_widget = qt_plugin_report.QtPluginErrReporter() qtbot.addWidget(report_widget) # the null option plus the one we created assert report_widget.plugin_combo.count() >= 2 # the message should appear somewhere in the text area report_widget.set_plugin('test_plugin') assert error_message in report_widget.text_area.toPlainText() # mock_webbrowser_open def mock_webbrowser_open(url, new=0): assert new == 2 assert "Errors for plugin 'test_plugin'" in url assert "Traceback from napari" in url monkeypatch.setattr(webbrowser, 'open', mock_webbrowser_open) qtbot.mouseClick(report_widget.github_button, Qt.LeftButton) # make sure we can copy traceback to clipboard report_widget.copyToClipboard() clipboard_text = QGuiApplication.clipboard().text() assert "Errors for plugin 'test_plugin'" in clipboard_text # plugins without errors raise an error with pytest.raises(ValueError): report_widget.set_plugin('non_existent') report_widget.set_plugin(None) assert not report_widget.text_area.toPlainText()
def dropEvent(self, event): """Add local files and web URLS with drag and drop. Parameters ---------- event : qtpy.QtCore.QEvent Event from the Qt context. """ shift_down = QGuiApplication.keyboardModifiers() & Qt.ShiftModifier filenames = [] for url in event.mimeData().urls(): if url.isLocalFile(): filenames.append(url.toLocalFile()) else: filenames.append(url.toString()) self.viewer.open(filenames, stack=bool(shift_down))
def clipboard(self, flash=True): """Take a screenshot of the currently displayed screen and copy the image to the clipboard. Parameters ---------- flash : bool Flag to indicate whether flash animation should be shown after the screenshot was captured. """ import warnings warnings.warn( trans. _("'window.qt_viewer.screenshot' is deprecated and will be removed in v0.4.14. Please use 'window.screenshot(canvas_only=True)' instead" ), FutureWarning, stacklevel=2, ) cb = QGuiApplication.clipboard() cb.setImage(self._screenshot(flash))
def test_qt_viewer_clipboard_with_flash(make_napari_viewer, qtbot): viewer = make_napari_viewer() # make sure clipboard is empty QGuiApplication.clipboard().clear() clipboard_image = QGuiApplication.clipboard().image() assert clipboard_image.isNull() # capture screenshot viewer.window.qt_viewer.clipboard(flash=True) clipboard_image = QGuiApplication.clipboard().image() assert not clipboard_image.isNull() # ensure the flash effect is applied assert viewer.window.qt_viewer._canvas_overlay.graphicsEffect() is not None assert hasattr(viewer.window.qt_viewer._canvas_overlay, "_flash_animation") qtbot.wait(500) # wait for the animation to finish assert viewer.window.qt_viewer._canvas_overlay.graphicsEffect() is None assert not hasattr(viewer.window.qt_viewer._canvas_overlay, "_flash_animation") # clear clipboard and grab image from application view QGuiApplication.clipboard().clear() clipboard_image = QGuiApplication.clipboard().image() assert clipboard_image.isNull() # capture screenshot of the entire window viewer.window.clipboard(flash=True) clipboard_image = QGuiApplication.clipboard().image() assert not clipboard_image.isNull() # ensure the flash effect is applied assert viewer.window._qt_window.graphicsEffect() is not None assert hasattr(viewer.window._qt_window, "_flash_animation") qtbot.wait(500) # wait for the animation to finish assert viewer.window._qt_window.graphicsEffect() is None assert not hasattr(viewer.window._qt_window, "_flash_animation")
def __init__(self, parent, history): super().__init__(parent=parent) self.setWindowTitle("History") layout = QVBoxLayout() text = QPlainTextEdit() font = QFont() font.setFamily("monospace") font.setStyleHint(QFont.Monospace) text.setFont(font) highlighter = PythonHighlighter(text.document()) # noqa: F841 text.setReadOnly(True) text.setPlainText(history) layout.addWidget(text) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) clipboardbutton = QPushButton("Copy to clipboard") buttonbox.addButton(clipboardbutton, QDialogButtonBox.ActionRole) clipboard = QGuiApplication.clipboard() clipboardbutton.clicked.connect( lambda: clipboard.setText(history + "\n")) layout.addWidget(buttonbox) self.setLayout(layout) buttonbox.accepted.connect(self.accept) self.resize(700, 500)
def main(): import sys app = QGuiApplication(sys.argv) a = Window() a.setFramePosition(QPoint(10, 10)) a.setTitle("Window A") a.setObjectName(a.title()) a.setVisible(True) b = Window() b.setFramePosition(QPoint(100, 100)) b.setTitle("Window B") b.setObjectName(b.title()) b.setVisible(True) child = Window(b) child.setObjectName("ChildOfB") child.setVisible(True) windows = [] screens = app.screens() for screen in screens: if screen == app.primaryScreen(): continue window = Window(screen) geometry = window.geometry() geometry.moveCenter(screen.availableGeometry().center()) window.setGeometry(geometry) window.setVisible(True) window.setTitle(screen.name()) window.setObjectName(window.title()) windows.append(window) return app.exec() sys.exit(app.exec_())
def __to_clipboard(widget): QGuiApplication.clipboard().setText('\n'.join([widget.item(i).text() for i in range(widget.count())]))
def move_to(self, position='top', *, win_ratio=0.9, min_length=0): """Move popup to a position relative to the QMainWindow. Parameters ---------- position : {str, tuple}, optional position in the QMainWindow to show the pop, by default 'top' if str: must be one of {'top', 'bottom', 'left', 'right' } if tuple: must be length 4 with (left, top, width, height) win_ratio : float, optional Fraction of the width (for position = top/bottom) or height (for position = left/right) of the QMainWindow that the popup will occupy. Only valid when isinstance(position, str). by default 0.9 min_length : int, optional Minimum size of the long dimension (width for top/bottom or height fort left/right). Raises ------ ValueError if position is a string and not one of {'top', 'bottom', 'left', 'right' } """ if isinstance(position, str): window = self.parent().window() if self.parent() else None if not window: raise ValueError( "Specifying position as a string is only posible if " "the popup has a parent") left = window.pos().x() top = window.pos().y() if position in ('top', 'bottom'): width = window.width() * win_ratio width = max(width, min_length) left += (window.width() - width) / 2 height = self.sizeHint().height() top += (24 if position == 'top' else (window.height() - height - 12)) elif position in ('left', 'right'): height = window.height() * win_ratio height = max(height, min_length) # 22 is for the title bar top += 22 + (window.height() - height) / 2 width = self.sizeHint().width() left += (12 if position == 'left' else (window.width() - width - 12)) else: raise ValueError('position must be one of ' '["top", "left", "bottom", "right"]') elif isinstance(position, (tuple, list)): assert len(position) == 4, '`position` argument must have length 4' left, top, width, height = position else: raise ValueError(f"Wrong type of position {position}") # necessary for transparent round corners self.resize(self.sizeHint()) # make sure the popup is completely on the screen # In Qt ≥5.10 we can use screenAt to know which monitor the mouse is on if hasattr(QGuiApplication, "screenAt"): screen_geometry: QRect = QGuiApplication.screenAt( QCursor.pos()).geometry() else: # This widget is deprecated since Qt 5.11 from qtpy.QtWidgets import QDesktopWidget screen_num = QDesktopWidget().screenNumber(QCursor.pos()) screen_geometry = QGuiApplication.screens()[screen_num].geometry() left = max(min(screen_geometry.right() - width, left), screen_geometry.left()) top = max(min(screen_geometry.bottom() - height, top), screen_geometry.top()) self.setGeometry(left, top, width, height)
def create_screenshots(app, window, is_darkstyle): """Save screenshots for different application views and quit.""" from qtpy.QtCore import QCoreApplication from qtpy.QtGui import QGuiApplication from qtpy.QtWidgets import QDockWidget, QTabWidget theme = 'dark' if is_darkstyle else 'normal' print('\nCreating {} screenshots'.format(theme)) docks = window.findChildren(QDockWidget) tabs = window.findChildren(QTabWidget) widget_data = { 'containers_buttons.png': [ 'Containers - No Tabs', 'Buttons', ], 'containers_tabs_displays.png': [ 'Containers - Tabs', 'Displays', ], 'views_inputs_no_fields.png': [ 'Views', 'Inputs - No Fields', ], 'widgets_inputs_fields.png': [ 'Widgets', 'Inputs - Fields', ], } prefix = 'qdarkstyle_' if is_darkstyle else 'no_dark_' screen = QGuiApplication.primaryScreen() QCoreApplication.processEvents() tab = [tab for tab in tabs if tab.count() >= 12][0] tab.setCurrentIndex(11) QCoreApplication.processEvents() for fname_suffix, widgets in widget_data.items(): QCoreApplication.processEvents() png_path = os.path.join(SCREENSHOTS_PATH, prefix + fname_suffix) print('\t' + png_path) for dockwidget_name in widgets: dockwidget = [ dw for dw in docks if dw.windowTitle() == dockwidget_name ] if dockwidget: dockwidget = dockwidget[0] dockwidget.show() dockwidget.raise_() QCoreApplication.processEvents() dockwidget = None QCoreApplication.processEvents() pixmap = screen.grabWindow(window.winId()) img = pixmap.toImage() img.save(png_path) QCoreApplication.processEvents() window.showNormal() QCoreApplication.processEvents() print('\n') app.exit()
def copyToClipboard(self) -> None: """Copy current plugin traceback info to clipboard as plain text.""" plugin = self.plugin_combo.currentText() err_string = format_exceptions(plugin, as_html=False) cb = QGuiApplication.clipboard() cb.setText(err_string)
def copySelectedShape(self): from qtpy.QtGui import QGuiApplication shapes_json = self.canvas.copySelectedShapes() clipboard = QGuiApplication.clipboard() clipboard.setText(shapes_json)