【问题标题】:How to use float in a QML spinbox如何在 QML 旋转框中使用浮点数
【发布时间】:2017-09-10 10:27:21
【问题描述】:

我使用 QML Spinbox,但我无法在其中使用浮点数。 如果我写类似 value: 5.0 的东西,它将显示为 5 ,因此显示为 int 而不是 float。

你知道如何进行吗?

非常感谢,祝你有美好的一天!

【问题讨论】:

标签: qt qml qtquick2 qspinbox


【解决方案1】:

您可以使用自定义文本创建 Spinbox

DoubleSpinBox.qml

import QtQuick 2.0

import QtQuick.Controls 2.1

Item {
    property int decimals: 2
    property real realValue: 0.0
    property real realFrom: 0.0
    property real realTo: 100.0
    property real realStepSize: 1.0

    SpinBox{
        property real factor: Math.pow(10, decimals)
        id: spinbox
        stepSize: realStepSize*factor
        value: realValue*factor
        to : realTo*factor
        from : realFrom*factor
        validator: DoubleValidator {
            bottom: Math.min(spinbox.from, spinbox.to)*spinbox.factor
            top:  Math.max(spinbox.from, spinbox.to)*spinbox.factor
        }

        textFromValue: function(value, locale) {
            return parseFloat(value*1.0/factor).toFixed(decimals);
        }

    }
}

例子:

DoubleSpinBox{
    realValue: 5.0
    realStepSize: 0.01
}

【讨论】:

  • 非常感谢,这很简单,很好! :)
  • 基本上和GrecKo的链接的第二个例子一样,只是不太好,因为你忘记了很多部分:valueFromText所以有人可以输入一个文本,并且它被验证了。而且您无法通过(我找不到任何理由)将SpinBox 封装在Item 中来读出隐藏它的值。我看不到您如何将当前值写回您的 Item
  • 我建议使用@GrecKo 已经发布的链接的第二个示例。再次:doc.qt.io/qt-5/qml-qtquick-controls2-spinbox.html#custom-values
  • @derM 不是同一个例子,如果你测试代码你会发现你得到的不是5.0而是5,把它放在一个item里面隐藏属性因子。
  • 我测试了代码,它运行良好。事实上,它不是真正的 float point-Spinbox,而是一个 fixed point,并且 fixed point 可以通过将其内部存储为 int 来适当地处理。唯一可以改进的是一种方法,最初设置 realValue - 但这需要更复杂的双重绑定机制,这会破坏示例的范围。 (您需要确定更改是内部的还是外部的......)但是对于程序员来说,设置它应该相当简单,记住,如果您设置了两位小数,则 100 的值等于 1.00。
【解决方案2】:

当前 SpinBox (Controls 2.0 - 2.4) 有一个限制,因为它只接受 to/from +/- 0x7FFF FFFF 的范围(它甚至不能处理无符号整数,顺便说一句)。对于reals,这意味着(可能很严重)对于小数点后需要的每个数字,您都会丢失小数点前的一个数字。例如,如果您需要 6 位十进制精度,则最大可能值为 2147.483647。到目前为止,这会影响此答案中提出的两种解决方案。

如果您尝试将tofrom 设置为超出int 的限制(例如,乘以此处建议中的因子),您将获得非常“奇怪”的行为,这可能会非常令人费解.如果您尝试直接超出这些限制(例如to: 0xFFFFFFFF),则会收到错误消息。

查看(并投票给:))QTBUG-67349

我意识到这不是问题的答案,但希望它可以像我一样为某人节省几个小时的挫败感。

到目前为止,我能想到的唯一解决方法是简单地使用带有 DoubleValidator 的文本编辑字段。我会试着用一个简化的例子回到这里。

编辑:

Here is my version of a DoubleSpinBox (docs)。代码太长,无法在此处粘贴(恕我直言),我宁愿不维护多个版本。

简而言之,这个想法是完全绕过 SpinBox value,只使用基本的 Controls.2 SpinBox 作为按钮和整体外观/感觉。这意味着它不需要为每个主题(Fusion/Material/etc)进行定制。

不幸的是,这涉及重新实现所有事件处理程序(按钮按下/重复、滚轮滚动、文本编辑)以及一些其他的诡计。但是(到目前为止)我认为它比使用自定义按钮/等从头开始重新实现整个事情,同时尝试匹配所有不同的主题。

它还添加了一些标准 SpinBox 中没有的选项...因为,为什么不呢? :)

【讨论】:

    【解决方案3】:

    这是一个@eyllanesc 固定版本。所有其他缺少的属性必须是别名。如果发现错误,请向我报告。

    DoubleSpinBox.qml

    import QtQuick 2.0
    import QtQuick.Controls 2.2
    import QmlUtils 1.0
    
    Item
    {
        id: doublespinbox
        width: 140
        height: 40
        property int decimals: 1
        property alias value: valuePreview.value
        property real from: 0
        property real to: 99
        property real stepSize: 1
        property alias editable: spinbox.editable
        property alias font: spinbox.font
    
        SpinBox
        {
            id: spinbox
            property bool init: false
            property real factor: Math.pow(10, decimals)
    
            function setValue(preview)
            {
                init = true
                value = preview.value * factor
                init = false
                preview.value = value / factor
            }
    
            DoubleValuePreview
            {
                id: valuePreview
                onValuePreview: spinbox.setValue(preview)
            }
    
            anchors.fill: parent
            editable: true
            stepSize: doublespinbox.stepSize * factor
            to : doublespinbox.to * factor
            from : doublespinbox.from * factor
    
            onValueChanged:
            {
                if (init)
                    return
    
                valuePreview.setValueDirect(value / factor)
            }
    
            validator: DoubleValidator
            {
                bottom: Math.min(spinbox.from, spinbox.to)
                top: Math.max(spinbox.from, spinbox.to)
            }
    
            textFromValue: function(value, locale)
            {
                return Number(value / factor).toLocaleString(locale, 'f', doublespinbox.decimals)
            }
    
            valueFromText: function(text, locale)
            {
                doublespinbox.value = Number.fromLocaleString(locale, text)
                return doublespinbox.value * factor
            }
        }
    }
    

    QmlValuePreview.h

    #pragma once
    
    #include <qobject.h>
    #include <qqmlengine.h>
    
    class QDoubleValueArg : public QObject
    {
        Q_OBJECT
    public:
        QDoubleValueArg(double value) : Value(value)
        {
            QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
        }
    public:
        Q_PROPERTY(double value MEMBER Value)
    public:
        double Value;
    };
    
    class QmlDoubleValuePreview : public QObject
    {
        Q_OBJECT
    public:
        using QObject::QObject;
    public:
        Q_PROPERTY(double value READ getValue WRITE setValue NOTIFY valueChanged)
    public:
        Q_INVOKABLE void setValueDirect(double value)
        {
            if (m_value == value)
                return;
    
            m_value = value;
            emit valueChanged();
        }
    public:
        inline double getValue() const { return m_value; }
        inline void setValue(double value)
        {
            if (m_value == value)
                return;
    
            QDoubleValueArg arg(value);
            emit valuePreview(&arg);
            if (m_value == arg.Value)
                return;
    
            m_value = arg.Value;
            emit valueChanged();
        }
    signals:
        void valueChanged();
        void valuePreview(QDoubleValueArg *preview);
    private:
        double m_value = 0;
    };
    

    注册:

    #define URI "QmlUtils"
    #define VERSION_MAJOR 1
    #define VERSION_MINOR 0
    
    void registerTypes()
    {
        qRegisterMetaType<QDoubleValueArg *>("QDoubleValueArg *");
        qmlRegisterType<QmlDoubleValuePreview>(URI, VERSION_MAJOR, VERSION_MINOR, "DoubleValuePreview");
    }
    

    【讨论】:

    • 我认为,由于精度损失,这可能会导致某些值的绑定循环。最好不要使用相同的 API,而是使用健壮的 API。
    • 如果子类化没有相同的 API 会导致错误,但我同意这段代码可能仍然存在问题。解决方案可能根本不依赖绑定,或者只是采用原始 SpinBox 代码并用真实属性重写它。
    • 由于 REAL 和 INT 存在差异,因此无论如何它不会是相同的 API。那么为什么不使用不同但清晰的 API,它并没有隐藏它内部是一个 INT 的事实,所以每个人都会知道它的含义。
    • 那是个人喜好问题。我更喜欢具有相同名称的属性,但正如您所说,稳健性具有更高的优先级。
    • @derM,我删除了与外部值属性的绑定。也尊重从,到的限制。你现在看起来怎么样?
    【解决方案4】:

    我在上面的代码中发现了两个问题。

    1. 如果显示像 1.299(句号) 这样的数字会出错 DoubleSpinBox 将显示 1.2 而不是 1.3 为了修复它,你应该写

      值 = Math.round(preview.value * 因子)

    在 DoubleSpinBox.qml 函数中设置值(预览)

    1. 在这一行我们有覆盖绑定

      doublespinbox.value = Number.fromLocaleString(locale, text)

    因为我们有 “属性别名值:valuePreview.value” 并且可能'值'会在代码中绑定一些 所以你不能这样做,完全可以写这个:

    function(text, locale) {
            return Number.fromLocaleString(locale, text) * factor
    }
    
    1. 我想我们也可以删除这条线

      preview.value = 值/因子

    在函数 setValue(preview)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-17
      • 1970-01-01
      • 2016-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      相关资源
      最近更新 更多