0

It might be a very simple question but I can't figure out how to do it.

The context: I am making an application that can draw polygons in a QGraphicsScene. I would like to implement interactive actions. For example, the user clicks on a button, then clicks on the scene to draw temporary lines, finally the app cuts the polygon with the line.

What I try to achieve

What I try to achieve

What I did Since I didn't find anything here or in the doc, I looked in the LibreCad repository to know how they do it.

https://github.com/LibreCAD/LibreCAD/blob/768285653c46e734a75460928142cda1a33c2c53/librecad/src/lib/actions/rs_actioninterface.h

Their base class inherits from QObject to handle events. It seems that they don't inherit from QAction despite line 40 where one can read "class QAction;". My C++ skills end here. In an act of faith, I wrote the following minimal non-working example in python with PySide.

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PySide2.QtCore import QObject
from PySide2.QtWidgets import (QApplication, QMainWindow, QWidget,
                               QGraphicsView, QGraphicsScene,
                               QToolBar, QAction)

class Action(QAction, QObject):
    def __init__(self, name, parent=None):
        super().__init__(name, parent)
        # I also tried to add this, obviously I don't know what I am doing
        #QObject.__init__(parent) 
        
    def keyPressEvent(self, event):
        print("Event handled")
        super().keyPressEvent(event)


class MainWindow(QMainWindow,):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.centralwidget = QWidget()
        self.graphicsView = QGraphicsView(self.centralwidget)
        self.setCentralWidget(self.centralwidget)
        self.scene = QGraphicsScene(0,0,1000,1000)
        self.graphicsView.setScene(self.scene)
        
        toolbar = QToolBar("My toolbar")
        self.addToolBar(toolbar)
        
        knifeActionButton = Action("Knife", self)
        knifeActionButton.setShortcut("K")
        knifeActionButton.triggered.connect(self.cutPolygon)
        toolbar.addAction(knifeActionButton)
    
    def cutPolygon(self):
        print("Action triggered")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exec_()

The question: How to handle events in a QAction to make it interactive?

Please excuse my English, I am a non-native speaker

Parisa.H.R
  • 3,303
  • 3
  • 19
  • 38
Simon
  • 11
  • 4
  • 1. QAction already inherits from QObject, so there's no point in that multiple inheritance (also, multiple inheritance of Qt objects usually doesn't work at all on PyQt/PySide) 2. Mouse/key events are not handled by the action, but by the widgets that umplement them, such as a QMenu or a QToolBar, which eventually would call the action's functions or emit their signals. Besides that, it's not really clear what you're trying to achieve: once the action that "starts" your drawing procedure is triggered, then it's up to you to manage any other events based on the "context" of the "current" action – musicamante Aug 12 '21 at 11:02
  • Thank you for your comment. Where should I handle the events needed for my drawing procedures ? I was trying to avoid implementing all the work in the main window or in the QGraphisScene since it would be cumbersome to check "if currentAction == myAction, handle the event like that, elif ..." In addition, this if-ladder would be duplicated in mousePressEvent, mouseReleaseEvent, keyPressEvent... I was thinking of something like the action (or another object) takes control and handle the events while it is active and then, when done, give the control back. – Simon Aug 12 '21 at 12:37
  • Keyboard and mouse events can only be managed by the widgets that receive them, so you cannot avoid that. You have many options, depending on what better suits you: the individual QGraphicsView event handlers, its `viewportEvent`, the QGraphicsScene `event` or its handlers. And you don't have to duplicate code: just use a single function that can handle any event, call it from every event handler and check for the type to decide how to manage the event. – musicamante Aug 12 '21 at 13:21
  • An *alternative* could be to install an event filter on the view or the scene whenever the action is triggered, but I believe that it would only make the code unnecessarily complex, as it shouldn't be responsibility of the action to manage those events: that's not the purpose of a QAction. – musicamante Aug 12 '21 at 13:24
  • A `QAction` is essentially just a trigger. Your code must then do something when the action is triggered. The action itself should not "do" anything like interacting with the UI. One _could_ create some Frankenstein QAction which may actually do something, but this would not be at all what it is meant for. To avoid code duplication (as per your comment) you create reusable methods within the classes which are best equipped to handle the action (eg. the widget doing/managing the drawing). You then call those methods from various places where the same logic is needed (programming 101). – Maxim Paperno Aug 14 '21 at 04:32

0 Answers0