Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Draw in QPainter



  • hi,
    im trying to periodically generate random lines and append them to each other to create a path. i use QPainter

    i'm calling my custom method in the paintEvent

    but all i get is

    QPainter::setPen: Painter not active
    how should i activate it ?

    is there a more suited qt class for what i'm doing

    thanks

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QPainter>
    #include <QTimer>
    #include <QDebug>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    protected:
        void paintEvent(QPaintEvent *event)
        {
            Q_UNUSED(event)
    
            drawLineW(qrand()*100, qrand()*100,(qrand()%10 > 5)?true:false);
    
        }
    
        void drawLineW(int x, int y, bool down){
            qDebug()<<down;
    
            if(down){
                painter->setPen(QPen(Qt::blue, 8, Qt::DashDotLine, Qt::RoundCap));
            }else{
                painter->setPen(QPen(Qt::red, 2, Qt::DashDotLine, Qt::RoundCap));
            }
    
            painter->drawLine(lastX,lastY,x,y);
    
            lastX = x;
            lastY = y;
        }
    
    private:
        int lastX=0;
        int lastY=0;
        QPainter* painter;
        QTimer * drawTimer;
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    
    //________________________________________
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        drawTimer = new QTimer(this);
       
        painter = new QPainter(this);
        
        drawTimer->setInterval(100);
    
        QObject::connect(drawTimer,&QTimer::timeout,[=](){
            update();
        });
    
        drawTimer->start();
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    
    


  • This was 1000 times easier using Graphics View...

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    private:
        int oldX=0;
        int oldY=0;
        Ui::MainWindow *ui;
        QGraphicsScene *scene;
        QTimer *drawTimer;
        QGraphicsLineItem *vect;
    
        QVector <QPoint> path;
    
        int nLine = 0;
    
        void createPath(){
            for(int i=1;i<20;i++){
                  path.append(QPoint(i*10,(i%2)*10));
            }
        }
    };
    //___ cpp
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        scene = new QGraphicsScene(this);
        ui->graphicsView->setScene(scene);
    
        drawTimer = new QTimer(this);
        drawTimer->setInterval(50);
    
        createPath();
    
        QObject::connect(drawTimer,&QTimer::timeout,[=](){
            if(nLine>=path.length()){
    
                drawTimer->stop();
                return;
            }
    
            int newX = path.at(nLine).x();
            int newY = path.at(nLine).y();
    
            vect = scene->addLine(oldX,oldY,newX,newY);
    
            oldX= newX;
            oldY= newY;
    
            nLine++;
    
        });
        drawTimer->start();
    }
    

  • Moderators

    You need to create the painter in the paint event, I think, just like in the docs: https://doc.qt.io/qt-5/qpainter.html



  • hi @sierdzio
    that is the first test thing i did, yes.

    but if i create one QPainter on the stack it is deleted every time

    i want to keep the old path picture and add new line to it


  • Moderators

    @LeLev Why don't you then simply append the new lines to a member QVector and simply call drawLines(vector) inside the paint event?


  • Moderators

    @LeLev said in Draw in QPainter:

    hi @sierdzio
    that is the first test thing i did, yes.

    but if i create one QPainter on the stack it is deleted every time

    i want to keep the old path picture and add new line to it

    Then at the end of paint event, save the painter to a picture (QPixmap). Then in next paint event, first paint the pixmap, then add your new line to it (and save another pixmap).


  • Moderators

    Perhaps you can work around this by using member pointer like you do, but then calling QPainter::begin() in the beginning of your event handler. I'm not sure if it will work, though.



  • @J-Hilk i will try,
    but that means every time you add a new line, you re-draw all the lines in the vector (from the beginning) ?

    @sierdzio
    like this, the first line is drawn and then a crash.

      void paintEvent(QPaintEvent *event)
        {
            Q_UNUSED(event)
            if(!painter->isActive()){
             painter->begin(this);
            }
            drawLineW(qrand()*100, qrand()*100,(qrand()%10 > 5)?true:false);
        }
    

    is there an other type that will not clear the screen, and i can simply add lines ? im not forced to use QPainter


  • Lifetime Qt Champion

    @LeLev said in Draw in QPainter:

    you re-draw all the lines in the vector (from the beginning) ?

    You can draw in a QImage when you add a line and draw the image in paintEvent



  • hi
    @jsulm said in Draw in QPainter:

    You can draw in a QImage

    sorry i can't find methods to draw in a QImage nor in QPaintDevice

    i have to pass the image to my QPainter so i can draw right ?

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QPainter>
    #include <QTimer>
    #include <QVector>
    #include <QDebug>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    protected:
        void paintEvent(QPaintEvent *event)
        {
            Q_UNUSED(event)
            if(!painter->isActive()){
             painter->begin(this);
            }
    
            drawLineW(qrand()*100, qrand()*100,(qrand()%10 > 5)?true:false);
        }
    
        void drawLineW(int x, int y, bool down){
           
            painter->drawLine(lastX,lastY,x,y);
            lastX = x;
            lastY = y;
        }
    
    private:
        int lastX=0;
        int lastY=0;
        //QVector <QPoint> lines; 
        QImage pathImg;
    
        QPainter* painter;
        QTimer * drawTimer;
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    
    //__________________________________
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        drawTimer = new QTimer(this);
    
        painter = new QPainter(&pathImg);
    
        drawTimer->setInterval(100);
    
        QObject::connect(drawTimer,&QTimer::timeout,[=](){
    
            update();
        });
    
        drawTimer->start();
    
    }
    
    

    sorry im struggling so hard now, i can't get this done fo 3 houres.. I m not used to QWidgets at all.
    i did the same thing in qml/js in about 10minutes


  • Moderators

    @LeLev said in Draw in QPainter:

    i have to pass the image to my QPainter so i can draw right ?

    You can use setBackground() or drawPixmap() first, then proceed with your drawLine() call.


  • Lifetime Qt Champion

    @LeLev said in Draw in QPainter:

    sorry i can't find methods to draw in a QImage

    Sorry, it's drawPixmap.

    "i have to pass the image to my QPainter so i can draw right ?" - no, use one of the drawPixmap() methods..



  • @sierdzio

    i call begin() as you suggested
    and i also have to call end() at the end of my method.

    now i can see my randomly generated lines, but every time i add a line the rest of lines are deleted/cleaned.

    @sierdzio said in Draw in QPainter:

    Then at the end of paint event, save the painter to a picture (QPixmap). Then in next paint event, first paint the pixmap

    now i'm trying to do this.
    There is a QPainter::drawPixmap to draw the saved image, but i can't find how to save the QPainter content to a QPixmap first, can you please give me a hint ?



  • almost working, just need to fix the save and reload feature

    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    protected:
        void paintEvent(QPaintEvent *event)
        {
    
            Q_UNUSED(event)
            if(!painter->isActive()){
                painter->begin(this);
            }
    
            drawLineW(qrand()%250, qrand()%250,(qrand()%10 > 5)?true:false);
        }
    
        void drawLineW(int x, int y, bool down){
    
            // load last path/picture
            painter->drawPixmap(0,0,this->height(),this->width(),oldPath);
    
            if(down){
                painter->setPen(QPen(Qt::blue, 3, Qt::DashDotLine, Qt::RoundCap));
            }else{
                painter->setPen(QPen(Qt::red, 3, Qt::DashDotLine, Qt::RoundCap));
            }
    
            painter->drawLine(lastX,lastY,x,y);
            painter->end();
    
            lastX = x;
            lastY = y;
    
            // save current path/picture
            oldPath = QPixmap::fromImage(oldPathImg);
    
        }
    
    private:
        int lastX=0;
        int lastY=0;
    
        QImage oldPathImg;
    
        QPixmap oldPath;
    
        QPainter* painter;
        QTimer * drawTimer;
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    
    
    // ___
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        drawTimer = new QTimer(this);
    
        painter = new QPainter(&oldPathImg);
    
        drawTimer->setInterval(1000);
    
        QObject::connect(drawTimer,&QTimer::timeout,[=](){
            update();
        });
    
        drawTimer->start();
    }
    
    
    


  • This was 1000 times easier using Graphics View...

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    private:
        int oldX=0;
        int oldY=0;
        Ui::MainWindow *ui;
        QGraphicsScene *scene;
        QTimer *drawTimer;
        QGraphicsLineItem *vect;
    
        QVector <QPoint> path;
    
        int nLine = 0;
    
        void createPath(){
            for(int i=1;i<20;i++){
                  path.append(QPoint(i*10,(i%2)*10));
            }
        }
    };
    //___ cpp
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        scene = new QGraphicsScene(this);
        ui->graphicsView->setScene(scene);
    
        drawTimer = new QTimer(this);
        drawTimer->setInterval(50);
    
        createPath();
    
        QObject::connect(drawTimer,&QTimer::timeout,[=](){
            if(nLine>=path.length()){
    
                drawTimer->stop();
                return;
            }
    
            int newX = path.at(nLine).x();
            int newY = path.at(nLine).y();
    
            vect = scene->addLine(oldX,oldY,newX,newY);
    
            oldX= newX;
            oldY= newY;
    
            nLine++;
    
        });
        drawTimer->start();
    }
    

Log in to reply