Прежде всего: нет способа сделать потокобезопасным метод getMainWinPtr
, поэтому этот псевдо-синглтон-хак, вероятно, должен go удалиться. Вы можете передать некоторый глобальный контекст приложения всем объектам, которые делают глобальные приложения, такие как предоставление обратной связи с пользователем. Скажем, есть MyApplication : QObject
(не производные от QApplication
, это не нужно). Это можно обойти при создании новых объектов, а затем вы сможете контролировать относительное время жизни вовлеченных объектов непосредственно в функции main()
:
void main(int argc, char **argv) {
QApplication app(argc, argv);
MainWindow win;
MyApplication foo;
win.setApplication(&foo);
// it is now guaranteed by the semantics of the language that
// the main window outlives `MyApplication`, and thus `MyApplication` is free to assume
// that the window exists and it's OK to call its methods
...
return app.exec();
}
Конечно, MyApplication
должен позаботиться о том, чтобы рабочие потоки останавливаются до того, как его деструктор возвращается.
Для передачи асинхронных изменений в QObject
, проживающих в (не перегруженных) QThread
s (включая основной поток), используйте встроенный внутренний поток коммуникация, присущая дизайну Qt: события и слот вызывают пересекающие границы потоков.
Итак, учитывая метод DisplayInGUI
, вам необходим потокобезопасный способ его вызова:
std::string newOutput = ...;
QMetaObject::invokeMethod(mainWindow, [mainWindow, newOutput]{
mainWindow->displayInGUI(newOutput);
});
Это учитывает аспект безопасности потока. Теперь у нас есть еще одна проблема: главное окно может быть забито этими обновлениями гораздо быстрее, чем частота обновления экрана sh, поэтому нет смысла в потоке уведомлять главное окно чаще, чем с какой-то разумной скоростью, он просто тратит ресурсы .
Лучше всего это сделать, сделав метод DisplayInGUI
поточно-ориентированным и используя API синхронизации в Qt:
class MainWindow : public QWidget {
Q_OBJECT
...
static constexpr m_updatePeriod = 1000/25; // in ms
QMutex m_displayMutex;
QBasicTimer m_displayRefreshTimer;
std::string m_newDisplayText;
bool m_pendingRefresh;
...
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == m_displayRefreshTimer.timerId()) {
QMutexLocker lock(&m_displayMutex);
std::string text = std::move(m_newDisplayText);
m_pendingRefresh = false;
lock.release();
widget->setText(QString::fromStdString(text));
}
QWidget::timerEvent(event);
}
void DisplayInGUI(const std::string &str) {
// Note pass-by-reference, not pass-by-value. Pass by value gives us no benefit here.
QMutexLocker lock(&m_displayMutex);
m_newDisplayText = str;
if (m_pendingRefresh) return;
m_pendingRefresh = true;
lock.release();
QMetaObject::invokeMethod(this, &MainWindow::DisplayInGui_impl);
}
private:
Q_SLOT void DisplayInGui_impl() {
if (!m_displayRefreshTimer.isActive())
m_displayRefreshTimer.start(this, m_updatePeriod);
}
};
В более сложной ситуации вы, вероятно, захотите вычеркните настройку свойства cross-thread для некоторого «вспомогательного» класса, который будет выполнять такие операции без шаблона.