【问题标题】:Passing an argument to a slot将参数传递给插槽
【发布时间】:2011-07-06 09:45:09
【问题描述】:

我想用一堆 QActions 和 QMenus 覆盖 mouseReleaseEvent...

connect(action1, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action5, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action10, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action25, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

connect(action50, SIGNAL(triggered()), this, SLOT(onStepIncreased()));

所以我想将参数传递给插槽onStepIncreased(您可以想象它们是 1、5、10、25、50)。你知道我该怎么做吗?

【问题讨论】:

  • 考虑分析信号内部的sender(),而不是传递参数。

标签: qt qsignalmapper qt-slot


【解决方案1】:

您可以使用std::bind 这是一个功能对象适配器,它允许功能对象适应给定数量的参数。

例如,您创建自己的聊天服务器。其中包含两个类:ChatServer 和 ServerWorker。

ChatServer 是 QTcpServer 类,ServerWorker 是 QTcpSocket(管理服务器端的套接字)。

ServerWorker 标头中的信号:

void error();

在您的 ChatServer 标头中,您定义这些私有插槽:

void userError(ServerWorker *sender);

在 cpp 文件中创建这些对象,并在套接字连接后运行的 incomingConnection 方法中,使用 std::bind 连接插槽和信号:

void ChatServer::incomingConnection(qintptr socketDescriptor)
{
    //some code
    connect(worker, &ServerWorker::error, this, std::bind(&ChatServer::userError, this, worker));
}

std::bind 创建一个带有一些固定参数的函子。例如connect(worker, &ServerWorker::error, this, std::bind(&ChatServer::userError, this, worker)); 将导致this->userError(worker);每次worker发出错误信号时调用。

userErrorslot 每次连接到客户端的套接字遇到错误时都会执行。它有签名:

void ChatServer::userError(ServerWorker *sender)
{
    //some code
}

Example

【讨论】:

    【解决方案2】:
    QVector<QAction*> W(100);
     W[1]= action1;
     W[5]= action5;
     W[10]= action10;
     W[25]= action25;
     W[50]= action50;
    
    for (int i=0; i<100; ++i)
    {
      QSignalMapper* signalmapper = new QSignalMapper();
      connect (W[i], SIGNAL(triggered()), signalmapper, SLOT(map())) ;
      signalmapper ->setMapping (W[i], i);
      connect (signalmapper , SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int)));
    } 
    

    【讨论】:

      【解决方案3】:

      使用QSignalMapper。像这样:

      QSignalMapper* signalMapper = new QSignalMapper (this) ;
      connect (action1, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
      connect (action5, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
      connect (action10, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
      connect (action25, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
      connect (action50, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
      
      signalMapper -> setMapping (action1, 1) ;
      signalMapper -> setMapping (action5, 5) ;
      signalMapper -> setMapping (action10, 10) ;
      signalMapper -> setMapping (action25, 25) ;
      signalMapper -> setMapping (action50, 50) ;
      
      connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int))) ;
      

      【讨论】:

      • 我还记得,当 Qt 没有 QSignalMapper 时,唯一的解决方案是在连接到同一插槽的对象上设置属性并使用 sender()->property(...)
      • @Kamil Klimek 你不必这样做;您可以编写自己的映射器:)
      • 如果我的 context 参数将 a 定位到一个无法访问操作的类,如何使用它?所以无论如何,context signalmapper 将无法访问这些操作,或者如果我将 signalmapper 放入同一个类中,那么连接槽的上下文将是错误的。
      • 值得注意的是 (2018, Qt5, C++11) QSignalMapper 已被弃用。来自链接:“此类已过时。提供它是为了保持旧源代码正常工作。我们强烈建议不要在新代码中使用它。”下面@kuba 的答案现在是一个更好的答案。
      【解决方案4】:

      使用 Qt 5 和 C++11 编译器,做这些事情的惯用方法是给 connect 一个仿函数:

      connect(action1,  &QAction::triggered, this, [this]{ onStepIncreased(1); });
      connect(action5,  &QAction::triggered, this, [this]{ onStepIncreased(5); });
      connect(action10, &QAction::triggered, this, [this]{ onStepIncreased(10); });
      connect(action25, &QAction::triggered, this, [this]{ onStepIncreased(25); });
      connect(action50, &QAction::triggered, this, [this]{ onStepIncreased(50); });
      

      connect 的第三个参数名义上是可选的。它用于设置函子将在其中执行的线程上下文。当仿函数使用QObject 实例时,它总是必要的。如果仿函数使用多个 QObject 实例,它们应该有一些共同的父对象来管理它们的生命周期,并且仿函数应该引用该父对象,或者应该确保对象的寿命比仿函数长。

      在 Windows 上,这适用于 MSVC2012 及更高版本。

      【讨论】:

      • 这种 C++11 lambdas 和 Qt 5 将函子连接到信号的能力的组合是一个非常有用但被低估的功能。
      • 我根据我的情况调整了这个解决方案。但是,如果我使用SIGNAL(triggered(bool)) 而不是&amp;QAction::triggered,则会引发错误。有人可以解释一下为什么吗?
      • 它不会“抛出”错误。编译器会抱怨,错误消息应该告诉你原因:没有QObject::connect 重载将const char * 作为第二个参数,将仿函数作为第三或第四个参数。 Qt4 风格的connect 语法不与新语法混合。如果您希望使用旧语法,那么您将失去连接仿函数的便利性(即使如果您有 C++11 编译器但使用 Qt 4,它也可以近似)。
      【解决方案5】:

      QObject::sender() 函数返回一个指向已向槽发出信号的对象的指针。您可以使用它来找出触发了哪个操作

      【讨论】:

      • 对不起? slot 是 QObject 子类的成员,因此它也有一个 QObject::sender() 成员。只需调用 sender(),您将获得一个指向您的操作的 QObject*。之后,您可以使用已获取操作的 objectName() 或 property() 来收集更多信息。如果你真的想要,你也可以将它转换为一个动作对象,但我不建议这样做。
      【解决方案6】:

      也许您可以使用 m_increase 成员变量对 QAction 进行子类化。
      将 trigger() 信号连接到新 QAction 子类上的插槽,并使用正确的参数发出新信号(例如,trigger(int number))。
      例如

      class MyAction:public QAction  
      {  
      public:  
          MyAction(int increase, ...)  
              :QAction(...), m_increase(increase)
          {  
              connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));  
          }  
      protected Q_SLOTS:  
          void onTriggered()  
          {  
              emit triggered(m_increase);  
          }  
      
      Q_SIGNALS:
          void triggered(int increase);   
      
      private:  
          int m_increase;  
      };
      

      【讨论】:

        猜你喜欢
        • 2020-03-12
        • 2022-01-10
        • 2011-05-08
        • 2012-01-08
        • 1970-01-01
        • 2013-11-11
        • 2016-02-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多