Как сделать так, чтобы функция была временным слотом в 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().

3

Решение

Если вы можете использовать Qt5, вы можете подключить сигналы к простым функциям и статическим методам (которые по сути являются простыми именами простых функций):

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

куда action это QAction экземпляр и SmallData::statF статический метод SmallData,

Отредактируйте комментарий Кристиана Рау, чтобы вызвать конкретный экземпляр, вы также можете подключиться к лямбде:

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

Уже с Qt4 вы можете использовать QSignalMapper добиться того же эффекта, с помощью нескольких объектов. Это позволяет вам добавить добавить параметр (в этом случае, вероятно, целочисленный индекс к вашему vec) сигнализировать, исходя из того, какой объект его испустил. Но в Qt4 получатель должен всегда быть объектом QObject.

8

Другие решения

Для использования механизма слота сигнала, вы не сможете обойти 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 не очень любит шаблоны).


РЕДАКТИРОВАТЬ: В конце концов решение, предложенное Фрэнк Остерфельд в его комментарии может быть гораздо более простой подход к вашей ситуации, чем описанное выше мое чрезмерное безумие при жизни объекта: просто подключите действие к одному слоту объекта более высокого уровня (ваш главный виджет или, возможно, модель элемента, содержащую вектор данных) и вызовите F на текущий выбранный пункт:

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

Я не думаю, что то, что вы пытаетесь сделать, возможно в Qt.

Если вы действительно не хотите наследовать QObject, то я предлагаю вам взглянуть на механизм буст-сигналов и слотов.

0
По вопросам рекламы [email protected]