Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Solved Jitter artefacts when reimplementing drawForeground

    General and Desktop
    qgraphicsview painter maptoscene
    3
    4
    290
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • A
      Another Qt Beginner last edited by Another Qt Beginner

      Hi,
      I'm subclassing QGraphicsView to create a simple widget that supports panning and zooming to cursor position (for now).
      I want to create a vignette like border around the view:

      vignette.png
      So I reimplemented drawForeground, but there are two things I can't find a reason for:

      The polygon I use for the left side isn't drawn where it is suposed to:

      init.png
      ... it fixes itself after the first zooming operation or resizing of the window:

      fine.png
      This might be caused by mapToScene, giving unexpected result (see the qDebug further down)
      But the zooming and resizing works really well. The Panning on the other hand does not. It creates some ugly jittering, repeating the vignette and causing both my cpu and gpu to complain:

      gifDebugging.gif

      mydview.h

      class MyView : public QGraphicsView
      {
      	Q_OBJECT
      public:
      	MyView(QWidget *parent = nullptr);
      	~MyView();
      private:
      	const int maxZoomStepsOut = 10;
      	const int maxZoomStepsIn = -15;
      	int actualZoomStep;
      	
      	// panning
      	void shift(qreal dx, qreal dy);
      	void shift(QPointF point);
      	
      	virtual void mouseMoveEvent(QMouseEvent *event) override;
      	virtual void mousePressEvent(QMouseEvent *event) override;
      	virtual void mouseReleaseEvent(QMouseEvent *event) override;
      	
      	bool isPanning;		// wether view is in a panning progress or not
      	QPoint panningStart;	// startpoint of panning
      	
      	// zooming
      	virtual void wheelEvent(QWheelEvent *event) override;
      	const double zoomFactor = 1.2;
      	
      	// reimplement Foreground for Vignette
      	void drawForeground(QPainter *painter, const QRectF &rect) override;
      	const int vignetteSize = 40;
      	
      	// rects to store widget-coords
      	QPoint *vignetteLeftWidgetPolygon = new QPoint[4];
      	
      	// rects mapped to scene-coords
      	QPointF *vignetteLeftPolygon = new QPointF[4];
      	
      	// initalizes first properties of vignette-items, which won't change
      	void initVignette();
      	
      	// update the rects in scene-coords
      	void updateVignetteSceneCoords();
      	
      	// update vignetteGeometry in widget-coords in case of resize event
      	void updateVignetteGeometry();
      	virtual void resizeEvent(QResizeEvent *event) override;
      };
      

      myview.cpp

      MyView::MyView(QWidget *parent)
      {
      	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
      	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
      	setRenderHint(QPainter::Antialiasing);
      	setResizeAnchor(AnchorViewCenter);
      	
      	actualZoomStep = 0;
      }
      
      void MyView::shift(qreal dx, qreal dy)
      {
      	horizontalScrollBar()->setValue(  horizontalScrollBar()->value() + dx  );
      	verticalScrollBar()->setValue(  verticalScrollBar()->value() + dy  );
      	
      	// remap Vignette coords to Scene
      	updateVignetteSceneCoords();
      }
      
      void MyView::mouseMoveEvent(QMouseEvent *event)
      {
      	if ( isPanning )
      	{	// shift the view
      		shift( -( event->x() - panningStart.x() ),
      		       -( event->y() - panningStart.y() )	);
      		panningStart.setX( event->x() );
      		panningStart.setY( event->y() );
      		return;
      	}
      }
      
      void MyView::mousePressEvent(QMouseEvent *event)
      {
      	if (event->button() == Qt::MiddleButton)
      	{
      		isPanning = true;
      		panningStart.setX(event->x());
      		panningStart.setY(event->y());
      		setCursor(Qt::ClosedHandCursor);
      		event->accept();
      		return;
      	}
      	event->ignore();
      }
      
      void MyView::mouseReleaseEvent(QMouseEvent *event)
      {
      	if (event->button() == Qt::MiddleButton)
      	{
      		isPanning = false;
      		setCursor(Qt::ArrowCursor);
      		event->accept();
      		return;
      	}
      	event->ignore();
      }
      
      void MyView::wheelEvent(QWheelEvent *event)
      {	// if zooming out
      	if (event->delta() < 0) {
      		// if transform is not already at maxZoomStepsOut
      		if (actualZoomStep < maxZoomStepsOut) {
      				
      				scale(1.0 / zoomFactor, 1.0 / zoomFactor);
      				
      				// shift the view, so that the position under 
      				// the ouse cursor stays the same
      				QPointF viewportCenter = viewport()->rect().center();
      				QPointF mousePos = event->position();
      				QPointF diffNow = mousePos - viewportCenter;
      				QPointF diffNext = diffNow * zoomFactor;
      				QPointF diffNowToNext = diffNow - diffNext;
      				actualZoomStep++;
      				
      				shift(diffNowToNext.x(), diffNowToNext.y());		
      		}
      	// if zooming in
      	} else {
      		// if transform is not already at maxZoomStepsIn
      		if (actualZoomStep > maxZoomStepsIn) {	
      		
      				scale(zoomFactor, zoomFactor);
      				
      				// shift the view, so that the position under 
      				// the ouse cursor stays the same
      				QPointF viewportCenter = viewport()->rect().center();
      				QPointF mousePos = event->position();
      				QPointF diffNow = mousePos - viewportCenter;
      				QPointF diffNext = diffNow / zoomFactor;
      				QPointF diffNowToNext = diffNow - diffNext;
      				actualZoomStep--;
      				
      				shift(diffNowToNext.x(), diffNowToNext.y());
      		}
      	}
      }
      
      void MyView::drawForeground(QPainter *painter, const QRectF &rect)
      {
      	painter->setPen(Qt::NoPen);
      	painter->setBrush( QBrush(Qt::gray) );
      	painter->drawPolygon( vignetteLeftPolygon, 4 );
      }
      
      void MyView::initVignette()
      {
      	// init vignetteRects in widget-coordinates
      	updateVignetteGeometry();
      }
      
      void MyView::updateVignetteGeometry() // in case of resize event
      {
      	// update Rects
      	vignetteLeftWidgetPolygon[0] = QPoint(0, 0);
      	vignetteLeftWidgetPolygon[1] = QPoint(vignetteSize, vignetteSize);
      	vignetteLeftWidgetPolygon[2] = QPoint(vignetteSize, height()-vignetteSize);
      	vignetteLeftWidgetPolygon[3] = QPoint(0, height());
      	
      	qDebug() << "\n";
      	qDebug() << "call inside geometry";
      	qDebug() << "Polygon-Widget:" << vignetteLeftWidgetPolygon[0];
      	qDebug() << "Polygon-Widget:" << vignetteLeftWidgetPolygon[1];
      	qDebug() << "Polygon-Widget:" << vignetteLeftWidgetPolygon[2];
      	qDebug() << "Polygon-Widget:" << vignetteLeftWidgetPolygon[3];	
      	
      	// always remap to scene-coords when updating geometry
      	updateVignetteSceneCoords();
      }
      
      void MyView::updateVignetteSceneCoords()
      {
      	// map gradients
      		
      	// map Polygons
      	vignetteLeftPolygon[0] = mapToScene( vignetteLeftWidgetPolygon[0] );
      	vignetteLeftPolygon[1] = mapToScene( vignetteLeftWidgetPolygon[1] );
      	vignetteLeftPolygon[2] = mapToScene( vignetteLeftWidgetPolygon[2] );
      	vignetteLeftPolygon[3] = mapToScene( vignetteLeftWidgetPolygon[3] );
      	
      	qDebug() << "\n";
      	qDebug() << "call inside sceneCoords";
      	qDebug() << "Polygon:" << vignetteLeftPolygon[0];
      	qDebug() << "Polygon:" << vignetteLeftPolygon[1];
      	qDebug() << "Polygon:" << vignetteLeftPolygon[2];
      	qDebug() << "Polygon:" << vignetteLeftPolygon[3];
      }
      
      void MyView::resizeEvent(QResizeEvent *event)
      {
      	updateVignetteGeometry();
      }
      

      qDebug output:

      call inside geometry
      Polygon-Widget: QPoint(0,0)
      Polygon-Widget: QPoint(40,40)
      Polygon-Widget: QPoint(40,560)
      Polygon-Widget: QPoint(0,600)
      call inside sceneCoords
      Polygon: QPointF(-1178,-119)
      Polygon: QPointF(-1138,-79)
      Polygon: QPointF(-1138,441)
      Polygon: QPointF(-1178,481)
      ------------------------------------------------
      call inside geometry
      Polygon-Widget: QPoint(0,0)
      Polygon-Widget: QPoint(40,40)
      Polygon-Widget: QPoint(40,560)
      Polygon-Widget: QPoint(0,600)
      call inside sceneCoords
      Polygon: QPointF(-1258,-179)
      Polygon: QPointF(-1218,-139)
      Polygon: QPointF(-1218,381)
      Polygon: QPointF(-1258,421)
      

      The Debugs are called first at initialization -> here mapToScene() gives weird results
      The 2nd call is when I manuallly rsize the window -> here mapToScene() works fine

      Some things I realized:

      • The zoom function uses shift() as well (to move the scene back to the mouse cursor), but that doesn't cause artifacts.
      • I guess drawForeground() doesn't delete it's items, drawing them on top of each other. That's why my cpu/gpu is running crazy, creating LOTS of items.
      • calling eraseRect() in drawForeground() first only deletes my scene items, the jittering stays.

      Thank you so much for any clues!

      I am on Windows 10 Education version 1803 OS build 17134.1184
      using Qt 5.14.0 and QtCreator 4.11.0

      PS: I created a complete new Project to isolate the problem, this is actually the second version of this thread. I hope this helps a bit.

      PPS: I reduced he code a little (Destructors, includes) and left out main.cpp, mainwindow.h and mainwindow.cpp as those are really short and straight forward in my case.
      But I will post them as well, if someone is interested.

      1 Reply Last reply Reply Quote 1
      • mrjj
        mrjj Lifetime Qt Champion @Another Qt Beginner last edited by

        @Another-Qt-Beginner
        Hi
        Did you try setting
        https://doc.qt.io/qt-5/qgraphicsview.html#ViewportUpdateMode-enum
        to FullViewportUpdate to see if that reduces the issues.

        1 Reply Last reply Reply Quote 2
        • SGaist
          SGaist Lifetime Qt Champion last edited by

          Hi,

          For people to be able to check more effectively, you should post a complete minimal compile example.

          You should also add which version of Windows you are developing for as well as Qt version you are using.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          A 1 Reply Last reply Reply Quote 1
          • A
            Another Qt Beginner @SGaist last edited by

            @SGaist thanks for the advise

            mrjj 1 Reply Last reply Reply Quote 0
            • mrjj
              mrjj Lifetime Qt Champion @Another Qt Beginner last edited by

              @Another-Qt-Beginner
              Hi
              Did you try setting
              https://doc.qt.io/qt-5/qgraphicsview.html#ViewportUpdateMode-enum
              to FullViewportUpdate to see if that reduces the issues.

              1 Reply Last reply Reply Quote 2
              • First post
                Last post