Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How can I use QPainter to paint on QGraphicsView?
Forum Updated to NodeBB v4.3 + New Features

How can I use QPainter to paint on QGraphicsView?

Scheduled Pinned Locked Moved Unsolved General and Desktop
qpainterqgraphicsviewdraw objectsqgraphicsscene
11 Posts 7 Posters 19.1k Views 4 Watching
  • 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.
  • elmLiuE Offline
    elmLiuE Offline
    elmLiu
    wrote on last edited by
    #1

    I am trying to develop an app to draw stuff like lines, rectangles, and arcs and to adjust them. I know that QGraphicsView Class and QGraphicsScene Class provides methods to generate and manage geometries. But I don't know how can I update my canvas instantly while I'm moving my mouse to draw. Since QGraphicsView Class inherits QWidget Class, I thought that QPainter can be used to do so. But here's error information:

    Starting F:\QtProj\build-scene-Desktop_Qt_5_6_3_MinGW_32bit-Debug\debug\scene.exe...
    QWidget::paintEngine: Should no longer be called
    QPainter::begin: Paint device returned engine == 0, type: 1
    QPainter::setPen: Painter not active
    

    And of course nothing was displayed on the screen.
    Here's myview.h:

    #ifndef CANVASWIDGET_H
    #define CANVASWIDGET_H
    
    #include <QWidget>
    #include <QGraphicsView>
    
    class MyView : public QGraphicsView
    {
        Q_OBJECT
    
    public:
        explicit MyView(QWidget *parent = 0);
        int drawType = 0;
        QPointF pStart, pMove;
        int started = 0;
    
    signals:
    
    private slots:
        void setCursor(int);
        void setType(int);
        void resetPos();
        qreal getLength();
    
    protected:
        void mousePressEvent(QMouseEvent *event);
        void mouseMoveEvent(QMouseEvent *event);
        void paintEvent(QPaintEvent *event);
    };
    
    #endif // CANVASWIDGET_H
    
    

    Here's myview.cpp:

    #include "myview.h"
    #include <QDebug>
    #include <QWidget>
    #include <QGraphicsView>
    #include <QMouseEvent>
    #include <QPainter>
    #include <math.h>
    
    MyView::MyView(QWidget *parent)
    {
    
    }
    void MyView::resetPos(){
        pStart.setX(0.0);  pStart.setY(0.0);
        pMove.setX(0.0);  pMove.setY(0.0);
    }
    qreal MyView::getLength(){
        return sqrt((pStart.x()-pMove.x())*(pStart.x()-pMove.x()) + (pStart.y()-pMove.y())*(pStart.y()-pMove.y()));
    }
    void MyView::setType(int type){
    //    qDebug()<<"scene:"<<type;
        drawType = type;
    }
    void MyView::setCursor(int type){
    //    qDebug()<<"view:"<<type;
    
        if(type == 1 || type == 2 || type == 3){
            viewport()->setCursor(Qt::CrossCursor);
        }
        if(type == 4){
            viewport()->setCursor(Qt::SizeAllCursor);
        }
        if(type == 5){
            viewport()->setCursor(Qt::ArrowCursor);
        }
    }
    void MyView::mousePressEvent(QMouseEvent *event){
        //  Click to draw
            if(drawType==1 || drawType==2 || drawType==3){
                if(!started){
                    pStart = event->localPos();
                    started = 1;
                    setMouseTracking(true);
                }
                if(started){
                        setMouseTracking(false);
                        this->update();
                        started = 0;
                }
            }
    }
    void MyView::mouseMoveEvent(QMouseEvent *event){
        if(drawType == 1 || drawType == 2 || drawType == 3){
            pMove = event->localPos();
        }
    }
    void MyView::paintEvent(QPaintEvent *event){
        QPainter painter(this);
        QPen pen;
        pen.setWidth(1);
        pen.setStyle(Qt::SolidLine);
        painter.setPen(pen);
    
        switch (drawType) {
            case 1: painter.drawLine(pStart, pMove);
                    break;
            case 2: painter.drawRect(QRectF(pStart, pMove));
                    break;
            case 3: pen.setStyle(Qt::DashLine);  painter.setPen(pen);
                    painter.drawLine(pStart, pMove);
                    pen.setStyle(Qt::SolidLine);  painter.setPen(pen);
                    painter.drawEllipse(pStart, getLength(), getLength());
                    break;
        }
    }
    

    Here's mainwindow.cpp:

    #include "mainwindow.h"
    #include "myitem.h"
    #include "myscene.h"
    #include "myview.h"
    #include "ui_mainwindow.h"
    #include <QGraphicsScene>
    #include <QGraphicsView>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        //New MyScene
        MyScene *s = new MyScene;
        //New View
        MyView *v = new MyView;
        v->setScene(s);
        setCentralWidget(v);
    
        QObject::connect(this, SIGNAL(drawType(int)), v, SLOT(setCursor(int)));
        QObject::connect(this, SIGNAL(drawType(int)), s, SLOT(setType(int)));
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_actionDrawLine_triggered()
    {
        emit drawType(1);
        ui->statusBar->showMessage(tr("Type: Line"));
    }
    
    void MainWindow::on_actionDrawRect_triggered()
    {
        emit drawType(2);
        ui->statusBar->showMessage(tr("Type: Rect"));
    }
    
    void MainWindow::on_actionDrawArc_triggered()
    {
        emit drawType(3);
        ui->statusBar->showMessage(tr("Type: Circle"));
    }
    void MainWindow::on_actionDrag_triggered()
    {
        emit drawType(4);
        ui->statusBar->showMessage(tr("Type: Drag"));
    }
    void MainWindow::on_actionClear_triggered()
    {
        emit drawType(5);
        ui->statusBar->showMessage(tr("Type: Clear"));
    }
    

    Many thanks if you can help me!

    1 Reply Last reply
    0
    • mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi
      Normally you would make a custom widget based on QWidget and
      use http://doc.qt.io/qt-5/qgraphicsproxywidget.html
      to have it in scene.
      Drawing directly on the view would make it impossible to have support for
      all the features of moving / zooming etc.

      elmLiuE 1 Reply Last reply
      1
      • mrjjM mrjj

        Hi
        Normally you would make a custom widget based on QWidget and
        use http://doc.qt.io/qt-5/qgraphicsproxywidget.html
        to have it in scene.
        Drawing directly on the view would make it impossible to have support for
        all the features of moving / zooming etc.

        elmLiuE Offline
        elmLiuE Offline
        elmLiu
        wrote on last edited by
        #3

        @mrjj I'd try this. I am sure it would work. Thank you so much!

        1 Reply Last reply
        1
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Hi and welcome to devnet,

          Are you looking for something like the Scribble example ?

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

          1 Reply Last reply
          1
          • BuckwheatB Offline
            BuckwheatB Offline
            Buckwheat
            wrote on last edited by
            #5

            hi @elmLiu and Welcome!

            First of all, if you are going to use a QGraphicsView, always use the QGraphicsScene! I draw and interact with 10000s of elements (lines, arcs, text, points, etc.) and have real-time panning, zooming, and rotation at least 20Hz (I have clocked it at 100Hz) for the moving map! Actual mouse interaction is instantaneous.

            If all you want to do is draw something yourself, derive your own QGraphicsScene override the drawForeground method. This will give you the ability draw anything you desire.

            I use drawForeground to draw things like a site scale, compass rose, vehicle, or a navigation overlay over the linework and surfaces. If you do not want to have widgets as part of your scene, the QGraphicsView accepts and works nicely with layouts (see buttons on the right of the plan screen).

            0_1533326482036_7cb0ec68-9b84-40c5-889a-8faa9fa021c3-image.png

            Don't be afraid!

            Dave Fileccia

            elmLiuE D 2 Replies Last reply
            4
            • BuckwheatB Buckwheat

              hi @elmLiu and Welcome!

              First of all, if you are going to use a QGraphicsView, always use the QGraphicsScene! I draw and interact with 10000s of elements (lines, arcs, text, points, etc.) and have real-time panning, zooming, and rotation at least 20Hz (I have clocked it at 100Hz) for the moving map! Actual mouse interaction is instantaneous.

              If all you want to do is draw something yourself, derive your own QGraphicsScene override the drawForeground method. This will give you the ability draw anything you desire.

              I use drawForeground to draw things like a site scale, compass rose, vehicle, or a navigation overlay over the linework and surfaces. If you do not want to have widgets as part of your scene, the QGraphicsView accepts and works nicely with layouts (see buttons on the right of the plan screen).

              0_1533326482036_7cb0ec68-9b84-40c5-889a-8faa9fa021c3-image.png

              Don't be afraid!

              elmLiuE Offline
              elmLiuE Offline
              elmLiu
              wrote on last edited by
              #6

              @Buckwheat Hi, before trying to use QGraphicsView & QGraphicsScene, I customized QWidget Class as my canvas, setting it central widget, and used mousePressEvent & mouseMoveEvent & mouseReleaseEvent & paintEvent to instantly update my canvas while using mouse to draw geometries, and also, to detect certain points on geometries such as end point & mid point. So far I've made it happen, but I just don't know how to do the same using QGraphicsView & QGraphicsScene.
              Which part can the drawForeground method do? I know that all items are stored in Scene and displayed via View, but in which class or whose method shall I try to instantly update my canvas?
              I'm new to Qt, may you forgive my doubts :)

              1 Reply Last reply
              0
              • BuckwheatB Offline
                BuckwheatB Offline
                Buckwheat
                wrote on last edited by
                #7

                Hi @elmLiu

                I was new to Qt back in 2012. You will find that you will learn to use it quickly. Being older (and hopefully wizened) I find that a lot of people like to build Qt from scratch (why I do not know). I just want to use it and learn it. It makes developing fun in some respects.

                Please derive MyGraphicsScene from QGraphicsScene and override mousePressEvent, mouseMoveEvent, mouseReleaseEvent and if desired, wheelEvent. These will give you the mouse position and the last mouse position in scene coordinates in QGraphicsSceneMouseEvent. From there you can do your magic! I pan, rotate, zoom, select, etc. using

                Again, if you wish to manage your own data and paint it yourself, you can override drawBackground (always is the bottom layer) and drawForeground (always the top layer) and you will have complete control. I do a mixture of both. As I do not want to manage 10000s of items but only the overlays I need.

                Unfortunately, if you want complete control of zooming, you have to jump through hoops to disable the scrolling features. I had to do this because of the nature of my work. I consider this the only problem with QGraphicsView. I will there were two classes: QGraphicsView (no scrolling at all and acts more like a pure painter) and QGraphicsScrollView (this woudl behave like the current).

                Enjoy!

                Dave Fileccia

                elmLiuE 1 Reply Last reply
                3
                • BuckwheatB Buckwheat

                  Hi @elmLiu

                  I was new to Qt back in 2012. You will find that you will learn to use it quickly. Being older (and hopefully wizened) I find that a lot of people like to build Qt from scratch (why I do not know). I just want to use it and learn it. It makes developing fun in some respects.

                  Please derive MyGraphicsScene from QGraphicsScene and override mousePressEvent, mouseMoveEvent, mouseReleaseEvent and if desired, wheelEvent. These will give you the mouse position and the last mouse position in scene coordinates in QGraphicsSceneMouseEvent. From there you can do your magic! I pan, rotate, zoom, select, etc. using

                  Again, if you wish to manage your own data and paint it yourself, you can override drawBackground (always is the bottom layer) and drawForeground (always the top layer) and you will have complete control. I do a mixture of both. As I do not want to manage 10000s of items but only the overlays I need.

                  Unfortunately, if you want complete control of zooming, you have to jump through hoops to disable the scrolling features. I had to do this because of the nature of my work. I consider this the only problem with QGraphicsView. I will there were two classes: QGraphicsView (no scrolling at all and acts more like a pure painter) and QGraphicsScrollView (this woudl behave like the current).

                  Enjoy!

                  elmLiuE Offline
                  elmLiuE Offline
                  elmLiu
                  wrote on last edited by
                  #8

                  @Buckwheat Thanks a lot! By overriding drawForeground method I reimplemented my drawing demand! I'll dig more of QGraphicsScene! : )

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    Michael Scopchanov
                    wrote on last edited by Michael Scopchanov
                    #9

                    In your MyView::paintEvent write QPainter painter(viewport()); instead of QPainter painter(this);.

                    1 Reply Last reply
                    2
                    • M Offline
                      M Offline
                      medyakovvit
                      wrote on last edited by
                      #10

                      For those who still want to draw on a QGraphicsView instead of a viewport. For example when you set margins around the viewport with serViewportMargins() and want to draw something in that free space around the viewport.

                      The quick answer:
                      QPainter painter(this); in paintEvent() - leads to an error 'QWidget::paintEngine: Should no longer be called'.

                      Instead of overriding painteEvent() you need to go with the overriding event() for events with QEvent::Paint type.

                      bool MyGraphicsView::event(QEvent* e)
                      {
                          const bool result = QGraphicsView::event(e);
                      
                          if (e->type() == QEvent::Paint)
                          {
                               QPainter painter(this);
                               // No errors 
                               // paint what you need
                          }
                      
                          return result;
                      }
                      

                      The longer answer:
                      Usually, all events for widgets come to event() function. The type of the event is checked and for example events with QEvent::Paint type are redirected to painterEvent() function, or events with QEvent::Resize type are redirected to resizeEvent() function, and so on.

                      But that works in a different way for QAbstractScrollArea which is the base class for QGraphicsView. If you look into sources of QAbstractScrollArea::event() then you can see that there is some drawing code (btw calling QPainter p(this) with no errors) and the code follows to QFrame::paintEvent() instead of paintEvent(). But when paintEvent() of QGraphicsView is called then? It's called when the viewport widget (child widget) receives QEvent::Paint event.

                      As you can know QWidget is the sub-class of QPaintDevice. But painting in fact can occur not on the widget itself but on so-called "redirected" painting device (probably the top-most parent, see QWidgetPrivate::setRedirected). The usual flow is next:

                      1. set redirected device for the widget
                      2. send paint event for the widget
                      3. handle event in event() and redirect it to paintEvent()
                      4. restore redirected device for the widget (set it to nullptr)

                      So when the paint event is handled in QAbstractScrollArea::event() the redirected device for QGraphicsView is valid and there are no errors when creating and using a painter object.
                      But when QAbstractScrollArea::paintEvent() is called the redirected device of the QGraphicsView is already restored and now it's enabled for the viewport widget instead. So at this moment using
                      QPainter painter(this) causes an errors
                      QPainter painter(viewport()) doesn't cause any errors.

                      I don't think that qt developers made that intentionally so no one can draw on the QGraphicsView. I hope that will help someone:)

                      1 Reply Last reply
                      2
                      • BuckwheatB Buckwheat

                        hi @elmLiu and Welcome!

                        First of all, if you are going to use a QGraphicsView, always use the QGraphicsScene! I draw and interact with 10000s of elements (lines, arcs, text, points, etc.) and have real-time panning, zooming, and rotation at least 20Hz (I have clocked it at 100Hz) for the moving map! Actual mouse interaction is instantaneous.

                        If all you want to do is draw something yourself, derive your own QGraphicsScene override the drawForeground method. This will give you the ability draw anything you desire.

                        I use drawForeground to draw things like a site scale, compass rose, vehicle, or a navigation overlay over the linework and surfaces. If you do not want to have widgets as part of your scene, the QGraphicsView accepts and works nicely with layouts (see buttons on the right of the plan screen).

                        0_1533326482036_7cb0ec68-9b84-40c5-889a-8faa9fa021c3-image.png

                        Don't be afraid!

                        D Offline
                        D Offline
                        dschiller
                        wrote on last edited by dschiller
                        #11

                        @Buckwheat How do you avoid panning and transform ( Zoom ) for those site scale and compass rose ?

                        Found the solution: Use painter.resetTransform().

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved