PDA

View Full Version : call to QGraphicsScene::update() hangs when compiled with Qt4.6



yazwas
5th March 2011, 22:29
Good day

I have an application that is running fine on Qt 4.5 under both Windows and Linux, but when trying to compile it with Qt 4.6 or later, it hangs when calling QGraphicsScene::update() function inside grandchild object

The hierarchy of the design as follow
QGraphicsScene (DrawerScene), which has one object of type QGraphicsRectItem (PageItem)
in the pageItem we can add multiple QGraphicsPixmapItem objects (DroppedItem)

DroppedItem can be selected, resized, and moved.

This is the dropEvent code in the PageItem class


void PageItem::dropEvent(QGraphicsSceneDragDropEvent * event)
{
// verify that we drop an image.
if (event->mimeData()->hasImage())
{
QPointF pt = event->pos(); //QPointF pt = mapToItem(this, event->pos());

QByteArray byteArray = event->mimeData()->data("image/jpg");
QPixmap pixmap = QPixmap::fromImage( QImage::fromData(byteArray, "jpg") );
new DroppedItem(pixmap, pt, 1.0, this);
}
}
in the DroppedItem class, the hang happens in the paint function when calling the update function of the scene object, as in the code below


void DroppedItem::paint(QPainter *painter, const QStyleOptionGraphicsItem
*option, QWidget *widget)
{
painter->drawImage(boundingRect().toRect(),getScaledImage() );

//the line below works OK for Qt 4.5, but hangs (on Linux) for Qt 4.6, 4.7)
//the line below is needed to update the scene so that it reflects this item is
//being resized.
//I've tried to comment it, and add it to the zoom function (see full code below),
//but it does not reflect the same behavior.
scene()->update(scene()->itemsBoundingRect());
}

Below is the full source code for the DroppedItem.cpp file, I can provide any other info if needed.


#include "droppeditem.h"
#include "pageitem.h"

#include <QKeyEvent>
#include <QtDebug>
#include <QTime>
#include <QGraphicsScene>
#include <QMessageBox>
#include <QPainter>

DroppedItem::DroppedItem(const QPixmap & pixmap, const QPointF & scenePos, qreal perc, QGraphicsItem * parent /*=0*/) :
QGraphicsPixmapItem(pixmap, parent),
m_originalPixmap(pixmap),
m_imageWidth(m_originalPixmap.width())
{
// set position with scene position.
setPos(scenePos);
setZValue(0.9);
// make the item selectable, movable & focusable
setFlag(QGraphicsItem::ItemIsSelectable, true);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsFocusable,true);

m_pageItem = static_cast<PageItem*> (parentItem());

setPixmap(m_originalPixmap);
m_originalImage = m_originalPixmap.toImage();
}

DroppedItem::~DroppedItem()
{
}

int DroppedItem::type() const
{
return Type;
}

void DroppedItem::keyPressEvent(QKeyEvent * event)
{
if (event->key() == Qt::Key_Delete && isSelected())
{
// remove item when key press event is Suppr.
scene()->removeItem(this);
return;
}
else
{
//check if the new position is within the boundary of the page
QGraphicsPixmapItem::keyPressEvent(event);
}
}

void DroppedItem::zoom(int value)
{
m_currentValue = value;
update(this->boundingRect().toRect());
}

void DroppedItem::mousePressEvent(QGraphicsSceneMouseEv ent *event)
{
if (event->button() != Qt::LeftButton)
{
event->ignore();
return;
}

checkBoundary(event->pos().toPoint(), true);
if(m_tweakingpart != "")
m_tweaking = true;

QGraphicsPixmapItem::mousePressEvent(event);
}

void DroppedItem::mouseMoveEvent(QGraphicsSceneMouseEve nt *event)
{
if(m_tweaking)
{
QPoint pt = event->pos().toPoint();
m_rectangle = this->boundingRect().toRect();
qreal value = 1.0;
if ( m_tweakingpart == "bottomRight" ) { m_rectangle . setBottomRight ( pt ) ; value = static_cast<qreal> (m_rectangle.height() / (m_originalPixmap.height()*1.0) );}
else if ( m_tweakingpart == "bottom" ) { m_rectangle . setBottom ( pt . y () ) ; value = static_cast<qreal> (m_rectangle.height() / (m_originalPixmap.height()*1.0) );}
else if ( m_tweakingpart == "right" ) { m_rectangle . setRight ( pt . x () ) ; value = static_cast<qreal> (m_rectangle.width() / (m_originalPixmap.width() *1.0) );}

zoom(value*100);
}
else
{
QGraphicsPixmapItem::mouseMoveEvent(event);
}
}

void DroppedItem::mouseReleaseEvent(QGraphicsSceneMouse Event *event)
{
m_tweaking = false ;
m_tweakingpart = "" ;

setCursor(QCursor(Qt::OpenHandCursor));
QPointF pt = pos();
checkBoundary(pt.x(), pt.y());
QGraphicsPixmapItem::mouseReleaseEvent(event);
}

void DroppedItem::checkBoundary(QPoint pt, bool t)
{
m_rectangle = this->boundingRect().toRect();

if ( m_rectangle.isValid() )
{
QPoint m_tl = m_rectangle.topLeft();
QPoint m_tr = m_rectangle.topRight();
QPoint m_bl = m_rectangle.bottomLeft();
QPoint m_br = m_rectangle.bottomRight();

const QPoint off(20, 20), offx(20, -20), offy(-20, 20);

if( QRect( m_br-off, m_br+off).contains(pt) )
{
if (t)
m_tweakingpart = "bottomRight" ;
this->setCursor( Qt::SizeFDiagCursor );
}
else if( QRect( m_bl+offx, m_br-offx ).contains(pt) )
{
if (t)
m_tweakingpart = "bottom";
this->setCursor( Qt::SizeVerCursor ) ;
}
else if( QRect( m_tr+offy, m_br-offy ).contains(pt) )
{
if (t)
m_tweakingpart = "right";
this->setCursor( Qt::SizeHorCursor );
}
}
}

void DroppedItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->drawImage(boundingRect().toRect(),getScaledImage() );
//this causes the program to hang when compiled with Qt >= 4.6
//(I checked only on Linux, but I think it will hang on Windows as well)
scene()->update(scene()->itemsBoundingRect());
}

QRectF DroppedItem::boundingRect() const
{
qreal w = (m_currentValue/100.0) * m_originalPixmap.width();
qreal h = (m_currentValue/100.0) * m_originalPixmap.height();
return QRectF(QPointF(0,0), QPointF(w, h));
}


QImage DroppedItem::getScaledImage()
{
qreal w = (m_currentValue/100.0) * m_originalPixmap.width();
qreal h = (m_currentValue/100.0) * m_originalPixmap.height();

QSizeF size(w, h);
QImage image(size.toSize(), QImage::Format_RGB888);

image = m_originalImage.scaledToWidth(w, Qt::SmoothTransformation);
return image;
}

d_stranz
5th March 2011, 23:29
Have you set a breakpoint to see if calling scene()->update() from within the DroppedItem::paint() method isn't resulting in an infinite loop?

yazwas
6th March 2011, 15:53
setting a breakpoint where?
thanks

wysota
6th March 2011, 18:15
What sense does it make to update a scene from a painting routine? To me it seems you should just add a call to prepareGeometryChange() at the end of your DroppedItem::zoom() method instead of the call to update() there.

yazwas
14th March 2011, 00:45
Good day Wysota
I've done that, it now does not hang, and it updates correctly, but I have one issue that shows with this solution

When I change the size of the dropped item, it changes, and resizes correctly. but then, the new boundaries are not detected by mouse events, for example, if the original rect was 50X50, and after resizing it became 100X100, the mouseMoveEvent, mousePressEvent, mouseReleaseEvent and mouseDoubleClickEvent will only detect area within the old boundaries, even it does shows correctly.
even if the mouse if over the new area, the mouse pointer belongs to the parent item (PageItem).

Any suggestions?
Thanks

wysota
14th March 2011, 07:44
When I change the size of the dropped item, it changes, and resizes correctly. but then, the new boundaries are not detected by mouse events, for example, if the original rect was 50X50, and after resizing it became 100X100, the mouseMoveEvent, mousePressEvent, mouseReleaseEvent and mouseDoubleClickEvent will only detect area within the old boundaries, even it does shows correctly.
even if the mouse if over the new area, the mouse pointer belongs to the parent item (PageItem).

Does your implementation of boundingRect() reflect those changes properly? How did you implement it?

yazwas
14th March 2011, 09:09
Does your implementation of boundingRect() reflect those changes properly? How did you implement it?

Hi
This is how I implemented it, and also you can see the full source code in the original posting above



QRectF DroppedItem::boundingRect() const
{
qreal w = (m_currentValue/100.0) * m_originalPixmap.width();
qreal h = (m_currentValue/100.0) * m_originalPixmap.height();
return QRectF(QPointF(0,0), QPointF(w, h));
}


regards,

wysota
14th March 2011, 09:20
And where are you calling prepareGeometryChange()?

yazwas
14th March 2011, 11:02
And where are you calling prepareGeometryChange()?
As you suggested, I used it in the zoom function instead of call to update. I've also added it to mouseMoveEvent function, but that also did not change anything.

any suggestions?
thanks

wysota
14th March 2011, 11:05
Everything should work fine then. If it doesn't then apparently your mouseMoveEvent() is incorrectly implemented.

yazwas
14th March 2011, 12:58
Everything should work fine then. If it doesn't then apparently your mouseMoveEvent() is incorrectly implemented.

can you look at the code posted earlier (in the first post), the logic behind it is as follow
on mousePressEvent(), I check for boundary, if its on the borders, then moseMoveEvent() updates the rectangle
my problem is even the rectangle is resized, but the mouse over the new area, will remain show the mouse cursor that belongs to the pageItem object

regards

wysota
14th March 2011, 14:07
Your logic is too complicated. It's much easier to do this in the scene and using transformation system built into the GraphicsView architecture.

There is some flaw in my code but nevertheles it works most of the time so it's something to base your solution upon:


#include <QtGui>

class Scene : public QGraphicsScene {
public:
Scene() {
activeItem = 0;
}

protected:
void mousePressEvent(QGraphicsSceneMouseEvent *me) {
activeItem = itemAt(me->scenePos());
if(activeItem) {
QRectF sR = activeItem->sceneBoundingRect();
if(me->pos().x() < sR.left()+10
|| me->pos().x() > sR.right()-10
|| me->pos().y() < sR.top()+10
|| me->pos().y() > sR.bottom()-10)
{

} else activeItem = 0;
}
QGraphicsScene::mousePressEvent(me);
}

void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
activeItem = 0;
QGraphicsScene::mouseReleaseEvent(event);
}

void mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if(activeItem) {
QPointF scPos = event->scenePos();
QPointF lastScPos = event->lastScenePos();
QRectF sR = activeItem->sceneBoundingRect();
//qreal aspectRatio = sR.width()/sR.height();
QRectF newRect = sR;
qreal zoomLevel = 1.0;
if(qAbs(lastScPos.x()-scPos.x()) > qAbs(lastScPos.y()-scPos.y())) {
// width is leading the transform
if(lastScPos.x() < sR.center().x()) {
newRect.setLeft(scPos.x());
} else if(lastScPos.x() > sR.center().x()) {
newRect.setRight(scPos.x());
}
zoomLevel = newRect.width()/sR.width();
} else {
// height is leading the transform
if(lastScPos.y() < sR.center().y()) {
newRect.setTop(scPos.y());
} else if(lastScPos.y() > sR.center().y()) {
newRect.setBottom(scPos.y());
}
zoomLevel = newRect.height()/sR.height();
}
activeItem->scale(zoomLevel, zoomLevel);
activeItem->setPos(activeItem->mapFromScene(newRect.topLeft()));


} else QGraphicsScene::mouseMoveEvent(event);
}

private:
QGraphicsItem *activeItem;
};

int main(int argc, char **argv){
QApplication app(argc, argv);
QGraphicsView view;
Scene scene;
view.setScene(&scene);
view.show();
scene.addPixmap(QPixmap("/usr/share/wallpapers/Midnight_in_Karelia/contents/screnshot.jpg"));
return app.exec();
}