0
votes

I am trying something that would seem quite easy, but am struggling a bit with it. I want to be able to draw rectangles in a GUI. What I am doing now is:

  • I create a GUI with Qt Designer, in which I include a QGraphicsView.

  • In my main window I create a myGraphicsScene (class derived from QGraphicsScene, but with mouse press, move and release events overridden) and I set the scene to the QGraphicsView created in the UI.

  • The problem is that I am not able to properly control the refresh and update of the view when I change the scene.

Here is the relevant part of the code:

MainGUIWindow constructor and initialization:

MainGUIWindow::MainGUIWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainGUIWindow)
{

    ui->setupUi(this);
    _init();
}

void MainGUIWindow::_init()
{

    scene = new myGraphicsScene(ui->frame_drawing);
    scene->setSceneRect(QRectF(QPointF(-100, 100), QSizeF(200, 200)));

    ui->graphicsView->setScene(scene);
    QRect rect(10, 20, 80, 60);
    scene->addText("Hello world!");
    scene->addRect(rect, QPen(Qt::black), QBrush(Qt::blue));
}

I can perfectly see the HelloWorld text and this rectangle. However when I start with clicking events, I don't properly get updates anymore.

myGraphicsScene class header:

#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H

#include <QGraphicsScene>

class QGraphicsSceneMouseEvent;
class QPointF;
class QColor;

class myGraphicsScene : public QGraphicsScene
{
    Q_OBJECT

public:
    explicit myGraphicsScene(QObject *parent = 0);

public slots:

signals:

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;

private:
    QPen* pen;
    QBrush* brush;
    QRect* rect;
    QPoint* p1;
    QPoint* p2;

    bool firstClick;

    // bool startedRect;
};

#endif

myGraphicsScene class implementation:

#include "myGraphicsScene.h"

#include <QGraphicsSceneMouseEvent>
#include <QRect>

myGraphicsScene::myGraphicsScene(QObject *parent)
    : QGraphicsScene(parent)
{
    pen = new QPen(Qt::black);
    brush = new QBrush(Qt::blue);
    rect = 0;
    // startedRect = false;
    firstClick = true;
}


void myGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (mouseEvent->button() != Qt::LeftButton)
        return;


    // rect = new QRect((mouseEvent->scenePos()).toPoint(), (mouseEvent->scenePos()).toPoint());
    // addRect(*rect, *pen, *brush);
    // startedRect = true;

    if(firstClick)
    {
        p1 = new QPoint((mouseEvent->scenePos()).toPoint());
        QRect tmp_rect(*p1, *p1);
        addRect(tmp_rect, *pen, *brush);
    }
    else
    {
        p2 = new QPoint((mouseEvent->scenePos()).toPoint());
        QRect tmp_rect(*p2, *p2);
        addRect(tmp_rect, *pen, *brush);
    }
    QGraphicsScene::mousePressEvent(mouseEvent);
}

void myGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    // if(startedRect)
    // {
    //     rect->moveBottomRight((mouseEvent->scenePos()).toPoint());
    //     qDebug("Mouse Position: %d, %d", (mouseEvent->scenePos()).toPoint().x(), (mouseEvent->scenePos()).toPoint().y());
    //     qDebug("Rectangle BottomRight Position: %d, %d", rect->bottomRight().x(), rect->bottomRight().y());
    // }
    QGraphicsScene::mouseMoveEvent(mouseEvent);
}

void myGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{

    if (mouseEvent->button() != Qt::LeftButton)
        return;
    // rect = 0;
    // startedRect = false;
    if(firstClick)
    {
        firstClick = false;
    }
    else
    {
        rect = new QRect(*p1, *p2);
        addRect(*rect, *pen, *brush);
        p1 = 0;
        p2 = 0;
        rect = 0;
        firstClick = true;
    }
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}

My original idea was to draw the rectangles in a drag and drop fashion, but in the end ended up trying the two-clicks approach, which worked a bit better but still doesn't completely update the view when it should.

I am new to Qt, so I am not very familiar with how it should be done. Any help would be appreciated, since I have been a bit stuck here for some time now.

Thanks!

3
First of all, store QPen, QBrush, etc. by value. It'll make the code much easier to read and maintain. Secondly, don't store rect nor p1 nor p2: you don't need them. The items know where they are and you can always refer to the items themselves. You will want to keep the items by value as well - just hide them before you need them visible.Kuba hasn't forgotten Monica

3 Answers

3
votes

In each of your mouse*Event() overloads, call QGraphicsScene::update() method. It will schedule redraw on the view.

http://doc.qt.io/qt-5/qgraphicsscene.html#update

1
votes

I just found the solution: I had to actually change the item added to the scene, and not the rectangle. So, now, my code looks like:

#include "myGraphicsScene.h"

#include <QGraphicsSceneMouseEvent>
#include <QRect>
#include <QGraphicsRectItem>

myGraphicsScene::myGraphicsScene(QObject *parent)
    : QGraphicsScene(parent)
{
    pen = new QPen(Qt::black);
    brush = new QBrush(Qt::blue);

    tmp_rect = 0;
    startedRect = false;
    // firstClick = true;
}


void myGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (mouseEvent->button() != Qt::LeftButton)
        return;


    // Drag and drop approach

    startedRect = true;
    p1 = new QPointF(mouseEvent->scenePos());
    tmp_rect = new QRectF(*p1, *p1);
    // addRect(*tmp_rect, *pen, *brush);
    tmp_rect_item = new QGraphicsRectItem(*tmp_rect);
    rectangles.push_back(tmp_rect_item);
    addItem(rectangles.back());

    // Two-clicks approach
    // if(firstClick)
    // {
    //     p1 = new QPointF(mouseEvent->scenePos());
    //     tmp_rect_item = addRect(QRect(p1->toPoint(), p1->toPoint()), *pen, *brush); //save it to remove it after
    // }
    // else
    // {
    //     p2 = new QPointF(mouseEvent->scenePos());
    //     // QRect tmp_rect(*p2, *p2);
    //     // addRect(tmp_rect, *pen, *brush);
    // }


    update();
    QGraphicsScene::mousePressEvent(mouseEvent);
}

void myGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if(startedRect)
    {
        tmp_rect_item->setRect(QRectF(*p1, mouseEvent->scenePos()));
        qDebug("Mouse Position: %d, %d", (mouseEvent->scenePos()).toPoint().x(), (mouseEvent->scenePos()).toPoint().y());
        qDebug("Rectangle BottomRight Position: %d, %d", tmp_rect->bottomRight().x(), tmp_rect->bottomRight().y());
        update();
    }
    QGraphicsScene::mouseMoveEvent(mouseEvent);
}

void myGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{

    if (mouseEvent->button() != Qt::LeftButton)
        return;

    // Drag and drop approach:

    tmp_rect = 0;
    startedRect = false;


    // Two-clicks approach

    // if(firstClick)
    // {
    //     firstClick = false;
    // }
    // else
    // {
    //     removeItem(tmp_rect_item);
    //     tmp_rect_item = new QGraphicsRectItem(QRectF(*p1, *p2));
    //     //            *tmp_rect, *pen, *brush);
    //     rectangles.push_back(tmp_rect_item);

    //     addItem(rectangles.back());
    //     p1 = 0;
    //     p2 = 0;
    //     tmp_rect = 0;
    //     firstClick = true;
    // }

    update();
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
0
votes

one other helpful hint I found was that when I extended my view, I had put the mouse event handler under "public" instead of "protected". I made the change the calls were made consistently.