Jitter artefacts when reimplementing drawForeground
-
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:
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:
... it fixes itself after the first zooming operation or resizing of the window:
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: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 fineSome 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.0PS: 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. -
@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. -
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.
-
@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.