1

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!

Angel
  • 71
  • 1
  • 1
  • 5
  • 1
    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 Mar 28 '17 at 15:11

3 Answers3

4

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

Garrappachc
  • 719
  • 5
  • 19
  • I just tried that, and it certainly makes my two-clicks approach apparently better, but the drag and drop kind of drawing still doesn't work. The thing is that I am debugging, as you see in the commented code, and the rectangle really changes size... – Angel Mar 28 '17 at 09:27
  • 1
    This only works around the fundamental issues elsewhere. `QGraphicsScene::update()` is almost always a catchall for buggy code. If you need to use it, you're doing something wrong in 99%+ of cases. When you modify an item in the scene, the item will notify the scene of the change, the scene will notify any views, and the views will redraw the scene. Nothing extra should be done for this to happen. If you do something "to" an item and it isn't immediately visible, you're not doing what you think you're doing: the item isn't on the scene, or is hidden, or you're not modifying the item at all. – Kuba hasn't forgotten Monica Mar 28 '17 at 15:07
1

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);
}
Angel
  • 71
  • 1
  • 1
  • 5
0

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.

M.A
  • 1
  • 2