Qt Создать QML Slider, чувствительный к сенсорным событиям

Я создаю игру для сенсорных экранов, которая требует от 2-4 игроков, чтобы у каждого был доступ к паре элементов управления ползунком. Проблема в том, что элемент управления QML Slider реагирует на прикосновения как событие мыши и захватывает фокус. Тогда только один игрок может получить доступ к одному элементу управления одновременно. Мне нужно несколько ползунков, чтобы реагировать на сенсорные события одновременно. У меня вопрос как это сделать?

С помощью различных сообщений о переполнении стека я смог создать свой собственный ответ, который пока что работает. Я подробно опишу ответ ниже в разделе ответов, чтобы избавить других новичков, таких как я, от неприятностей.

1

Решение

Я не мог найти чистый 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 sliderValColumnLayout{
Label {
text: qsTr(sliderTitle)
font.pointSize: 10

}
Slider {
id: touchSlider1minimumValue: 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.2Item {
// 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 sliderWidthColumnLayout{
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 игрока без проблем. Мы увидим.

1

Другие решения

Существует чисто qml решение этой проблемы. Объект TouchSlider C ++ в моем первом ответе (где-то еще в этой теме) был ненужным. Здесь я изменил код в код TouchSlider qml, чтобы исключить ссылки на сенсорный слайдер (объект 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 sliderValColumnLayout{
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 не добавляют никакого значения.

1

По вопросам рекламы [email protected]