Unsolved QGraphicsItem paint event called continually during QWidget::event loop
-
I am creating an application with a QMainWindow, QGraphicsScene and QGraphicsView with many QGraphicsItems within the view. My items' paint() method is being called continually and I only want them to be called when I update an item. I backtraced the item's paint call and here is what is happening:
The main window's QWidget::event calls QWidgetPrivate::drawWidget which calls QWidgetPrivate::sendPaintEvent and that calls QGraphicsView paintEvent. This draws the items in the scene and calls paint on each item. The loop repeats continually. How do I stop this/why is this happening in the first place? I was under the impression that an item's paint event is called during update() or repaint(). I never call these functions.
-
Hi and welcome to devnet,
The paint event should happen when needed (e.g moving a window) or requested (e.g. with update).
However, without seeing your code, it's pretty much impossible to get an idea about what might be going wrong.
-
Hi,
Fair enough. I can't post my exact code for privacy reasons but this is an abstracted version:
mainwindow.hclass MainWindow : public QMainWindow { Q_OBJECT private: Frame* myFrame; };
mainwindow.cpp
MainWindow:: MainWindow( QWidget *parent ) : QMainWindow(parent) { setCentralWidget(myFrame); } bool MainWindow:: event( QEvent *event ) { if ( event->type( ) == QEvent::WindowStateChange ) { if ( this->windowState( ).testFlag( Qt::WindowMinimized ) ) { // Tell other windows emit notifyControl_minimizeApplicationWindow( ); LOG(INFO) << "hiding"; return true; } else if ( this->windowState( ).testFlag( Qt::WindowNoState ) ) { emit notifyControl_restoreApplicationWindow( ); LOG(INFO) << "restoring"; return true; } } return QWidget::event( event ); }
frame.h
class Frame :public QFrame { Q_OBJECT private: QGraphicsView* myGraphicsView; DiagramScene* myScene; };
frame.cpp
Frame:: Frame(MainWindow* parent) { myDiagramScene = new DiagramScene( this ); myGraphicsView = new QGraphicsView( this ); myGraphicsView->setScene( myScene ); }
diagramscene.h
class DiagramScene : public QGraphicsScene { Q_OBJECT private: QVector<Rectangle*> myItems; };
diagramscene.cpp
DiagramScene:: DiagramScene(Frame* parent) : QGraphicsScene(parent) { //create and add items to the scene here }
rectangle.h
class Rectangle : public QGraphicsRectItem { public: int type( ) const Q_DECL_OVERRIDE { return Type; } void updatePosition( ); void paint( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *widget = 0 ) Q_DECL_OVERRIDE; void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); private: QGraphicsLineItem* myLine; };
rectangle.cpp
Rectangle::Rectangle() QGraphicsRectItem() { setLine(); setRectangle(); } void Rectangle:: paint( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * ) { painter->setPen( this->myColor ); painter->setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing); if ( this->isSelected( ) ) { painter->setPen( QPen( Qt::red, 2 ) ); painter->setBrush( QColor( Qt::red ) ); } else { painter->setPen( QPen( Qt::blue, 2) ); painter->setBrush( QColor( Qt::blue ) ); } painter->drawRect( this->rect( ) ); painter->drawLine( this->myLine->line( ) ); }
Hope this helps
Edit - added code tags - p3c0
-
@qtshah While posting the code enclose them within ```
-
Is it normal that you don't use any layout in your
Frame
class ? -
My mistake there is a layout in the Frame class.
QHBoxLayout * layout = new QHBoxLayout; layout->addWidget(myGraphicsView); setLayout(layout);
-
So now it's working as you want ?
-
No the paint method is still being called repeatedly for each rectangle in the scene, which is time consuming. I only want paint to be called when something changes in the rectangle like when it is moved or the color is changed. It may be that I am misunderstanding when the paint method is triggered. Is it supposed to be triggered at every QWidget event? Because that is what seems to be happening right now.
-
One other detail, you are not calling the correct base class implementation of MainWindow.
Also, do you really need Frame ? Looks like you could put the QGraphicsView directly as central widget, no ?
-
No the QFrame is not necessary, we will need to refactor that. I'm not sure what you mean by not calling the correct base class implementation. Are you referring to the QMainWindow constructor?
-
In
MainWindow::event
, you are not callingQMainWindow::event
butQWidget::event
. -
Oh yes that is true. I tried switching it to QMainWindow::event but the result was the same
-
What else are you doing in your application ?
-
We have a palette of icons in a dock widget that the user can add into the scene, copy, paste and delete. Each graphics item has a proxy widget that holds properties for the item. The items are rectangles and lines that connect between them so that moving a rectangle should move the lines that are connected to it. I want the items to update at a reasonable event, for example a mouse release event, instead of repeatedly like they doing now which is causing some lag. Basically I want to install an event filter, but am first trying to understand why all QMainWindow events are triggering a graphics item paint.
-
By proxy do you mean you have QProxyGraphicsProxyWidget ?
-
yes that's it
-
How many are they at once on your scene ?
-
There's no limit to how many can be in the scene and the scenes are likely to get complicated . Most likely there will be between ~10 and ~50 items at a time.
-
Are you still experiencing that if you avoid the proxy widgets ?
-
Yes we are