【问题标题】:Is there a way to know what activated QAction?有没有办法知道激活的 QAction 是什么?
【发布时间】:2023-12-13 13:41:01
【问题描述】:

我在QGraphicsView 子类中创建了QAction 的实例,并将其连接到我在同一个类中的插槽。

QAction *action   = new QAction(tr("New"), this);
action->setObjectName("addStopAction");
action->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_N));
connect(action, SIGNAL(triggered()), this, SLOT(addNew()));
addAction(action);

Slot 是在分配给QGraphicsView 的场景上创建QGraphicsItem 的新实例的函数。

void MyGraphicsView::addNew() {
    // Insert new item at cursor position
}

我还将这个动作添加到一个QMenu 作为我的类上下文菜单。

QMenu *contextMenu = new QMenu(this);
contextMenu->addAction(action);

一切正常。当我按 Command/Ctrl + N 时,会在光标位置创建新项目。但是当我右键单击并从上下文菜单中选择操作时,我希望在菜单位置创建新项目。

当然,如果 SLOT 在 contextMenuEvent 之后被调用或类似的东西,我可以做一些小技巧来标记,但我想知道的是:

有什么方法可以找出是什么让QAction 在连接的SLOT 内发出triggered() 信号?这样,当我应该将新项目放置在光标位置以及在 SLOT 实现内的上下文菜单位置时,我可以处理。

【问题讨论】:

    标签: c++ qt qt4 signals-slots


    【解决方案1】:

    当然,您可以找出连接的 SLOT 内发出的信号。

    只需使用 QObject::sender()。在你的情况下:

    void MyGraphicsView::addNew() {
        QAction* pAction = qobject_cast<QAction*>(sender());
        Q_ASSERT(pAction);
        // do something with pAction
    }
    

    【讨论】:

      【解决方案2】:

      我认为您可以使用 QAction 对象可以包含的自定义数据。 您可以在创建上下文菜单时设置它:

      void showContextMenu(const QPoint &pos)
      {
          ...
          action->setData(pos);
          ...
      }
      

      然后在addNew() 函数中检查数据是否存在并在最后重置它:

      void addNew()
      {
          QPoint pos;
          QPoint posFromAction = action->data()->toPoint();
          if (posFromAction.isNull())
          {
              pos = QCursor::pos(); ///< pos will be current cursor's position
          }
          else
          {
              pos = posFromAction; ///< pos will be menu's position
          }
      
          doYourStuffAt(pos)
      
          action->setData(QPoint()); ///< reset action's data
      }
      

      【讨论】:

        【解决方案3】:

        我通过将菜单连接到 connect (menu, SIGNAL( triggered(QAction*) ), this, SLOT( menuAction_triggered(QAction*) )); 之类的功能来管理类似的东西

        当您执行上下文菜单时,QMenu::exec(QPoint) 将返回指向操作的指针,因此您可能不需要额外的函数/插槽。

        您可以通过文本QAction::text() 来检查操作的名称,或者通过比较地址来检查您是否将指针存储在某处。

        苏长在

        【讨论】:

        • 我确实尝试过。这种方法的问题是,当我从上下文菜单激活操作时,两个 SLOT 都将被调用(操作一和菜单一)。这样我可以设置一些标志来描述该操作是使用上下文菜单触发的(在上下文菜单触发(QAction*)处理程序中),然后处理这种情况(在操作触发()处理程序中),但它也可以以相同的方式执行此操作与contextMenuEvent.
        • 另一种可能的解决方案是将QAction::trigered(bool) 信号连接到您选择的插槽。所以你不需要处理触发了什么动作或触发了什么发送者。
        【解决方案4】:

        您可以在 customContextMenuRequested 信号调用的函数中引用 self.sender()。

        self.tree1.customContextMenuRequested.connect(self.menu1pop)  # rightclick menu signal
        
        ...
        
        def menu1pop(self, pos):
        
            widget = self.sender()
        
            item = widget.itemAt(pos)
            if item is None: return            # only show contextmenu if on an item
            self.setProperty("mywidget", widget)  # pass current widget 
            self.menu1.popup(QCursor.pos())    # show menu at right click cursor position
        

        self.sender() 将返回您的小部件对象,然后您可以在属性中设置小部件对象。

        然后当你的动作函数被调用时,你可以通过读取属性来调用小部件对象。

        widget = self.property("mywidget")
        

        这有点小技巧,但却是一种简单而可靠的方法来了解您的操作是从哪个小部件调用的。

        【讨论】:

          【解决方案5】:

          您可以在接收调用的槽中使用 QObject::sender()。虽然没有尝试过这个动作。它可能比您提出的“hack”(您实际上可以使用作用域类很好地实现它)有点丑陋。

          【讨论】:

          • 我已经尝试从 QObject::sender() 获取此信息已有一段时间了,但找不到任何可以帮助我区分激活的操作的信息。
          • 不,这不会在这里完成工作。 sender() 只告诉你最初发出信号的对象实例(总是== action),而不是事件源。
          • @leemes 啊-认为可能是这种情况,因此在我的回答中可能会大写。
          • 只有有根据的猜测的回复应该是评论。 -1.