Results 1 to 9 of 9

Thread: QtJambi: QTimer can only be used with threads started with QThread

  1. #1
    Join Date
    Apr 2008
    Posts
    7
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Question QtJambi: QTimer can only be used with threads started with QThread

    Hi, I have been trying to solve this problem for while now, but failed: I want to perform long operations (multiple http get) in a separate thread, to not block the UI.

    While the operations worked when done in the main thread, as soon as I move them in a separate thread, they stopped being triggered. This was the initial problem that I wanted to submit for your review.

    As the code is quite long with multiple classes, I designed a smaller piece of code to post here (still not so small, so that you can have a good view of my technique, which is obvioulsy wrong somewhere). Unfortunately, when I simulated the http requests with timers, I got an additional problem with timers : the message "QObject::startTimer: QTimer can only be used with threads started with QThread" is displayed twice on the console.

    I start with this second problem, because I feel it is something critical to understand before going further. Then I will modify my exemple to ask for the initial problem linked probably to signals.

    I believe the timer problem is linked to the lack of event loop due to the way I create the additional thread. I searched hard on the topic, but now give up and need to ask someone clever. I believe the solution for QtJambi may be similar but not identical to the one for C++.

    I apologize for the length of the code. I'd be grateful if you could comment my code and provide an advice in this complex (for me) combination of threads and signals.

    Thanks by advance.

    I have two classes: a main class that post requests, and the handler which processes requests and sends a signal to the posting object at completion of each request. The handler is supposed to self-destroy when all requests have been answered.

    Main class:

    Qt Code:
    1. package test;
    2. import com.trolltech.qt.gui.QApplication;
    3. import com.trolltech.qt.gui.QCheckBox;
    4. import com.trolltech.qt.gui.QVBoxLayout;
    5. import com.trolltech.qt.gui.QWidget;
    6. /**
    7.  * Creates a UI with two check boxes representing status of operations.
    8.  * Request two "long operations" to be performed using a special handler.
    9.  * Updates the check boxes according to the completion status of the
    10.  * operation. Is non blocking.
    11.  */
    12. public class Test extends QWidget {
    13. // static main
    14. public static void main(String[] args) {
    15. QApplication.initialize(args);
    16. new Test().show();
    17. QApplication.exec();
    18. }
    19.  
    20. // to display the status of each requested operation.
    21. private QCheckBox checkBox1;
    22. private QCheckBox checkBox2;
    23.  
    24. /**
    25.   * Constructor
    26.   */
    27. public Test(){
    28. // construct some UI with two checkboxes
    29. checkBox1 = new QCheckBox("Op 1 completed", this);
    30. checkBox2 = new QCheckBox("Op 2 completed", this);
    31. setLayout(new QVBoxLayout());
    32. layout().addWidget(checkBox1);
    33. layout().addWidget(checkBox2);
    34.  
    35. // create a long operation handler and connect its signal
    36. LongOpHandler op = new LongOpHandler();
    37. op.opCompleted.connect(this, "opCompleted(int)");
    38.  
    39. // request two "long operations"
    40. op.enqueue(1);
    41. op.enqueue(2);
    42. }
    43.  
    44. /**
    45.   * Slot for completion of an operation
    46.   * @param id the number of the operation, i.e. 1 or 2
    47.   */
    48. public void opCompleted(int id) {
    49. // just update the status of the operations
    50. if (id == 1) checkBox1.setChecked(true); else checkBox2.setChecked(true);
    51. }
    52. }
    To copy to clipboard, switch view to plain text mode 


    Handler:

    Qt Code:
    1. package test;
    2. import java.util.Random;
    3. import com.trolltech.qt.QSignalEmitter;
    4. import com.trolltech.qt.QThread;
    5. import com.trolltech.qt.core.QObject;
    6. import com.trolltech.qt.core.QTimer;
    7. /**
    8.  * Handler for long operations. Allows operations queueing
    9.  * and emit a signal when all operations are completed.
    10.  * Uses its own thread to not block the application.
    11.  */
    12. public class LongOpHandler extends QObject implements Runnable {
    13. public QSignalEmitter.Signal1<Integer> opCompleted = new QSignalEmitter.Signal1<Integer>();
    14.  
    15. private boolean allDone = false; // flag for all operations completed
    16. private Object timer1 = null; // reference to timer1
    17. private boolean op1Requested = false; // simulate queueing of operation 1
    18. private boolean op2Requested = false; // simulate queueing of operation 2
    19. private boolean op1Done = false; // flag for completion of operation 1
    20. private boolean op2Done = false; // flag for completion of operation 2
    21. /**
    22.   * Starts a new thread so that the requester is not blocked
    23.   * while requested operations are performed
    24.   */
    25. public LongOpHandler() {
    26. // rename current thread for convenience
    27. thread().setName("Thread-for-UI");
    28. // start a new thread to execute run(), give it a name for convenience
    29. new QThread(this, "Thread-for-operations").start();
    30. }
    31.  
    32. /**
    33.   * Simulate the queueing of an operation.
    34.   * @param opId the number of the operation to queue: 1 or 2
    35.   */
    36. public synchronized void enqueue(int opId) {
    37. // queue the requested operation, it will be started within run()
    38. if (opId == 1) op1Requested = true; else op2Requested = true;
    39. // wake up the Thread-for-operations in case it is executing wait()
    40. notifyAll();
    41. }
    42.  
    43. /**
    44.   * Method executed in a separate thread to avoid blocking of the main
    45.   * thread by long operations. Operations here are simulated by timers.
    46.   * Queued operations are dequeued and executed. When all operations
    47.   * have been executed, run() is exited. wait() is call when there is
    48.   * nothing to do. Queueing and completion of operation must wake up the
    49.   * separate thread executing wait().
    50.   */
    51. public synchronized void run() {
    52. // simulated operations
    53. QTimer timer1 = new QTimer();
    54. QTimer timer2 = new QTimer();
    55.  
    56. // store reference to timer1 at instance level to allow access to its value
    57. this.timer1 = timer1;
    58.  
    59. // connect signals to slots
    60. timer1.timeout.connect(this, "done()");
    61. timer2.timeout.connect(this, "done()");
    62.  
    63. // loop to monitor new requests and completion of all requests
    64. while (!allDone) {
    65. // look for queued requests
    66. if (op1Requested) {
    67. timer1.setSingleShot(true);
    68. timer1.setInterval(new Random().nextInt(500) + 500);
    69. // QTimer.start() triggers:
    70. // "QObject::startTimer: QTimer can only be used with threads started with QThread"
    71. timer1.start();
    72. op1Requested = false;
    73. op1Done = false;
    74. }
    75.  
    76. if (op2Requested) {
    77. timer2.setSingleShot(true);
    78. timer2.setInterval(new Random().nextInt(500) + 500);
    79. // QTimer.start() triggers:
    80. // "QObject::startTimer: QTimer can only be used with threads started with QThread"
    81. timer2.start();
    82. op2Requested = false;
    83. op2Done = false;
    84. }
    85.  
    86. // test if all operations are completed
    87. // for the purpose of this test, "all" means timers 1 and 2 timed out
    88. allDone = op1Done && op2Done;
    89.  
    90. // wait for something to do
    91. while (!allDone) {
    92. try {wait();} catch (InterruptedException e) {}
    93. // update allDone flag
    94. allDone = op1Done && op2Done;
    95. }
    96. }
    97. // operations now completed, resources created by Thread-for-operations
    98. // will be destroyed after exiting run()
    99. }
    100.  
    101. @SuppressWarnings("unused")
    102. private synchronized void done() {
    103. // determine which operation has completed
    104. int opId = (signalSender() == timer1) ? 1 : 2;
    105.  
    106. // inform the requester of the completion
    107. opCompleted.emit(opId);
    108.  
    109. // record the completion
    110. if (opId == 1) op1Done = true; else op2Done = true;
    111.  
    112. // release Thread-for-operations if it is waiting
    113. notifyAll();
    114. }
    115. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by further; 25th May 2008 at 15:43.

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QtJambi: QTimer can only be used with threads started with QThread

    Can't you derive your thread from QThread and call QThread::exec() to start the event loop? Then you'll be able to get rid of your while() loop and base everything on signals and slots.

  3. #3
    Join Date
    Apr 2008
    Posts
    7
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QtJambi: QTimer can only be used with threads started with QThread

    Thanks Wysota. I've a problem with subclassing QThread in QtJambi. It seems the class is final, if I refer to http://java.trunat.fr/qtjambi/com/tr...t/QThread.html though Trolltech documentation doesn't mention this point (because it just forward to the Qt description, which obviously can be inherited in C++. Similarly there is no exec method in the QtJambi version of QThread (cf. same doc above). Java programmers are a little bit more alone than C++ colleagues .

    The fact is that Eclipse doesn't allow me to subclass QThread in Java. Anyway there is a constructor which return a QThread instance from a Runnable object. I think I have already tried this method too.

    I'll check again. I just don't understand the meaning of the error message. Thanks for the suggestion. Will let you know the outcome.

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QtJambi: QTimer can only be used with threads started with QThread

    From what I see you should not subclass QThread but simply use it.

    public QThread(java.lang.Runnable target)

    This constructor takes a runnable object that will be executed as the new thread. As for exec() I don't see a solution in the docs... If you have a Qt commercial licence, I suggest contacting Trolltech Support about it.

  5. The following user says thank you to wysota for this useful post:

    further (27th May 2008)

  6. #5
    Join Date
    Apr 2008
    Posts
    7
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QtJambi: QTimer can only be used with threads started with QThread

    I don't see any other way to create a QThread object than using new QThread(Runnable object).start() like I did. I appears to me that QThread is a final class in QtJambi and doesn't provide exec(). About getting rid of the loop for signal/slot construct, could you be more specific? Thanks.

  7. #6
    Join Date
    Apr 2008
    Posts
    7
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Lightbulb Re: QtJambi: QTimer can only be used with threads started with QThread

    It seems there is no solution with QtJambi, I've posted a request for support in the Trolltech news group for QtJambi (I've only the open source version), but noone has something to propose so far. Could be a very puzzling limitation of the Java implementation of Qt. Will look if JFace which is Java native could offer a better choice. Thanks anyway for your suggestion.

  8. #7
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QtJambi: QTimer can only be used with threads started with QThread

    Send a question to qtbugs at trolltech.com, they might suggest a solution. Just make sure you submit it as a bug report or a suggestion, not a direct request for help.

  9. #8
    Join Date
    Apr 2008
    Posts
    7
    Thanks
    1
    Qt products
    Qt4
    Platforms
    Windows

    Thumbs up Re: QtJambi: QTimer can only be used with threads started with QThread

    Many thanks Wysota for contacting Trolltech, this can help many of us. I had a second thought about being too much dependent on the deployment and strategy of QtJambi. If Swing is weak and not so modern, the other Java libraries should be sufficiently powerful, so I'm now rewriting my application using Sun libraries for non-UI needs.

  10. #9
    Join Date
    Mar 2013
    Posts
    1
    Qt products
    Qt/Embedded
    Platforms
    Windows

    Default Re: QtJambi: QTimer can only be used with threads started with QThread

    Quote Originally Posted by further View Post
    Hi, I have been trying to solve this problem for while now, but failed: I want to perform long operations (multiple http get) in a separate thread, to not block the UI.

    While the operations worked when done in the main thread, as soon as I move them in a separate thread, they stopped being triggered. This was the initial problem that I wanted to submit for your review.

    As the code is quite long with multiple classes, I designed a smaller piece of code to post here (still not so small, so that you can have a good view of my technique, which is obvioulsy wrong somewhere). Unfortunately, when I simulated the http requests with timers, I got an additional problem with timers : the message "QObject::startTimer: QTimer can only be used with threads started with QThread" is displayed twice on the console.

    I start with this second problem, because I feel it is something critical to understand before going further. Then I will modify my exemple to ask for the initial problem linked probably to signals.

    I believe the timer problem is linked to the lack of event loop due to the way I create the additional thread. I searched hard on the topic, but now give up and need to ask someone clever. I believe the solution for QtJambi may be similar but not identical to the one for C++.

    I apologize for the length of the code. I'd be grateful if you could comment my code and provide an advice in this complex (for me) combination of threads and signals.

    Thanks by advance.

    I have two classes: a main class that post requests, and the handler which processes requests and sends a signal to the posting object at completion of each request. The handler is supposed to self-destroy when all requests have been answered.

    Main class:

    Qt Code:
    1. package test;
    2. import com.trolltech.qt.gui.QApplication;
    3. import com.trolltech.qt.gui.QCheckBox;
    4. import com.trolltech.qt.gui.QVBoxLayout;
    5. import com.trolltech.qt.gui.QWidget;
    6. /**
    7.  * Creates a UI with two check boxes representing status of operations.
    8.  * Request two "long operations" to be performed using a special handler.
    9.  * Updates the check boxes according to the completion status of the
    10.  * operation. Is non blocking.
    11.  */
    12. public class Test extends QWidget {
    13. // static main
    14. public static void main(String[] args) {
    15. QApplication.initialize(args);
    16. new Test().show();
    17. QApplication.exec();
    18. }
    19.  
    20. // to display the status of each requested operation.
    21. private QCheckBox checkBox1;
    22. private QCheckBox checkBox2;
    23.  
    24. /**
    25.   * Constructor
    26.   */
    27. public Test(){
    28. // construct some UI with two checkboxes
    29. checkBox1 = new QCheckBox("Op 1 completed", this);
    30. checkBox2 = new QCheckBox("Op 2 completed", this);
    31. setLayout(new QVBoxLayout());
    32. layout().addWidget(checkBox1);
    33. layout().addWidget(checkBox2);
    34.  
    35. // create a long operation handler and connect its signal
    36. LongOpHandler op = new LongOpHandler();
    37. op.opCompleted.connect(this, "opCompleted(int)");
    38.  
    39. // request two "long operations"
    40. op.enqueue(1);
    41. op.enqueue(2);
    42. }
    43.  
    44. /**
    45.   * Slot for completion of an operation
    46.   * @param id the number of the operation, i.e. 1 or 2
    47.   */
    48. public void opCompleted(int id) {
    49. // just update the status of the operations
    50. if (id == 1) checkBox1.setChecked(true); else checkBox2.setChecked(true);
    51. }
    52. }
    To copy to clipboard, switch view to plain text mode 


    Handler:

    Qt Code:
    1. package test;
    2. import java.util.Random;
    3. import com.trolltech.qt.QSignalEmitter;
    4. import com.trolltech.qt.QThread;
    5. import com.trolltech.qt.core.QObject;
    6. import com.trolltech.qt.core.QTimer;
    7. /**
    8.  * Handler for long operations. Allows operations queueing
    9.  * and emit a signal when all operations are completed.
    10.  * Uses its own thread to not block the application.
    11.  */
    12. public class LongOpHandler extends QObject implements Runnable {
    13. public QSignalEmitter.Signal1<Integer> opCompleted = new QSignalEmitter.Signal1<Integer>();
    14.  
    15. private boolean allDone = false; // flag for all operations completed
    16. private Object timer1 = null; // reference to timer1
    17. private boolean op1Requested = false; // simulate queueing of operation 1
    18. private boolean op2Requested = false; // simulate queueing of operation 2
    19. private boolean op1Done = false; // flag for completion of operation 1
    20. private boolean op2Done = false; // flag for completion of operation 2
    21. /**
    22.   * Starts a new thread so that the requester is not blocked
    23.   * while requested operations are performed
    24.   */
    25. public LongOpHandler() {
    26. // rename current thread for convenience
    27. thread().setName("Thread-for-UI");
    28. // start a new thread to execute run(), give it a name for convenience
    29. new QThread(this, "Thread-for-operations").start();
    30. }
    31.  
    32. /**
    33.   * Simulate the queueing of an operation.
    34.   * @param opId the number of the operation to queue: 1 or 2
    35.   */
    36. public synchronized void enqueue(int opId) {
    37. // queue the requested operation, it will be started within run()
    38. if (opId == 1) op1Requested = true; else op2Requested = true;
    39. // wake up the Thread-for-operations in case it is executing wait()
    40. notifyAll();
    41. }
    42.  
    43. /**
    44.   * Method executed in a separate thread to avoid blocking of the main
    45.   * thread by long operations. Operations here are simulated by timers.
    46.   * Queued operations are dequeued and executed. When all operations
    47.   * have been executed, run() is exited. wait() is call when there is
    48.   * nothing to do. Queueing and completion of operation must wake up the
    49.   * separate thread executing wait().
    50.   */
    51. public synchronized void run() {
    52. // simulated operations
    53. QTimer timer1 = new QTimer();
    54. QTimer timer2 = new QTimer();
    55.  
    56. // store reference to timer1 at instance level to allow access to its value
    57. this.timer1 = timer1;
    58.  
    59. // connect signals to slots
    60. timer1.timeout.connect(this, "done()");
    61. timer2.timeout.connect(this, "done()");
    62.  
    63. // loop to monitor new requests and completion of all requests
    64. while (!allDone) {
    65. // look for queued requests
    66. if (op1Requested) {
    67. timer1.setSingleShot(true);
    68. timer1.setInterval(new Random().nextInt(500) + 500);
    69. // QTimer.start() triggers:
    70. // "QObject::startTimer: QTimer can only be used with threads started with QThread"
    71. timer1.start();
    72. op1Requested = false;
    73. op1Done = false;
    74. }
    75.  
    76. if (op2Requested) {
    77. timer2.setSingleShot(true);
    78. timer2.setInterval(new Random().nextInt(500) + 500);
    79. // QTimer.start() triggers:
    80. // "QObject::startTimer: QTimer can only be used with threads started with QThread"
    81. timer2.start();
    82. op2Requested = false;
    83. op2Done = false;
    84. }
    85.  
    86. // test if all operations are completed
    87. // for the purpose of this test, "all" means timers 1 and 2 timed out
    88. allDone = op1Done && op2Done;
    89.  
    90. // wait for something to do
    91. while (!allDone) {
    92. try {wait();} catch (InterruptedException e) {}
    93. // update allDone flag
    94. allDone = op1Done && op2Done;
    95. }
    96. }
    97. // operations now completed, resources created by Thread-for-operations
    98. // will be destroyed after exiting run()
    99. }
    100.  
    101. @SuppressWarnings("unused")
    102. private synchronized void done() {
    103. // determine which operation has completed
    104. int opId = (signalSender() == timer1) ? 1 : 2;
    105.  
    106. // inform the requester of the completion
    107. opCompleted.emit(opId);
    108.  
    109. // record the completion
    110. if (opId == 1) op1Done = true; else op2Done = true;
    111.  
    112. // release Thread-for-operations if it is waiting
    113. notifyAll();
    114. }
    115. }
    To copy to clipboard, switch view to plain text mode 
    thank you for sharing the code with us...!!!
    I was searching for a few days for this little help...!!!

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.