如何在Qt中使函数暂时成为插槽
How to make a function to be a slot temporally in Qt?
要使类的函数成为插槽,该类必须从QObject继承。但是,QObject 占用了相当多的内存。我不确定它是多少,以及内存是针对每个类还是每个对象。我的代码有许多小数据,其功能有时可能是一个插槽。我想知道是否有办法在使用时使类函数暂时成为插槽。使用后,插槽成本的内存将被删除。以下代码说明了该要求。
class SmallData // size of 2 or 3 integers.
{
public:
virtual void F(); // use it as a slot.
virtual QMenu* createMenu(); // use this to create the context menu with
// an action connected to F()
...
};
// use the small data
vector<SmallData> vec(1000000); // the vector is put at a tree view. When an
// item in the tree view is selected, a context
// menu pop up with an action to run F().
SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()), data, SLOT(F())); // How to make F() to be
// a slot just here.
// The action is from
// data->createMenu().
如果你可以使用Qt5,你可以将信号连接到普通函数和静态方法(本质上是有趣的命名为普通函数):
connect(action, &QAction::triggered,
&SmallData::statF);
其中action
是QAction
实例,SmallData::statF
是静态SmallData
方法。
根据 Christian Rau 的评论进行编辑,要调用特定实例,您还可以连接到 lambda:
connect(action, &QAction::triggered,
[data]() { data->F(); });
在Qt4中,您可以使用QSignalMapper
来实现大致相同的效果,但要多做一些对象。它允许您添加添加参数(在本例中,可能是vec
的整数索引)到信号,基于哪个对象发出它。但在Qt4中,接收器仍然必须始终是QObject。
对于使用信号槽机制,你不会绕过QObject
,但你可以做的是创建一个临时对象,该对象有一个调用你的函数的槽。您只需要注意正确释放对象即可。像这样:
class Callback : public QObject
{
Q_OBJECT
public:
typedef std::function<void()> FunctionType;
Callback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr)
: QObject(parent), fn_(std::move(fn)), oneShot_(oneShot) {}
public slots:
void call()
{
fn_(); //delegate to callback
if(oneShot_)
deleteLater(); //not needed anymore
}
private:
FunctionType fn_;
bool oneShot_;
};
Callback* makeCallback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr)
{
return new Callback(std::move(fn), oneShot, parent);
}
然后,您只需在每次需要时创建一个(或多或少临时的)Callback
对象:
SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()),
makeCallback(std::bind(&SmallData::F, data)), SLOT(call()));
使用 oneShot
参数,您可以控制插槽在触发后是否应自动解散。
唯一的问题是,如果这个插槽从未被调用,那么你就会有一个泄漏的Callback
挂在周围。为了适应这一点,你可以在parent
参数中传递一些有意义的东西,以便Qt至少在以后的某个时间点关心正确的删除:
SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()),
makeCallback(std::bind(&SmallData::F, data), true, this), SLOT(call()));
这样,您还可以将回调对象的生存期(以及信号槽连接)绑定到其他对象(例如,操作本身并在未选择任何项目时删除操作,或类似内容)。
或者,您也可以记住当前选定项目的Callback
对象,并在删除后自行注意正确删除。
免责声明:请注意,上面的例子包含大量的 C++11,但我没有心情为 C++03 重写它。同样,这个解决方案也可以进一步改进,也许使用模板化函子而不是std::function
(但如果我没记错的话,Qt 元对象系统不太喜欢模板)。
编辑:最后,Frank Osterfeld在他的评论中提出的解决方案可能比我上面过于通用的对象生命周期疯狂更简单:只需将操作连接到更高级别对象的单个插槽(您的主小部件或可能包含数据向量的项目模型),然后对当前选定的项目调用F
:
connect(action, SIGNAL(triggered()), this, SLOT(callF()));
...
void MyController::callF()
{
treeView.selectedItem()->F();
}
我不认为你尝试做的事情在Qt中是不可能的。
如果你真的不想继承QObject,那么我建议你看看升压信号和插槽机制。
- 为什么 connect() 函数看不到插槽?
- 在Qt中使用C++Lambda函数作为插槽是否有助于保持库的二进制兼容性?
- 为什么插槽函数即使成功调用也无法插入文本
- 传递函数指针代替插槽函数
- C++ QT:获取指向信号的指针和插槽功能的函数
- 这是在Qt信号和插槽中使用参数调用函数的好方法吗?
- C / QT中lambda函数中的paramater插槽
- 在 for 循环中使用 lambda 函数连接信号插槽
- Qt - 使用λ函数修改先前连接信号的插槽参数
- 通过插槽打印/显示QT函数调用的内容
- 呼叫函数直接与发射信号(QT-信号和插槽)
- 如何调用作为插槽的qt函数
- 从Qt调用C++函数(插槽不起作用)
- Qt 我可以在构造函数中将信号/插槽连接到自身吗?
- 如何在主函数中从插槽中调用 showNormal
- 如何使用新的QObject::连接语法与函数指针将QSslSocket::错误信号连接到插槽
- 为什么我不能从基类插槽Qt调用虚函数
- QTableWidget-QMenu上下文菜单-AddAction插槽不调用函数
- 矢量函数"push_back"不断将值重写到内存中的同一插槽
- 如何将抽象信号连接到接口构造函数中的插槽?