Qt 中简单的无键盘触摸屏小部件

Posted

技术标签:

【中文标题】Qt 中简单的无键盘触摸屏小部件【英文标题】:Simple keyboardless touchscreen widgets in Qt 【发布时间】:2016-02-23 17:11:48 【问题描述】:

我正在寻找一种简单的方法来制作触摸屏小部件,它允许用户在运行代码的计算机上设置时间和 IP 地址,并提供一个简单的(大写拉丁字母)名称。

这个问题不是关于如何实际设置系统时间或IP地址的;我只是在寻找有关如何自己制作图形小部件的信息。

我想要将每个可编辑属性(时间、地址和名称)划分为“可滚动”字段,其中“时间”字段是小时、分钟、可能是秒,以及 AM/PM/24- hr,地址/名称字段是单个字符。每个字段的上方和下方都有一个箭头,触摸箭头将滚动浏览该字段的有效值。

我认为这是一种非常常见的 UX 模式,尤其是在肉类空间中(例如在闹钟上),但如果不清楚我要描述的内容,这里有一个用户编辑“名称”属性的示例:

^^^
BN
vvv

用户按下“N”下方的“向下”: ^^^ 博 呜呜呜

用户在空白处按下“向下”键:

^^^^
BOA
vvvv

...再次在同一个向下箭头上:

^^^^
BOB
vvvv

我正在使用带有 Qt 5 的 C++14 编写此代码。(如果最坏的情况发生,我愿意使用不同的语言和/或框架编写一个单独的应用程序,但我不是要求框架建议在这里;如果你有,请告诉我,我会在 Software Recommendations SE 上提出相应的问题。)

我在 Qt 5 小部件库中看不到任何这样的东西;大多数输入小部件都是文本字段。 QSpinBox 看起来很有希望,但是对于我的触摸屏来说,箭头可能太小了,并且为每个字母使用单独的旋转框可能会令人困惑和难看。

总的来说,我对 Qt 或 GUI 编程的了解还不够,无法自信地尝试从头开始编写自己的小部件,但是这个界面看起来很简单,我希望几行 QML 就能让我很好地掌握我的方式。

【问题讨论】:

【参考方案1】:

ListViewPathView 可以产生所需的结果,但行为和性能略有不同。与ListView 不同,PathView 是循环的,即元素可以通过仅使用一个选择控件来连续迭代。通过PathAttribute 类型完全自定义PathView 中路径的行为也更容易。根据问题,无论如何路径自定义似乎不是必需的功能。

如果您通过ListView 实施解决方案,您应该确保只显示一个元素并且处理任何模型。

Component 
    id: spinnnnnnnner

    Column  
        width: 100
        height: 110
        property alias model: list.model
        property string textRole: ''
        spacing: 10

        Item 
            width: 100
            height: 25
            Text  anchors.centerIn: parent; text: "-"; font.pixelSize: 25; font.bold: true 
            MouseArea anchors.fill: parent; onClicked: list.decrementCurrentIndex() 
        

        ListView 
            id: list
            clip: true
            width: 100
            height: 55
            enabled: false          // <--- remove to activate mouse/touch grab
            highlightRangeMode: ListView.StrictlyEnforceRange   // <--- ensures that ListView shows current item

            delegate: Text 
                width: ListView.view.width
                horizontalAlignment: Text.AlignHCenter
                font.pixelSize: 50
                font.bold: true
                text: textRole === "" ? modelData :
                                        ((list.model.constructor === Array ? modelData[textRole] : model[textRole]) || "")
            
        

        Item 
            width: 100
            height: 25
            Text  anchors.centerIn: parent; text: "+"; font.pixelSize: 25; font.bold: true 
            MouseArea anchors.fill: parent; onClicked: list.incrementCurrentIndex() 
        
    

对模型的检查确保任何类型的模型都可以传递给组件。下面是一个使用三种截然不同的模型的示例:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1

ApplicationWindow 
    visible: true

    width: 400
    height: 300

    ListModel 
        id: mod
        ListElement texty: "it1"
        ListElement texty: "it2"
        ListElement texty: "it3"
    

    Row 
        Repeater 
            id: rep
            model: 3
            delegate: spinnnnnnnner

            Component.onCompleted: 
                rep.itemAt(0).model = mod                       // listmodel
                rep.itemAt(0).textRole = "texty"
                rep.itemAt(1).model = 10                        // number model
                //
                rep.itemAt(2).model = ["foo", "bar", "baz"]     // array model
            
        
    

PathView 的实现与ListView 的实现没有太大区别。在这种情况下,定义一个垂直的path 并通过pathItemCount 指定一次只有一个元素可见就足够了。最后,设置preferredHighlightBegin/preferredHighlightEnd 确保可见元素在视图中居中。重新访问的组件如下:

Component 
    id: spinnnnnnnner

    Column  
        width: 100
        height: 110
        property alias model: list.model
        property string textRole: ''
        spacing: 10

        Item 
            width: 100
            height: 25
            Text  anchors.centerIn: parent; text: "-"; font.pixelSize: 25; font.bold: true 
            MouseArea anchors.fill: parent; onClicked: list.decrementCurrentIndex() 
        

        PathView 
            id: list
            clip: true
            width: 100
            height: 55
            enabled: false          // <--- remove to activate mouse/touch grab
            pathItemCount: 1
            preferredHighlightBegin: 0.5
            preferredHighlightEnd: 0.5

            path: Path 
                startX: list.width / 2; startY: 0
                PathLine  x: list.width / 2; y: list.height  
            

            delegate: Text 
                width: PathView.view.width
                horizontalAlignment: Text.AlignHCenter
                font.pixelSize: 50
                font.bold: true
                text: textRole === "" ? modelData :
                                        ((list.model.constructor === Array ? modelData[textRole] : model[textRole]) || "")
            
        

        Item 
            width: 100
            height: 25
            Text  anchors.centerIn: parent; text: "+"; font.pixelSize: 25; font.bold: true 
            MouseArea anchors.fill: parent; onClicked: list.incrementCurrentIndex() 
        
    

【讨论】:

以上是关于Qt 中简单的无键盘触摸屏小部件的主要内容,如果未能解决你的问题,请参考以下文章

关闭小部件时出现嵌入式 Qt GUI 工件

如何避免鼠标单击一个小部件触发 Qt 中其他小部件的信号?

QT 绘画到小部件

Qt“直通”或“容器”小部件

Qt 创建者:小部件未添加到布局中

自定义 Qt 小部件