[英]Qt Create a QML Slider Sensitive to Touch Events
I am creating a game for touch screens that requires 2-4 players to each have access to a pair of slider controls. 我正在为触摸屏创建一个游戏,该游戏需要2-4个玩家才能访问一对滑块控件。 The problem is that the QML Slider control responds to touch as a mouse event and seizes the focus. 问题在于QML滑块控件会在鼠标事件发生时对触摸做出响应并抓住焦点。 Then only one player can access a single control at a time. 这样一来,一次只有一个玩家可以访问单个控件。 I need multiple sliders to respond to touch events simultaneously. 我需要多个滑块来同时响应触摸事件。 My question is how to do that? 我的问题是怎么做?
With the help of a variety of stack overflow posts, I have been able to create my own answer that so far seems to work. 借助各种堆栈溢出帖子的帮助,到目前为止,我已经能够创建自己的答案了,而且似乎还行得通。 I detail the answer below in the answer section to save other newbies like me the trouble. 我在“答案”部分详细介绍了以下答案,以免其他像我这样的新手遇到麻烦。
I could not find a pure QML way to solve the problem but I wanted to minimize the use of C++. 我找不到解决问题的纯QML方法,但我想尽量减少使用C ++。 Using C++, I create an object TouchSlider and add it to my qml scene. 使用C ++,我创建了一个对象TouchSlider并将其添加到我的qml场景中。 The TouchSlider object has a simple function to update the value of a vertical slider according to a position argument. TouchSlider对象具有一个简单的功能,可以根据位置参数更新垂直滑块的值。 Then in the QML code, I add a MultiPointTouchArea on top of a regular slider and respond to the touch events by calling C++ function. 然后在QML代码中,我在常规滑块顶部添加了MultiPointTouchArea,并通过调用C ++函数来响应触摸事件。
Here are all my files for a project called SliderPair. 这是我所有名为SliderPair的项目的文件。
SliderPair.pro: 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: 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: 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: 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: 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: 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: 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
}
}
}
The result should look like this: 结果应如下所示:
On a touch screen like my Surface Pro, I can control each slider simultaneously with two fingers. 在类似Surface Pro的触摸屏上,我可以用两个手指同时控制每个滑块。 Since Windows supports up to 10 simultaneous touches that should mean I can have 2-4 players without a problem. 由于Windows支持多达10个同时触摸,这意味着我可以毫无问题地拥有2-4个播放器。 We shall see. 我们将会看到。
There is a pure qml solution to this problem. 有一个纯qml解决方案可以解决此问题。 The TouchSlider C++ object in my first answer (elsewhere in this thread) was unnecessary. 我的第一个答案(此线程中的其他地方)中的TouchSlider C ++对象是不必要的。 Here I have modified the code to the TouchSlider qml code to eliminate references to touchslider (the TouchSlider C++ object). 在这里,我已将代码修改为TouchSlider qml代码,以消除对touchslider(TouchSlider C ++对象)的引用。
TouchPoint.qml: 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);
}
}
}
}
The touchslider.h and touchslider.cpp files add no value. touchslider.h和touchslider.cpp文件没有任何价值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.