class MultiLayerSelect: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value("locale/userLocale")[0:2] locale_path = os.path.join(self.plugin_dir, "i18n", "MultiLayerSelect_{}.qm".format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Init settings self.settings = QSettings() self.settings.beginGroup("plugins/multilayerselect") def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # pylint: disable=invalid-name return QCoreApplication.translate("MultiLayerSelect", message) def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" # pylint: disable=invalid-name # Create settings dialog self.settings_dialog = SettingsDialog(self.settings, self.iface.mainWindow()) self.expression_dialog = None try: QgsProject.instance().selectionColorChanged.connect( self.on_color_changed) QgsProject.instance().selectionColorChanged.connect( self.settings_dialog.on_project_color_changed) except AttributeError: # QGIS < 3.10 self.settings_dialog.colorChanged.connect(self.on_color_changed) QgsProject.instance().readProject.connect( self.settings_dialog.on_project_color_changed) self.settings_dialog.settingsChanged.connect(self.on_settings_changed) self.toolbar = QToolBar("Multilayer Select", self.iface.mainWindow()) self.toolbar.setObjectName("MultiSelectToolbar") self.about_action = QAction( QIcon(":/plugins/multilayerselect/icons/about.svg"), self.tr("About"), parent=self.iface.mainWindow(), ) self.about_action.triggered.connect(self.show_about) self.settings_action = QAction( QIcon(":/images/themes/default/console/iconSettingsConsole.svg"), self.tr("Settings"), parent=self.iface.mainWindow(), ) self.settings_action.setObjectName("actionMultiLayerSelectSettings") self.settings_action.setToolTip( self.tr("<b>Multilayer Select Settings</b>")) self.settings_action.triggered.connect(self.show_settings) self.plugin_menu = self.iface.pluginMenu().addMenu( QIcon(":/plugins/multilayerselect/icons/icon.svg"), "Multilayer Select") self.plugin_menu.addAction(self.about_action) self.plugin_menu.addAction(self.settings_action) self.selection_tool_button = QToolButton(self.toolbar) self.selection_tool_button.setPopupMode(QToolButton.MenuButtonPopup) self.selection_tool_button.setObjectName("selectionToolButton") self.advanced_selection_tool_button = QToolButton(self.toolbar) self.advanced_selection_tool_button.setPopupMode( QToolButton.MenuButtonPopup) self.advanced_selection_tool_button.setObjectName( "advancedSelectionToolButton") self.select_rect_tool = MultiSelectionAreaTool(self.iface.mapCanvas()) self.select_polygon_tool = MultiSelectionPolygonTool( self.iface.mapCanvas()) self.select_freehand_tool = MultiSelectionFreehandTool( self.iface.mapCanvas()) self.select_radius_tool = MultiSelectionRadiusTool( self.iface.mapCanvas()) self.actions_settings = [ SelectAction( text=self.tr("Select Features"), tooltip=self.tr( "<b>Select Features by area or single click</b>"), icon=":/plugins/multilayerselect/icons/selectRectangle.svg", objectname="actionMultiSelectByRectangle", tool=self.select_rect_tool, ), SelectAction( text=self.tr("Select Features by Polygon"), icon=":/plugins/multilayerselect/icons/selectPolygon.svg", objectname="actionMultiSelectByPolygon", tool=self.select_polygon_tool, ), SelectAction( text=self.tr("Select Features by Freehand"), icon=":/plugins/multilayerselect/icons/selectFreehand.svg", objectname="actionMultiSelectByFreehand", tool=self.select_freehand_tool, ), SelectAction( text=self.tr("Select Features by Radius"), icon=":/plugins/multilayerselect/icons/selectRadius.svg", objectname="actionMultiSelectByRadius", tool=self.select_radius_tool, ), ] def on_select_tool(tool, action): self.selection_tool_button.setDefaultAction(action) if self.embedded_selection_tool_button: self.embedded_selection_tool_button.setDefaultAction(action) self.iface.mapCanvas().setMapTool(tool) self.select_actions = [] for select_action in self.actions_settings: action = QAction(select_action.text) action.setToolTip(select_action.tooltip) action.setObjectName(select_action.objectname) action.setCheckable(True) select_action.tool.setAction(action) action.triggered.connect( partial(on_select_tool, select_action.tool, action)) self.selection_tool_button.addAction(action) if not self.selection_tool_button.defaultAction(): self.selection_tool_button.setDefaultAction(action) self.select_actions.append(action) self.toolbar.addWidget(self.selection_tool_button) self.select_all_action = QAction( self.tr("Select all features from all layers"), ) self.select_all_action.setToolTip("<b>{}</b>".format( self.select_all_action.text())) self.select_all_action.setObjectName("actionMultiSelectAll") self.select_all_action.triggered.connect(self.select_all) self.advanced_selection_tool_button.addAction(self.select_all_action) self.advanced_selection_tool_button.setDefaultAction( self.select_all_action) self.invert_all_action = QAction( self.tr("Invert selection for all layers"), ) self.invert_all_action.setToolTip("<b>{}</b>".format( self.invert_all_action.text())) self.invert_all_action.setObjectName("actionMultiSelectInvert") self.invert_all_action.triggered.connect(self.invert_all) self.advanced_selection_tool_button.addAction(self.invert_all_action) self.select_by_expr_action = QAction( QIcon(":/images/themes/default/mIconExpressionSelect.svg"), self.tr("Select Features by Expression..."), ) self.select_by_expr_action.setToolTip("<b>{}</b>".format( self.select_by_expr_action.text())) self.select_by_expr_action.setObjectName("actionMultiSelectExpr") self.select_by_expr_action.triggered.connect(self.select_by_expression) self.advanced_selection_tool_button.addAction( self.select_by_expr_action) self.toolbar.addWidget(self.advanced_selection_tool_button) self.deselect_all_action = QAction( self.tr("Deselect features from all layers")) self.deselect_all_action.setToolTip("<b>{}</b>".format( self.deselect_all_action.text())) self.deselect_all_action.setObjectName("actionDeselectAll") self.deselect_all_action.triggered.connect(self.deselect_all) self.toolbar.addAction(self.deselect_all_action) self.toolbar.addAction(self.settings_action) self.iface.mainWindow().addToolBar(self.toolbar) # Embedded actions self.embedded_selection_tool_button_action = None self.embedded_selection_tool_button = None self.embedded_advanced_tool_button_action = None self.embedded_advanced_tool_button = None self.on_color_changed() self.on_settings_changed() def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" # Delete Settings dialog self.settings_dialog.deleteLater() # Remove menu from plugins menu self.iface.pluginMenu().removeAction(self.plugin_menu.menuAction()) self.select_freehand_tool.deleteLater() self.select_polygon_tool.deleteLater() self.select_radius_tool.deleteLater() self.select_rect_tool.deleteLater() self.iface.mainWindow().removeToolBar(self.toolbar) self.toolbar.deleteLater() self.replace_default_action(False) try: QgsProject.instance().selectionColorChanged.disconnect( self.on_color_changed) QgsProject.instance().selectionColorChanged.disconnect( self.settings_dialog.on_project_color_changed) except AttributeError: # QGIS < 3.10 pass def show_about(self): """ Show the about dialog """ # Used to display plugin icon in the about message box bogus = QWidget(self.iface.mainWindow()) bogus.setWindowIcon(QIcon(":/plugins/multilayerselect/icons/icon.svg")) cfg = configparser.ConfigParser() cfg.read(os.path.join(os.path.dirname(__file__), "metadata.txt")) version = cfg.get("general", "version") homepage = cfg.get("general", "homepage") tracker = cfg.get("general", "tracker") repository = cfg.get("general", "repository") QMessageBox.about( bogus, self.tr("About Multilayer Select"), "<b>Version</b> {3}<br><br>" "<b>{4}</b> : <a href={0}>GitHub</a><br>" "<b>{5}</b> : <a href={1}>GitHub</a><br>" "<b>{6}</b> : <a href={2}>GitHub Pages</a>".format( repository, tracker, homepage, version, self.tr("Source code"), self.tr("Report issues"), self.tr("Documentation"), ), ) bogus.deleteLater() def show_settings(self): """ Show the settings dialog """ geometry = self.settings_dialog.geometry() # The first time the dialog is shown (y=0), explicitely set its geometry # which allow to restore the geometry on subsequent calls if geometry.y() == 0: self.settings_dialog.show() self.settings_dialog.raise_() self.settings_dialog.setGeometry(self.settings_dialog.geometry()) return self.settings_dialog.show() self.settings_dialog.raise_() def on_color_changed(self): """ Called when the selection color has changed. Replace every icon """ color = self.iface.mapCanvas().selectionColor() color = QColor.fromHsv(color.hue(), color.saturation() * 0.9, color.value() * 0.95, color.alpha()) for i in range(len(self.select_actions)): path = self.actions_settings[i].icon icon = create_icon(path, color) self.select_actions[i].setIcon(icon) icon = create_icon(":/plugins/multilayerselect/icons/deselectAll.svg", color) self.deselect_all_action.setIcon(icon) icon = select_all_icon(color) self.select_all_action.setIcon(icon) icon = invert_selection_icon(color) self.invert_all_action.setIcon(icon) icon = expression_select_icon(color) self.select_by_expr_action.setIcon(icon) def on_settings_changed(self): """ Called when any setting has changed """ if self.settings.value("show_settings", True, bool): self.toolbar.addAction(self.settings_action) else: self.toolbar.removeAction(self.settings_action) self.replace_default_action( self.settings.value("replace_actions", False, bool)) def deselect_all(self): """ Deselect every feature """ for layer in QgsProject.instance().mapLayers().values(): if isinstance(layer, QgsVectorLayer): layer.removeSelection() update_status_message() def select_all(self): """ Select all the features from every vector layer """ for layer in vector_layers(): layer.selectAll() self.advanced_selection_tool_button.setDefaultAction( self.select_all_action) if self.embedded_advanced_tool_button: self.embedded_advanced_tool_button.setDefaultAction( self.select_all_action) update_status_message() def invert_all(self): """ Invert the selection of every vector layer """ for layer in vector_layers(): layer.invertSelection() self.advanced_selection_tool_button.setDefaultAction( self.invert_all_action) if self.embedded_advanced_tool_button: self.embedded_advanced_tool_button.setDefaultAction( self.invert_all_action) update_status_message() def select_by_expression(self): """ Create and open the Expression builder dialog""" if self.expression_dialog: self.expression_dialog.deleteLater() self.expression_dialog = MultiLayerSelectionExpressionBuilder() self.expression_dialog.show() self.advanced_selection_tool_button.setDefaultAction( self.select_by_expr_action) if self.embedded_advanced_tool_button: self.embedded_advanced_tool_button.setDefaultAction( self.select_by_expr_action) update_status_message() def replace_default_action(self, value): """Replace the default QGIS selection action with the multilayer ones Args: value (bool): If true, replace the actions, else put the multi actions inside their own toolbar """ toolbar = self.iface.attributesToolBar() main_window = self.iface.mainWindow() main_window.findChild(QAction, "ActionSelect").setVisible(not value) main_window.findChild(QAction, "ActionSelection").setVisible(not value) main_window.findChild(QAction, "mActionDeselectAll").setVisible(not value) actiontable = main_window.findChild(QAction, "mActionOpenTable") actionform = main_window.findChild(QAction, "mActionSelectByForm") # Remove the multi layer tool buttons from the QGIS attribute toolbar toolbar.removeAction(self.embedded_selection_tool_button_action) toolbar.removeAction(self.embedded_advanced_tool_button_action) if value: # Create the QToolButtons that will be added to the default toolbar self.embedded_selection_tool_button = QToolButton() self.embedded_selection_tool_button.setPopupMode( QToolButton.MenuButtonPopup) # Add selection tools action to the button (Rect, Polygon, Radius, Freehand) self.embedded_selection_tool_button.addActions(self.select_actions) self.embedded_selection_tool_button.setDefaultAction( self.select_actions[0]) self.embedded_advanced_tool_button = QToolButton() self.embedded_advanced_tool_button.setPopupMode( QToolButton.MenuButtonPopup) # Add Invert, Select All, Select from value and Select from expressions self.embedded_advanced_tool_button.addAction( self.select_all_action) self.embedded_advanced_tool_button.setDefaultAction( self.select_all_action) self.embedded_advanced_tool_button.addAction( self.invert_all_action) self.embedded_advanced_tool_button.addAction( self.select_by_expr_action) self.embedded_advanced_tool_button.addAction(actionform) self.embedded_selection_tool_button_action = toolbar.insertWidget( actiontable, self.embedded_selection_tool_button) self.embedded_advanced_tool_button_action = toolbar.insertWidget( actiontable, self.embedded_advanced_tool_button) # Add the deselect all action toolbar.insertAction(actiontable, self.deselect_all_action) # If the settigns is enabled add the show settings action if self.settings.value("show_settings", True, bool): toolbar.insertAction(actiontable, self.settings_action) else: toolbar.removeAction(self.settings_action) self.toolbar.hide() else: # Remove the multi actions from the default toolbar, and show # the custom toolbar self.embedded_selection_tool_button = None self.embedded_advanced_tool_button = None toolbar.removeAction(self.deselect_all_action) toolbar.removeAction(self.settings_action) self.toolbar.show()
class FullscreenableWidget(QMainWindow): widgetFullscreened = Signal(bool) def __init__(self, parent=None, icon_size=None): QMainWindow.__init__(self, parent) self.toolbar = QToolBar('tools', parent=self) self.toolbar.setContentsMargins(0, 0, 0, 0) self.icon_provider = IconProvider(self) self.dark_mode = self.icon_provider.get_theme_mode(self) if icon_size is not None: self.toolbar.setIconSize(QSize(icon_size, icon_size)) self.addToolBar(Qt.RightToolBarArea, self.toolbar) self.action_fullscreen = QAction(self) self.action_fullscreen.setIcon( QIcon(self.icon_provider.get_icon_path('fullscreen.svg'))) self.toolbar.addAction(self.action_fullscreen) self.action_windowed = QAction(self) self.action_windowed.setIcon( QIcon(self.icon_provider.get_icon_path('fullscreen_exit.svg'))) # signalling: self.action_fullscreen.triggered.connect(self.go_fullscreen) self.action_windowed.triggered.connect(self.go_windowed) self.windowed_parent = None self.win_parent_layout = None self.index_in_layout = None self.windowed_flags = None self.windowed_geometry = None self.position_in_grid = None def go_fullscreen(self): self.windowed_flags = self.windowFlags() self.windowed_geometry = self.geometry() cur_scr = self.screen() # to check which screen s_count = len(qApp.screens()) for i in range(s_count): if qApp.screens()[i] == cur_scr: screen = qApp.screens()[i] if self.parent() is not None: self.windowed_parent = self.parent() self.win_parent_layout = self.windowed_parent.layout() self.index_in_layout = self.win_parent_layout.indexOf(self) if isinstance(self.win_parent_layout, QGridLayout): self.position_in_grid = \ self.win_parent_layout.getItemPosition( self.index_in_layout) self.win_parent_layout.removeWidget(self) self.setParent(None) self.toolbar.insertAction(self.action_fullscreen, self.action_windowed) self.toolbar.removeAction(self.action_fullscreen) self.move(screen.geometry().x(), screen.geometry().y()) self.showFullScreen() self.widgetFullscreened.emit(True) def go_windowed(self): self.showNormal() if self.windowed_parent is not None: if isinstance(self.win_parent_layout, (QBoxLayout, QSplitter)): self.win_parent_layout.insertWidget(self.index_in_layout, self) elif isinstance(self.win_parent_layout, QGridLayout): self.win_parent_layout.addWidget(self, *self.position_in_grid) self.setParent(self.windowed_parent) self.windowed_parent = None self.setGeometry(self.windowed_geometry) self.toolbar.insertAction(self.action_windowed, self.action_fullscreen) self.toolbar.removeAction(self.action_windowed) self.widgetFullscreened.emit(False)
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setup_ui() # UI connections self.action_open_amazon_view.triggered.connect(self.open_amazon) self.action_open_vendor_view.triggered.connect(self.open_vendor) self.action_open_operations.triggered.connect(self.open_operations) self.action_edit_vendors.triggered.connect(self.on_edit_vendors) self.tabs.tabCloseRequested.connect(self.close_tab) self.tabs.currentChanged.connect(self.tab_changed) self.current_tab = None # Set up the database connection. self.dbengine = create_engine('sqlite:///prowler.db') self.dbsession = Session(bind=self.dbengine) Base.metadata.create_all(self.dbengine) amazon = Vendor(id=0, name='Amazon', url='www.amazon.com') self.dbsession.add(self.dbsession.merge(amazon)) self.dbsession.commit() def setup_ui(self): """Initialize the main window's UI components""" # Scale the window to the size of the screen desktop = QApplication.desktop() size = desktop.availableGeometry() self.resize(size.width() * .9, size.height() * .9) # Set up the toolbar self.toolBar = QToolBar(self) self.addToolBar(self.toolBar) # Create toolbar actions self.action_open_amazon_view = QAction(QIcon('icons/amazon.png'), 'Open Amazon view', self) self.action_open_vendor_view = QAction(QIcon('icons/folder.png'), 'Open Vendor view', self) self.action_open_operations = QAction(QIcon('icons/ops_view.png'), 'Open Operations', self) self.action_edit_vendors = QAction(QIcon('icons/vendor.png'), 'Edit vendors', self) # Add actions and separators to the toolbar self.toolBar.addActions([self.action_open_amazon_view, self.action_open_vendor_view, self.action_open_operations]) self.toolBar.addSeparator() self.toolBar.addAction(self.action_edit_vendors) self.toolBar.addSeparator() # Create the central tab widget self.tabs = QTabWidget(self) self.tabs.setDocumentMode(True) self.tabs.setTabsClosable(True) self.setCentralWidget(self.tabs) def on_edit_vendors(self): """Show the Edit Vendors dialog.""" dialog = EditVendorDialog(parent=self) dialog.exec() def open_amazon(self): """Open a new AmazonView.""" view = AmazonView(self) idx = self.tabs.addTab(view, 'Amazon') self.tabs.setCurrentIndex(idx) def open_vendor(self): """Open a new VendorView.""" view = VendorView(self) idx = self.tabs.addTab(view, 'Sources') self.tabs.setCurrentIndex(idx) def open_operations(self): """Open, or re-focus, the Operations view.""" # We only want one operations view open at a time for i in range(self.tabs.count()): if isinstance(self.tabs.widget(i), OperationsView): self.tabs.setCurrentIndex(i) return view = OperationsView(self) idx = self.tabs.addTab(view, 'Operations') self.tabs.setCurrentIndex(idx) def close_tab(self, index): """Respond to a tabCloseRequested signal.""" self.tabs.removeTab(index) def tab_changed(self, index): """Re-populate the toolbar with the actions specific to the new tab.""" if self.current_tab: for action in self.current_tab.toolbar_actions: self.toolBar.removeAction(action) self.current_tab = self.tabs.currentWidget() if self.current_tab: for action in self.current_tab.toolbar_actions: self.toolBar.addAction(action)