Неожиданное связывание в QML с помощью Q_GADGET

#qt #qml

#qt #qml

Вопрос:

У меня есть гаджет:

 class ZScoreParams
{
    Q_GADGET

    Q_PROPERTY(quint64 lag MEMBER lag);
    Q_PROPERTY(qreal threshold MEMBER threshold);
    Q_PROPERTY(qreal influence MEMBER influence);

public:

    std::size_t lag;
    double threshold;
    double influence;
};

Q_DECLARE_METATYPE(ZScoreParams)
 

и свойство объекта типа QVariant :

     Q_PROPERTY(QVariant zscoreParams READ zscoreParams WRITE setZscoreParams NOTIFY zscoreParamsChanged)
 

его получатель возвращает либо QVariant{} то, что есть null в QML, либо QVariant::fromValue(gadget_val) установщик имеет ту же логику.

Не могу понять, как мне создать диалоговое окно с тремя спин-блоками для редактирования моих параметров ZScoreParams. Каков правильный способ его создания?

Я попытался это ZParamsEdit.qml :

 GridLayout
{
    property var params

    property var refreshChart: function () {}

    signal zparamsChanged()

    Component.onCompleted:
    {
        refreshChart = function ()
        {
            params.lag = lagEdit.value
            params.threshold = thresholdEdit.value
            params.influence = influenceEdit.realValue
            logTable.debug("qml", JSON.stringify(params))
            zparamsChanged()
        }
    }

    Layout.margins: 5

    columns: 2
    flow: GridLayout.LeftToRight

    Label {
        text: qsTr("Lag:")
        Layout.alignment: Qt.AlignRight
    }

    SpinBox {
        id: lagEdit
        value: params.lag
        from: 100
        to: 1000000
        stepSize: 100
        editable: true
        Layout.alignment: Qt.AlignLeft
        onValueChanged: refreshChart()
    }

    Label {
        text: qsTr("Threshold:")
        Layout.alignment: Qt.AlignRight
    }

    SpinBox {
        id: thresholdEdit
        value: params.threshold
        from: 0
        to: 100
        stepSize: 1
        editable: true
        Layout.alignment: Qt.AlignLeft
        onValueChanged: refreshChart()
    }

    Label {
        text: qsTr("Influence:")
        Layout.alignment: Qt.AlignRight
    }

    RealEdit
    {
        Layout.alignment: Qt.AlignLeft

        id: influenceEdit

        decimals: 2
        realValue: params.influence
        realFrom: 0
        realTo: 1.0
        realStepSize: 0.1
        onValueChanged: refreshChart()
    }
}
 

а затем в main.qml :

 Dialog
{
    id: zdlg
        ZParamsEdit
        {
            id: zedit
        }
}

            IconButton {
                icon.source: "images/analyze.svg"
                onClicked: {
                    zedit.params = market.zscoreParams 
                    zdlg.open()
                }
            }
 

и получил эффект, которого я не ожидал. Каждый раз, когда изменяется окно редактирования, market.zscoreParams вызывается параметр установки свойств.

Как ZParamsEdit сохранить копию моего гаджета и установить значение моего свойства только один раз (для всего гаджета), когда в диалоговом окне нажата кнопка «ОК»?

Также пробовал

 JSON.parse(JSON.stringify(market.zscoreParams))
 

но получил и нечто неожиданное.

ПРАВКА1:

Я, вероятно, нашел уродливое решение с явно определенной функцией, которая создает копию гаджета:

     Q_INVOKABLE QVariant cloneZScoreParams(QVariant v)
    {
        std::optional<ZScoreParams> val;

        util::FromVariant(v, val);

        return util::ToVariant(val);
    }
 

и затем

 globalSettings.cloneZScoreParams(market.zscoreParams)
 

в QML.

Есть ли что-то лучше в QT?

Комментарии:

1. Почему вы определяете refreshChart функцию внутри Component.OnCompleted?

2. refreshChart Я думаю, @JarMan можно определить напрямую.

Ответ №1:

Если я правильно понимаю вашу проблему, вам просто нужно какое-то локальное свойство / свойства для хранения копии ваших данных, а затем вам нужна какая-то функция, которая записывает эти копии обратно в источник данных.

Вот как я бы изменил ваш код:

 GridLayout
{
    property var params

    // Copy the data into local properties
    property lag: params.lag
    property threshold: params.threshold
    property influence: params.influence

    // Only update the local properties when spinboxes change
    function refreshChart()
    {
        lag = lagEdit.value
        threshold = thresholdEdit.value
        influence = influenceEdit.realValue
        zparamsChanged()
    }

    // Call this when OK button is clicked
    function save()
    {
        params.lag = lag
        params.threshold = threshold
        params.influence = influence
    }

    signal zparamsChanged()

    ...

    SpinBox {
        id: lagEdit
        value: lag

        ...

        onValueChanged: refreshChart()
    }

    SpinBox {
        id: thresholdEdit
        value: threshold

        ...

        onValueChanged: refreshChart()
    }

    RealEdit
    {
        id: influenceEdit
        realValue: influence

        ...

        onValueChanged: refreshChart()
    }
}
 

Обновить:

Чтобы избежать вызова setter 3 раза, вы можете предоставить вызываемую функцию в своем классе C :

 Q_INVOKABLE void save(quint64 l, qreal t, qreal i)
{
    lag = l;
    threshold = t;
    influence = i;
}
 

Теперь функция save () будет выглядеть следующим образом:

     function save()
    {
        params.save(lag, threshold, influence)
    }
 

Комментарии:

1. Мне нужно локальное свойство типа Q_GADGET. Если вы устанавливаете params save() таким образом, zscoreParams средство установки свойств вызывается три раза, проверьте это самостоятельно с помощью кода на C .