【问题标题】:Center Text QML type based on the dot of a decimal number基于小数点的中心文本 QML 类型
【发布时间】:2017-01-28 14:41:03
【问题描述】:

我有一个简单的TextQML 类型:

Item {
    anchors.fill: parent

    Text {
        id: centerText
        text: "6.9"
        anchors.horizontalCenter: parent.horizontalCenter
        y: 570
    }
}

目前,Text 将根据字符串的整个长度居中。 但我想将 Text 基于点字符居中。

我正在考虑将实际的Text 分成三部分:圆形部分、点和小数部分。但是我不认为我可以将 float 变量拆分为只获取小数部分。

还有没有更方便和适应QML的方式?

【问题讨论】:

    标签: qt text qml center


    【解决方案1】:

    这是一个快速示例,您可以如何实现这一目标。 “虚拟”隐藏文本用于衡量数字的整数部分的宽度,通过使用Math.floor() 获得。然后,您只需定位它,使小数点始终位于父对象的中心,无论数字是多少。

     Column {
        x: 100
        y: 200
        spacing: 5
        Slider {
          id: sl
          width: 400
          minimumValue: 0
          maximumValue: 100
        }
        Rectangle {
          width: 400
          height: 100
          Text {
            x: parent.width * .5 - dummy.width
            y: parent.height * .5 - height * .5
            text: sl.value
            Text {
              id: dummy
              text: Math.floor(sl.value)
              visible: false
            }
          }
        }
      }
    

    顺便说一句,当你有 anchors.fill: parent 时,anchors.bottom: parent.bottom 是多余的。

    【讨论】:

    • 也许可以使用 FontMetrics 元素代替隐藏的虚拟文本,doc.qt.io/qt-5/qml-qtquick-fontmetrics.html
    • @KevinKrammer - 这是个好主意,尽管 FontMetric 从 5.4 开始可用,但我没有注意到它。我的大部分代码库仍然是 QtQuick 2.5。
    【解决方案2】:

    编辑:最后我使用了ddriver 的解决方案。如果您使用自定义字体,请不要忘记在虚拟Text 中包含其选项!

    我无法重现 ddriver 的解决方案。 我想出了自己的解决方案。我搜索了如何在 JavaScript 中获取浮点值的小数部分,发现:

    (4.89 + "").split(".")[1]; => 89
    

    这比 C++ modf 函数更方便,因为它不返回“0”。部分。

    多亏了这个,我只需要创建三个 Text QML 类型:

    // Center horizontally number based on the dot
    Text {
        id: testValueRoundPart
        anchors.right: testSpeedDot.left
        y: 570
        text:  (6.9 + "").split(".")[0]
    }
    
    Text {
        id: testSpeedDot
        y: 570
        text:  "."
        anchors.horizontalCenter: parent.horizontalCenter
    }
    
    Text {
        id: testSpeedValueDecimalPart
        y: 570
        anchors.left: testSpeedDot.right
        text: (6.9 + "").split(".")[1];
    }
    

    我一直将它与自定义字体(Sans-Guilt 和 Universalis;我不认为这些是等宽字体)一起使用,并且效果很好。 它可能不是最优化的解决方案,但它符合我的需求。

    【讨论】:

    • 在我的示例中,值来自滑块,只是为了交互性。只需删除列、滑块,然后将矩形替换为 OP 中的常规项目。该解决方案将比您的解决方案更有效 - 只有 2 个文本元素而不是 3 个和更少的锚定。这个概念很简单 - 将文本放在父元素的中心减去整数部分的宽度,以便点位于中间。
    • 这次我已经能够通过删除我的Textfont.familyfont.pixelSize 使其工作。但是,如果我使用我在答案中引用的一种字体,它就不起作用。我想我们需要考虑自定义字体的粗细才能使其正常工作。
    • 嗯,这并不重要,只要两个文本元素使用相同的字体和样式。我会看看它如何处理自定义字体。
    • 不,没关系!我的错。我忘记在我的虚拟Text 中添加我的字体选项...我会修改我的答案并勾选你的。
    • 是的,我就是这么想的 ;)
    【解决方案3】:

    您可能必须使用基于 0 的填充来实现所需的对齐方式——或者找到等宽字体,然后使用空格而不是 0

    无论如何——这可能是一个解决方案:

    import QtQuick 2.0
    
    
    Item {
          height: 480
          width: 320
          /* list of float strings to pad */
          property var myFloats: ["6.9", "36.21", "4.632", "4225.2", "12.0"]
       
    
           /*  apply  zero-based padding to the strings on startup
               and output to the console */
          Component.onCompleted: {
               console.log(pad_floats());
          }
    
       
      /* function takes each decimal,and each integer separated by the .
          and figures out the length of the longest one, then uses
          a padding algorithm to add 0s to the start of the integers
          and 0s to the back of decimals and returns an array */
          function pad_floats() {
              var longest_int = 0;
              var longest_float = 0;
              var newFloats = [];
              for (var i = 0; i < myFloats.length; i++) {
                  var tmp_float = myFloats[i];
                  var tmp_int = tmp_float.split(".")[0]; // left side
                  var tmp_dec = tmp_float.split(".")[1]; // right side
    
                  /* check if either number is more digits than our longest one */
                  if (tmp_int.length > longest_int) {
                        longest_int = tmp_int.length;
                  }
                  if (tmp_dec.length > longest_float) {
                        longest_float = tmp_dec.length;
                  }
    
              }
    
              for (var u = 0; u < myFloats.length; u++) {
                  var tmp_float = myFloats[u];
                  var tmp_int = tmp_float.split(".")[0]; // left side of .
                  var tmp_dec = tmp_float.split(".")[1]; // right side of .
    
                  /* pad the two numbers */
                  var new_int = pad(parseInt(tmp_int), longest_int, "0"); 
                  var new_dec = pad(parseInt(tmp_dec), 0 - longest_float, "0");
    
                  /* combine the new numbers back into one */
                  var new_float = new_int + "." + new_dec;
    
                  /* add to our array  to return */
                  newFloats.push(new_float);
    
            }
            return newFloats;
    
    
      }
    
      function pad(n, width, z) {
           z = z || '0';
           n = n + '';
           return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
      }
    
       /*   Heres the ListView to show the actual list of floats */
      ListView {
           width: 320
           height: 480
           anchors.fill: parent
           anchors.left: parent.left
           model: pad_floats()  // our returned array from before
           delegate: listDelegate  // pointer to the Component below
    
    
      }
    
      Component {
          id: listDelegate
          Text {
              text: modelData
              font.pointSize: 11
              verticalAlignment: Text.AlignVCenter
              horizontalAlignment: Text.AlignHCenter
          }
      }

    运行此操作的结果是一个包含以下条目的 ListView:

    0006.9
    0036.21
    0004.632
    4225.2
    0012.0
    

    就是这样......就像我在答案顶部所说的那样 - 为了创建只有数字而没有额外 0 的所需结果,您必须使用具有相同空间量的字体对于每个字符,包括(空格)字符。

    这些通常称为 Monotype 或 Monospace 字体。

    通常这些字体会将“Mono”一词与其名称相关联,以便更轻松地找到它们。

    找到好的单型字体后,在 pad_floats() 函数中替换以下代码:

    旧代码

          var new_int = pad(parseInt(tmp_int), longest_int, "0");
          var new_dec = pad(parseInt(tmp_dec), 0 - longest_float, "0");
    

    新代码

          var new_int = pad(parseInt(tmp_int), longest_int, " ");
          var new_dec = pad(parseInt(tmp_dec), 0 - longest_float, " ");
    

    注意我如何将最后一个“0”参数更改为“”?最后一个字符串是填充字符串,所以你可以在里面放任何东西,它会用那个字符串填充。

    享受吧。

    【讨论】:

    • 我意识到这不是做这篇文章的“最有效”的方式——但我写了一种更“冗长”的方式来做这件事,而不是将一堆操作组合成一个大块
    • 没有等宽字体是不行的,等宽字体在UI设计中很少使用,它们是文本控制台类型的字体,主要用于编码。由于无法从 QML 访问字体指标,因此使用隐藏的文本元素来测量宽度是唯一可行的解​​决方案。
    • 是的 - 无论如何,使用隐藏文本框似乎是一种更有效的处理方式 - 在 QML 中运行 JS 函数似乎永远不会成功
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 2014-12-31
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    相关资源
    最近更新 更多