qtembeddedqmltouch-eventscreensaver

How can I reset a timer every time I receive a touch event from a qml page


import QtQuick 2.6;
import QtQuick.Controls 2.1 ;
import  QtQuick.Layouts 1.3 ;

Page{
    id: page
    width:  800
    height: 1024
    background:   Rectangle { 
        color: "black" ;
        anchors.fill:parent ;
    }

    Rectangle {

        id:rect1

        x: 0
        y:10
        width: 100
        height: 100
        color :  "red"

        MouseArea {

           anchors.fill: parent
           onClicked: tmr.restart()
        }

    }

    Rectangle {

        id:rect2
        x: 0
        y:110
        width: 100
        height: 100
        color :  "blue"
       MouseArea {
           anchors.fill: parent
           onClicked: tmr.restart()
        }

    }

    Timer {

     id : tmr 
     interval : 30000
     repeat : true 
     running: true
     onTriggered: {

           console.log ("hello world ")

     }

   }
}  

I develop a software for embedded imx6 freescale device using qt framework.

Basically I just want to restart my timer every time I click and every time I get a touch event on my screen whether the click/touch happen inside the mouse area of my rectangles or outside of them.

The idea is similar to a screensaver.


Solution

  • There are multiple ways, and the right way depends on your requirements.

    If you don't need to guarantee that the timer triggers during a input you can just layer a MouseArea on top of everything. In this MouseArea you handle the pressed-signals, but dont accept them.

    This allows you to handle the mouse input in the lower layers later. However you only realize whenever a new press happens, and the Timer might trigger e.g. during a half-an-hour finger-move input.

    The second way is to have all MouseAreas report uppon their handled signals, that the signal happend, to reset the Timer. For all unhandled signals, you layer a MouseArea beneath everything else, handle all signals there to catch what has been falling through.

    Resorting to C++ you might create a Item at the root of your Item-tree, and override the childMouseEventFitler
    See my answer here for more on this.

    In this case you should add a MouseArea right inside this Item, so it has something to filter at any place.

    Note! This method will be triggered for each MouseArea that might be under your click. But in your scenario, this would be fine, I guess.


    Thanks to GrecKo I looked into the general eventFilter again, and indeed it is really easy.

    1. you create a simple QObject following the singleton pattern, in which you reimplement the eventFilter-method, so that it will emit a signal

    mouseeventspy.h

    #pragma once
    #include <QObject>
    #include <QtQml>
    #include <QQmlEngine>
    #include <QJSEngine>
    
    
    class MouseEventSpy : public QObject
    {
        Q_OBJECT
    public:
        explicit MouseEventSpy(QObject *parent = 0);
    
        static MouseEventSpy* instance();
        static QObject* singletonProvider(QQmlEngine* engine, QJSEngine* script);
    
    protected:
        bool eventFilter(QObject* watched, QEvent* event);
    
    signals:
        void mouseEventDetected(/*Pass meaningfull information to QML?*/);
    
    };
    

    mouseeventspy.cpp

    #include "mouseeventspy.h"
    #include <QQmlEngine>
    #include <QJSEngine>
    #include <QEvent>
    
    MouseEventSpy::MouseEventSpy(QObject *parent) : QObject(parent)
    {
        qDebug() << "created Instance";
    }
    
    // This implements the SINGLETON PATTERN (*usually evil*)
    // so you can get the instance in C++
    MouseEventSpy* MouseEventSpy::instance()
    {
        static MouseEventSpy* inst;
        if (inst == nullptr)
        {
            // If no instance has been created yet, creat a new and install it as event filter.
            // Uppon first use of the instance, it will automatically
            // install itself in the QGuiApplication
            inst = new MouseEventSpy();
            QGuiApplication* app = qGuiApp;
            app->installEventFilter(inst);
        }
        return inst;
    }
    
    // This is the method to fullfill the signature required by
    // qmlRegisterSingletonType.
    QObject* MouseEventSpy::singletonProvider(QQmlEngine *, QJSEngine *)
    {
        return MouseEventSpy::instance();
    }
    
    // This is the method is necessary for 'installEventFilter'
    bool MouseEventSpy::eventFilter(QObject* watched, QEvent* event)
    {
        QEvent::Type t = event->type();
        if ((t == QEvent::MouseButtonDblClick
             || t == QEvent::MouseButtonPress
             || t == QEvent::MouseButtonRelease
             || t == QEvent::MouseMove)
                && event->spontaneous() // Take only mouse events from outside of Qt
                )
            emit mouseEventDetected();
        return QObject::eventFilter(watched, event);
    }
    
    1. Than you register it as singleton type to QML like this:

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include "mouseeventspy.h"
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        qmlRegisterSingletonType<MouseEventSpy>("MouseEventSpy", 1, 0, "MouseEventSpy", MouseEventSpy::singletonProvider);
    
        // We do this now uppon creation of the first instance.
        // app.installEventFilter(MouseEventSpy::instance());
    
        engine.load(QUrl(QStringLiteral("main.qml")));
    
        return app.exec();
    }
    
    1. Now in QML you can import the instance of the singleton in the necessary files and use the signal, e.g. to reset a Timer

    main.qml

    import QtQuick 2.6
    import QtQuick.Window 2.2
    import MouseEventSpy 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Connections {
            target: MouseEventSpy
            onMouseEventDetected: myTimer.restart()
        }
    
        Timer {
            id: myTimer
            interval: 1000
            onTriggered: console.log('It has been 1 seconds since the last mouse event')
        }
    
        Text {
            anchors.center: parent
            text: myTimer.running ? 'Timer is Running\nMove the mouse to reset'
                                  : 'Move the Mouse to make the timer run again.'
        }
    
    }