3

I would like my QGraphicsWidget to scale its size based on the size of the scene. The QGraphicsWidget I have currently is a fixed size depending on the return value of sizeHint (QGraphicsWidget is always 200 x 200). Attached below is minimal example:

MainWindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QGraphicsScene>
#include <QGraphicsView>

#include "RectangleWidget.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QGraphicsScene * m_scene;
    QGraphicsView * m_view;
    RectangleWidget * m_rectangleWidget;
};

#endif // MAINWINDOW_H

MainWindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    m_scene = new QGraphicsScene(this);

    m_view = new QGraphicsView(m_scene, this);
    m_view->setAlignment(Qt::AlignLeft | Qt::AlignTop);

    m_rectangleWidget = new RectangleWidget();

    m_scene->addItem(m_rectangleWidget);

    setCentralWidget(m_view);
}

MainWindow::~MainWindow()
{
    delete ui;
}

RectangleWidget.h:

#ifndef RECTANGLEWIDGET_H
#define RECTANGLEWIDGET_H

#include <QGraphicsLinearLayout>
#include <QGraphicsWidget>


class RectangleWidget: public QGraphicsWidget
{
public:
    RectangleWidget(QGraphicsWidget* parent = nullptr);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

    void setGeometry(const QRectF &geom) override;
    QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override;
};

#endif // RECTANGLEWIDGET_H

RectangleWidget.cpp:

#include "rectanglewidget.h"
#include <QPainter>

RectangleWidget::RectangleWidget(QGraphicsWidget* parent)
{

}

void RectangleWidget::paint(QPainter *painter,
    const QStyleOptionGraphicsItem *option, QWidget *widget /*= 0*/)
{
    Q_UNUSED(widget);
    Q_UNUSED(option);

    //Draw border
    painter->drawRoundedRect(boundingRect(), 0.0, 0.0);

}

QRectF RectangleWidget::boundingRect() const
{

    return QRectF(QPointF(0,0), geometry().size());
}

void RectangleWidget::setGeometry(const QRectF &geom)
{
    prepareGeometryChange();
    QGraphicsLayoutItem::setGeometry(geom);
    setPos(geom.topLeft());
}

QSizeF RectangleWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    switch (which) {
    case Qt::MinimumSize:
        return QSizeF(200, 200);

    default:
        break;
    }
    return constraint;
}

Window small

enter image description here

Any help on this would be appreciated.

scopchanov
  • 7,966
  • 10
  • 40
  • 68
Michael Japzon
  • 201
  • 1
  • 2
  • 10

1 Answers1

1

Background

Your QGraphicsWidget have to be aware of two things:

  1. When it is added to a scene

In order to do that you have to reimplement QGraphicsWidget::itemChange and look for a change of type QGraphicsItem::ItemSceneHasChanged.

  1. When the size of this scene changes

This could be done by connecting a slot or a lambda function to the QGraphicsScene::sceneRectChanged signal.

Solution

Based on the given explanation, my solution would be the following:

In RectangleWidget.h after QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override; add:

protected:
    QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;

private:
    QSize m_rectSize;

In RectangleWidget.cpp change return QSizeF(200, 200); to return m_rectSize; and add at the end:

QVariant RectangleWidget::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    if (change == ItemSceneHasChanged) {
        connect(value.value<QGraphicsScene *>(), &QGraphicsScene::sceneRectChanged, [this](const QRectF &rect){
            m_rectSize.setWidth(rect.size().width());
            m_rectSize.setHeight(rect.size().height());
        });
    }

    return QGraphicsWidget::itemChange(change, value);
}

Finally, in MainWindow.cpp after m_scene->addItem(m_rectangleWidget); set the sceneRect as follows:

m_scene->setSceneRect(0, 0, 100, 400);

Note: The rectangle will respond to the changes of the scene, not the view. So if you resize the window, the rectangle will not be resized.

Adjustment

This will make the rectangle exactly the same size as the scene. If you want a different ratio, say 0.5, instead of m_rectSize.setWidth(rect.size().width()); write m_rectSize.setWidth(rect.size().width() / 2);, respectively m_rectSize.setHeight(rect.size().height() / 2);.

Community
  • 1
  • 1
scopchanov
  • 7,966
  • 10
  • 40
  • 68
  • I am now getting a fixed rectangle 50x200 now with your changes. Are we missing something. Can you send the full 4 files? – Michael Japzon Aug 28 '18 at 18:40
  • Is there a way to connect the window size to the scene size? – Michael Japzon Aug 28 '18 at 18:57
  • @MichaelJapzon, there is. However, this is already another question and requires another solution. At best mark this one as solved and ask another one about the QGraphicsView. – scopchanov Aug 28 '18 at 19:00