In previous Qt projects, I often found that slot functions only need to be executed once. That is to say, after the slot function is executed once, it needs to disconnect the corresponding connection. However, the actual operation is actually quite cumbersome, or inelegant. Because you need to save the QMetaObject::Connection
object generated when connecting before, and save it cannot use local variables, usually need to be saved to the member variables of the class, or other places with a long enough life cycle to prevent the When you disconnect it, it is already invalid. In short, the user is required to maintain by himself, thereby increasing the burden on the user.
It would be nice if there was a way to automatically disconnect the connection after the slot function is executed. I searched for a while on the Internet, but I couldn't find a suitable solution, and there are relatively few related discussions. Maybe this is not a very common requirement. However, I still found a related library on GitHub: https://github.com/misje/once , but looking at its source code, it feels more complicated. It wrote the corresponding implementation for each case of the QObject::connect
function, which always feels too complicated, there should be a more general method.
After reading the template-related chapters in C++ Primer recently, it occurred to me that maybe the implementation can be simplified with something related to perfect forwarding. So I tried to write it, it seems to be really possible, the code is as follows:
ConnectionUtil.h:
#pragma once
#include <QObject>
#include <functional>
namespace ConnectionUtil
{
typedef QMetaObject::Connection Conn;
class ReceiverObj : public QObject
{
Q_OBJECT
public:
explicit ReceiverObj(Conn *conn1, Conn *conn2) : mConn1(conn1), mConn2(conn2) {}
public slots:
void slot()
{
QObject::disconnect(*mConn1);
QObject::disconnect(*mConn2);
delete mConn1;
delete mConn2;
deleteLater();
}
private:
Conn *mConn1, *mConn2;
};
// 处理信号为SIGNAL(...)的情况
template <typename Sender, typename ...Args>
void connectOnce(Sender &&sender, const char *signal, Args &&...args)
{
Conn *conn1 = new Conn;
Conn *conn2 = new Conn;
*conn1 = QObject::connect(std::forward<Sender>(sender), signal, std::forward<Args>(args)...);
*conn2 = QObject::connect(std::forward<Sender>(sender), signal, new ReceiverObj(conn1, conn2), SLOT(slot()));
}
// 处理其他情况
template <typename Sender, typename Signal, typename ...Args>
void connectOnce(Sender &&sender, Signal &&signal, Args &&...args)
{
Conn *conn1 = new Conn;
Conn *conn2 = new Conn;
*conn1 = QObject::connect(std::forward<Sender>(sender), std::forward<Signal>(signal), std::forward<Args>(args)...);
*conn2 = QObject::connect(std::forward<Sender>(sender), std::forward<Signal>(signal), std::bind(&ReceiverObj::slot, new ReceiverObj(conn1, conn2)));
}
}
This implementation assumes QObject::connect
of all overloaded functions the first two parameters are Sender and Signal respectively, and it is. The key point is that an additional connection is established, and after receiving the signal, the user's connection is disconnected. However, I always feel that this implementation may have bugs in the case of multi-threading, but after a simple test, I haven't found it for the time being. The usage example is as follows:
ConnectionUtil::connectOnce(this, SIGNAL(bong(int)), obj, SLOT(slotBong(int)), Qt::QueuedConnection);
ConnectionUtil::connectOnce(this, &MainWindow::bong, obj, &SomeObject::slotBong, Qt::QueuedConnection);
ConnectionUtil::connectOnce(this, &MainWindow::bong, this, []() {
qDebug() << "bingo";
});
Please criticize and correct.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。