KernelPanic
KernelPanic

Reputation: 2432

C++ signal does not get caught on QML side

I was checking this example and I cannot catch Qt C++ signal in QML. Here is my UeSettings class header:

#ifndef UESETTINGS_H
#define UESETTINGS_H

#include <QObject>
#include <QSqlDatabase>
#include <QString>
#include <QStringList>
#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
#include <QDir>
#include <QDateTime>

#include "uedefaults.h"
#include "models/ueprintersmodel.h"
#include "uetypes.h"

/**
 * @brief The UeSettings class
 */
class UeSettings : public QObject
{
    Q_OBJECT

private:
    /**
     * @brief m_ueServerAddress
     */
    QString m_ueServerAddress;

    /**
     * @brief m_ueServerPort
     */
    QString m_ueServerPort;

    /**
     * @brief m_ueDatabaseName
     */
    QString m_ueDatabaseName;

    /**
     * @brief m_ueDatabaseUsername
     */
    QString m_ueDatabaseUsername;

    /**
     * @brief m_ueDatabasePassword
     */
    QString m_ueDatabasePassword;

    /**
     * @brief m_ueHostname
     */
    QString m_ueHostname;

    /**
     * @brief m_ueNumberOfTopSalesProductsShown
     */
    QString m_ueNumberOfTopSalesProductsShown;

    /**
     * @brief m_ueDb
     */
    QSqlDatabase m_ueDb;

    /**
     * @brief m_ueAutomaticStockUpdateFromFirstWarehouse
     */
    bool m_ueAutomaticStockUpdateFromFirstWarehouse;

    /**
     * @brief m_ueAutoLogoffWorkerAtBill
     */
    bool m_ueAutoLogoffWorkerAtBill;

    /**
     * @brief m_uePrintersModel
     */
    UePrintersModel* m_uePrintersModel;

    /**
     * @brief ueSetServerAddress
     * @param address
     */
    inline void ueSetServerAddress(const QString& address)
        { this->m_ueServerAddress=address; }

    /**
     * @brief ueSetServerPort
     * @param port
     */
    inline void ueSetServerPort(const QString& port)
        { this->m_ueServerPort=port; }

    /**
     * @brief ueSetDatabaseName
     * @param dbName
     */
    inline void ueSetDatabaseName(const QString& dbName)
        { this->m_ueDatabaseName=dbName; }

    /**
     * @brief ueSetDatabaseUsername
     * @param dbUsername
     */
    inline void ueSetDatabaseUsername(const QString& dbUsername)
        { this->m_ueDatabaseUsername=dbUsername; }

    /**
     * @brief ueSetDatabasePassword
     * @param dbPassword
     */
    inline void ueSetDatabasePassword(const QString& dbPassword)
        { this->m_ueDatabasePassword=dbPassword; }

    /**
     * @brief ueSetNumberOfTopSalesProductsShown
     * @param topN
     */
    inline void ueSetNumberOfTopSalesProductsShown(const QString& topN)
        { this->m_ueNumberOfTopSalesProductsShown=topN; }

    /**
     * @brief ueSetHostname
     * @param hostname
     */
    inline void ueSetHostname(const QString& hostname)
        { this->m_ueHostname=hostname; }

    /**
     * @brief ueSetAutomaticStockUpdateFromFirstWarehouse
     * @param automaticStockUpdate
     */
    inline void ueSetAutomaticStockUpdateFromFirstWarehouse(const bool& automaticStockUpdate)
        { this->m_ueAutomaticStockUpdateFromFirstWarehouse=automaticStockUpdate; }

    /**
     * @brief ueSetAutoLogoffWorkerAtBill
     * @param automaticLogoffWorkerAtBill
     */
    inline void ueSetAutoLogoffWorkerAtBill(const bool& automaticLogoffWorkerAtBill)
        { this->m_ueAutoLogoffWorkerAtBill=automaticLogoffWorkerAtBill; }

    /**
     * @brief uePrintersModel
     * @return
     */
    inline UePrintersModel* uePrintersModel() const
        { return this->m_uePrintersModel; }

    /**
     * @brief ueSetPrintersModel
     * @param printersModel
     */
    inline void ueSetPrintersModel(UePrintersModel* const printersModel=0)
        { this->m_uePrintersModel=printersModel; }

    /**
     * @brief ueDatabase
     * @return
     */
    QSqlDatabase ueDatabase()
        { return this->m_ueDb; }

    /**
     * @brief ueSetDatabase
     * @param database
     */
    void ueSetDatabase(const QSqlDatabase& database)
        { this->m_ueDb=database; }

    /**
     * @brief ueCreateDatabase
     * @return true if settings database successfully created, otherwise false
     */
    bool ueCreateDatabase();

    /**
     * @brief ueDatabaseExists
     * @return true if settings database exists, otherwise false
     */
    bool ueDatabaseExists();

public:
    /**
     * @brief UeSettings
     * @param parent
     * @param printersModel
     */
    explicit UeSettings(QObject *parent = 0,
                        UePrintersModel* const printersModel=0);

    /**
     * @brief ~UeSettings
     */
    ~UeSettings();

    Q_INVOKABLE inline QString ueServerAddress() const
        { return this->m_ueServerAddress; }
    Q_INVOKABLE inline QString ueServerPort() const
        { return this->m_ueServerPort; }
    Q_INVOKABLE inline QString ueDatabaseName() const
        { return this->m_ueDatabaseName; }
    Q_INVOKABLE inline QString ueDatabaseUsername() const
        { return this->m_ueDatabaseUsername; }
    Q_INVOKABLE inline QString ueDatabasePassword() const
        { return this->m_ueDatabasePassword; }

    Q_INVOKABLE inline QString ueHostname() const
        { return this->m_ueHostname; }

    Q_INVOKABLE inline bool ueAutomaticStockUpdateFromFirwstWarehouse() const
        { return this->m_ueAutomaticStockUpdateFromFirstWarehouse; }

    Q_INVOKABLE inline QString ueNumerOfTopSalesProducts() const
        { return this->m_ueNumberOfTopSalesProductsShown; }

    Q_INVOKABLE inline bool ueAutomaticLogoffWorkerAtBill() const
        { return this->m_ueAutoLogoffWorkerAtBill; }

    /**
     * @brief ueConnectToDatabase
     * @return true if connetion to database successfull, otherwise false
     */
    bool ueConnectToDatabase();

    /**
     * @brief ueSaveSettings
     * @param address
     * @param port
     * @param dbName
     * @param dbUsername
     * @param dbPassword
     * @param hostName
     * @param autoStockUpdate
     * @param topN
     * @param autoLogoffWorkerAtBill
     * @return true if setitngs were saved in database successfully, otherwise false
     */
    Q_INVOKABLE bool ueSaveSettings(const QString& address,
                                    const QString& port,
                                    const QString& dbName,
                                    const QString& dbUsername,
                                    const QString& dbPassword,
                                    const QString& hostName,
                                    const QString& autoStockUpdate,
                                    const QString& topN,
                                    const QString& autoLogoffWorkerAtBill/*,
                                    UeTypePrintersList* const printersList*/);
    /**
     * @brief ueLoadSettings
     * @return true if settings were loaded successfully, otherwise false
     */
    Q_INVOKABLE bool ueLoadSettings();

    /**
     * @brief ueTestConnection
     * @param address
     * @param port
     * @param dbName
     * @param dbUsername
     * @param dbPassword
     * @return true if successfully connected to database, otherwise false
     */
    Q_INVOKABLE bool ueTestConnection(const QString& address,
                                      const QString& port,
                                      const QString& dbName,
                                      const QString& dbUsername,
                                      const QString& dbPassword);

signals:
    /**
     * @brief ueSignalSettingsLoaded
     */
    void ueSignalSettingsLoaded();

    /**
     * @brief ueSignalSettingsSaved
     */
    void ueSignalSettingsSaved();

    /**
     * @brief ueSignalSettingsNotFound
     */
    void ueSignalSettingsNotFound();

    /**
     * @brief ueSignalTestDatabaseConnectionOk
     */
    void ueSignalTestDatabaseConnectionOk();

    /**
     * @brief ueSignalTestDatabaseConnectionFailed
     */
    void ueSignalTestDatabaseConnectionFailed();

    /**
     * @brief ueSignalSettingsDatabaseCreated
     */
    void ueSignalSettingsDatabaseCreated();

    /**
     * @brief ueSignalSettingsDatabaseEmpty
     */
    void ueSignalSettingsDatabaseEmpty();
public slots:
};

#endif // UESETTINGS_H

and here is method, which fires signals ueSignalSettingsDatabaseCreated() and ueSignalSettingsDatabaseEmpty:

bool UeSettings::ueCreateDatabase()
{
    bool result=false;
    bool connected=false;

    if(this->ueDatabase().isOpen())
    {
        connected=true;
    }
    else
    {
        if(this->ueConnectToDatabase())
        {
            connected=this->ueDatabase().open();
        }
        else
        {
            qDebug() << Q_FUNC_INFO
                     << this->ueDatabase().lastError().text();
        }   // if
    }   // if

    if(connected)
    {
        QSqlQuery queryCreateDatabase(this->ueDatabase());

        if(queryCreateDatabase.prepare(UeApplicationSettings::UeSqlQueries::UeCreateSettingsDatabase::QUERY_TURN_ON_FOREIGN_KEYS_SUPPORT))
        {
            if(queryCreateDatabase.exec())
            {
                if(queryCreateDatabase.prepare(UeApplicationSettings::UeSqlQueries::UeCreateSettingsDatabase::QUERY_CREATE_TABLE_SETTINGS))
                {
                    if(queryCreateDatabase.exec())
                    {
                        if(queryCreateDatabase.prepare(UeApplicationSettings::UeSqlQueries::UeCreateSettingsDatabase::QUERY_CREATE_TABLE_PRINTERS))
                        {
                            if(queryCreateDatabase.exec())
                            {
                                if(queryCreateDatabase.prepare(UeApplicationSettings::UeSqlQueries::UeCreateSettingsDatabase::QUERY_CREATE_TABLE_SCREEN))
                                {
                                    if(queryCreateDatabase.exec())
                                    {
                                        result=true;

                                        emit this->ueSignalSettingsDatabaseCreated();
                                        emit this->ueSignalSettingsDatabaseEmpty();
                                    }   // if
                                }   // if
                            }
                            else
                            {
                                qDebug() << Q_FUNC_INFO
                                         << queryCreateDatabase.lastError().text();
                            }   // if
                        }   // if
                        else
                        {
                            qDebug() << Q_FUNC_INFO
                                     << queryCreateDatabase.lastQuery()
                                     << queryCreateDatabase.lastError().text();
                        }   // if
                    }
                    else
                    {
                        qDebug() << Q_FUNC_INFO
                                 << queryCreateDatabase.lastError().text();
                    }   // if
                }
                else
                {
                    qDebug() << Q_FUNC_INFO
                             << queryCreateDatabase.lastError().text();
                }   // if
            }
            else
            {
                qDebug() << Q_FUNC_INFO
                         << queryCreateDatabase.lastError().text();
            }   // if
        }   // if
    }   // if

    return result;
}   // ueCreateDatabase

in lines 45 and 46:

emit this->ueSignalSettingsDatabaseCreated();
emit this->ueSignalSettingsDatabaseEmpty();

Now, the instance of object UeSettings, named ueSettings is created and exposed to QML in main.cpp:

UeBluetoothManager* ueBtManager=new UeBluetoothManager(qApp);
UePrintersModel* uePrintersModel=new UePrintersModel(qApp,
    ueBtManager);
UeSettings* ueSettings=new UeSettings(qApp,
    uePrintersModel);

engine.rootContext()->setContextProperty("ueBtManager",
    ueBtManager);
engine.rootContext()->setContextProperty("uePrintersModel",
    uePrintersModel);
engine.rootContext()->setContextProperty("ueSettings",
    ueSettings);

and I am trying to catch signals ueSignalSettingsDatabaseCreated() and ueSignalSettingsDatabaseEmpty() in some QML file with:

Connections
{
    target: ueSettings

    onUeSignalSettingsDatabaseCreated:
    {
        ueStatusText.text=qsTr("Settings Database Created.");
    }   // onUeSignalSettingsDatabaseCreated
}   // Connections

Connections
{
    target: ueSettings

    onUeSignalSettingsDatabaseEmpty:
    {
        ueStatusText.text=qsTr("Settings Database is Empty.");
    }   // onUeSignalSettingsDatabaseEmpty
}   // Connections

The signals from C++ side get emmited, I've triple-checked with debugger and ARE NOT caught on QML side since the text property does not get changed. Why? The debug console does not fire up any warning or runtime errors at all (related to issue).

Upvotes: 0

Views: 722

Answers (3)

KernelPanic
KernelPanic

Reputation: 2432

User talamaki was right, signal is emmited before Connections object is ready to receive signal on QML side. I've managed to solve the issue by connecting to signal QQmlApplicationEngine::objectCreated() to UeSettings::ueSlotQMLFileLoaded() slot and inside it I check the state of settings database instead of doing so in constructor:

void UeSettings::ueSlotQMLFileLoaded(QObject* object,
    const QUrl& url)
{
    if(!this->ueConnectToDatabase())
    {
        qDebug() << Q_FUNC_INFO
                 << this->ueDatabase().lastError().text();
    }   // if
}   // ueSlotQMLFileLoaded

The QML part now receives the signal. However, I still do no understand why the hell identical mechanism IS WORKING in example.

Upvotes: 0

talamaki
talamaki

Reputation: 5472

Probably, signal is emitted before Connections object is ready to receive the signal in QML. If you add a query method e.g.

Q_INVOKABLE bool UeSettings::isUeSignalSettingsDatabaseCreated()

it probably returns true when you call it from QML side.

If I was you I would use properties to follow the statuses. From QML, you can access the Q_PROPERTY by using the property name that is defined in the header file.

Upvotes: 2

Jan Win
Jan Win

Reputation: 147

You need to rerun qMake, clean and rebuild, so that the signals are connected correctly. At least that is often the case for me if I have this problem.

Upvotes: 0

Related Questions