Qt resize child rectangles - c++

Working on a school project, but I'm completely stuck. I have to write a paint application, in which you can draw Rectangles & Ellipses, select them, group them, move and resize them. After that I have to start implementing several design patterns.
I have to following problem. I have an abstract class Figure which inherits QRect, then there are the classes Rectangle, Ellipse and Group which all inherit the Figure class.
I am able to resize a single figure (ellipse or shape), by clicking on the corner of a selected shape, but the problem is resizing a group which contains several other figures (group, rectangle or ellipse), they have to resize relatively to their parent figure:
Mouse move event
void MainWindow::OnPaintBoxMouseMove(PaintBox *sender, QMouseEvent *event) {
//Mouse move event based on which tool is selected
switch(tool){
//When rectangle tool is selected, update rectangle size to mouse position
//in command
case rectangle: {
tempCmd->update(event->pos());
break;
}
//When ellipse tool is selected, update ellipse size to mouse position
//in command
case ellipse: {
tempCmd->update(event->pos());
break;
}
//When select tool is selected
case select: {
//If moving is selected, update shape in command
if (moving){
sRect->move(startPoint,event->pos());
tempCmd->update(event->pos());
startPoint = event->pos();
}
//If resizing is selected, update shape in command
else if (resizing){
sRect->resize(startPoint,event->pos());
tempCmd->update(event->pos());
startPoint = event->pos();
}
//Otherwise update size of selection rectangle
else if (sRect != nullptr) {
sRect->updateDimensions(event->pos());
}
break;
}
}
paintBox->update();
}
Resize method in resize command
called 'update' here
void ResizeCmd::update(QPoint mousePosition) {
switch (direction){
case 1: {
_figure->setTopLeft(_figure->topLeft()+mousePosition-startMousePosition);
break;
}
case 2: {
_figure->setTopRight(_figure->topRight()+mousePosition-startMousePosition);
break;
}
case 3: {
_figure->setBottomLeft(_figure->bottomLeft()+mousePosition-startMousePosition);
break;
}
case 4: {
_figure->setBottomRight(_figure->bottomRight()+mousePosition-startMousePosition);
break;
}
}
startMousePosition = mousePosition;
}
So the question is, how am I going to implement the resize method in the group class, which may contain other child groups as well?

I think you are looking for something like this Repo :
https://github.com/cl0ne/resizer-item
For doing your project you should use graphics libs of Qt like QGraphicsItem ,QGraphicsScene, and QGraphicsView
you shouldn't separate all your Items and write a resizer.
this repo is general for all QGraphicsItems which means that If you want Rectangle you should define one QGraphicsRectItem and add this resizer to it like this :
QGraphicsScene *scene = new QGraphicsScene(this);
QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 100, 100));
item->setPos(10, 10);
item->setFlag(QGraphicsItem::ItemIsMovable);
item->setPen(QColor(102, 102, 102));
item->setBrush(QColor(158, 204, 255));
scene->addItem(item);
GraphicsItemResizer *resizer = new GraphicsItemResizer(item);
resizer->setBrush(QColor(64, 64, 64));
resizer->setMinSize(QSizeF(30, 30));
resizer->setTargetSize(item->boundingRect().size());
resizer->setHandlersIgnoreTransformations(true);
QObject::connect(
resizer,
&GraphicsItemResizer::targetRectChanged,
[item](const QRectF & rect)
{
QPointF pos = item->pos();
item->setPos(pos + rect.topLeft());
QRectF old = item->rect();
item->setRect(QRectF(old.topLeft(), rect.size()));
}
);
ui->graphicsView->setScene(scene);

Related

How to display some text besides mouse pointer when the mouse hover over QGraphicsItem's in Qt?

I have a QGraphicsView, which contains many rectangle and polylines. I wanted to print every object names ( every rectangle, polylines have names) once I clicked them on view through mouse click. I did that and worked perfectly.
But now I want to do that over mouse hovering. It means, if I hover a mouse over particular object, it should show its name besides cursor.
I tried that, but
I am not understanding, on hovering, how that particular object should get selected ?
And how to print its name besides cursor point ?
I tried this way:
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
bool filterEvent = false;
switch(event->type())
{
case QEvent::MouseButtonPress:
{....}
case QEvent::MouseButtonRelease:
{...}
case QEvent::Enter:
{
QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event);
QPointF po = _view->mapToScene(mouseEvent->pos());
FindNamesOverHover(po);
}
return true;
default:
break;
}
return filterEvent;
}
void myClass::FindNamesOverHover(QPointF p)
{
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(_scene->itemAt(p,QTransform()));
if(rItem)
{
// some logic
qDebug()<< "Instance name is " << i->Name();
}
}
Constructor of class myClass
myClass::myClass(QWidget* parent) :
QDockWidget(parent)
{
scene = new QGraphicsScene(this);
view = new QGraphicsView(this);
view->setScene(_scene);
view->viewport()->installEventFilter(this);
}
Now above code works only,
When I select object through mouse click and then again hover over it.
Then it prints its name on console.
But I want only hovering ( no selection through mouse click) will show its name besides cursor point.
Can any one help me ?
You can override QGraphicsScene.mouseMoveEvent and inside it use QGraphicsScene.itemAt to query for item under cursor. To display you can use QGraphicsTextItem, just move it to correct position and apply text.

How to paint object by overriding paintEvent in Qt?

In my QGraphicsView, I have many QGraphicsItem. I want to search specific QGraphicsItem out of all the items present in view and highlight the matched item. For highlighting item, I am trying to use paintEvent.
So I am calling paintEvent, but not understanding how to heighlight border of the matched object ?
Should I need co-ordinates of that matched object ?
I tried like this:
foreach(QGraphicsItem* currentItem, _scene->items())
{
pEvent = false;
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(currentItem);
if(rItem)
{
// some logic to get i->Name()
QString name1 = i->Name();
QString name2 = "reN"; // I want to find reN named item in view
if(name1 == name2)
{
pEvent = true;
qDebug()<<"Object Found ";
this->repaint();
break;
}
}
}
void myClass::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
qDebug()<<"In paint event ";
if(pEvent)
{
QPainter qp(this);
drawBody(&qp);
}
}
void myClass::drawBody(QPainter *qp) {
Q_UNUSED(qp);
// want logic for heighlighting border of the item
}
I understand from your problem that you are overridng QGraphicsView in MyClass since paintEnent is defined QWidgets classes.
To solve your problem, if I understand correctly. It's necessary to save the QGraphicsRectItem coordinates:
QRectF rectF = item.boundingRect();
then use the following in the function drawBody:
qp.save();
const float width = 5;
QPen pen;
pen.setWidth(width );
pen.setColor("green");
painter->drawRect(rectF.x(), rectF().y(),
rectF().width(), rectF().height());
qp.restore();

Moving the visible area of QGraphicsView

I have a class based on QGraphicsView. I want to make the clicks on WASD move the visible scene to the appropriate direction. The task is simple and clear, wrote the following code:
GameScene::GameScene(QGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent) {
_scene = scene;
_resolution = new QPointF(1920, 1080);
_position = new QPointF(100, 100);
setSceneRect(_position->x(), _position->y(), _resolution->x(), _resolution->y());
installEventFilter(this);
scene->addItem(new QGraphicsLineItem(510, 510, 990, 990)); }
void GameScene::keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_W:
_position->setY(_position->y() - 100);
break;
case Qt::Key_S:
_position->setY(_position->y() + 100);
break;
case Qt::Key_A:
_position->setX(_position->x() - 100);
break;
case Qt::Key_D:
_position->setX(_position->x() + 100);
break;
default:
break;
}
setSceneRect(_position->x(), _position->y(), _resolution->x(), _resolution->y());
update();
}
This code even works, but there is one problem: the scene does not always respond to the setSceneRect function (checked through the debagger signal reaches, the values are sent to the function correct), that is, the key is pressed, the keyPressEvent function is executed, the desired case is selected, the values are updated, but the setSceneRect function does not do anything. What could be the problem?

How can I change the QGraphicView selected Item color

How can I change QGraphicView selected item color.In this code which can select items form QGraphicView and delete and also able to disable item moving.How can I change the color of selected Item.
void Widget::on_btnDelete_clicked()
{
foreach (QGraphicsItem *item,ui->graphicsView->scene()->selectedItems()) {
delete item;
}
}
void Widget::on_btnMoveDis_clicked()
{
foreach (QGraphicsItem *item,ui->graphicsView->scene()->selectedItems()) {
item->setFlag(QGraphicsItem::ItemIsMovable,false);
}
}
At first I suggest you to read QGraphicsView examples and docs carefully , I recommend Diagram Scene Example and Elastic Nodes Example as starting point. Because QGraphicsView frame work is smart if you use it smart otherwise you may pay penalties in performance.
And now your question :
whole answer is related to qgraphicsitem_cast, how to use it?
First let me provide some initializations look code below at first I add three rectangles to scene and set their flags.
void MainWindow::on_btnInit_clicked()
{
QColor color = Qt::red;
QBrush brush = Qt::SolidPattern;
brush.setColor(color);
QRect * rectObject = new QRect(QPoint(0,0) , QSize(20,20));
scene.addRect(*rectObject , color, brush);
rectObject = new QRect(QPoint(0,30) , QSize(20,20));
color = Qt::green;
brush.setColor(color);
scene.addRect(*rectObject , color, brush);
rectObject = new QRect(QPoint(0,60) , QSize(20,20));
color = Qt::blue;
brush.setColor(color);
scene.addRect(*rectObject , color, brush);
foreach (QGraphicsItem *item,ui->graphicsView->scene()->items()) {
item->setFlag(QGraphicsItem::ItemIsMovable , true);
item->setFlag(QGraphicsItem::ItemIsSelectable , true);
}
}
I have set ItemIsMovable and ItemIsSelectable flags so for items could be selectable.
and now change color of selected item.
void MainWindow::on_btnSelectItem_clicked()
{
foreach (QGraphicsItem *item, ui->graphicsView->scene()->selectedItems())
{
QGraphicsRectItem *rect = qgraphicsitem_cast<QGraphicsRectItem *>(item);
if (!rect)
continue;
QBrush br(Qt::SolidPattern);
br.setColor(Qt::black);
rect->setBrush(br);
rect->update();
}
}
I use QGraphicsRectItem to cast rectangular objects that i added to scene before. If you want to learn more on use qgraphicsitem read Elastic Nodes Example.

Qt moving an Object in a specific Region

I'm new to Qt programming. So far I made a QGraphicsScene draw some rectangles (looks like a Chess Board) and then added an own QGraphicsItem to the scene. For this item I set the ItemIsMovable flag. Now I can move it but I would like to restrict the movement to the area where the Chess Board is.
Would I have to unset the flag and realize the movement manually or is there like an option or flag where I can specify the area it can be moved in ?
renderableObject::renderableObject(QObject */*parent*/)
{
pressed = false;
setFlag(ItemIsMovable);
}
You can reimplement the QGraphicsItem's itemChange() if you want to restrict movable area. Repositioning the item will cause itemChange to get called. You should note that ItemSendsGeometryChanges flag is needed to capture the change in position of QGraphicsItem.
class MyItem : public QGraphicsRectItem
{
public:
MyItem(const QRectF & rect, QGraphicsItem * parent = 0);
protected:
virtual QVariant itemChange ( GraphicsItemChange change, const QVariant & value );
};
MyItem::MyItem(const QRectF & rect, QGraphicsItem * parent )
:QGraphicsRectItem(rect,parent)
{
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges);
}
QVariant MyItem::itemChange ( GraphicsItemChange change, const QVariant & value )
{
if (change == ItemPositionChange && scene()) {
// value is the new position.
QPointF newPos = value.toPointF();
QRectF rect = scene()->sceneRect();
if (!rect.contains(newPos)) {
// Keep the item inside the scene rect.
newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
return newPos;
}
}
return QGraphicsItem::itemChange(change, value);
}
This will keep item moving limited to scene rect. You can define any arbitrary rect if you like.