QGraphicsScene: moving an item inside an item
-
Hey,
since I already have some experience using Qt, it is the first time I try using some graphic elements.
To get a first impression how it works I watched a YouTube video. I did what he says and it works fine.
I created a QGraphicsScene where I placed a box which is movable and as long as I click on it the colour is changed. "oneBox" inherits from QGraphicsItem.oneBox::oneBox() { bPressed = false; setFlag(ItemIsMovable); } QRectF oneBox::boundingRect() const { return QRectF(0,0,200,200); } void oneBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QRectF rec = boundingRect(); if(bPressed) { brush.setColor(Qt::red); } else { brush.setColor(Qt::green); } painter->fillRect(rec,brush); painter->drawRect(rec); } void oneBox::mousePressEvent(QGraphicsSceneMouseEvent *event) { bPressed = true; update(); QGraphicsItem::mousePressEvent(event); } void oneBox::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { bPressed = false; update(); QGraphicsItem::mouseReleaseEvent(event); }
Now, what I want to have is a horizontal line that divides this box in an upper and a lower part. The user can move the line up and down in order the change the size of the upper and the lower part. So, the line should be movable but only in y - direction. The lower part should have another colour than the upper one.
I guess I need a qgraphicslineitem. But I am not sure where to place it.
When I create a new class which lives parallel to "oneBox" I could add the line to the same scene as the "oneBox". But then I can move the line also outside of the box. That should not be the case.
Then I thought I could create a new QGraphicsScene inside "oneBox" which has the same size as the boundingRect. In this case the line would be only movable inside the "oneBox". But I am not sure if the new GraphicsScene then lies on top of the "oneBox" so I cannot grab the "oneBox" anymore to move it around.I really hope you got what I mean and you can tell me what I have to do.
Cheers
Chris -
@LeoC
I don't think you want anything like "multiple graphics scenes". I'm not sure whether an extra graphics item is the best way to do your dividing line, someone will probably tell you to do it in drawing the box item. But if you do want to do it via a separate item, did you try making that line a child of the box item? This is only a guess, but I would have thought that would be how you would do one graphics item living on top of another? -
The short form is:
- create a line item (
QGraphicsLineItem
) - add it to your box (box becomes parent) and make it moveable.
- control the movement and the position to re-paint the box below and above that line
I can give you an example later. Just try it yourself first, based on this information :)
- create a line item (
-
In addition to what Pl45m4 outlined, you may need to reimplement the itemChange() method to limit the movement of the line to horizontal movements.
-
I didnt limit the movement of the handle in X-direction but that should not be a problem to add later.
#ifndef BOXHANDLE_H #define BOXHANDLE_H #include <QGraphicsLineItem> class BoxHandle: public QGraphicsLineItem { public: BoxHandle(); BoxHandle(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent = nullptr); protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); }; #endif // BOXHANDLE_H
#include "boxhandle.h" #include <QDebug> #include <QPen> BoxHandle::BoxHandle(): QGraphicsLineItem() {} BoxHandle::BoxHandle(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent): QGraphicsLineItem(x1, y1, x2, y2, parent) { setFlag(ItemIsMovable); QPen pen(Qt::red, 5); setPen(pen); } void BoxHandle::mousePressEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mousePressEvent(event); } void BoxHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mouseReleaseEvent(event); } void BoxHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mouseMoveEvent(event); }
#ifndef MYBOX_H #define MYBOX_H #include <QGraphicsItem> #include <boxhandle.h> class MyBox: public QGraphicsItem { public: MyBox(); QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); private: bool pressed; BoxHandle *handle; }; #endif // MYBOX_H
#include "mybox.h" #include <QBrush> #include <QPainter> #include <QDebug> #include <QGraphicsSceneMouseEvent> MyBox::MyBox(): QGraphicsItem(), pressed(false), handle(new BoxHandle(0, 0, 200, 0, this)) // HandleSize = BoxWidth, probably requires some adjustment later { setFlag(ItemIsMovable); } QRectF MyBox::boundingRect() const { return QRectF(0, 0, 200, 200); } void MyBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QRectF rec = boundingRect(); // Get Handle Pos qreal handlePosY = handle->mapToParent(0, 0).y(); // Rect below handle QRectF below = QRectF(0, handlePosY, boundingRect().width(), boundingRect().height() - handlePosY); painter->fillRect(below, Qt::green); // Rect above handle QRectF above = QRectF(0, 0, boundingRect().width(), boundingRect().height() - (boundingRect().height() - handlePosY)); painter->fillRect(above, Qt::blue); painter->drawRect(rec); } void MyBox::mousePressEvent(QGraphicsSceneMouseEvent *event) { pressed = true; update(); QGraphicsItem::mousePressEvent(event); } void MyBox::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { pressed = false; update(); QGraphicsItem::mouseReleaseEvent(event); }
Also I skipped the
itemChange
part... Dont want to do all of your work :)
And there are probably better solutions than doing all the stuff in paint event (I guess?!)Btw: The "artifacts" near the border of the box are not really there. They are only visible in the recorded gif.
-
@Pl45m4 now I implemented everything as you suggested and it works. Thank you for your support. It would have took me days to figure this out.
I also added the following to BoxHandle
void oneBoxHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mouseMoveEvent(event); setPos(0, y()); if(y() < 0) setPos(x(),0); if(y() > rectY) setPos(x(),rectY); }
and now the red line only moves in y direction inside the box.
So in the end it looks like it should. Now I can create everything else around it.
btw: the artifacts are also visible on my screen. So it is not a matter of the gif :)