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 ]