Search code examples
c++qtoopsignalsclass-design

Qt: signal/slot design and performance


I've recently started using Qt and I need some clarification on signal/slot mechanism. I understand how it's a great tool for the GUI and communication between objects living in separate threads, but I'm not quite sure whether I should use it in a simple cases like the following one.

I have three classes, let's call them MainWindow, DataManager and DataWorker. DataWorker lives in a separate thread and signals when new data is ready for collection. It is then visualised in MainWindow widgets after some processing. I've created DataManager class as to not pollute the GUI class with processing code.

Now, how should I handle the communication between DataManager and the MainWindow.


Option #1 - have a pointer to MainWindow as a member and just call its method

class MainWindow
{
private:
    DataManager* dm;

public:
    MainWindow() : dm(new DataManager(this)) { }
    displayData(const char* processedData);
}

class DataManager : QObject
{
private:
    MainWindow *mw;

private slots;
    eventNewData()
    {
        // get and process the data
        mw = this->QObject.parent();
        mw->displayData(const char* processedData);
        // release data
    }
}

Option #2 - signal new data to call MainWindow slot

class MainWindow
{
private:
    DataManager* dm;

private slots:
    displayData(const char* processedData);

public:
    MainWindow() : dm(new DataManager(this)) { QObject::connect(dm, SIGNAL(newData(const char*)), this, SLOT(displayData(const char*)); }

};

class DataManager : QObject
{    
signals:
    newData(const char* processedData);

private slots;
    eventNewData()
    {
        // get and process the data
        emit newData(processedData);
        // release data
    }
}

Option 1 seems more intuitive to me, but then again I don't know Qt very well. I can see the benefit of using signals and slots if there will be more classes that I'd like to react to a newData() signal.

So what's a better option and is there any difference in performance between the two?


Solution

  • The first option does provide better performance than using the signal/slot mechanism, but it also has the drawback that there is a tight coupling between MainWindow and DataManager. Both know each other, so they can't really be used separately. That alone should already be a reason to refactor the code.

    That said, as a third option you could let MainWindow have the slot that received the signal from DataWorker and let DataManager be a helper class that just converts the data into a usable format for MainWindow.

    class MainWindow
    {
    private:
        DataManager* dm;
    
    public:
        MainWindow() : dm(new DataManager()) { }
        displayData(QString processedData);
    private slots;
        eventNewData()
        {
            // get the data
            QString processedData = dm->preprocessData(data);
            displayData(processedData);
            // release data
        }
    };
    
    class DataManager
    {
    public:
        QString preprocessData(...);
    };