Запуск кода в основном цикле
Мне нужен способ запустить собственную функцию обновления в главном потоке. Я не мог найти сигнал, который бы отмечал меня каждый раз, когда запускается основной цикл.
Я делаю это неправильно? Является ли Qt принудительным выполнением пользовательского кода в потоках, если мы хотим запустить что-то в цикле?
3 ответа
QTimer::singleShot(0, []{/* your code here */});
Вот и все, правда. Использование таймера 0 мс означает, что ваш код будет выполняться на следующей итерации цикла событий. Если вы хотите убедиться, что код не будет работать, если определенный объект больше не существует, предоставьте объект контекста:
QTimer::singleShot(0, contextObj, []{/* your code here */});
Я использовал лямбду здесь только для примера. Очевидно, что вы можете предоставить функцию слота, если код длинный.
Если вы хотите, чтобы ваш код выполнялся повторно на каждой итерации цикла событий, а не только один раз, тогда используйте обычный QTimer, который не находится в режиме одиночного выстрела:
auto timer = new QTimer(parent);
connect(timer, &QTimer::timeout, contextObj, []{/* your code here */});
timer->start();
(Примечание: интервал по умолчанию равен 0 мс, если вы не установили его, поэтому QTimer::timeout()
генерируется каждый раз, когда события заканчивают обработку.)
Вот где это поведение задокументировано.
И само собой разумеется, что если выполняемый код занимает слишком много времени, ваш GUI будет зависать во время выполнения.
В качестве альтернативы, если вы хотите выполнять свой код каждый раз, когда выполняется цикл обработки событий, вы можете использовать вызов метода slot через соединение с очередями:
class EvtLoopTicker : public QObject
{
Q_OBJECT
public:
EvtLoopTicker(QObject *pParent = nullptr)
: QObject(pParent)
{}
public slots:
void launch()
{
tickNext();
}
private slots:
void tick()
{
qDebug() << "tick";
// Continue ticking
tickNext();
}
private:
void tickNext()
{
// Trigger the tick() invokation when the event loop runs next time
QMetaObject::invokeMethod(this, "tick", Qt::QueuedConnection);
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
EvtLoopTicker ticker;
ticker.launch();
return a.exec();
}
Еще один способ был бы переопределить:
bool QCoreApplication::event(QEvent *e)
зарегистрировать пользователя QEvent
и опубликуйте мероприятие в QCoreApplicatio::instance()
, Очевидно, что QTimer
подход превосходен, но этот будет работать, даже если исходящий поток не был создан Qt (QThread
).
пример:
class MainThreadEvent: public QEvent
{
std::function<void()> f_;
public:
template <typename F>
explicit MainThreadEvent(F&& f) :
QEvent(event_type()),
f_(std::forward<F>(f))
{
}
void invoke()
{
f_();
}
static auto event_type()
{
static int et{-1};
return QEvent::Type(-1 == et ? et = registerEventType() : et);
}
template <typename F>
static void post(F&& f)
{
auto const app(QCoreApplication::instance());
app->postEvent(app, new MainThreadEvent(std::forward<F>(f)));
}
};
class UserApplication: public QApplication
{
using QApplication::QApplication;
bool event(QEvent* const e) final
{
if (MainThreadEvent::event_type() == e->type())
{
return static_cast<MainThreadEvent*>(e)->invoke(), true;
}
else
{
return QApplication::event(e);
}
}
};