QCustomPlot Discussion and Comments

(Multi) Touch and GesturesReturn to overview

Hello there,

I was looking for a plotting tool, which can use Touch, Multitouch and Gestures. Thanks to QCustomPlot that it is open source!

For all those, who are looking for QCustomPlot with touch functionality, here is my workaround:

1. Create a new value within the private section of QCustomPlot:

	private:
		QPointF currentTouchPointPos;

2. Extend the constructor of QCustomPlot:

	setAttribute( Qt::WA_AcceptTouchEvents );
	grabGesture( Qt::PinchGesture );

3. Overwrite the standard event-handler of QWidget within the QCustomPlot:

	bool QCustomPlot::event( QEvent *event ){
		switch( event->type() ){
			case QEvent::Gesture: {
				QGestureEvent *gestureEve = static_cast<QGestureEvent*>(event);
				if( QGesture *pinch = gestureEve->gesture(Qt::PinchGesture) ){
					QPinchGesture *pinchEve = static_cast<QPinchGesture *>(pinch);
					qreal scaleFactor = pinchEve->totalScaleFactor( );
					if( scaleFactor > 1.0 ){
						scaleFactor *= 10;
					}else{
						scaleFactor *= -10;
					}
					QWheelEvent *wheelEve = new QWheelEvent( currentTouchPointPos, scaleFactor, Qt::NoButton, Qt::NoModifier, Qt::Vertical );
					this->wheelEvent( wheelEve );
				}
				return true;
			}
			case QEvent::TouchBegin:
			case QEvent::TouchUpdate:
			case QEvent::TouchEnd: {
				QTouchEvent *touchEvent = static_cast<QTouchEvent *>( event );
				QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints( );
				if( touchPoints.count( ) == 1 ){
					const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first( );
					currentTouchPointPos = touchPoint0.pos();
					QMouseEvent *mouseEve = new QMouseEvent(QEvent::MouseButtonPress,currentTouchPointPos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
					if( touchEvent->touchPointStates() == (Qt::TouchPointStates)Qt::TouchPointPressed ){
						this->mousePressEvent( mouseEve );
					}else if( touchEvent->touchPointStates() == (Qt::TouchPointStates)Qt::TouchPointMoved ){
						this->mouseMoveEvent( mouseEve );
					}else if( touchEvent->touchPointStates() == (Qt::TouchPointStates)Qt::TouchPointReleased ){
						this->mouseReleaseEvent( mouseEve );
					}
				}
				return true;
			}
			default: {
				break;
			}
		}
		
		return QWidget::event( event );
	}


4. If you don't want to use the wheelevent for the pinch gesture you can also use this code snippet:
	qreal scaleFactor = pinchEve->totalScaleFactor( );
	if( scaleFactor > 1.0 ){
		scaleFactor = fabs(1.0 - scaleFactor) - 1.0;
	}else if( scaleFactor < 1.0 ){
		scaleFactor = fabs(1.0 - scaleFactor) + 1.0;
	}

	if( scaleFactor < 0.98 )
		scaleFactor = 0.98;
	else if( scaleFactor > 1.02 )
		scaleFactor = 1.02;

	this->yAxis->scaleRange( scaleFactor, this->yAxis->range().center());
	this->xAxis->scaleRange( scaleFactor, this->xAxis->range().center());
	this->replot();

Hopefully that helps some people to get into it, because I've found nothing in the forum/comments about using QCustomPlot with touch.

Other ideas are welcome at any time.

Greetings,
Mike

I forgot to remind, that all functions based on setInteractions() of QCustomPlot because I'm using the default events.

5. If you include another scroll/pinch algorithm without the usage of the wheel-event you should embrace your code with the interactions-flag:

if( QGesture *pinch = gestureEve->gesture(Qt::PinchGesture) ){
	if( this->interactions().testFlag(QCP::iRangeZoom) ){
	}
}

Mike,
Thanks for the code! I am also looking at implementing qcustomplot on touch and wondering if others would be looking to do so. Might be a nice feature add in a future release.

I don't care as much about pinch to zoom as I do tap and hold for right clicks, but I will post my code here once I am done.

I am stuck, trying to figure out how to trigger the context menu with a press and hold.

I added the code:

            else if( QGesture *tapAndHold = gestureEve->gesture(Qt::TapAndHoldGesture) )
            {
                QMouseEvent *mouseEve = new QMouseEvent(QEvent::MouseButtonPress,currentTouchPointPos,Qt::RightButton,Qt::RightButton,Qt::NoModifier);
                this->mousePressEvent( mouseEve );
            }

to try and trigger the context menu, and the code is executed but the the context menu slot is not called.

I also tried:

            else if( QGesture *tapAndHold = gestureEve->gesture(Qt::TapAndHoldGesture) )
            {
                QContextMenuEvent *menuEvent = new QContextMenuEvent (QContextMenuEvent::Mouse,currentTouchPointPos.toPoint());
                this->contextMenuEvent( menuEvent );
            }

Tried the code above in my MainWindow code with the same results.

Tried the AA_SynthesizeMouseForUnhandledTouchEvents attribute, didn't help.

Any ideas or suggestions? I am building for iOS using Qt 5.4 if that makes any difference.

Hi, I have make some modifications for 1 touch to have fluid move of my QCPplot but for zoom it's not working fine, it's not fluid. Can you help me ?

bool CustomPlot::event(QEvent* event)
{
    if(event->type() == QEvent::TouchBegin ||
            event->type() == QEvent::TouchUpdate ||
            event->type() == QEvent::TouchEnd ){
        QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
        QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();

        //if(touchPoints.count() == 1 && touchEvent->touchPointStates().testFlag(Qt::TouchPointReleased))
        //    _release2touch = false;

        if (touchPoints.count() == 1 && !_release2touch)
        {
            switch (event->type()) {
            case QEvent::TouchBegin:
            {
                QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
                QTouchEvent::TouchPoint touchPoints = touchEvent->touchPoints().first();
                QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress,
                                                 touchPoints.pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);

                mousePress(e); // ==> meilleure methode
            }break;

            case QEvent::TouchUpdate:
            {
                QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
                QTouchEvent::TouchPoint touchPoints = touchEvent->touchPoints().first();
                QMouseEvent *e = new QMouseEvent(QEvent::MouseMove,
                                                 touchPoints.pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);

                mouseMove(e);
            }break;

            case QEvent::TouchEnd:{
                QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
                QTouchEvent::TouchPoint touchPoints = touchEvent->touchPoints().first();
                QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonRelease,
                                                 touchPoints.pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);

                mouseRelease(e);
            }break;

            default:
                break;
            }
        }else if (touchPoints.count() == 2) {
            _release2touch = true;
            // determine scale factor
            const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
            const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.at(1);//   touchPoints.last();
            double currentScaleFactor =
                    QLineF(touchPoint0.pos(), touchPoint1.pos()).length() /
                    QLineF(touchPoint0.lastPos(), touchPoint1.lastPos()).length();

            QPointF centreZoom = QPointF((touchPoint0.pos().x()+ touchPoint1.pos().x())/2 ,
                                         (touchPoint0.pos().y()+ touchPoint1.pos().y())/2);
            QPointF lastCenterZoom = QPointF((touchPoint0.lastPos().x()+ touchPoint1.lastPos().x())/2 ,
                                             (touchPoint0.lastPos().y()+ touchPoint1.lastPos().y())/2);

            if (touchEvent->touchPointStates().testFlag(Qt::TouchPointReleased))
            {
                currentScaleFactor = 1;
                _release2touch = false;
            }

            if(currentScaleFactor<1)
                currentScaleFactor = currentScaleFactor + (1-currentScaleFactor)/8;
            else
                currentScaleFactor = currentScaleFactor+(currentScaleFactor-1)/8;

            currentScaleFactor =1/currentScaleFactor;

            double diffX = this->xAxis->pixelToCoord(lastCenterZoom.x())
                    - this->xAxis->pixelToCoord(centreZoom.x());

            double diffY = this->yAxis->pixelToCoord(lastCenterZoom.y())
                    - this->yAxis->pixelToCoord(centreZoom.y());

            emit merde(diffX,diffY);

            if(!touchEvent->touchPointStates().testFlag(Qt::TouchPointReleased)){
                this->xAxis->moveRange(diffX);
                this->yAxis->moveRange(diffY);
                this->xAxis->scaleRange(currentScaleFactor,this->xAxis->pixelToCoord((double)centreZoom.x()));
                this->yAxis->scaleRange(currentScaleFactor,this->yAxis->pixelToCoord((double)centreZoom.y()));
                //this->replot();
            }
        }

        return true;
    }
    return QWidget::event(event);
}

Thank a lot for your code, Mike.
It works perfect except one think. For me works better
scaleFactor = -10 / scaleFactor;
instead of
scaleFactor *= -10;

I also used
pinchEve->lastCenterPoint()
instead of
QPointF currentTouchPointPos;

The behavior is a little bit different, but there have not to be added a member variable.

we can use zooming event only for mouse event if i want to use zooming for touch screen how can i do?