Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 2

Qt-interest Archive, April 2008
Qt Graphics Framework is not completely reentrant


Message 1 in thread

I'm not sure if this can be considered a bug, but I did not find any notice
in the docs that the graphics framework should not be fully reentrant.

My case is this:

The scene is derived from QGraphicsScene and reimplements the mouse move
event to draw a crosshair cursor over the whole scene.
Adding a rectangle to this scene and setting the flags ItemIsMovable and
ItemIsSelectable, about 50% of the rectangles added are not selectable and
not movable. When the drawing of the crosshair is disabled, every rectangle
added works as expected. 

I am using Qt 4.3.3 commercial on Windows XP.

Regards,
Acenes

--
 [ signature omitted ] 

Message 2 in thread

On Sun, Apr 06, 2008 at 03:31:17PM +0200, Acenes wrote:
> I'm not sure if this can be considered a bug, but I did not find any notice
> in the docs that the graphics framework should not be fully reentrant.
> 
> My case is this:
> 
> The scene is derived from QGraphicsScene and reimplements the mouse move
> event to draw a crosshair cursor over the whole scene.
> Adding a rectangle to this scene and setting the flags ItemIsMovable and
> ItemIsSelectable, about 50% of the rectangles added are not selectable and
> not movable. When the drawing of the crosshair is disabled, every rectangle
> added works as expected. 
> 
> I am using Qt 4.3.3 commercial on Windows XP.

I'm not clear on how your problem involves reentrancy, but here is the
documentation concerning it:

http://doc.trolltech.com/4.3/threads.html#reentrancy-and-thread-safety

> Regards,
> Acenes
--Greg

--
 [ signature omitted ] 

Message 3 in thread

> I'm not clear on how your problem involves reentrancy, but here is the
documentation concerning it

So most of Qt including the graphics framework is not thread-safe.

My example is a reantrance problem because items are drawn/modified by the
main thread, as well as by the mouse event which is basicly a separate
thread because of ist asynchronous execution.
 
Thanks for the pointer, Greg.

Regards,
Acenes

--
 [ signature omitted ] 

Message 4 in thread

> My example is a reantrance problem because items are drawn/modified by
> the
> main thread, as well as by the mouse event which is basicly a separate
> thread because of ist asynchronous execution.
> 
> Thanks for the pointer, Greg.
> 
> Regards,
> Acenes
No...  Mouse events are processed in the same thread as the main
thread... no re-reentrancy issue here.  That said... it may very well be
a race condition...

Scott

--
 [ signature omitted ] 

Message 5 in thread

Also... Post a minimal example of the code... And we would probably be
able to help out in finding the bug...



> -----Original Message-----
> From: Acenes [mailto:acenes@xxxxxxx]
> Sent: Sunday, April 06, 2008 7:23 AM
> To: qt-interest@xxxxxxxxxxxxx
> Subject: AW: Qt Graphics Framework is not completely reentrant
> 
> > I'm not clear on how your problem involves reentrancy, but here is
> the
> documentation concerning it
> 
> So most of Qt including the graphics framework is not thread-safe.
> 
> My example is a reantrance problem because items are drawn/modified by
> the
> main thread, as well as by the mouse event which is basicly a separate
> thread because of ist asynchronous execution.
> 
> Thanks for the pointer, Greg.
> 
> Regards,
> Acenes
> 
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with
> "unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/

--
 [ signature omitted ] 

Message 6 in thread

> Also... Post a minimal example of the code... And we would probably be
> able to help out in finding the bug...

Below is a stripped down sample. In fact the problem is even worse with this
sample, because disabling the crosshair does not help here at all while it
did the trick in the real application. I guess it is really something like a
race condition.

The sample works this way: 

- Click on the "add" button and then go to the drawing space. 
- Click down and pull up the rectangle. 
  The rectangle you see while pulling up is handled by "Scene" and only
temporary.
- At mouse button release, signal Scene::rectangleMade is emitted and
creates the real rectangle
  in Workspace::createRectangle.
- The created rectangle should be selectable and movable, but is not in most
cases.



#include <QtGui>

#define WIDTH 400
#define HEIGHT 300

/***************************************************************************
***
 * S C E N E
 
****************************************************************************
**/

class Scene : public QGraphicsScene
{
	Q_OBJECT
	enum Mode { Idle, MakeRect, DrawRect };

public:
	Scene(QObject* aParent) : QGraphicsScene(aParent)
	{
		mMode = Idle;

		// crosshair vertical
		mVcursor = addLine(0.0, 0.0, 0.0, HEIGHT,
QPen(QColor(Qt::lightGray)));
		mVcursor->setEnabled(false);
		mVcursor->hide();

		// crosshair horizontal
		mHcursor = addLine(0.0, 0.0, WIDTH, 0.0,
QPen(QColor(Qt::lightGray)));
		mHcursor->setEnabled(false);
		mHcursor->hide();

		// temp rectangle to pull up
		mRect = addRect(QRectF());
		mRect->hide();
	} // constructor

public slots:
	void showCrosshair(bool aShow)
	{
		mVcursor->setVisible(aShow);
		mHcursor->setVisible(aShow);
	} // showCrosshair

	void makeRectangle()
	{
		mMode = MakeRect;
	} // makeRectangle

signals:
	void rectangleMade(const QRectF& aRectangle);

protected:
	void mousePressEvent(QGraphicsSceneMouseEvent* aMouseEvent)
	{
		if (mMode==MakeRect &&
aMouseEvent->button()==Qt::LeftButton) {
			QPointF pos = aMouseEvent->scenePos();
			mRect->setRect(QRectF(pos,pos));
			mRect->show();
			mMode = DrawRect;
		} else
			QGraphicsScene::mousePressEvent(aMouseEvent);
	} // mousePressEvent

	void mouseMoveEvent(QGraphicsSceneMouseEvent* aMouseEvent)
	{
		QPointF pos = aMouseEvent->scenePos();
		if (mHcursor->y() != pos.y()) mHcursor->setPos(0.0,
pos.y());
		if (mVcursor->x() != pos.x()) mVcursor->setPos(pos.x(),
0.0);
		if (mMode == DrawRect) {
			QRectF rect = mRect->rect();
			rect.setBottomRight(pos);
			mRect->setRect(rect);
		} // if
	} // mouseMoveEvent 

	void mouseReleaseEvent(QGraphicsSceneMouseEvent* aMouseEvent)
	{
		if (mMode == DrawRect) {
			QRectF rect = mRect->rect();
			rect.setBottomRight(aMouseEvent->scenePos());
			mRect->hide();
			mMode = Idle;
			emit rectangleMade(mRect->rect());
		} // if
	} // mouseReleseEvent

private:
	Mode mMode;
	QGraphicsRectItem* mRect;
	QGraphicsLineItem* mVcursor;
	QGraphicsLineItem* mHcursor;
}; // Scene

/***************************************************************************
***
 * V I E W
 
****************************************************************************
**/

class View : public QGraphicsView
{
	Q_OBJECT

public:
	View(QGraphicsScene* aScene, QWidget* aParent = 0) 
		: QGraphicsView(aScene, aParent)
	{
		setMinimumSize(WIDTH, HEIGHT);
		setMaximumSize(WIDTH, HEIGHT);
		setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
		setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
		setCursor(Qt::CrossCursor);
		setFixedSize(WIDTH, HEIGHT);
		setSceneRect(0.0, 0.0, WIDTH, HEIGHT);
		setInteractive(true);
	} // constructor

signals:
	void showCrosshair(bool aShow);

protected:
	void enterEvent(QEvent* aEvent) { emit showCrosshair(true); }
	void leaveEvent(QEvent* aEvent) { emit showCrosshair(false); }
}; // view

/***************************************************************************
***
 * W O R K S P A C E
 
****************************************************************************
**/

class Workspace : public QWidget
{
	Q_OBJECT

public:
	Workspace(QWidget *aParent = 0)	: QWidget(aParent)
	{
		mScene = new Scene(this);
		mView = new View(mScene, this);
		mAddButton = new QPushButton(tr("Add Rectangle"));
		QHBoxLayout* layout = new QHBoxLayout;
		layout->addWidget(mView);
		layout->addWidget(mAddButton);
		setLayout(layout);
		connect(mView, SIGNAL(showCrosshair(bool)), 
				mScene, SLOT(showCrosshair(bool)));
		connect(mAddButton, SIGNAL(clicked()), 
				mScene, SLOT(makeRectangle()));
		connect(mScene, SIGNAL(rectangleMade(const QRectF&)),
				this, SLOT(createRectangle(const QRectF&)));
	} // constructor

private slots:
	void createRectangle(const QRectF& aRectangle)
	{
		QGraphicsRectItem* rect = mScene->addRect(aRectangle);
		rect->setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsSelectable);
	} // createRectangle

private:
	Scene* mScene;
	View* mView;
	QPushButton* mAddButton;
}; // Workspace


/***************************************************************************
***
 * M A I N
 
****************************************************************************
**/

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	Workspace w;
	w.show();
	a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
	return a.exec();
} // main

#include "main.moc"


--
 [ signature omitted ] 

Message 7 in thread

Sorry for bugging you with this. I figured out that the problem of the
sample is in the mouse move and release methods where the event did not get
forwarded to the base class. So those methods should be corrected as below.
Still strange that some created rectangles do work while others dont in my
app.

	void mouseMoveEvent(QGraphicsSceneMouseEvent* aMouseEvent)
	{
		QPointF pos = aMouseEvent->scenePos();
		if (mHcursor->y() != pos.y()) mHcursor->setPos(0.0,
pos.y());
		if (mVcursor->x() != pos.x()) mVcursor->setPos(pos.x(),
0.0);
		if (mMode == DrawRect) {
			QRectF rect = mRect->rect();
			rect.setBottomRight(pos);
			mRect->setRect(rect);
		} // if
		QGraphicsScene::mouseMoveEvent(aMouseEvent);
	} // mouseMoveEvent 

	void mouseReleaseEvent(QGraphicsSceneMouseEvent* aMouseEvent)
	{
		if (mMode == DrawRect) {
			QRectF rect = mRect->rect();
			rect.setBottomRight(aMouseEvent->scenePos());
			mRect->hide();
			mMode = Idle;
			emit rectangleMade(mRect->rect());
		} else
			QGraphicsScene::mouseReleaseEvent(aMouseEvent);
	} // mouseReleseEvent


--
 [ signature omitted ] 

Message 8 in thread

Hello Acenes,
 in this example you highlight a common misuse of the QGraphcisScene :-) You 
break the mouse handling mechanism of QGraphicsScene (the mechanism that by 
default selects and move objects!) in your own reimplementation.

Try to apply this patch and see that your example works as expected:
----------------------------
--- main.cpp.orig	2008-04-06 20:07:35.000000000 +0200
+++ main.cpp	2008-04-06 20:07:09.000000000 +0200
@@ -76,7 +76,8 @@
                         QRectF rect = mRect->rect();
                         rect.setBottomRight(pos);
                         mRect->setRect(rect);
-                } // if
+                } else
+                        QGraphicsScene::mouseMoveEvent(aMouseEvent);
         } // mouseMoveEvent 
 
         void mouseReleaseEvent(QGraphicsSceneMouseEvent* aMouseEvent)
@@ -87,7 +88,8 @@
                         mRect->hide();
                         mMode = Idle;
                         emit rectangleMade(mRect->rect());
-                } // if
+                } else
+                        QGraphicsScene::mouseReleaseEvent(aMouseEvent);
         } // mouseReleseEvent
 
 private:
----------------------------

I called the default implementation, as you did on the mousePressEvent, but 
forgot to do on the others ;-)

Cheers, 
 Enrico

On Sunday 06 April 2008 19:42:06 Acenes wrote:
> #include <QtGui>
>
> #define WIDTH 400
> #define HEIGHT 300
>
> /**************************************************************************
>* ***
>  * S C E N E
>  
> ***************************************************************************
>* **/
>
> class Scene : public QGraphicsScene
> {
>         Q_OBJECT
>         enum Mode { Idle, MakeRect, DrawRect };
>
> public:
>         Scene(QObject* aParent) : QGraphicsScene(aParent)
>         {
>                 mMode = Idle;
>
>                 // crosshair vertical
>                 mVcursor = addLine(0.0, 0.0, 0.0, HEIGHT,
> QPen(QColor(Qt::lightGray)));
>                 mVcursor->setEnabled(false);
>                 mVcursor->hide();
>
>                 // crosshair horizontal
>                 mHcursor = addLine(0.0, 0.0, WIDTH, 0.0,
> QPen(QColor(Qt::lightGray)));
>                 mHcursor->setEnabled(false);
>                 mHcursor->hide();
>
>                 // temp rectangle to pull up
>                 mRect = addRect(QRectF());
>                 mRect->hide();
>         } // constructor
>
> public slots:
>         void showCrosshair(bool aShow)
>         {
>                 mVcursor->setVisible(aShow);
>                 mHcursor->setVisible(aShow);
>         } // showCrosshair
>
>         void makeRectangle()
>         {
>                 mMode = MakeRect;
>         } // makeRectangle
>
> signals:
>         void rectangleMade(const QRectF& aRectangle);
>
> protected:
>         void mousePressEvent(QGraphicsSceneMouseEvent* aMouseEvent)
>         {
>                 if (mMode==MakeRect &&
> aMouseEvent->button()==Qt::LeftButton) {
>                         QPointF pos = aMouseEvent->scenePos();
>                         mRect->setRect(QRectF(pos,pos));
>                         mRect->show();
>                         mMode = DrawRect;
>                 } else
>                         QGraphicsScene::mousePressEvent(aMouseEvent);
>         } // mousePressEvent
>
>         void mouseMoveEvent(QGraphicsSceneMouseEvent* aMouseEvent)
>         {
>                 QPointF pos = aMouseEvent->scenePos();
>                 if (mHcursor->y() != pos.y()) mHcursor->setPos(0.0,
> pos.y());
>                 if (mVcursor->x() != pos.x()) mVcursor->setPos(pos.x(),
> 0.0);
>                 if (mMode == DrawRect) {
>                         QRectF rect = mRect->rect();
>                         rect.setBottomRight(pos);
>                         mRect->setRect(rect);
>                 } // if
>         } // mouseMoveEvent
>
>         void mouseReleaseEvent(QGraphicsSceneMouseEvent* aMouseEvent)
>         {
>                 if (mMode == DrawRect) {
>                         QRectF rect = mRect->rect();
>                         rect.setBottomRight(aMouseEvent->scenePos());
>                         mRect->hide();
>                         mMode = Idle;
>                         emit rectangleMade(mRect->rect());
>                 } // if
>         } // mouseReleseEvent
>
> private:
>         Mode mMode;
>         QGraphicsRectItem* mRect;
>         QGraphicsLineItem* mVcursor;
>         QGraphicsLineItem* mHcursor;
> }; // Scene
>
> /**************************************************************************
>* ***
>  * V I E W
>  
> ***************************************************************************
>* **/
>
> class View : public QGraphicsView
> {
>         Q_OBJECT
>
> public:
>         View(QGraphicsScene* aScene, QWidget* aParent = 0)
>                 : QGraphicsView(aScene, aParent)
>         {
>                 setMinimumSize(WIDTH, HEIGHT);
>                 setMaximumSize(WIDTH, HEIGHT);
>                 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
>                 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
>                 setCursor(Qt::CrossCursor);
>                 setFixedSize(WIDTH, HEIGHT);
>                 setSceneRect(0.0, 0.0, WIDTH, HEIGHT);
>                 setInteractive(true);
>         } // constructor
>
> signals:
>         void showCrosshair(bool aShow);
>
> protected:
>         void enterEvent(QEvent* aEvent) { emit showCrosshair(true); }
>         void leaveEvent(QEvent* aEvent) { emit showCrosshair(false); }
> }; // view
>
> /**************************************************************************
>* ***
>  * W O R K S P A C E
>  
> ***************************************************************************
>* **/
>
> class Workspace : public QWidget
> {
>         Q_OBJECT
>
> public:
>         Workspace(QWidget *aParent = 0) : QWidget(aParent)
>         {
>                 mScene = new Scene(this);
>                 mView = new View(mScene, this);
>                 mAddButton = new QPushButton(tr("Add Rectangle"));
>                 QHBoxLayout* layout = new QHBoxLayout;
>                 layout->addWidget(mView);
>                 layout->addWidget(mAddButton);
>                 setLayout(layout);
>                 connect(mView, SIGNAL(showCrosshair(bool)),
>                                 mScene, SLOT(showCrosshair(bool)));
>                 connect(mAddButton, SIGNAL(clicked()),
>                                 mScene, SLOT(makeRectangle()));
>                 connect(mScene, SIGNAL(rectangleMade(const QRectF&)),
>                                 this, SLOT(createRectangle(const
> QRectF&))); } // constructor
>
> private slots:
>         void createRectangle(const QRectF& aRectangle)
>         {
>                 QGraphicsRectItem* rect = mScene->addRect(aRectangle);
>                 rect->setFlags(QGraphicsItem::ItemIsMovable |
> QGraphicsItem::ItemIsSelectable);
>         } // createRectangle
>
> private:
>         Scene* mScene;
>         View* mView;
>         QPushButton* mAddButton;
> }; // Workspace
>
>
> /**************************************************************************
>* ***
>  * M A I N
>  
> ***************************************************************************
>* **/
>
> int main(int argc, char *argv[])
> {
>         QApplication a(argc, argv);
>         Workspace w;
>         w.show();
>         a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
>         return a.exec();
> } // main
>
> #include "main.moc"



Message 9 in thread

> in this example you highlight a common misuse of the QGraphcisScene :-) 
> You break the mouse handling mechanism of QGraphicsScene (the mechanism
that by default selects and move objects!) in your own reimplementation.


Hi Enrico,

Yes, that was a bug in the stripped down sample, but in the real app it was
that way. 
In the real app, about one out of 5 rectangles created are not
selectable/movable.

In the sample even with the fixes the problem is still present, however it
is much more seldom.
One time I needed to create 20 rects until a dead one appeared, and one time
even 50.

So still there must be some basic other problem in the application.

Kind Regards,
Acenes


--
 [ signature omitted ]