0

I am writing a Qt application in C++ that executes a long-running task and displays updates and final results. The UI is of barely-past-tutorial-size complexity with nested, partly custom, QWidgets. The task is executed by an object on a different QThread that has no other dependencies and is constructed in main() along with my MainWindow.

The specific thing I want to do is hook up the "update" and "finished" signals of the executor to the slots of the displaying widget in my UI. To do that, I need references to that UI widget and the executor in one place, but can't think of a satisfying way to distribute references/pointers in my application.

Things I've considered that didn't get me anywhere:

  1. Passing the reference to the executor through the UI Widget hierarchy. I use Qt Designer which generates the whole UI setup code including calls to Widget constructors - so I can not pass that reference as constructor parameters because Qt Designer doesn't pass them. I would have to manually pass the references down after the fact with setters and connect the signals then, which strikes me as Very Ugly®.

  2. Relaying signals through the top window, so every component would only have to connect upwards in the hierarchy, not sideways. The Executor would signal to the MainWindow, the DisplayWidget would listen to the MainWindow. This does not work, because I want to call connect() inside my DisplayWidget's constructor, which in turn is called from the MainWindow's constructor - so the MainWindow I'm connect()'ing to isn't done constructing yet.

  3. Using a global ApplicationStructure object that holds references to my main application components and passes them to anyone who asks. We all know that the mere thought of this requires that I must be burned at the stake.

  4. Dependency Injection. I have no experience with DI in C++. Would that be the way to go? Is there a framework someone can recommend that works well with Qt?

  5. I'm Doing It Wrong™. Maybe I am approaching this completely the wrong way and need a more profound nudge.

Any comments or hints are appreciated.

My problem in (pseudo)code:

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    Executor e;
    MainWindow m(&e);

    m.show();
    return a.exec();
};

// Runs on a different QThread.
class Executor {
    Q_OBJECT

public:
    void run() {
        // very long noise.
        emit done();
    }

signals:
    void done();
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(Executor* e, QWidget *parent = 0): e(e) {
        ui->setupUi(this);
        e->moveToThread(&workerThread);
    };
    ~MainWindow();

private:
    Ui::MainWindow *ui; // Inside here is a DisplayWidget.
    Executor* e;
    QThread workerThread;
};

class DisplayWidget : public QWidget
{
    Q_OBJECT

public:
    // Can not change parameters, call is generated.
    DisplayWidget(QWidget *parent = 0) { 
        ui->setupUi(this);

        // Right here, the pointer e: How do I get it here?
        Executor* e;
        connect(e, &Executor::done, this, &DisplayWidget::update);
    };
    ~DisplayWidget();

public slots:
    void update();

private:
    Ui::DisplayWidget *ui;

};
  • One good thing to me could be to write all the UI using QML, and C++ for update and process datas. You could use some thread if you need ones as well. – Antoine Morrier Aug 09 '17 at 11:23
  • You could connect the signal in your MainWindows's constructor. This way your DisplayWidget is independent of the worker; which is cleaner IMHO. The widget is responsible for displaying data, not where the data comes from. But somewhere you have to wire the independent components - in your case the Main Window (which is curiosoly also responsible for creating the worker thread...) – king_nak Aug 10 '17 at 12:34
  • @king_nak Right now I'm going with something similar to Hajdu's answer below: Writing my own QApplication override. That instantiates the main components, establishes the connections that go across areas of concern - Would also be a good place to handle threads and who runs where, what do you think? – Thomas Blank Aug 10 '17 at 13:10
  • I don't think I fully understand the approach in the answer. I see no real benefit of subclassing `QApplication` for this, other to have a global pointer (which you could do in other ways, too). Also, this would work only for 1 execution class, what if you have more? And for the multithreaded part: As I understand, you have some QObject living in that thread, having an event loop for communication. with Qt::Concurrent, you do 1-time tasks that run to completion... It might fit your requirements, but I would not opt for that solution – king_nak Aug 10 '17 at 14:42
  • https://stackoverflow.com/a/54577124/3096593 – francek Feb 07 '19 at 16:25

1 Answers1

2

I would derive the Executor from the QApplication class, and use it instead of the default one. This way, it can be reached from the UI classes with the qApp macro.

int main(int argc, char *argv[]) {
    Executor a(argc, argv);

    MainWindow m();

    m.show();
    return a.exec();
};

class Executor : public QApplication {
    Q_OBJECT

public:
    Executor(int argc, char** argv) : QApplication(argc, argv) {}

    void run() {
        // noise.
        emit done();
    }

signals:
    void done();
};

...
connect(qApp, &Executor::done, this, &DisplayWidget::update);
...

For the multithread changes, i have the following idea. If everything is starting from the run() method you can use QConcurrent to start it asyncronously:

void (*func)() = &run; //or &Executor::run
QFuture<void> future = QtConcurrent::run(func);

If the calculation is more complicated, then a worker class is needed which is moved to a new thread, possibly created with QThread

Hajdu Gábor
  • 329
  • 1
  • 12