How to draw over an old picture in QGraphicsScene



  • Hi!
    I draw in QGraphicsScene
    If I draw not transparent color, then I can draw on top of the old picture. If I draw with a transparent color, the non-transparent color is not visible. How to erase an old drawing when drawing?

    #include "paintscene.h"
    
    
    paintScene::paintScene(QObject *parent) : QGraphicsScene(parent)
    {
        //myQGraphicsScene = new QGraphicsView();
        //scene = new paintScene();       // Инициализируем графическую сцену
    
        //Pen
        myPen.setWidth(20);
        myPen.setColor(Qt::green);
        //myPen.setColor(QColor(255, 0, 0, 10));
        myPen.setCapStyle(Qt::RoundCap);
        myPen.setStyle(Qt::SolidLine);
    
    }
    
    paintScene::~paintScene()
    {
    
    }
    
    void paintScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        // При нажатии кнопки мыши отрисовываем эллипс
        addEllipse(event->scenePos().x() -  myPen.width()/2,
                   event->scenePos().y() -  myPen.width()/2,
                   myPen.width(),
                    myPen.width(),
                   QPen(Qt::NoPen),
                   QBrush(myPen.color()));
        // Сохраняем координаты точки нажатия
        previousPoint = event->scenePos();
        buttonIsPressed = true;
    }
    
    void paintScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
        if (buttonIsPressed)
        {
            // Отрисовываем линии с использованием предыдущей координаты
            addLine(previousPoint.x(),
                    previousPoint.y(),
                    event->scenePos().x(),
                    event->scenePos().y(),
                    myPen);
            // Обновляем данные о предыдущей координате
            previousPoint = event->scenePos();
        }
    }
    
    void paintScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    {
        buttonIsPressed = false;
    }
    
    


  • Perhaps you need to draw not vector and pixel. But how to do it?



  • Tried to make so , but I receive an error : QPainter::drawPoints: Painter not active. Please tell me how to fix it

    void paintScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        myPainter.setPen(myPen);
        myPainter.drawPoint(event->scenePos().x(),event->scenePos().y());
     
    }
    

  • Qt Champions 2018

    @Mikeeeeee said in How to draw over an old picture in QGraphicsScene:

    Please tell me how to fix it

    Don't use painter in mousePressEvent, use it in paintEvent.
    In mousePressEvent you only store needed information (x/y) which you then use in paintEvent.
    Also, create local QPainter instance in paintEvent.



  • Did so, but the picture is not drawn.

    
    void paintScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        x = event->scenePos().x();
        y = event->scenePos().x();
    }
    
    void paintScene::paintEvent(QPaintEvent *event)
    {
        QPainter myPainter; 
        myPainter.setPen(myPen);
        myPainter.drawPoint(x,y);
    }
    
    

    void paintScene::paintEvent(QPaintEvent *event) does not work.



  • and if I prescribe void paintEvent(QPaintEvent *event); to the main class, it automatically fires 1 time when the application is loaded, 1 time when the left mouse button is pressed and no longer works.



  • I did it in the main class. but get the same error:
    QPainter::setPen: Painter not active
    QPainter::drawPoints: Painter not active

    void Paint::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event);
        QPen myPen;
        myPen.setWidth(20);
        myPen.setColor(Qt::green);
        //myPen.setColor(QColor(255, 0, 0, 10));
        myPen.setCapStyle(Qt::RoundCap);
        myPen.setStyle(Qt::SolidLine);
    
        QPainter myPainter; // Создаём объект отрисовщика
        myPainter.setPen(myPen);
        myPainter.drawPoint(scene->x,scene->y);
        myPainter.drawEllipse(scene->x, scene->y, scene->x+50, scene->x+50);
        qDebug()<<scene->x<<scene->y;
    }
    

  • Lifetime Qt Champion

    @Mikeeeeee said in How to draw over an old picture in QGraphicsScene:

    QPainter myPainter; // Со

    it should be
    QPainter myPainter(this);



  • If I do so in a class inherited from QGraphicsScene, I get an error: no matching constructor for initialization of 'QPainter'
    and if I prescribe void paintEvent(QPaintEvent *event); to the main class, it automatically fires 1 time when the application is loaded, 1 time when the left mouse button is pressed and no longer works.


  • Lifetime Qt Champion

    @Mikeeeeee
    QGraphicsScene do not have paintEvent so that is why its not working.
    Normally one would use a custom widget and simply insert into scene.



  • Made such class.
    How to add x and y coordinates?
    How to call void widgetForPainter::paintEvent(QPaintEvent *event) when clicking on a widget?

    .h

    #ifndef WIDGETFORPAINTER_H
    #define WIDGETFORPAINTER_H
    
    #include "QWidget"
    #include "QPainter"
    #include "QPen"
    #include "QDebug"
    
    class widgetForPainter: public QWidget
    {
    public:
        widgetForPainter();
        ~widgetForPainter() {;};
        QPen myPen;
        int x,y;
    
    
        private slots:
        void paintEvent(QPaintEvent *event);
    
    public slots:
        void paintPoint(int x1,int y1);
    
    };
    
    #endif // WIDGETFORPAINTER_H
    
    

    .cpp

    #include "widgetforpainter.h"
    
    widgetForPainter::widgetForPainter()
    {
        //Pen
        myPen.setWidth(20);
        myPen.setColor(Qt::green);
        //myPen.setColor(QColor(255, 0, 0, 10));
        myPen.setCapStyle(Qt::RoundCap);
        myPen.setStyle(Qt::SolidLine);
    }
    
    void widgetForPainter::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event);
        QPainter myPainter; // Создаём объект отрисовщика
        myPainter.setPen(myPen);
        myPainter.drawPoint(x,y);
        myPainter.drawEllipse(x, y, x+50, x+50);
        qDebug()<<x<<y;
    }
    
    void widgetForPainter::paintPoint(int x1, int y1)
    {
        QPainter myPainter; // Создаём объект отрисовщика
        myPainter.setPen(myPen);
        myPainter.drawPoint(x1,y1);
        myPainter.drawEllipse(x1, y1, x+50, x+50);
        qDebug()<<x1<<y1;
    }
    

    In the constructor of the main class did:

    newGraphicsView =  new graphicsView();
        newGraphicsView->setScene(scene);    
        
        ui->verticalLayout->addWidget(newGraphicsView);
    
    
        widgetForPainter *myWidgetForPainter = new widgetForPainter();
        ui->verticalLayout->addWidget(myWidgetForPainter);
    

    Probably in the paint void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) need to add widget For void Painter::paintPoint(int x1, int y1) but how to do it?



  • This example works. Could you help me with the right code in my project.


  • Qt Champions 2018

    @Mikeeeeee said in How to draw over an old picture in QGraphicsScene:

    How to add x and y coordinates?

    Come on, in the same way: override mousePressEvent as before.



  • Found such class. Is it possible to make it not the main class?
    .h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include <QPainter>
    #include <QMouseEvent>
    #include <QEvent>
    #include <QFileDialog>
    #include <QImage>
    #include <QPixmap>
    
    namespace Ui {
    class Widget;
    }
    
    class Widget : public QWidget
    {
        Q_OBJECT
    public:
        explicit Widget(QWidget *parent = 0);
        ~Widget();
        bool eventFilter(QObject *obj, QEvent *event);
    
    private slots:
        void on_open_clicked();
    
        void on_save_clicked();
    
        void on_R_valueChanged(int value);
    
        void on_G_valueChanged(int value);
    
        void on_B_valueChanged(int value);
    
        void on_pen_dial_valueChanged(int value);
    
        void on_pushButton_8_clicked();
    
    private:
        QPen pen;
        QPen pen2;
        QPixmap pixmap;
        QPixmap pixman;
        QPoint lastPoint;
        int r;
        int g;
        int b;
        Ui::Widget *ui;
    };
    
    #endif // WIDGET_H
    
    

    .cpp

    #include "widget.h"
    #include "ui_widget.h"
    
    
    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
        pixmap = QPixmap(ui->label->width(),ui->label->height());
        pixmap.fill();
        pen = QPen( QBrush( Qt::black), 5.0f );
        r = 0;
        g = 0;
        b = 0;
    
        ui->label->setPixmap(pixmap);
    
        ui->label->installEventFilter(this);
        ui->red_color->installEventFilter(this);
        ui->orange_color->installEventFilter(this);
        ui->yellow_color->installEventFilter(this);
        ui->green_color->installEventFilter(this);
        ui->blue_color->installEventFilter(this);
        ui->purple_color->installEventFilter(this);
        ui->pushButton_8->installEventFilter(this);
        ui->R->installEventFilter(this);
        ui->G->installEventFilter(this);
        ui->B->installEventFilter(this);
        ui->setcolor->installEventFilter(this);
        ui->pen_dial->installEventFilter(this);
        ui->clear_pixmap->installEventFilter(this);
    
    
        ui->red_color->setStyleSheet("Background-color: red;");
        ui->orange_color->setStyleSheet("Background-color: #ffa500");
        ui->yellow_color->setStyleSheet("Background-color: yellow");
        ui->green_color->setStyleSheet("Background-color: green");
        ui->blue_color->setStyleSheet("Background-color: blue");
        ui->purple_color->setStyleSheet("Background-color: #C400AB");
    
        pen2 = QPen(QBrush(Qt::black), 61.0f);
        QBrush st(QColor(Qt::black),Qt::Dense7Pattern);
        pixman = QPixmap(ui->label_3->width(),ui->label_3->height());
        pixman.fill();
        QPainter p2(&pixman);
        p2.setPen(Qt::SolidLine);
        p2.setBrush(st);
        p2.drawRect(0,0,ui->label_3->width(),ui->label_3->height());
        p2.end();
        ui->label_3->setPixmap(pixman);
        
        //this->setFixedSize(this->width(), this->height());
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    bool Widget::eventFilter(QObject *obj, QEvent *event)
    {
        if(obj == ui->label and event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent *mous = (QMouseEvent*)event;
            QPainter p(&pixmap);
            p.setPen(pen);
            p.drawPoint(mous->pos());
            p.end();
            lastPoint = mous->pos();
            ui->label->setPixmap(pixmap);
        }
        if(obj == ui->label and event->type() == QEvent::MouseMove)
        {
            QMouseEvent *mous = (QMouseEvent*)event;
            QPainter p(&pixmap);
            p.setPen(pen);
            p.drawLine(lastPoint,mous->pos());
            p.end();
            lastPoint = mous->pos();
            ui->label->setPixmap(pixmap);
        }
        if(obj == ui->red_color and event->type() == QEvent::MouseButtonPress) pen.setColor(Qt::red);
        if(obj == ui->orange_color and event->type() == QEvent::MouseButtonPress) pen.setColor(QColor(255, 128, 0));
        if(obj == ui->yellow_color and event->type() == QEvent::MouseButtonPress) pen.setColor(Qt::yellow);
        if(obj == ui->green_color and event->type() == QEvent::MouseButtonPress) pen.setColor(Qt::green);
        if(obj == ui->blue_color and event->type() == QEvent::MouseButtonPress) pen.setColor(Qt::blue);
        if(obj == ui->purple_color and event->type() == QEvent::MouseButtonPress) pen.setColor(QColor(196, 0, 171));
        if(obj == ui->setcolor and event->type() == QEvent::MouseButtonPress)
        {
            bool ok = true;
            r = ui->lineEdit->text().toInt(&ok,10);
            g = ui->lineEdit_2->text().toInt(&ok,10);
            b = ui->lineEdit_3->text().toInt(&ok,10);
            if((r <= 255 or g <= 255 or b <= 255)) pen.setColor(QColor(r,g,b));
            else ui->info->text() = "Введены неверные значения";
        }
        if(obj == ui->pen_dial and event->type() == QEvent::MouseButtonPress and QEvent::MouseMove)
        {
        }
        if(obj == ui->clear_pixmap and event->type() == QEvent::MouseButtonPress)
        {
            pixmap.fill(QColor(Qt::white));
            ui->label->setPixmap(pixmap);
        }
        if(obj == ui->label_3 and event->type() == QEvent::MouseButtonPress)
        {
            pen.setBrush(Qt::Dense5Pattern);
            ui->label->setPixmap(pixmap);
        }
    }
    
    void Widget::on_open_clicked()
    {
        QString file;
        file = QFileDialog::getOpenFileName(this,tr("Открыть файл"),tr("Картинка.jpg"),tr("JPG (*.jpg)"));
        pixmap.load(file);
    }
    
    void Widget::on_save_clicked()
    {
        QString file;
        file = QFileDialog::getSaveFileName(this,tr("Сохранить файл"),tr("Картинка.jpg"),tr("JPG (*.jpg"));
        pixmap.save(file);
    }
    
    void Widget::on_R_valueChanged(int value)
    {
        pen.setColor(QColor(value,ui->B->value(),ui->G->value()));
        ui->Value_1->setNum(value);
    }
    
    void Widget::on_G_valueChanged(int value)
    {
        pen.setColor(QColor(ui->R->value(),value,ui->G->value()));
        ui->Value_2->setNum(value);
    }
    
    void Widget::on_B_valueChanged(int value)
    {
        pen.setColor(QColor(ui->R->value(),ui->B->value(),value));
        ui->Value_3->setNum(value);
    }
    
    void Widget::on_pen_dial_valueChanged(int value)
    {
        pen.setWidthF(value);
        ui->label_2->setNum(value);
    }
    
    void Widget::on_pushButton_8_clicked()
    {
    
    }
    
    

  • Qt Champions 2018

    @Mikeeeeee said in How to draw over an old picture in QGraphicsScene:

    Is it possible to make it not the main class?

    I don't understand this question.
    What is a "main class"?



  • I have a MainWindow class, class for QLabel the ImageViewer. How to use the widget class code to draw in ImageViewer?


  • Qt Champions 2018

    @Mikeeeeee said in How to draw over an old picture in QGraphicsScene:

    How to use the widget class code to draw in ImageViewer?

    Like any other widget



  • But how? Do I have to transfer the code of their Widget class to the MainWindow class ?


  • Qt Champions 2018

    @Mikeeeeee You have to include the header file of that widget in your MainWindow, C++ basics.



  • It's not that simple. The class addresses .ui . I didn't create the right label in the designer. And main.cpp I have not created a class object and cannot access "this".


  • Qt Champions 2018

    @Mikeeeeee Then change the code of that class, so it does not use .ui file (add needed widgets manually).
    "And main.cpp I have not created a class object and cannot access "this"" - well, you have to create an instance of the widget, how else do you want to use it?



  • I decided to transfer the functionality of the class to the class "MainWindow". The "Image Viewer" class has the function "bool Image Viewer::eventFilter(QObject *obj, QEvent *event)". In the class "MainWindow" I need a function to draw "bool eventFilter(QObject obj, QEvent event);" if I add it I get an error: "undefined reference to `MainWindow::eventFilter(QObject, QEvent)'". How can this be corrected?


  • Qt Champions 2018

    @Mikeeeeee said in How to draw over an old picture in QGraphicsScene:

    How can this be corrected?

    Well, you have to define MainWindow::eventFilter(QObject, QEvent)...



  • I append 2 images on QLabel

        QImage mapImage(":/Images/Images/mapMain.png");
        myImageViewer->setImage(mapImage);
        QImage base(":/Images/Images/mapMain.png"); // set to some file/size
        QImage overlay(":/Images/Images/mapTop.png");  // set to some file/size
        QPainter paint(&base);
        paint.drawImage(0,0,overlay);
        myImageViewer->imageLabel->setPixmap(QPixmap::fromImage(base));
        ui->verticalLayout->addWidget(myImageViewer);
    
        //Pen
        myPen.setWidth(20);
        myPen.setColor(Qt::green);
        //myPen.setColor(QColor(255, 0, 0, 10));
        myPen.setCapStyle(Qt::RoundCap);
        myPen.setStyle(Qt::SolidLine);
        myImageViewer->installEventFilter(this);
    

    In the Mainwindow constructor, I wrote:

        QPixmap myPixmap;
        QPen myPen;
        QPoint lastPoint;
        bool eventFilter(QObject *obj, QEvent *event);
    

    And wrote such eventFilter:

    bool MainWindow::eventFilter(QObject *obj, QEvent *event)
    {
        qDebug()<<"eventFilter";
        if(obj == myImageViewer and event->type() == QEvent::MouseButtonPress)
        {
            qDebug()<<" QEvent::MouseButtonPress";
            QMouseEvent *mous = (QMouseEvent*)event;
            QPainter p(&myPixmap);
            p.setPen(myPen);
            p.drawPoint(mous->pos());
            p.end();
            lastPoint = mous->pos();
            myImageViewer->imageLabel->setPixmap(myPixmap);
        }
        if(obj == myImageViewer and event->type() == QEvent::MouseMove)
        {
            QMouseEvent *mous = (QMouseEvent*)event;
            QPainter p(&myPixmap);
            p.setPen(myPen);
            p.drawLine(lastPoint,mous->pos());
            p.end();
            lastPoint = mous->pos();
            myImageViewer->imageLabel->setPixmap(myPixmap);
        }
    }
    

    When eventfilter is triggered, the picture disappears and a white screen appears. And Qt gives an error message:

    QEvent::MouseButtonPress
    QPainter::begin: Paint device returned engine == 0, type: 2
    QPainter::setPen: Painter not active
    QPainter::drawPoints: Painter not active
    QPainter::end: Painter not active, aborted


  • Qt Champions 2018

    @Mikeeeeee Why do you paint in eventFilter?!
    You already were told to paint in paintEvent.
    In eventFilter you only store needed information (like x/y).



  • But in the example that I showed you drawing eventfilter occurs, and if you do so, then paintEvent works only a couple of times and then for some reason no longer works.

    void MainWindow::paintEvent(QPaintEvent *event)
    {
        qDebug()<<"paintEvent";
    }
    

  • Qt Champions 2018

    @Mikeeeeee After getting new coordinates you can call https://doc.qt.io/qt-5/qwidget.html#update to trigger paint event.



  • It turns out to draw in eventFilter, but it was necessary to add this line to the constructor.

    myPixmap  = QPixmap(myImageViewer->image.width(),myImageViewer->image.height());
    

    I draw so:

    bool MainWindow::eventFilter(QObject *obj, QEvent *event)
    {
        if(obj == myImageViewer and event->type() == QEvent::MouseButtonPress)
        {
            qDebug()<<" QEvent::MouseButtonPress";
            QMouseEvent *mous = (QMouseEvent*)event;
            QPainter p(&myPixmap);
            p.setPen(myPen);
            p.drawPoint(mous->pos());
            p.end();
            lastPoint = mous->pos();
            myImageViewer->imageLabel->setPixmap(myPixmap);
        }
    }
    
    

    But when drawing the picture disappears and the screen turns black. How to fix? Can I set the drawing on the QImage overlay(":/Images/Images/mapTop.png");?



  • And if I do so, the screen becomes white.

    myPixmap.fill();
    

    How to make myPixmap transparent?


  • Lifetime Qt Champion

    myPixmap.fill(Qt::transparent);



  • Thanks. Do so:

        myPixmap  = QPixmap(myImageViewer->image.width(),myImageViewer->image.height());
        myPixmap.fill(Qt::transparent);
        myPixmap = QPixmap(":/Images/Images/mapTop.png");
    

    But after drawing the picture disappears :

    myImageViewer->setImage(mapImage);
    

    How to get myPixmap the images:":/Images/Images/mapMain.png" and ":/Images/Images/mapTop.png" , but draw only on:
    ":/Images/Images/mapTop.png"?


  • Lifetime Qt Champion

    @Mikeeeeee
    You mean draw myPixmap on mapImage and then
    show the combined image with myImageViewer->setImage(mapImage); ?
    Thats like before where we combined them.



  • This post is deleted!


  • Yes.The background will be mapImage, and I want to draw on it, with the ability to erase and make transparent Qpixmap. You may need to use: void QPixmap::setMask(const Bitmap &mask)



  • Tried to do so, but got an error: reference to type 'const QBitmap' could not bind to an lvalue of type 'QBitmap *'

        QBitmap *myQBitmap = new QBitmap();
        myQBitmap->fromImage(mapImage);
        myPixmap.setMask(myQBitmap);
    

  • Lifetime Qt Champion

    @Mikeeeeee Please, read the documentation.

    QBitmap::fromImage is a static method. You're using it wrong.
    QPixmap::setMask takes a const reference to a QBitmap, not a pointer.



  • But how to set 2 images and draw on top image?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.