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

Simple keyboardless touchscreen widgets in Qt

本文关键字:触摸屏 小部 键盘 简单 Qt      更新时间:2023-10-16

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

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

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

我认为这是一种非常常见的用户体验模式,尤其是在肉空间(例如闹钟上),但以防万一不清楚我想描述什么,这里有一个用户编辑"name"属性的示例:

^^^
BN
vvv

用户按"N"下方的"向下": ^^^ 博 VVV

用户在空白区域下方按"向下":

^^^^
BOA
vvvv

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

^^^^
BOB
vvvv

我使用 Qt 14 的 C++5 来写这篇文章。(如果最坏的情况发生,我愿意使用不同的语言和/或框架编写一个单独的应用程序,但我在这里不要求框架建议;如果你有,请告诉我,我会在软件推荐SE上提出相应的问题。

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

我对Qt或GUI编程的了解还不够多,无法自信地从头开始编写自己的小部件,但是这个界面看起来很简单,我希望几行QML会让我顺利进行。

ListView以及PathView都可以产生所需的结果,但行为和性能略有不同。与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() }
        }
    }
}