@StudentScripter
Below is code that I can share. It's a complete example which perhaps does what you need. The void HandlesItem::paint() function is where the graphics transformations are done. It's rather long-winded but it at least shows what's required. It draws a painter path around the model items and deals with scaling and rotation of the model items as well as scaling of the graphics view.
I'm sorry there is so much code: it's mostly the test framework. It uses mouse wheel events + modifiers to set the scale and rotation of items and the view.
// main.cpp
#include <QGraphicsRectItem>
#include <QGraphicsRectItem>
#include <QRandomGenerator>
#include <QPainter>
// A graphics path item that paints a selection graphic rectangle around a model item.
class HandlesItem : public QGraphicsPathItem
{
public:
HandlesItem(QGraphicsItem * modelItem, QGraphicsItem * parent =nullptr);
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget =nullptr) override;
void setViewScale(double scale);
protected:
QGraphicsItem * mModelItem{ nullptr };
};
// A contentless graphics item that acts as a proxy for a model item and manages a HandlesItem for it.
// The model item position must be set by setting the Proxy item position.
// The model item scale and rotation must be set by the setItemScale() and setItemRotation() functions.
// Model items and HandleItems are assumed to transform about their center points.
class Proxy : public QGraphicsRectItem
{
public:
Proxy(QGraphicsItem * modelItem, QGraphicsItem * parent =nullptr);
void setSelected(bool selected);
bool isSelected() const;
void setItemRotation(double angle);
void setItemScale(double scale);
void setViewScale(double scale); // call to set the HandleItem's pen width according to the view scale.
protected:
QGraphicsItem * mModelItem{ nullptr };
HandlesItem * mHandlesItem{ nullptr };
bool mSelected{ false };
};
// --------------------------------------
HandlesItem::HandlesItem(QGraphicsItem * modelItem, QGraphicsItem * parent)
: QGraphicsPathItem{ parent },
mModelItem{ modelItem }
{
setViewScale(1.0);
setTransformOriginPoint(mModelItem->boundingRect().center());
}
void HandlesItem::setViewScale(double scale)
{
QPen pen{ Qt::yellow };
pen.setWidthF(4.0/scale);
setPen(pen);
}
void HandlesItem::paint(QPainter * painter, [[ maybe_unused ]] const QStyleOptionGraphicsItem * option, [[ maybe_unused ]] QWidget * widget)
{
const double scale{ mModelItem->scale() };
const double margin{ 8.0 / scale };
QRectF r{ mModelItem->boundingRect().adjusted(-margin, -margin, margin, margin) };
const QPointF tl{ r.topLeft() }, tr{ r.topRight() }, br{ r.bottomRight() }, bl{ r.bottomLeft() };
QTransform transform;
transform.translate(r.center().x(), r.center().y());
transform.scale(mModelItem->scale(), mModelItem->scale());
transform.rotate(mModelItem->rotation());
transform.translate(-r.center().x(), -r.center().y());
const QPointF tlt{ transform.map(tl) }, trt{ transform.map(tr) }, brt{ transform.map(br) }, blt{ transform.map(bl) };
QPainterPath path;
path.moveTo(tlt); path.lineTo(trt); path.lineTo(brt); path.lineTo(blt); path.lineTo(tlt);
setPath(path);
QGraphicsPathItem::paint(painter, option, widget);
}
Proxy::Proxy(QGraphicsItem * modelItem, QGraphicsItem * parent)
: QGraphicsRectItem{ parent },
mModelItem{ modelItem }
{
mModelItem->setPos(0, 0); // the model item is positioned by the Proxy.
mModelItem->setParentItem(this);
setFlag(QGraphicsItem::ItemHasNoContents);
mHandlesItem = new HandlesItem{ modelItem, this };
setSelected(false);
}
void Proxy::setSelected(bool selected)
{
mSelected = selected;
mHandlesItem->setVisible(mSelected);
update();
}
bool Proxy::isSelected() const
{
return mSelected;
}
void Proxy::setItemRotation(double angle)
{
if (mModelItem)
{
mModelItem->setTransformOriginPoint(mModelItem->boundingRect().center());
mModelItem->setRotation(mModelItem->rotation() + angle);
}
}
void Proxy::setItemScale(double scale)
{
if (mModelItem)
{
mModelItem->setTransformOriginPoint(mModelItem->boundingRect().center());
mModelItem->setScale(mModelItem->scale() * scale);
}
}
void Proxy::setViewScale(double scale) {
mHandlesItem->setViewScale(scale);
}
// The remaining code is a QGraphicsView and QGraphicsScene test framework for the above.
#include <QGraphicsView>
class GraphicsView : public QGraphicsView
{
public:
GraphicsView(QWidget * parent);
void initialise(QGraphicsScene& scene);
protected:
void mousePressEvent(QMouseEvent * event) override;
void mouseMoveEvent(QMouseEvent * event) override;
void mouseReleaseEvent(QMouseEvent *) override;
void wheelEvent(QWheelEvent *) override;
void setSelection(Proxy * item);
void initialiseScene();
void setViewTransform();
QTransform makeTransform() const;
QList<Proxy *> mItems;
Proxy * mSelectedItem{ nullptr };
Proxy * mDragging{ nullptr };
QPointF mMoveScenePoint;
double mScale{ 1.0 };
double mRotation{ 0.0 };
};
#include <QMainWindow>
#include <QGraphicsScene>
#include <QBoxLayout>
class HandlesMainWindow : public QMainWindow
{
public:
HandlesMainWindow(QWidget * parent =nullptr);
protected:
QGraphicsScene mScene;
};
HandlesMainWindow::HandlesMainWindow(QWidget * parent)
: QMainWindow(parent)
{
QWidget * centralwidget;
QVBoxLayout * verticalLayout;
GraphicsView * graphicsView;
resize(532, 377);
centralwidget = new QWidget(this);
centralwidget->setObjectName("centralwidget");
verticalLayout = new QVBoxLayout(centralwidget);
verticalLayout->setObjectName("verticalLayout");
graphicsView = new GraphicsView(centralwidget);
graphicsView->setObjectName("graphicsView");
verticalLayout->addWidget(graphicsView);
setCentralWidget(centralwidget);
graphicsView->initialise(mScene);
}
#include <QMouseEvent>
#include <QWheelEvent>
namespace {
QRandomGenerator& rng() { static QRandomGenerator sRng; return sRng; }
QColor randomColor() {
return QColor{ rng().bounded(64, 255), rng().bounded(64, 255), rng().bounded(64, 255) };
}
QPointF randomPoint() {
return QPointF{ 1.0*rng().bounded(20, 400), 1.0*rng().bounded(20, 400) };
}
QRectF randomRect() {
return QRectF{ 0.0, 0.0, 1.0*rng().bounded(20, 100), 1.0*rng().bounded(20, 100) };
}
}
GraphicsView::GraphicsView(QWidget * parent)
: QGraphicsView{ parent } {
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setRenderHint(QPainter::Antialiasing);
setTransformationAnchor(ViewportAnchor::NoAnchor);
}
void GraphicsView::initialise(QGraphicsScene& scene) {
setScene(&scene);
initialiseScene();
}
void GraphicsView::mousePressEvent(QMouseEvent * event) {
const QPoint p{ event->pos() };
QGraphicsItem * item{ itemAt(p) };
if (item) {
mDragging = qgraphicsitem_cast<Proxy *>(item->parentItem());
mMoveScenePoint = mapToScene(p);
} else {
mDragging = nullptr;
}
}
void GraphicsView::mouseMoveEvent(QMouseEvent * event) {
const QPoint p{ event->pos() };
if (mDragging) {
const QPointF sp{ mapToScene(p) };
mDragging->moveBy(sp.x() - mMoveScenePoint.x(), sp.y() - mMoveScenePoint.y());
mMoveScenePoint = sp;
}
}
void GraphicsView::mouseReleaseEvent(QMouseEvent * event) {
mDragging = nullptr;
const QPoint p{ event->pos() };
QGraphicsItem * item{ itemAt(p) };
if (item) {
setSelection(qgraphicsitem_cast<Proxy *>(item->parentItem()));
} else {
setSelection(nullptr);
}
}
void GraphicsView::wheelEvent(QWheelEvent * event) {
if (event->modifiers() == Qt::NoModifier) {
double deltaScale{ 1.0 };
QPoint p = event->angleDelta();
if (p.y() > 0) {
deltaScale = 1.1;
} else if (p.y() < 0) {
deltaScale = 1.0/1.1;
}
event->accept();
if (mSelectedItem) {
mSelectedItem->setItemScale(deltaScale);
} else {
mScale = mScale * deltaScale;
setViewTransform();
}
}
else if (event->modifiers() == Qt::ControlModifier){
QPoint p = event->angleDelta();
double rotation{ 0.0 };
if (p.y() > 0) {
rotation = 2.0;
} else if (p.y() < 0) {
rotation = -2.0;
}
event->accept();
if (mSelectedItem) {
mSelectedItem->setItemRotation(rotation);
} else {
mRotation += rotation;
setViewTransform();
}
}
}
void GraphicsView::setSelection(Proxy * item) {
mSelectedItem = item;
if (item) {
item->setSelected(true);
}
for (auto i : mItems) {
if (not (i == item)) {
i->setSelected(false);
}
}
}
void GraphicsView::setViewTransform() {
setTransform(makeTransform());
for (auto i : mItems) {
i->setViewScale(mScale);
}
}
QTransform GraphicsView::makeTransform() const {
QTransform result;
const QPointF center{ rect().center() };
result.translate(center.x(), center.y());
result.scale(mScale, mScale);
result.rotate(mRotation);
result.translate(-center.x(), -center.y());
return result;
}
void GraphicsView::initialiseScene() {
scene()->setBackgroundBrush(QBrush{ Qt::cyan });
for (int i = 0; i < 20; i++) {
QGraphicsRectItem * item{ new QGraphicsRectItem(::randomRect()) };
item->setBrush(QBrush{ ::randomColor() });
Proxy * proxy{ new Proxy{ item } };
proxy->setPos(::randomPoint());
scene()->addItem(proxy);
mItems << proxy;
}
}
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
HandlesMainWindow w;
w.show();
return a.exec();
}