Solved QGraphicsItem not resizing
-
I am trying to change the size of an item(StateItem) which inherits the QGraphicsItem class.
This is where I am tring to change the size of the item. ptrCA is the pointer to the QGraphicsScene object where the objects are drawn.
void StateItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
prepareGeometryChange();
this->updateDimensions(300,300);
ptrCA->QGraphicsScene::update();
}void StateItem::updateDimensions(int state_width, int state_height)
{prepareGeometryChange(); update(drawPos->x(),drawPos->y(),state_width, state_height); //Not working ptrCA->QGraphicsScene::update();
}
Please help. Thanks.
-
Hi,
From what I can see you are currently not modifying your item geometry. You are just asking to redraw a square of size 300.
On a side note, you item having a pointer to the scene that contains it is a bad sign.
perpareGeometryChange
's role is to trigger an update of the scene if necessary. -
On the point of having a pointer to the scene in a object is a bad design. I could not understand how. I want to update the scene whever I make changes to the graphics item. If you could please tell me a better way to do it.
Thanks.
-
I think you could emit a signal instead.
Whenever the item is changed an you need the scene to be updateemit itemChanged()
And when you add the item to the scene:
connect(myItem, SIGNAL(itemChanged()), myScene, SLOT(update())); myScene->addItem(myItem);
But also, I think you should check that what you want is not automatic behavior.
I'd think update() is called automatically on the scene whenever a change is made in one of its object. Maybe you are just re-implementing something that'l already here. -
@faiszalkhan
What kind of changes do you make to the graphics item that does not do an update automatically?
As @SGaist says, when you make a change to the item you usually call prepareGeometryChange() at the time of making the change an update should be called for you.
have you re-implemented boundingRect() or shape() so your changes will be known to the scene? -
@kenchan I have reimplemented boundingRect.
-
Then please show the complete code of your custom item.
It's explained in the documentation of prepareGeometryChange.
-
I have made a an item which inherits from QGraphicsItem with default width and height.
Now, When the user clicks context menu on it, I would want to change the width and height of the item using a dialog input.The procedure I have can be seen in the contextMenuEvent() below, I am calling prepare Geometry change to notify the scene that I would be changing the boundingRect of the item and then I call the update function of the GraphicsItem. @SGaist , if I want to change the width or the height how else can I change it apart from using update().
You can find my file below,
/* This class is used for implementing a state in the Canvas */ #include <QPainter> #include "stateitem.h" #include <QGraphicsItem> //#include <QGraphicsSceneMouseEvent> //This might be required later #include "mainwindow.h" #include "stateproperties.h" #include "canvas.h" extern MainWindow *mainWindowPtr; //Remove this if menu is not required #include <QMenu> #include <QAction> #include <QGraphicsSceneEvent> //StateItem::StateItem(QPointF pos) StateItem::StateItem(QPointF pos, Canvas *ptr) { qreal scale = 1.0; /* Get the position from the caller */ drawPos = new QPointF(pos.x(), pos.y()); /* Set the item as movable and selectable */ setFlags(QGraphicsItem::ItemIsSelectable); setFlags(QGraphicsItem::ItemIsMovable); /* Save the pointer to the canvas */ ptrCA = ptr; /* TODO: Remove this. The value of the scale can be taken from the zoom in and zoom out from the user. This same principle shall be used for the entire canvas */ setScale(scale); } QRectF StateItem::boundingRect() const { return QRectF(drawPos->x(), drawPos->y(), DEFAULT_WIDTH, DEFAULT_HEIGHT); } /* Explicit call to this function is not required */ void StateItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { /* Set the pen */ QPen blackpen(Qt::black); blackpen.setWidth(2); painter->setPen(blackpen); /* Set colour of the state */ QColor statecolor(229,231,233); QBrush statebrush(statecolor); painter->setBrush(statebrush); /* Draw state item */ painter->drawRoundedRect(drawPos->x(),drawPos->y(),DEFAULT_WIDTH,DEFAULT_HEIGHT,8,8); //x,y,width,height,xradius,yradius QPoint p(drawPos->x(),(drawPos->y() + 10)); QPoint q((drawPos->x() + 100), (drawPos->y() + 10)); QLine stateLine(p,q); painter->drawLine(stateLine); } void StateItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { qDebug("Context menu has been invoked"); //=====TODO:Make it work here first //prepareGeometryChange(); this->updateDimensions(300,300); ptrCA->QGraphicsScene::update(); //========== // stateproperties = new StateProperties(this); // stateproperties->show(); //This might be required later // QMenu menu; // QAction *removeAction = menu.addAction("Remove"); // QAction *markAction = menu.addAction("Mark"); // QAction *selectedAction = menu.exec(event->screenPos()); } /* function to update the height and width of the state item */ void StateItem::updateDimensions(int state_width, int state_height) { qDebug("Update Dimensions has been called"); prepareGeometryChange(); update(drawPos->x(),drawPos->y(),state_width, state_height); }
Thanks.
-
@faiszalkhan
I see the items DEFAULT_WIDTH, DEFAULT_HEIGHT in you code. I see you use them in your bounding box so I guess they are a member of the StateItem?
Where are you initialising them? I can't see that anywhere in your code?
Then you set the dimensions with the updateDimensions() function but I don't see where the DEFAULT_WIDTH, DEFAULT_HEIGHT values get updated. You use those values in the paint() function but if you never change them how will it change in size; Are you setting DEFAULT_WIDTH, DEFAULT_HEIGHT somewhere you have not shown us? -
@kenchan DEFAULT_WIDTH and DEFAULT height are just macros with value 100 and 50 intended as the default values for width and height. Now that I want to change the height and width for the item, I am raplacing them with 300 and 300 by calling update just to see if the changes are seen on the Item.
-
@faiszalkhan
But you are still using the macros in the paint function? so they do not change when you paint it?
The thing is what you paint is what you get. if you don't change the size used in the paint function how is it going to change size? and if you don't change the size used in the bounding box function how can the scene know how big it is?
How does calling update change the values in the macros? -
@faiszalkhan said in QGraphicsItem not resizing:
return QRectF(drawPos->x(), drawPos->y(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
- You always return a boundingRect of width DEFAULT_WIDTH and height DEFAULT_HEIGHT. If you want to change the size of your item, you need to provide a different size. An easy way is to make a QSizeF member in the class, initialize it with DEFAULT_WIDTH and DEFAULT_HEIGHT, and change it when necessary (after calling prepareGeometryChange)
- The bounding rect is in item coordinates. Using your own position within the bounding rect will lead to weird effects. If you want your "reference position" to be the top-left corner of your item, return QRectF(QPointF(), mySize)
As others have said, it should not be necessary to have a pointer to the scene. Calling update() on the item should be sufficient in almost all cases.
-
@Asperamanca @kenchan @SGaist @Andeol Thanks!
The issue has almost been resolved. On a side note, I wanted to ask about two things related to this thread,
-
Consider I have a properties widget(Implemented as a class) which is opened on right clicking a QGraphicsItem. The object of the properties widget shall change some attribute of the QGraphicsItem object. Which is the best way to do this?
I have passed a pointer of the QGraphicsItem object to the Properties object which works. Please suggest. -
In the same scenario, I want to make changes in the mainwindow object. There can be several objects which would do some changes to main window object. What is the best way to acheive this?
I have passed the pointer of the mainWindow to the individual objects which works but doesn't feel like the best solution.
Thanks Again!
-
-
- Don't, you are creating a tight coupling. Give your dialog accessors for the properties you are editing and once you return from the dialog, update the item.
- Again, don't. Keep you widgets clearly separated. If you need "live" changes, use signals and slots.
-
@faiszalkhan
You don't need to keep the scene pointer in a QGraphicsItem object because it always know if it is in a scene. You get it using the scene() function of the QGraphicsItem.