【问题标题】:Bound c++ value not updating in QML绑定的 c++ 值未在 QML 中更新
【发布时间】:2018-06-12 16:02:22
【问题描述】:

我从 .ini 文件中读取到 c++ 中的整数,然后通过绑定在 QML 中使用该整数。在运行时,可以更改 .ini 文件中的值,这会导致 c++ 整数也发生更改。我发现虽然整数在 c++ 中确实发生了变化(通过 qDebug() 验证),但 QML 中的绑定值没有改变,尽管发出了所需的 changed() 信号。我的应用程序结构的摘录如下所示:

main.cpp:

//Establish the QQmlApplication engine, set context properties for the two c++ files and load the QML file.

QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty(QStringLiteral("MainCpp"), new MainCpp());
engine.rootContext()->setContextProperty(QStringLiteral("Config"), new Config());
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

maincpp.h:

public:
    explicit MainCpp(QObject *parent = nullptr);

    Q_PROPERTY(int someValue READ someValue NOTIFY someValueChanged)
    int someValue(){return m_someValue;}

signals:
    void someValueChanged();

private:
    int    m_someValue;

config.h:

public:
    explicit Config(QObject *parent = nullptr);

    Q_PROPERTY(int someOtherValue READ someOtherValue NOTIFY someOtheralueChanged)
    int someOtherValue(){return m_someOtherValue;}

signals:
    void someOtherValueChanged();

public:
    void loadSettings();

public:
    int      m_someOtherValue;
    QString  m_File;

config.cpp:

Config::Config(QObject *parent) : QObject(parent)
{
    m_File = "/some/path/to/settings/file/config.ini";
    loadSettings();
}

void Config::loadSettings()
{
    QSettings settings(m_File, QSettings::IniFormat);
    settings.beginGroup("GROUP_NAME");
    m_someOtherValue = settings.value("someOtherValueConfig", "").toInt();
    settings.endGroup();
}

maincpp.cpp:

MainCpp::MainCpp(QObject *parent = nullptr) : QObject(parent)
{
    Config configPointer;
    m_someValue = configPointer.someOtherValue();
    emit someValueChanged();
}

main.qml:

Window {
    width: 800
    height: 480

    Text {
        id: someText
        text: Config.someOtherValue //This does NOT update on changes to m_someOtherValue on the c++ side
        //text: MainCpp.someValue //This DOES update on change to m_someValue on the c++ side
    }
}

在 maincpp.cpp 中调用以下代码来更新 .ini 文件:

void MainCpp::update(int var)
{
    Config configPointer;

    QSettings settings("/some/path/to/settings/file/config.ini", QSettings::IniFormat);
    settings.setValue("GROUP_NAME/someOtherValueConfig", var);
    configPointer.m_someOtherValue = var;
    m_someValue = configPointer.someOtherValue;
    emit configPointer.someOtherValueChanged();
    emit someValueChanged();
}

我添加了“发出 someOtherValueChanged()”信号但无济于事。如前所述,我知道 m_someOtherValue 已更改,因为我使用 qDebug() 对其进行查询。如果 m_someValue 发生变化,为什么 QML 没有观察到 m_someOtherValue 的变化?

【问题讨论】:

  • 你说:.ini文件中的值是可以改的,请问你是怎么改的?
  • someVOtheralueChanged 更改为someOtheralueChanged,错字。
  • 我手动修改了 .ini,但我没有看到 2 个值发生变化,请更好地解释自己。
  • 它在用户输入时以编程方式更改。我已经通过 qDebug() 和在运行时查看 .ini 文件验证了此函数按预期工作。
  • 再补充一句,我们需要一个minimal reproducible example,如果你不放,它不会重现你的错误。

标签: c++ qt qml


【解决方案1】:

该行为是由于您有 3 个 Config 对象引起的:

  1. main.cpp

engine.rootContext()->setContextProperty(QStringLiteral("Config"), new Config());
  1. MainCpp 构造函数:

MainCpp::MainCpp(QObject *parent = nullptr) : QObject(parent)
{
    Config configPointer; 
    [...]
  1. 更新方法:

void MainCpp::update(int var)
{
    Config configPointer;

也就是说,如果更改其中一些对象并不会更改其他对象,因为它们是不同的。

一种可能的解决方案是使 Config 成为单例,这样整个应用程序中的所有对象都是相同的。

config.h

#ifndef CONFIG_H
#define CONFIG_H

#include <QObject>
#include <QSettings>

class Config : public QObject
{
    static Config* instance;
    Q_OBJECT
    Q_PROPERTY(int someOtherValue READ someOtherValue NOTIFY someOtherValueChanged)
    explicit Config(QObject *parent = nullptr);

public:
    static Config *getInstance();
    int someOtherValue(){return m_someOtherValue;}
    [...]

};

#endif // CONFIG_H

config.cpp

#include "config.h"

Config* Config::instance = 0;

Config::Config(QObject *parent):QObject(parent){
    m_File = "/some/path/to/settings/file/config.ini";
    loadSettings();
}

Config *Config::getInstance(){
    if (instance == 0)
        instance = new Config;
    return instance;
}

void Config::loadSettings(){
    [...]
}

然后通过getInstance()访问对象:

ma​​incpp.cpp

#include "maincpp.h"

MainCpp::MainCpp(QObject *parent):QObject(parent){
    Config *configPointer = Config::getInstance();
    m_someValue = configPointer->someOtherValue();
    emit someValueChanged();
}

void MainCpp::update(int var)
{
    Config *configPointer = Config::getInstance();
    QSettings settings("/some/path/to/settings/file/config.ini", QSettings::IniFormat);
    settings.setValue("GROUP_NAME/someOtherValueConfig", var);
    configPointer->m_someOtherValue = var;
    m_someValue = configPointer->someOtherValue();
    emit configPointer->someOtherValueChanged();
    emit someValueChanged();
}

要在 QML 中使用它,您必须在 qmlRegisterSingletonType() 的帮助下注册:

ma​​in.cpp

#include "maincpp.h"

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

static QObject *singletonTypeProvider(QQmlEngine *, QJSEngine *)
{
    return Config::getInstance();
}

int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QGuiApplication app(argc, argv);
    qmlRegisterSingletonType<Config>("Config", 1, 0, "Config", singletonTypeProvider);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty(QStringLiteral("MainCpp"), new MainCpp());
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

在 qml 中你必须导入模块并使用对象:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3

import Config 1.0

Window {
    width: 800
    height: 480
    visible: true

    ColumnLayout{

        Text {
            id: someText
            text: Config.someOtherValue
        }
        Text {
            id: anotherText
            text: MainCpp.someValue
        }

        Slider {
            value: 0.5
            maximumValue: 100
            onValueChanged: MainCpp.update(value)
        }
    }
}

完整的例子可以在下面的link找到。

【讨论】:

  • 一如既往的精彩,非常感谢。我以前没有使用过单例,所以我将详细介绍您的解决方案,在我的应用程序中实现它,并更新这篇文章(并相应地标记为已回答)。
  • 完美运行,再次感谢!我今天当然学到了一些东西:)
  • 但要小心单身人士!它们可能使许多事情变得容易,但通常一开始可能很容易,最终导致难以重构的混乱局面。他们可能是你的朋友,但也可能是你最可怕的伪装敌人。你会在网上找到很多关于单例的优点,甚至更多关于单例的缺点。
  • 显然,一个人必须小心,这是一个起点,每个最后都要检查它是否是你想要的,因为显示谁要求我的信息是最简单的选择,如果我有更多信息可以提供其他替代方案,感谢您的意见@derM:P
  • @eyllanesc:这不是要批评你。我认为特别是在 Qt 的上下文中,许多东西已经是单例,而其他的不会产生问题,作为一个,它们是解决某些问题的有效解决方案。只是那些刚发现单例的人往往很快就会滥用它,最终陷入混乱。我个人更喜欢使用 pseudo QML singletons 来发布 C++ 模型,而不是使用上下文属性,因为它可以防止阴影。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-12
  • 1970-01-01
  • 2014-08-01
  • 1970-01-01
相关资源
最近更新 更多