将参数传递给槽函数

97

我想用一系列的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)。你知道我该怎么做吗?


3
不要传递参数,考虑在信号内部分析sender()。 - Pavel Radzivilovsky
6个回答

150

使用 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 及更新版本。


10
C++11的lambda表达式与Qt 5中将函数对象连接到信号的能力相结合,是一个非常实用但却鲜为人知的特性。 - Carlton
我将这个解决方案适用于我的情况。但是,如果我使用SIGNAL(triggered(bool))而不是&QAction::triggered,它会抛出一个错误。有人能否解释一下为什么? - Deniz
1
它不会“抛出”错误。编译器会报错,错误信息应该会告诉你原因:没有QObject::connect重载函数可以将const char *作为第二个参数和函数对象作为第三个或第四个参数。Qt4风格的connect语法与新语法不兼容。如果您希望使用旧语法,则放弃连接到函数对象的便利性(即使在使用C++11编译器但使用Qt 4时也可以近似实现)。 - Kuba hasn't forgotten Monica

128

使用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))) ;

我仍然记得那些没有QSignalMapper的Qt时代,唯一的解决方案是在连接到同一槽的对象上设置属性并使用sender()->property(...)。 - Kamil Klimek
@Kamil Klimek,你不必这样做;你本可以编写自己的映射器 :) - Piotr Dobrogost
如果我的“context”参数针对一个没有访问权限的类,该如何使用它?因此,无论如何,“context”信号映射器都无法访问操作,或者如果我将信号映射器放入同一类中,则连接插槽的上下文将是错误的。 - dhein
值得注意的是(2018年,Qt5,C++11),QSignalMapper已被弃用。从链接中可以看到:“此类已过时。它提供了保持旧源代码工作的功能。我们强烈建议不要在新代码中使用它。”下面@kuba的答案现在更好。 - Robin Macharg

14

QObject::sender()函数返回一个指针,指向触发槽函数的对象。您可以使用此函数来确定是哪个动作被触发了。


2
请您再说一遍?该插槽是QObject的子类成员,因此它也有一个QObject :: sender() 成员。只需调用sender() 函数,便可获得指向操作的QObject*。之后,您可以使用已获取的操作的objectName() 或property() 来收集更多信息。如果您真的想这么做,还可以将其转换为Action对象,但我不建议这样做。 - Septagram

3
你可以使用 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
}

{{链接1:示例}}


1
也许您可以使用一个带有 m_increase 成员变量的 QAction 子类。
将 triggered() 信号连接到新 QAction 子类上的一个槽,并发出一个新信号(例如 triggered(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;  
};

0
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)));
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接