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

Qt-jambi-interest Archive, August 2007
Handling a QObject from another thread


Message 1 in thread

Hi,
i'm trying to handle a QGLWidget from another thread to use it in the 3d  
engine Xith3D.
The QtJambi canvas i've implemented for Xith is almost working. It works  
in runmode "RUN_IN_SEPARATE_THREAD_AND_WAIT",
that's mean you have to generate a new frame yourself with the Xith  
command nextFrame().
If anyone is interested, you can find the sources of this canvas  
implementation here : http://xith.org/forum/index.php/topic,482.0.html

The problem i meet is that the QGLWidget is not refreshed  when i want to  
generate new frames fastly,
for example in a loop for displaying a 3D animation.

To handle the qGLWidget from another thread, i use  
QApplication.invokeLater(Runnable).
But the problem with this method is that the runnable is called only when  
the application is idle (doing nothing).

So, is there another fast way to update the QGLWidget from another thread ?
Something like QApplication.invokeNow(runnable) would be great !
Or a fonction similar to the SWT fonction syncExec(Runnable).


Here is an extract of my canvas for Xith3D :

public class CanvasPeerImplQtJambi extends CanvasPeerImplBase
{
...
	//this class/method updates the qGLWidget
     public class RenderRunnable extends QObject implements Runnable
     {
//        public Signal1<QGLWidget> qGLSignal = new Signal1<QGLWidget>();

         QGLWidget qGL;

	// not used
         public void setQGL(QGLWidget _qGLWidget)
         {
             this.qGL = _qGLWidget;
         }

         public void run()
         {
             System.out.println("frame " + frameId);
             if (!destroyed)
             {
                 isRendering = true;

                 EnvironmentCapabilities capabilities =  
EnvironmentCapabilities.getInstance();

                 qGL = qGLWidget; // this QGLWidget is created in the main  
thread
                 qGL.makeCurrent();
                 context.makeCurrent();

                 gl = context.getGL();
                 if (swapIntervalChanged)
                 {
                     swapIntervalChanged = false;
                     if (capabilities.isVSyncSwitchingAllowed( OPENGL_LAYER  
))
                     {
                         gl.setSwapInterval( getSwapInterval() );
                     }
                 }
                 display();

                 qGL.swapBuffers();
                 qGL.updateGL();
                 qGL.update();

                 context.release();
                 isRendering = false;
             }
         }
     }
...
//this method is called from the Xith loop in another thread
     private final void doRender() {
...
	//renderRunnable is instantied in the constuctor
        QApplication.invokeLater(renderRunnable);

     }

}


Message 2 in thread

Hi, Ludovic.

Ludovic Marcé wrote:
>
>
> Something like QApplication.invokeNow(runnable) would be great !
> Or a fonction similar to the SWT fonction syncExec(Runnable).
>

A method for synchronous invocation of functions on the gui thread is 
indeed planned for Qt Jambi 4.4.0_01 (the next feature release.) I'm 
also attaching a quick implementation of such functionality to this mail 
that you might be able to use in the meantime.

Use the static invokeAndWait() method, and make sure an event loop is 
running in the gui thread. Then the method will not return until the 
runnable has actually finished executing.

-- Eskil
import com.trolltech.qt.core.QCoreApplication;
import com.trolltech.qt.core.QCoreApplication;
import com.trolltech.qt.core.QEvent;
import com.trolltech.qt.core.QObject;
import com.trolltech.qt.gui.QApplication;

public class SynchronousInvokable extends QObject {
        static QEvent.Type SYNCHRONOUS_INVOKABLE_EVENT = QEvent.Type.resolve(QEvent.Type.User.value() + 1);
        
        private Runnable runnable;
        private SynchronousInvokable(Runnable runnable) {
            disableGarbageCollection();
            if(QCoreApplication.instance().nativeId() != 0) {
                moveToThread(QCoreApplication.instance().thread());
                this.runnable = runnable;
            }                       
            
            if (runnable == null || Thread.currentThread().equals(QApplication.instance().thread())) {
                runnable.run();
                invoked = true;
            }
            
        }

        private Boolean invoked = false;
        private synchronized void waitForInvoked() {
            while (!invoked) {
                try { 
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            invoked = false;
        }                   
        
        public static void invokeAndWait(Runnable runnable) {
            SynchronousInvokable invokable = new SynchronousInvokable(runnable);            
            QCoreApplication.postEvent(invokable, new QEvent(SYNCHRONOUS_INVOKABLE_EVENT));
            invokable.waitForInvoked();                        
            invokable.disposeLater();
        }
        
        @Override
        public final boolean event(com.trolltech.qt.core.QEvent e) {
            if (e.type() == SYNCHRONOUS_INVOKABLE_EVENT && runnable != null) {
                runnable.run();
                synchronized (this) {
                    invoked = true;
                    notifyAll();
                }                
                return true;
            }
            return super.event(e);
        }
            
        
    }

Message 3 in thread

On Fri, 24 Aug 2007 10:21:34 +0200, Eskil Abrahamsen Blomfeldt  
<eblomfel@xxxxxxxxxxxxx> wrote:

> Hi, Ludovic.
>
> Ludovic Marcé wrote:
>>
>>
>> Something like QApplication.invokeNow(runnable) would be great !
>> Or a fonction similar to the SWT fonction syncExec(Runnable).
>>
>
> A method for synchronous invocation of functions on the gui thread is
> indeed planned for Qt Jambi 4.4.0_01 (the next feature release.) I'm
> also attaching a quick implementation of such functionality to this mail
> that you might be able to use in the meantime.
>
> Use the static invokeAndWait() method, and make sure an event loop is
> running in the gui thread. Then the method will not return until the
> runnable has actually finished executing.
>
> -- Eskil

Thanks you !

But I don't know how to add an event loop in the gui thread.

Is it something like that :

         	QEventLoop eventLoop = new QEventLoop();
		QApplication.invokeLater( new Runnable() {
			public void run() {
				System.out.println("eventLoop");
				eventLoop.exec();
			}
		} );


Message 4 in thread

Hi, Ludovic.

Ludovic Marcé wrote:
> Thanks you !
>
> But I don't know how to add an event loop in the gui thread.
>
> Is it something like that :
>
>             QEventLoop eventLoop = new QEventLoop();
>         QApplication.invokeLater( new Runnable() {
>             public void run() {
>                 System.out.println("eventLoop");
>                 eventLoop.exec();
>             }
>         } );
>

By gui thread, I mean the main thread where your call to 
QApplication.initialize() is located. This is the only thread that is 
allowed to access GUI resources. The invokeLater() method cannot be used 
to start an event loop in this thread, as it depends on the event loop 
already running. Usually you use QApplication.exec() to start a 
synchronous event loop in the gui thread, as follows:

    public static void main(String args[]) {
       QApplication.initialize(args);

       // Set up stuff (start other threads etc.)

       QApplication.exec(); // <-- This call runs an event loop and does 
not return until the application quits
    }

You can also use QCoreApplication.processEvents() if you want more 
control over the event processing in the application.

See:

    
http://doc.trolltech.com/qtjambi-4.3.1_01/com/trolltech/qt/gui/QApplication.html#exec()

    
http://doc.trolltech.com/qtjambi-4.3.1_01/com/trolltech/qt/core/QCoreApplication.html#processEvents()

-- Eskil