1

I am writing a plugin which shows a graph in a qGraphicsView based on some selected points. I want to be able to read out points clicking in the graph, so I've written a mosePressEvent eventhandler but it does never fire. To test it, I just did

def mousePressEvent(self, event):
      self.iface.messageBar().pushMessage(
                "Success", "Pressed",
                level=Qgis.Success, duration=3)

But no matter where I click on the widget, no message is shown. I do neither get any error messages. Do I need to import something or do something else special to make it work?

(The plugin is based on a template by the Plugin builder plugin, maybe I need to remove something)

The entire file is in https://github.com/sickel/qgisSpectre/blob/1beff9842ededc425d3200d566d11dde9c1d3bb7/qgisSpectre.py

1 Answer 1

4

This is really more of a IT/ PyQt question, so I will provide an answer in the context of QGIS plugin development, to try to keep on topic.

The reason your mousePressEvent does nothing is that you have defined it inside your main plugin class. The mousePressEvent method is designed to be re-implemented inside a sub class which inherits from one of the QWidget abstract base classes, in this case QGraphicsView.

You need to create a new class in your main plugin python file which sub-classes QGraphicsView and re-implements the mousePressEvent() method. Then create an instance of your subclass and add it to a layout of your dock widget.

I created a simple plugin, based on your ui file which just adds the dock widget to the QGIS main window, catches a mouse click event in the graphics view and pushes a message to the message bar showing which mouse button was clicked.

I modified your ui file by deleting the QGraphicsView object, so we can create an instance of our custom subclass in the __init__() method. Then, inside the initGui() method, we set its parent to the dock widget, and add it to the horizontalLayout_2 object.

the ui xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>qgisSpectreDockWidgetBase</class>
 <widget class="QDockWidget" name="qgisSpectreDockWidgetBase">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>564</width>
    <height>321</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Spectre &amp;Viewer</string>
  </property>
  <widget class="QWidget" name="dockWidgetContents">
   <property name="sizePolicy">
    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="1" column="0">
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QComboBox" name="cbLayer"/>
      </item>
      <item>
       <widget class="QComboBox" name="cbItem"/>
      </item>
     </layout>
    </item>
    <item row="2" column="0">
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <layout class="QVBoxLayout" name="verticalLayout"/>
      </item>
      <item>
       <widget class="QPushButton" name="pBCopy">
        <property name="text">
         <string>Copy</string>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

the contents of the plugin dialog python file:

import os

from PyQt5 import uic
from PyQt5 import QtWidgets

FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'dock_widget_example_dialog_base.ui'))


class DockWidgetExampleDialog(QtWidgets.QDockWidget, FORM_CLASS):
    def __init__(self, parent=None):
        super(DockWidgetExampleDialog, self).__init__(parent)
        self.setupUi(self)

And finally, the main plugin python file:

from PyQt5.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QGraphicsView

from .resources import *

from .dock_widget_example_dialog import DockWidgetExampleDialog
import os.path


class DockWidgetExample:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):

        # 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',
            'DockWidgetExample_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = DockWidgetExampleDialog(self.iface.mainWindow())

        # Declare instance attributes
        self.view = My_Graphics_View(self.iface)
        self.actions = []
        self.menu = self.tr(u'&Dockwidget Example')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'DockWidgetExample')
        self.toolbar.setObjectName(u'DockWidgetExample')

    # noinspection PyMethodMayBeStatic
    def tr(self, message):

        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('DockWidgetExample', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/dock_widget_example/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Add a Dock Widget'),
            callback=self.run,
            parent=self.iface.mainWindow())
        self.view.setParent(self.dlg)
        self.dlg.horizontalLayout_2.addWidget(self.view)

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&Dockwidget Example'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def run(self):
        self.iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self.dlg)
        self.dlg.show()


class My_Graphics_View(QGraphicsView):
    def __init__(self, iface):
        self.iface = iface
        QGraphicsView.__init__(self)

    def mousePressEvent(self, event):
        if event.button() == 1:
            self.iface.messageBar().pushMessage('Left click')
        elif event.button() == 2:
            self.iface.messageBar().pushMessage('Right click')

The result: after launching the plugin with the icon or from the menu item and clicking inside the QGraphicsView widget, we get a message about which button was clicked pushed to the message bar:

enter image description here

By the way, I would recommend keeping signal/ slot connections out of the run() method and putting them in the initGui() method instead.

2
  • Thanks a lot! If I ever could give two points to an answer, this would be the time "The mousePressEvent method is designed to be re-implemented inside a sub class which inherits from one of the QWidget abstract base classes, " was the piece of information I needed to know, but did not find. Oct 14, 2019 at 15:40
  • You're welcome @MortenSickel, glad it helped!
    – Ben W
    Oct 14, 2019 at 21:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.