C++ 信号不会在 QML 端被捕获

C++ signal does not get caught on QML side

我正在检查 this example,但我无法在 QML 中捕捉到 Qt C++ signal。这是我的 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

这里是方法,它触发信号 ueSignalSettingsDatabaseCreated()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

4546:

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

现在,名为 ueSettings 的 object UeSettings 的实例已创建并公开给 main.cpp 中的 QML:

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);

我正在尝试在某些 QML 文件中捕获信号 ueSignalSettingsDatabaseCreated()ueSignalSettingsDatabaseEmpty()

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

来自 C++ 端的信号被发射,我已经 triple-checked 使用调试器并且 没有 QML 端被捕获,因为text 属性 不会改变。为什么?调试控制台根本不会触发任何警告或运行时错误(与问题相关)。

您需要重新运行qMake、清理和重建,以便正确连接信号。如果我遇到这个问题,至少我经常遇到这种情况。

可能在 Connections 对象准备好接收 QML 中的信号之前发出信号。如果您添加查询方法,例如

Q_INVOKABLE bool UeSettings::isUeSignalSettingsDatabaseCreated()

当您从 QML 端调用它时,它可能 returns 正确。

如果我是你,我会使用属性来跟踪状态。在 QML 中,您可以使用头文件中定义的 属性 名称访问 Q_PROPERTY

用户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()UeSettings::ueSlotQMLFileLoaded() 插槽并在其中检查设置数据库的状态,而不是在构造函数中这样做:

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

QML 部分现在收到信号。但是,我仍然不明白为什么 example.

中完全相同的机制 正在工作