How do I make a function a timeslot in Qt?

For a class function to be a slot, the class must inherit from QObject. However, QObject takes up quite a lot of memory. I'm not sure how much this is, and if memory is for every class or every object. My code has a lot of small data, whose functions may be a slot sometime. I am wondering if there is a way to make the function of the class time slot when using it. After using it, the memory for the slot value will be deleted. The following code illustrates this requirement.

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

      

+3


source to share


3 answers


If you can use Qt5, you can wire signals to simple functions and static methods (which are essentially just named equal functions):

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

      

Where action

is an instance QAction

and a SmallData::statF

is a static method SmallData

.



Modify the comment on Christian Rau to call a specific instance, you can also hook into the lambda:

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

      

Already with Qt4 you can use QSignalMapper

to achieve the same effect with multiple objects. This allows you to add to a parameter (in this case, probably an integer index for yours vec

), based on which object selected it. But in Qt4, the receiver must still be a QObject.

+8


source


To use the signal slot mechanism, you cannot get around QObject

, but what you can do is create a temporary object that has a slot that calls your function. You just need to take care of freeing the object properly. Something like:

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

      

Then you just create a (more or less temporary) Callback

object every time you need it:

SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()), 
        makeCallback(std::bind(&SmallData::F, data)), SLOT(call()));

      

With the parameter, oneShot

you can control if the slot should automatically dissolve after starting.

The only problem is, if this slot is never called, you are leaking Callback

. To accommodate this, you can pass something meaningful as an argument parent

, so that Qt takes care of the correct deletion, at least at some later point in time:

SmallData* data = treeView.selectedItem();
connect(action, SIGNAL(triggered()), 
        makeCallback(std::bind(&SmallData::F, data), true, this), SLOT(call()));

      



This way you can also bind the lifetime of the callback object (and thus the signal slot connection) to another object (for example, the action itself and the removal of the action when no item is selected, or something similar).

Alternatively, you can also remember the object Callback

for the currently selected item and take care of deleting it correctly.

disclaimer: Beware that the above example contains a lot of C ++ 11, but I'm not in the mood to rewrite that for C ++ 03. Similarly, this solution could be extended, perhaps using a templated functor instead of std::function

(but if I remember correctly , Qt's meta-object system doesn't like templates very much).


EDIT: Ultimately the solution suggested by Frank Osterfeld in his comment may be a much simpler approach to your situation than my over-the-top general object life madness above: just wire the action to one slot of a higher level object (your main widget or maybe be, the model of the item containing the data vector) and call F

on the currently selected item:

connect(action, SIGNAL(triggered()), this, SLOT(callF()));
...
void MyController::callF()
{
    treeView.selectedItem()->F();
}

      

+3


source


I don't think what you are trying to do is possible in Qt.

If you really don't want to inherit from QObject, I suggest you take a look at the forcing signals and slots mechanism .

0


source







All Articles