PDA

View Full Version : Design issue - using QThread to populate QTableView/ItemModel



gig-raf
10th March 2015, 12:55
I am writing a small mp3 player and are having a problem with the playlist that I have writen using QTableView with QStandardItemModel. I am not wanting to use the build in playlist from QMultimedia as I am using this method to learn about Qt and C++.

Problem:
When I add files to the playlist (Drag and drop) the files are processed with TAGLIB and the mp3 tags are then populated into a taleview.
When the number of files dragged into the playlist exceeds 2000+ the main gui freezes up.

So it is clear I need to run the population of the playlist in a thread.

Question:

how would I fill the model with data from within the thread when the model lives in the main only? Should or can i give my model as references when I call the thread?

What would be the safest way of implementing such a solution where the worker thread has to update an object in another thread (main), not wanting to pass large result sets.


Any tips is most appriciated!

anda_skoa
10th March 2015, 14:45
So it is clear I need to run the population of the playlist in a thread.

Any specific insight to why that "is clear"?
Have you determined that the TagLib part is causing the slowdown?



how would I fill the model with data from within the thread when the model lives in the main only?

You can do the taglib stuff in a separete thread and then send the data over to the main thread which then adds it to the model.

Cheers,
_

gig-raf
11th March 2015, 13:14
Thanks for answering.


No, I think TagLib is not the problem. It just take long time to read 5000 tags from a HardDrive. The problem is that when parsing the mp3 files via the main thread, the whole gui locks up.
-This is why I think it is clear that I need to do the mp3 parsing in a thread, and populate the model only when all data is ready.

I know some tools will parse the mp3 tags and store them in a media database and populate the model sorely from a database lookup. This is good approach, then the parsing and inserting into the Database should be done via threads. Don't you agree?


So what I am doing now is as suggested by you. I send the URLS to a seperate thread, TAGLIB is then parsing and storing the data in a QLIST, when done it emits a signal to the main thread which will then populate the model.
THis works, I will test this tonight with a large set of data.

The main concern here is if it is thread safe.


once again thanks for you time!!!

anda_skoa
11th March 2015, 13:50
Yes, a cross-thread signal/slot connection is thread-safe, the receiver slot will be called in the context of the thread owning the receiver object.
(assuming you don't explicitly override this by passing Qt::DirectConnection as the fifth argumen to connect())

Cheers,
_

d_stranz
11th March 2015, 16:34
This is why I think it is clear that I need to do the mp3 parsing in a thread, and populate the model only when all data is ready.

No, it isn't clear at all why any multithreading is necessary. What is necessary is to implement parsing tags and populating the model in a way that keeps the UI responsive. Try reading Wysota's excellent Qt Quarterly article (http://doc.qt.digia.com/qq/qq27-responsive-guis.html) on the numerous ways to do that, including the use of threads.

gig-raf
12th March 2015, 12:30
Hi Guys,

I beleive I might have found out why my main thread was hanging.

..and I am starting to believe that threading the mp3 parsing is not needed at all, as suggested by d_stranz

I went ahead and implemented the threaded mp3 extraction using taglib. The parsing of 5000 files was very very fast! but whenever I was to insert the tags into my model the gui was again locking up.

Only at this time did I realize that I was autofitting the column width after every inserted row!!! remove this, and calling it only once at the very end fixed my locking problem.

I went back to my old way of doing the mp3 parsing, without the use of threads and to my surpise there was no problem at all with the freeze ups!


QUESTION
Is it not still a better idea to make the tag parsing in a thread. I mean fine, the main thread is not locking up, but what if I was to add 1 million files? Would it never freeze up?

Another question, If I do go with a threaded solution, what would then be the best way to update the model. Should i do this from within the main thread, or from the "parser thread"?.
-could I pass the model as a reference (signal) to the thread?



Thanks for your help!

anda_skoa
12th March 2015, 13:40
You can of course still to the parsing in a helper thread if you think a single file might take too long to be acceptable as delay for the UI.

On the question of when to modify the standard item model, you can only do that in the main thread.
A custom model could potentially do part of it in the worker thread, but it would become very complicated and not really worth the effort.

Cheers,
_

gig-raf
13th March 2015, 09:20
Thank you all for your time and support! it is much appriciated!

I will study the "Wysota's excellent Qt Quarterly article" too, looks very interesting indeed.

Cheers!

d_stranz
17th March 2015, 17:18
Is it not still a better idea to make the tag parsing in a thread. I mean fine, the main thread is not locking up, but what if I was to add 1 million files? Would it never freeze up?

Probably not, depending on how fast the parsing is. QTableView only updates the view of the model that is actually visible on screen. So unless you figure out a way to get a million rows visible at one time, the only thing that will be shown is the 50 or so rows that are actually visible.

You could also implement your model so that it does parsing on demand - only parse that which is requested by the table view, by implementing QAbstractItemModel::fetchMore() and QAbstractItemModel::canFetchMore().

Or do the parsing in the QAbstractItemModel::data() method - use a flag in your model to indicate whether the tag information has been parsed, and if not, parse it, save it in the model, and set the flag to shown it is now available. You can do this on a row-by-row basis.

In either case, you'll just need to have the list of file names available so you can read the tags when needed. Presumably you are storing the file names in your model anyway so the only real work you'd need to do when loading the model is to save these names. The tag extraction to fill the rest of the fields in your model can come later.