3

要使类的函数成为槽,该类必须继承自 QObject。但是,QObject 占用了相当多的内存。我不确定它是多少,以及内存是否适用于每个类或每个对象。我的代码有很多小数据,它们的功能有时可能是一个槽。我想知道在使用它时是否有一种方法可以使类的功能暂时成为一个插槽。使用后,slot cost的内存将被删除。以下代码说明了要求。

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().
4

3 回答 3

8

如果您可以使用 Qt5,您可以将信号连接到普通函数和静态方法(本质上是有趣的普通函数):

connect(action, &QAction::triggered, 
        &SmallData::statF); 

whereaction是一个QAction实例,并且SmallData::statF是 的静态方法SmallData

根据 Christian Rau 的评论进行编辑,以调用特定实例,您还可以连接到 lambda:

connect(action, &QAction::triggered,
        [data]() { data->F(); });

已经使用 Qt4,您可以使用QSignalMapper更多的对象来实现几乎相同的效果。它允许您添加一个参数(在这种情况下,可能是您的整数索引vec)来发出信号,基于哪个对象发出它。但是在 Qt4 中,receiver 仍然必须始终是一个 QObject。

于 2013-02-04T16:18:06.227 回答
3

对于使用信号槽机制,您将无法绕过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 重写它。同样,这个解决方案是否可以进一步改进,也许使用模板仿函数而不是 a std::function(但如果我没记错的话,Qt 元对象系统不太喜欢模板)。


编辑:最后,弗兰克奥斯特菲尔德在他的评论中提出的解决方案可能比我上面过于通用的对象生命周期疯狂更简单得多:只需将操作连接到更高级别对象的单个插槽(您的主小部件或可能是包含数据向量的项目模型)并调用F当前选定的项目:

connect(action, SIGNAL(triggered()), this, SLOT(callF()));
...
void MyController::callF()
{
    treeView.selectedItem()->F();
}
于 2013-02-04T15:59:57.883 回答
0

我认为您尝试做的事情在 Qt 中是不可能的。

如果你真的不想继承 QObject,那么我建议你看看boost 信号和槽机制

于 2013-02-04T15:46:32.967 回答