Results 1 to 7 of 7

Thread: Keeping your application responsive

  1. #1
    Join Date
    Jun 2008
    Posts
    88
    Thanks
    4
    Thanked 4 Times in 3 Posts
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11
    Wiki edits
    1

    Default Keeping your application responsive

    A little background... I've built a thumbnail view similar to what is outlined in this post. My previous version of a thumbnail view involved a QFlowLayout, a QScrollArea and a custom thumbnail widget. I also created a thumbnailing service, which is essentially a class with several thumbnail loading threads. Ask the service for a thumbnail, it hands off the job to a thread, emits a signal returning a QImage to the service, which emits a signal with a QPixmap to the requester.

    To make it faster and more responsive I've done the following:

    1. Use a QListWidget instead of custom widgets. This is way faster and more responsive. It uses the delayed layout feature of the QListWidget so I can easily resize the icons and maintain some responsiveness
    2. The thumbnail service batches up results from the thumbnail thread and only emits a signal after a timer has gone off, so as to not emit signals too often if the thumbnail loading threads finish in quick succession.


    The slow part seems to be when the thumbnail service gets QImages back and it converts them to QPixmaps but its a little hard to tell since this program is difficult to profile.

    Anyway, I wanted to share my technique for creating a responsive application and see if anyone has other ideas or techniques to keep the ui interactive while processing.

  2. #2
    Join Date
    Jul 2009
    Posts
    74
    Thanks
    2
    Thanked 6 Times in 6 Posts

    Default Re: Keeping your application responsive

    qtconcurrent is another approach you should try

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

    Default Re: Keeping your application responsive

    [wiki]Keeping the GUI Responsive[/wiki]
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  4. #4
    Join Date
    Jul 2009
    Posts
    74
    Thanks
    2
    Thanked 6 Times in 6 Posts

    Default Re: Keeping your application responsive

    wysota... do you know an EASY book or website to start learning about threads and pararell programming (basic concepts)? it's focused on Qt it would be nice.
    thanks

  5. #5
    Join Date
    Jun 2008
    Posts
    88
    Thanks
    4
    Thanked 4 Times in 3 Posts
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11
    Wiki edits
    1

    Default Re: Keeping your application responsive

    Good link. Aside from some details, that describes basically what my thumbnailing service does.

    The problem I ran into was that my thumbnailing service sometimes produces thumbnails so fast that the UI thread is swamped with repaint events, and causes the UI to become unresponsive.

    The way I approached this was to have my thumbnail scaling/loading threads emit a signal with the QImage it generated. The thumbnail service adds that thumbnail to a queue of thumbnails that need to be dispatched. Then it waits for a timer to go off. The timeout() signal is connected to a slot that dispatches the thumbnails:

    Pseudo code...

    Qt Code:
    1. void ThumbnailService::thumbnailLoadedSlot(QString path, QImage image) {
    2. thumbnailQueue.insert(path, image);
    3. timer.start();
    4. }
    5.  
    6. void ThumbnailService::processImageQueue() {
    7. timer.stop();
    8. QMapIterator<QString, QImage> it(thumbnailQueue);
    9. while (it.hasNext()) {
    10. it.next();
    11. QString path = it.key();
    12. QImage image = it.value();
    13.  
    14. // converting to a QPixmap is a slow operation
    15. QPixmap result = QPixmapCache.insert(path, QPixmap::fromImage(image));
    16. emit(SIGNAL(finished(QString, QPixmap)), path, image);
    17. }
    18. if (!thumbnailQueue.isEmpty())
    19. timer.start();
    20. }
    To copy to clipboard, switch view to plain text mode 

    The good thing is, if it takes a long time to thumbnail a directory of images, its actually a very responsive widget. The only time I have problems is when the thumbnails come back too quickly.

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

    Default Re: Keeping your application responsive

    Quote Originally Posted by javimoya View Post
    wysota... do you know an EASY book or website to start learning about threads and pararell programming (basic concepts)? it's focused on Qt it would be nice.
    Well... Silbershatz's book on operating systems is a classic. Besides that I don't know any such resources. In my opinion my Qt Quarterly article filled some of the gaps regarding Qt perfectly when it comes to practical approach to SIMD. If you need something specific then say so, I can try to do some research and write more about it. But basically it takes a lot of thinking and practice to understand threading and parallel programming on a decent level. You can look for articles on "grid computing" (or cloud computing), that's a popular subject nowadays and there are a lot of conferences dedicated to it, like this one: http://www.ppam.pl/ to just name the one I participated in (and it helped start my interest in parallel programming). The basic thing you should undestand is how processes in your OS work, then you can learn about threads and synchronization and then you can focus on more high-level aspects like parallel programming, SIMD and stuff like that. OpenCL or equivalent should then follow if you want a many-core (note, not "multi-core") solutions.

    Oh, and good computer science studies also help a lot

    Quote Originally Posted by chezifresh View Post
    Good link. Aside from some details, that describes basically what my thumbnailing service does.
    The main difference is that the solution described in the article adjusts to your machine and is more high-level.

    The problem I ran into was that my thumbnailing service sometimes produces thumbnails so fast that the UI thread is swamped with repaint events, and causes the UI to become unresponsive.
    There is an easy solution - process the data you receive in batches. The simplest way is to use a timer, like so:
    Qt Code:
    1. void MyModel::newImageReady(const QImage &img) {
    2. if(!m_batchTimer.isActive()) m_batchTimer.start(1000); // timer is single shot
    3. m_pending << img;
    4. if(m_pending.size()>=10) {
    5. // force update when 10 images are waiting
    6. m_batchTimer.stop();
    7. batchTimerTimeout();
    8. }
    9. }
    10.  
    11. void MyModel::batchTimerTimeout() {
    12. int minRow = rowCount(), maxRow = -1;
    13. foreach(const QImage &img, m_pending) {
    14. QModelIndex idx = indexFromText(img.text("index"));
    15. minRow = qMin(minRow, idx.row());
    16. maxRow = qMax(maxRow, idx.row());
    17. QPixmap px = QPixmap::fromImage(img);
    18. m_data[idx.row()].pixmap = px;
    19. }
    20. m_pending.clear();
    21. emit dataChanged(index(minRow, 0), index(maxRow, columnCount()-1));
    22. }
    To copy to clipboard, switch view to plain text mode 

    The way I approached this was to have my thumbnail scaling/loading threads emit a signal with the QImage it generated. The thumbnail service adds that thumbnail to a queue of thumbnails that need to be dispatched. Then it waits for a timer to go off. The timeout() signal is connected to a slot that dispatches the thumbnails:
    I should really read posts to the end before answering them Yeah, good approach but mine is better - queue in the model, not in the service, worker threads can't create pixmaps.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


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

    javimoya (30th March 2011)

  8. #7
    Join Date
    Jun 2008
    Posts
    88
    Thanks
    4
    Thanked 4 Times in 3 Posts
    Qt products
    Qt3 Qt4
    Platforms
    MacOS X Unix/X11
    Wiki edits
    1

    Default Re: Keeping your application responsive

    Quote Originally Posted by wysota View Post
    The main difference is that the solution described in the article adjusts to your machine and is more high-level.
    I'm doing some adjustments based on the machine. The problem is, I'm using PyQt, which is missing a lot of QtConcurrent APIs. All I have to work with are QThread and QThreadPool. So I just create QtCore.QThreadPool.maxThreadCount() - 1 threads (save one core for the ui thread) to do all of the work. I'm pretty sure it would be better if I could use QRunnable and QFutureWatcher though. They probably have a good reason for not implementing them, like the fact that Python cannot be truely multi-threaded do to the GIL, but QThread seems to get me the performance I need in terms of background, high I/O latency operations.


    Quote Originally Posted by wysota View Post
    ... queue in the model, not in the service
    I actually started off doing something very similar. I think my problem is that I have two at least two widgets that display the thumbnails and I didn't implement my own model. In other words I have a QListWidget and a QTreeWidget for a thumbnail view and detail list view respectively, as opposed to a QListView and QTreeView using the same model. That may turn out to be a mistake, but I think what I have might at least turn out to be an incremental improvement. A shared model makes sense in all sorts of ways, but thats a really good suggestion

    Quote Originally Posted by wysota View Post
    worker threads can't create pixmaps
    Definitely. The worker threads I currently emit a signal with the path and a QImage, which goes directly to the thumbnailing service, or thumbnail manager which runs in the main ui thread. In the main ui thread it then converts the QImage to a QPixmap and broadcasts that the QPixmap is available.

    I'll try what you've suggested in terms of batches, I was doing it time based, so that the foreach broke out if it took more than 0.1 secs to process the items in the queue. What your said might lead to more consistent results.

Similar Threads

  1. keeping aspect ratio while resizing
    By franco.amato in forum Qt Programming
    Replies: 2
    Last Post: 19th November 2009, 20:12
  2. Replies: 6
    Last Post: 20th July 2009, 05:48
  3. keeping a widget to be square
    By jmsbc in forum Qt Programming
    Replies: 5
    Last Post: 16th June 2009, 19:47
  4. How to keep the GUI responsive?
    By drjones in forum Qt Programming
    Replies: 8
    Last Post: 16th July 2008, 14:33
  5. Keeping focus at bottom of QListView
    By jakamph in forum Qt Programming
    Replies: 4
    Last Post: 10th January 2006, 14:45

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.