Qt 创建一个对触摸事件敏感的 QML 滑块
Posted
技术标签:
【中文标题】Qt 创建一个对触摸事件敏感的 QML 滑块【英文标题】:Qt Create a QML Slider Sensitive to Touch Events 【发布时间】:2017-01-01 21:53:10 【问题描述】:我正在为触摸屏创建一个游戏,该游戏需要 2-4 名玩家,每个玩家都可以使用一对滑块控件。问题是 QML Slider 控件将触摸作为鼠标事件响应并获取焦点。然后一次只有一个玩家可以访问一个控件。我需要多个滑块来同时响应触摸事件。我的问题是如何做到这一点?
在各种堆栈溢出帖子的帮助下,我已经能够创建自己的答案,到目前为止似乎有效。我在答案部分详细说明了下面的答案,以免像我这样的其他新手麻烦。
【问题讨论】:
仅供参考,Qt Quick Controls 2 在 Qt 5.9 中获得了多点触控支持,将于春季晚些时候发布。 那太好了。 对于它的价值,这是一个完全有效的问题。我不明白为什么这个问题被一些人否决了。回想起来,为 Qt Quick Controls 2 添加适当的触摸支持应该有更高的优先级,但不知何故,它被埋在无尽的待办事项列表中的其他事情之下...... 【参考方案1】:这个问题有一个纯qml的解决方案。我的第一个答案(在这个线程的其他地方)中的 TouchSlider C++ 对象是不必要的。这里我将代码修改为 TouchSlider qml 代码,以消除对 touchslider(TouchSlider C++ 对象)的引用。
TouchPoint.qml:
import QtQuick 2.0
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item
property string sliderTitle
property real sliderMin
property real sliderMax
property real sliderVal
ColumnLayout
id: column1
Label
text: qsTr(sliderTitle)
font.pointSize: 10
Slider
id: touchSlider1
minimumValue: sliderMin
maximumValue: sliderMax
orientation: Qt.Vertical
value: sliderVal
onValueChanged: function()
sliderVal = Math.round(this.value);
labelSliderValue.text = qsTr(JSON.stringify(sliderVal));
Label
id: labelSliderValue
text: qsTr(JSON.stringify(sliderVal))
font.pointSize: 10
function sliderSetValueFromTouch(position)
// Assume qs a vertical slider
var minvalue = touchSlider1.minimumValue;
var maxvalue = touchSlider1.maximumValue;
// Since this is a vertical slider, by assumption, get the height
var height = touchSlider1.height;
// Compute the new value based on position coordinate
var newvalue = (height-position)/height * (maxvalue-minvalue);
if (newvalue<minvalue) newvalue = minvalue;
if (newvalue>maxvalue) newvalue = maxvalue;
//qDebug() << newvalue;
// Set the value of the slider
touchSlider1.value = newvalue;
MultiPointTouchArea
anchors.fill: touchSlider1
touchPoints: [
TouchPoint
id: point1
onPressedChanged: function()
if(pressed)
//console.log("pressed");
//console.log(touchslider.testStringReturn());
//touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
column1.sliderSetValueFromTouch(point1.y);
]
onTouchUpdated: function()
//touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
column1.sliderSetValueFromTouch(point1.y);
touchslider.h 和 touchslider.cpp 文件没有任何价值。
【讨论】:
【参考方案2】:我找不到解决问题的纯 QML 方法,但我想尽量减少 C++ 的使用。使用 C++,我创建了一个对象 TouchSlider 并将其添加到我的 qml 场景中。 TouchSlider 对象有一个简单的函数来根据位置参数更新垂直滑块的值。然后在 QML 代码中,我在常规滑块顶部添加了一个 MultiPointTouchArea,并通过调用 C++ 函数来响应触摸事件。
这是我名为 SliderPair 的项目的所有文件。
SliderPair.pro:
QT += qml quick widgets
QT += quickcontrols2
QT += core
CONFIG += c++11
SOURCES += main.cpp \
touchslider.cpp
RESOURCES += \
qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH += qml
# Default rules for deployment.
qnx: target.path = /tmp/$$TARGET/bin
else: unix:!android: target.path = /opt/$$TARGET/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
touchslider.h
DISTFILES +=
main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
// add following includes for exposing new class TouchSlider to QML
#include <QQmlEngine>
#include <QQmlContext>
#include "touchslider.h"
int main(int argc, char *argv[])
QApplication app(argc, argv);
//Create an object of type TouchSlider
//When a scoped pointer goes out of scope the object is deleted from memory. Good housekeeping:
QScopedPointer<TouchSlider> touchslider (new TouchSlider);
QQmlApplicationEngine engine;
engine.addImportPath(QStringLiteral("qml"));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
//QML can now refer to the TouchSlider object using the handle "touchslider":
engine.rootContext()->setContextProperty("touchslider",touchslider.data());
return app.exec();
touchslider.h:
#ifndef TOUCHSLIDER_H
#define TOUCHSLIDER_H
#include <QObject>
#include <QDebug>
#include <QtQuickControls2>
class TouchSlider : public QObject
Q_OBJECT
public:
explicit TouchSlider(QObject *parent = 0);
//call Q_INVOKABLE macro to set up functions for QML
Q_INVOKABLE void testDebug(); //hello world from C++
Q_INVOKABLE QString testStringReturn(); //hello world to javascript
Q_INVOKABLE void sliderSetValueFromTouch(QQuickItem *qs,int position );//use touch event to set slider value
signals:
public slots:
;
#endif // TOUCHSLIDER_H
touchslider.cpp:
#include "touchslider.h"
TouchSlider::TouchSlider(QObject *parent) : QObject(parent)
void TouchSlider::testDebug()
qDebug() << "Hello from C++";
QString TouchSlider::testStringReturn()
QString message = "Hi from C++";
return message;
void TouchSlider::sliderSetValueFromTouch(QQuickItem *qs, int position)
// Assume qs a vertical slider
// Get its properties (its slider properties are accessible even though it is declared as QQuickItem)
// minimumValue and maximumValue are of type QVariant so we need to cast them as double
double minvalue = qs->property("minimumValue").value<double>();
double maxvalue = qs->property("maximumValue").value<double>();
// Since this is a vertical slider, by assumption, get the height
double height = qs->property("height").value<double>();
// Compute the new value based on position coordinate
double newvalue = (height-position)/height * (maxvalue-minvalue);
if (newvalue<minvalue) newvalue = minvalue;
if (newvalue>maxvalue) newvalue = maxvalue;
//qDebug() << newvalue;
// Set the value of the slider
qs->setProperty("value",newvalue);
TouchSlider.qml:
import QtQuick 2.0
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item
property string sliderTitle
property real sliderMin
property real sliderMax
property real sliderVal
ColumnLayout
Label
text: qsTr(sliderTitle)
font.pointSize: 10
Slider
id: touchSlider1
minimumValue: sliderMin
maximumValue: sliderMax
orientation: Qt.Vertical
value: sliderVal
onValueChanged: function()
sliderVal = Math.round(this.value);
labelSliderValue.text = qsTr(JSON.stringify(sliderVal));
Label
id: labelSliderValue
text: qsTr(JSON.stringify(sliderVal))
font.pointSize: 10
MultiPointTouchArea
anchors.fill: touchSlider1
touchPoints: [
TouchPoint
id: point1
onPressedChanged: function()
if(pressed)
//console.log("pressed");
//console.log(touchslider.testStringReturn());
touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
]
onTouchUpdated: function()
touchslider.sliderSetValueFromTouch(touchSlider1,point1.y);
PlayerControls.qml:
import QtQuick 2.0
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
Item
// These properties act as constants, useable outside this QML file
property string playerName
property real priceMin
property real priceMax
property real qualityMin
property real qualityMax
property real priceValue
property real qualityValue
property int sliderWidth
ColumnLayout
id: columnLayout1
width: 640
height: 480
Layout.minimumWidth: 640
Layout.fillWidth: true
anchors.fill: parent
spacing: 10.2
Label
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
id: labelPlayer1
text: qsTr(playerName)
font.pointSize: 10
RowLayout
ColumnLayout
Label
text: qsTr("")
font.pointSize: 10
width: 50
TouchSlider
width: sliderWidth
sliderTitle: "Price"
sliderMin: priceMin
sliderMax: priceMax
sliderVal: priceValue
TouchSlider
width: sliderWidth
sliderTitle: "Quality"
sliderMin: qualityMin
sliderMax: qualityMax
sliderVal: qualityValue
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow
visible: true
width: 640
height: 480
title: qsTr("SliderPair Test")
Item
PlayerControls
playerName: "Player 1"
priceMin: 0
priceMax: 200
priceValue: 100
qualityMin: 0
qualityMax: 50
qualityValue: 25
sliderWidth: 200
结果应如下所示:
在像 Surface Pro 这样的触摸屏上,我可以用两根手指同时控制每个滑块。由于 Windows 最多支持 10 个同时触摸,这意味着我可以毫无问题地拥有 2-4 个玩家。我们拭目以待。
【讨论】:
以上是关于Qt 创建一个对触摸事件敏感的 QML 滑块的主要内容,如果未能解决你的问题,请参考以下文章