Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Signal/slot vs direct call, and related question



  • Two questions: one stylistic, one implementation.

    GUI application, main window with several widget-windows on it, one of which is a QGraphicsScene (sub-classed). Objects there can be copy/pasted etc.

    1. Style: "To signal/slot or not to signal/slot, that is the question..."

    Inherited code which used QGraphicsScene::keyPressEvent() to capture and act on the Copy etc. key presses. Now that these actions have been added to the main window's menu with shortcuts, that stops working (and I need to deal with menu actions anyway).

    As I move to the action handler in the main window, I simply call directly the desired exported method from the graphics scene sub-class. Is there any good reason why I should instead raise a signal for these actions, and make the methods slots? The main window would still need to see those methods exported so that it can connect() to them, at which point am I just as good with a direct call?

    1. Implementation

    When the code was in QGraphicsScene::keyPressEvent() we knew that the key press was directed at the QGraphicsScene. Now the actions are on main menu we do not, it could be for other windows. What is the "rule" I need to test for (presumably in main window action handler) before I pass on to deal with in QGraphicsScene window? It's not enough to say "is there an item selected on the scene", is it? I presume I'm supposed to test for something like where the focus is, is that it?? Or is it "window is activated" (I don't think so)? Or what?



  • @JonB said in Signal/slot vs direct call, and related question:

    Now that these actions have been added to the main window's menu with shortcuts, that stops working (and I need to deal with menu actions anyway).

    Your keyPressEvents are shortcuts?!
    Why dont you use QShortcut?! You can set a ShortcutContext (click) to trigger your actions even when your scene doesn't have focus. Then you redirect your shortcut to your scene.

    @JonB said in Signal/slot vs direct call, and related question:

    What is the "rule" I need to test for (presumably in main window action handler) before I pass on to deal with in QGraphicsScene window? It's not enough to say "is there an item selected on the scene", is it? I presume I'm supposed to test for something like where the focus is, is that it?? Or is it "window is activated" (I don't think so)? Or what?

    QShortcut handles that for you, if you use WindowShortcut or ApplicationShortcut.
    If your window or widget, where your scene is in, is a child of MainWindow your scene doesnt even need focus (AppShortcut: your shortcut works everywhere, you just need one active window)



  • @Pl45m4
    Sorry, I don't understand any of this, and don't think it relates to what I am asking.

    I know how shortcuts work and how to use them. I'm (now) using shortcuts, as I explained.

    Inherited code which used QGraphicsScene::keyPressEvent() to capture and act on the Copy etc. key presses. Now that these actions have been added to the main window's menu with shortcuts, that stops working (and I need to deal with menu actions anyway).

    You suggested:

    to trigger your actions even when your scene doesn't have focus.

    That's 100% precisely what I don't want to happen. The whole point is asking how, when an action on a main window menu is triggered by click or shortcut (doesn't matter which), how you decide which widget is supposed to respond to it. I do not want to direct a Copy command to the scene if it does not have focus, do I? That is what I am asking about.



  • @JonB said in Signal/slot vs direct call, and related question:

    how you decide which widget is supposed to respond to it.

    Aren't your QActions in your menu connected to some slots (at destination)?

    It seems, that I really dont understand your problem :-)

    @JonB said in Signal/slot vs direct call, and related question:

    I do not want to direct a Copy command to the scene if it does not have focus, do I? That is what I am asking about.

    Ah, check. You want Qt::WidgetShortcut behavior, but still trigger your QActions from your mainWindow's menuBar (or wherever they are) to copy your GraphicItem?!


  • Lifetime Qt Champion

    Hi,

    How are you creating your actions ?



  • @Pl45m4 said in Signal/slot vs direct call, and related question:

    Ah, check. You want Qt::WidgetShortcut behavior, but still trigger your QActions from your mainWindow's menuBar (or wherever they are) to copy your GraphicItem?!

    This sounds like it. Yes I think to all. Let me go read about Qt::WidgetShortcut...! :)

    @SGaist
    Menu items/actions/shortcuts are created in Designer, with whatever default. Each item has an action triggered slot in the MainWindow class. I have, say, a Copy menu item/shortcut. The gfx scene does its own copy stuff. I want to know how I am supposed to know, when the action slot is hit, that it should be directed to the gfx widget.

    Since asking this earlier, I now have in Main Window slot something like

    def copy_action(self):
        if QApplication.focusWidget() is scene_view:
            scene.do_copy_action()
    

    and that works.

    See also my post today at https://forum.qt.io/topic/80019/find-which-widget-had-focus-when-menu-item-is-clicked/8.


  • Lifetime Qt Champion

    Do you have several different widgets which are providing copy behaviour ?



  • @JonB said in Signal/slot vs direct call, and related question:

    The gfx scene does its own copy stuff. I want to know how I am supposed to know, when the action slot is hit, that it should be directed to the gfx widget.

    Do you also have multiple scenes or graphicsViews?
    Otherwise why dont you connect your copy-action with a slot in your custom scene, determine which item shoud be copied (if this is relevant?!) and then let the scene do its copy work? :)



  • @SGaist said in Signal/slot vs direct call, and related question:

    Do you have several different widgets which are providing copy behaviour ?

    As of today, no. But as of tomorrow, quite possibly. There are a couple of other "panes" in the main window. They do not currently copy. But most importantly, if they have "focus" the I do not want the Copy/Paste menu actions/shortcuts to go to the gfx widget.

    @Pl45m4
    Only one scene/view. As of today.

    The scene does do the copy work. The code does not call slots in other modules (nor do I think Designer offers that). For consistency, all action trigger slots are in main window class. I expected to implement a "dispatcher" which figured which window had the focus and call its method as required. As I said, FWIW I haven't looked at Qt::WidgetShortcut, yet.


  • Lifetime Qt Champion

    You can associate an action with several widgets. Did you check if sender gives you the information you seek once you add the action to all the widgets you want to use it on ?



  • @SGaist said in Signal/slot vs direct call, and related question:

    once you add the action to all the widgets you want to use it on

    The code is not (presently) written that way. All menu items/toolbar items/shortcuts have actions & slots in the main window class. I guess all shortcuts are the default Qt::WindowShortcut. Most do not care what the focused widget is, e.g. New/Open/Save/Exit etc. In the case of Copy/Cut/Paste, however, it does matter whether, say, the graphics widget has the focus.

    I expected to handle that in the main window action slot by checking which widget has focus. It sounds like maybe if I look into using Qt::WidgetShortcut (or maybe Qt::WidgetWithChildrenShortcut?) on Copy/Cut/Paste for the gfx widget (and any others which might want these in the future), the menu items and shortcut keys will only activate when the desired window is focused, and my main window will not have to decide who to "dispatch" to. I need to have a look at how this behaves tomorrow....



  • @Pl45m4 , @SGaist
    Now I am trying to make this work with Qt::WidgetShortcut, which does seem like it should be the right approach. However, it does not work correctly at all! Please don't abandon me! Read on...

    • I have a QMainWindow. I design it, and add its toolbar/menu items/shortcuts in Designer.
    • It has 4 widgets inside it. One of them is the QGraphicsScene/View. That has code for, say, Cut. The others do not (at present) have anything for Cut.
    • In the case of a menu item/toolbar/shortcut key Cut I want that to go to the gfx view if it has the focus, else it should be ignored.

    This really should not be an unusual situation. Some menu items function regardless of which widget has focus (e.g. Save), some depend on which widget has focus (e.g. Cut).

    I start from the relevant code as generated (mainwindow.py), which includes:

    self.action_cut = QtWidgets.QAction(MainWindow)
    self.action_cut.setShortcutContext(QtCore.Qt.WindowShortcut)
    self.menuEdit.addAction(self.action_cut)
    self.toolBar.addAction(self.action_cut)
    self.action_cut.setShortcut(QtWidgets.QApplication.translate("MainWindow", "Ctrl+X", None, -1))
    

    To make this work, I have found I can go:

    self.action_cut.triggered.connect(self.cut)
    
    def cut(self):
        if QApplication.focusWidget() is self.graphicsView_model:
            self.model_scene.cutItems()
    

    At this point:

    • If I click the toolbar/menu item, regardless of where focus is my cut() is hit. Its code determines whether the gfx widget has focus or not, and correctly works accordingly.
    • If I click Ctrl+X, if the current focus widget absorbs that (e.g. a QLineEdit in some other widget). If not (e.g. on a plain QWidget) it goes via my cut(), and again that checks focus and works correctly.

    Now I try changing as best I can to make the Cut only apply to my gfx widget. I change/add things like:

    self.action_cut.setParent(self.graphicsView_model)
    # OR
    self.action_cut = QtWidgets.QAction(self.graphicsView_model)
    self.action_cut.setShortcutContext(QtCore.Qt.WidgetShortcut)
    # OR
    self.action_cut.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
    

    At this point:

    • If I click toolbar/menu it still calls my cut() regardless of where focus is. It has not made it so this is only called when self.graphicsView_model has focus. So I will still need to test if QApplication.focusWidget() is self.graphicsView_model.
    • Ctrl+X no longer does anything, regardless of focus.

    Both of these are no good/not expected.

    I came across thread starting at https://lists.qt-project.org/pipermail/qt-interest-old/2011-February/031257.html. I think that guy reports my findings trying to use Qt::WidgetShortcut. Summary:

    So if I install an action on a QWidget I would assume that the actions
    shortcut is only active if the QWidget has focus, right? But this seems
    to be broken somehow.
    .
    My educated guess would be that this "shortcut context" thing might only work between "dialog and main windows", but not between common QWidgets?

    In a word: if you expect Qt::WidgetShortcut to work for me, please tell me how?!

    My case should not be that unusual: some menu items/shortcuts apply regardless of focused window, some do not. If you do not want me to have to use my if QApplication.focusWidget() is self.graphicsView_model-type-dispatching, please tell me how?! I'm exhausted typing this all up....

    P.S.
    And this is only a P.S., I really don't think this is to do with the problem:
    @SGaist said in Signal/slot vs direct call, and related question:

    You can associate an action with several widgets. Did you check if sender gives you the information you seek once you add the action to all the widgets you want to use it on ?

    See the accepted solutuon at https://stackoverflow.com/a/6003452/489865. Note how the approach is still to look at QWidget::hasFocus() to decide which the user was in when trying to invoke the action. If I'm going to do that, my current code will suffice.


Log in to reply