简体   繁体   English

Qt创建对事件敏感的QML滑块

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM