Important: Please read the Qt Code of Conduct -

Threads and QGraphicsScene

  • I want to use threading to make my Qt app run smoother. I am using a thread instantiated using QtConcurrent::run that adds QGraphicsItems into a QGraphicsScene.

    The code to launch the thread in the main app looks like this:

    connect( &m_ThreadFuture , SIGNAL(finished()), this , SLOT(GraphicsDrawn()));
    m_ThreadFuture.setFuture( QtConcurrent::run( DrawGraphics ,  &m_Scene ));

    Where the DrawGraphics function takes a pointer to the QGraphicsScene object.

    To view, when the thread is finished, I assign the QGraphicsScene to a QGraphicsView using setScene; thats what the GraphicsDrawn SLOT is doing. It does work so I see the graphics I expect but the app is unstable and I see warnings in the Application Output window:

    QObject::startTimer: Timers cannot be started from another thread

    I am not using any timers in my App and all the QGraphicsItems are QGraphicsLineItems. If I don't use a thread and I call all the functionality within the main thread all is stable and no warnings appear in the Application Output window. Any thoughts?

    Thanks In Advance

    John T.

  • To my best knowledge, the QGraphicsView framework is not designed for multithreading.

    That said, there might well be alternatives to make your app run smoother. Have you actually identified item creation and initialization as one of the reasons for your problems? If so, what number of items are we talking about?

    Some wild ideas (without knowing the context):

    • Reuse items instead of deleting and new'ing them
    • Combine combinations of items you use often into one larger item (e.g. draw a polyline instead of inserting hundreds of QGraphicsLineItems)
    • While talking about lines: Don't draw with cosmetic pens of width > 1
    • Create a "initialization todo list". Have a timer run in the main thread. Every time the timer event is triggered, perform the initialization of x items, then yield control to keep the app responsive

  • Thanks for the reply. Yes I am almost certain you are correct. I have been looking at the stack traces when the app dies and in all cases the crash has something to do with an event and notification. Here is a stack trace:

    0	ntdll!RtlpNtMakeTemporaryKey	C:\Windows\SYSTEM32\ntdll.dll		0x770a56bd
    1	ntdll!EtwSetMark	C:\Windows\SYSTEM32\ntdll.dll		0x77071ab3	
    2	ntdll!RtlpNtMakeTemporaryKey	C:\Windows\SYSTEM32\ntdll.dll		0x770a6717	
    3	ntdll!EtwSetMark	C:\Windows\SYSTEM32\ntdll.dll		0x7706a13e	
    4	wcsnicmp	C:\Windows\SYSTEM32\ntdll.dll		0x770365a6	
    5	msvcrt!free	C:\Windows\system32\msvcrt.dll		0x758198cd	
    6	??			0x183f0000	
    7	QArrayData::deallocate		122	0x6b79a890	
    8	QTypedArrayData<QGraphicsItem*>::deallocate		222	0xd50b3f3	
    9	QVector<QGraphicsItem*>::freeData		484	0xd59b341	
    10	QVector<QGraphicsItem*>::~QVector		68	0xd59b4d2	
    11	QVector<QGraphicsItem*>::clear		385	0xd59ada9	
    12	QGraphicsScenePrivate::_q_polishItems		453	0xd47c4fa	
    13	QGraphicsScene::qt_static_metacall		180	0xd491d93	
    14	QMetaCallEvent::placeMetaCall		485	0x6b9536c0	
    15	QObject::event		1245	0x6b954485	
    16             QGraphicsScene::event		3471	0xd4856f3	
    17	QApplicationPrivate::notify_helper		3720	0xd1af4dd	
    18	QApplication::notify		3164	0xd1aceb0	
    19	QCoreApplication::notifyInternal		935	0x6b92ee90	
    20	QCoreApplication::sendEvent		228	0x6b9d4e9f	
    21	QCoreApplicationPrivate::sendPostedEvents		1552	0x6b930028	
    22	QCoreApplication::sendPostedEvents		1410	0x6b92fb0e	
    23	QWindowsGuiEventDispatcher::sendPostedEvents		81	0x2e5de899	
    24	qt_internal_proc(HWND__*, unsigned int, unsigned int, long)*16		412	0x6b97d638	
    25	gapfnScSendMessage	C:\Windows\system32\user32.dll		0x76bdc4e7	

    Looking at the man pages it appears that QGraphicsScene does emit a changed() signal whenever items are added so, if this is not in the main thread, I guess it could cause a problem.

    Thanks also for the advice:

    Create a "initialization todo list". Have a timer run in the main thread. Every time the timer event is triggered,
    perform the initialization of x items, then yield control to keep the app responsive.

    Funnily enough this is the way the original X/Motif application that I am porting actually works. I was just trying to be clever using threads; you win some you lose some.

    Thanks again for the response and the interest.

    John T.

Log in to reply