The design of the existing code is rotten. Let's fix it, starting with the dialog.
The Ui::
classes should not be public bases. They are an implementation detail that should never be exposed anywhere outside of the dialog. The dialog is an abstraction: you can perform some operations on it. They should be methods, and access the Ui::
object internally. The dialog should also not depend on any particular type of the parent window. If the dialog should interact with some other objects, it should emit signals that then get connected e.g. to the main window.
To demonstrate it, suppose that the dialog has a QLineEdit edit
element. The text can be set, and others informed of changes in the text. It should be designed as follows:
class ConnectToSource : public QDialog {
Q_OBJECT
public:
ConnectToSource(QWidget *parent = {}) : QDialog(parent) {
ui.setupUi(this);
connect(ui.edit, &QLineEdit::textChanged,
this, &ConnectToSource::textChanged); // forward the signal
}
Q_SLOT void setText(const QString & text) {
ui.edit->setText(text);
}
QString text() const {
return ui.edit->text();
}
Q_SIGNAL void textChanged(const QString &);
protected:
void keyPressEvent(QKeyEvent *) override { ... }
private:
Ui::ConnectToSource ui;
};
Now, let's look how we might access it from any thread. The key is to send some piece of code to execute in the main thread. See this answer for details. That piece of code - a functor - should carry all the data necessary to set up the dialog.
Then:
// https://stackoverflow.com/a/21653558/1329652
template <typename F>
static void postToThread(F && fun, QThread * thread = qApp->thread());
void setupDialog(MainWindow *parent, const QString &text) {
postToThread([=]{ // the functor captures parent and text by value
auto dialog = new ConnectToSource(parent);
dialog->setText(text);
connect(dialog, &ConnectToSource::textChanged, parent, &MainWindow::useText);
dialog->show();
dialog->setAttribute(Qt::WA_DeleteOnClose); // don't leak the dialog
});
}
The setupDialog
function is thread-safe, and can be executed in any thread as long as the thread doesn't outlive the parent
.
Note that the above code is essentially non-blocking. The functor is wrapped up in an event and is delivered to the main thread's event dispatcher, which then executes the functor. The thread executing setupDialog
may only get contented on the mutex to the main thread's event queue. This mutex is held only sporadically, for a very short time.