PDA

View Full Version : Custom QDirModel



dude
10th December 2007, 19:20
Hello everybody!

I'm using Qt 4.2.3 and facing the following problem:

I have a QTreeView and need to fill it with the local filesystem contents. I can use QDirModel for that purpose. But I need a custom top-level item in that tree, i.e. My Documents folder. So the tree should be looking as this:

|-MyDocs
|-C:
|-D:

Is that possible to achive that effect in some easy way, like inheriting from the QDirModel?

P.S. Somewhere in Trolltech's lists I found the QFSFileEngine class. Maybe it can help in some way?

Thanks in advance

wysota
10th December 2007, 19:43
I think the easiest way is to wrap QDirModel into a simple proxy model that will add an additional level of items on top of QDirModel. Especially that it's only a single item... I admit I don't see a reason for such a requirement, though... Maybe a title is enough?

dude
10th December 2007, 19:53
Any link regarding example of wrapping QDirModel with a proxy model? :) I'm completely lost in Qt's docs on model/view

About a single item. It's customer's requirement so I can't just throw away it. By the way, the custom item is supposed to be able to expand and show My Documents folder contents. So a title is not a solution :)

dude
10th December 2007, 19:55
Ok, I found QSortFilterProxyModel. But I cannot see how it can help me. :(

wysota
10th December 2007, 21:35
Any link regarding example of wrapping QDirModel with a proxy model?
No, sorry - you are the first one to do it.


About a single item. It's customer's requirement so I can't just throw away it.
But you can convince your customer. Often people think and say they want something even if they don't need it.


By the way, the custom item is supposed to be able to expand and show My Documents folder contents. So a title is not a solution :)
If it's a single item then when it is not expanded the view is useless and can be disabled or left empty. It's one of obvious GUI design rules. Besides, a view with a single item looks silly...



Ok, I found QSortFilterProxyModel. But I cannot see how it can help me. :(

It can't. You are looking for QAbstractProxyModel. You need to subclass it and implement mapToSource(), mapFromSource() and reimplement QAbstractItemModel::data(). But I still think the requirement is silly and you should avoid implementing it. Instead just enable or disable the view when needed.

dude
11th December 2007, 09:43
I think that there's maybe a misunderstanding. Maybe I explained the problem not too clear. :)

I'll try to describe it better.
So I need to insert a custom filesystem folder above the logical disks in a QTreeView using the QDirModel. That can be any user folder. For now it's a My Documents folder. I need that item to behave just like other filesystem items in the tree - expanding/collapsing, showing its contents, etc.


But you can convince your customer. Often people think and say they want something even if they don't need it.
I agree. But unfortunately, I'm not responsible for agreeing on project requirements, and they are already approved. I have to implement the task.


If it's a single item then when it is not expanded the view is useless and can be disabled or left empty
Yes, but to leave the item empty, I need to insert it in the tree in some way at first. :)


You are looking for QAbstractProxyModel. You need to subclass it and implement mapToSource(), mapFromSource() and reimplement QAbstractItemModel::data().
Can you please give me a hint on implementing these methods? As I understood, the main function of interest is mapToSource (). So if QDirModel stores indexes for the system logic drives as [0,1,2,...], I will need to shift them by value of 1, and when the model index equals 0, I'll need to perform some custom actions. But I can't understand how to do this

wysota
11th December 2007, 09:58
I'll try to describe it better.
So I need to insert a custom filesystem folder above the logical disks in a QTreeView using the QDirModel. That can be any user folder. For now it's a My Documents folder. I need that item to behave just like other filesystem items in the tree - expanding/collapsing, showing its contents, etc.
Believe me, I understand.


I agree. But unfortunately, I'm not responsible for agreeing on project requirements, and they are already approved. I have to implement the task.
Then use a proxy as advised.


Yes, but to leave the item empty, I need to insert it in the tree in some way at first. :)
Not the item - the view. It's like having a combobox with one item...



Can you please give me a hint on implementing these methods? As I understood, the main function of interest is mapToSource (). So if QDirModel stores indexes for the system logic drives as [0,1,2,...], I will need to shift them by value of 1, and when the model index equals 0, I'll need to perform some custom actions. But I can't understand how to do this

Hmm... I guess we might have misunderstood each other. I thought you meant:

My Documents
+--Drive 1
+--Drive 2
...

but it seems you want:
+My Documents
+Drive 1
+Drive 2
...

So My Documents is not "above" Drive 1 and Drive 2 but "before" them... right?

In that case I suggest you subclass QDirModel as you'll want to provide some data that doesn't exist in the model. Or do you just want to show the contents of some subfolder on one of the disks as children of the added item? If the latter, then you can go with a proxy. You just need to return one more row for an invalid parent, shift row numbers by 1 in mapToSource and mapFromSource for invalid parent and return the model index of the "linked" folder instead of the (0,0, invalid) index. You might have a problem the other way round as a source index of the target folder can be mapped into two indexes of the proxy and you'll have to decide which one to choose.

dude
11th December 2007, 10:30
but it seems you want:
+My Documents
+Drive 1
+Drive 2


Yes, that's what I need :)


So My Documents is not "above" Drive 1 and Drive 2 but "before" them... right?

Yes


In that case I suggest you subclass QDirModel as you'll want to provide some data that doesn't exist in the model.

I think that what I really need. I thought about it earlier but couldn't find any example of subclassing QDirModel neither at Trolltech, nor anywhere else :(


Or do you just want to show the contents of some subfolder on one of the disks as children of the added item? If the latter, then you can go with a proxy.
I didn't quite catch that :)

wysota
11th December 2007, 10:33
I think that what I really need. I thought about it earlier but couldn't find any example of subclassing QDirModel neither at Trolltech, nor anywhere else :(
That's because it's not meant to be subclassed.


I didn't quite catch that :)

"My Documents" is usually a link to some other real folder on the disk. I mean exactly such situation. Otherwise you'll have to craft the contents of the folder yourself.

dude
11th December 2007, 10:57
I really appreciate you're still trying to help me, but unfortunately I still don't see if QDirModel really fits my needs. I spend two days already just trying to find a way to implement my needs, but it seems to me that I'd better stay with good old QTreeWidget :)
The Interview framework takes too much much effort to make use of, unfortunately

wysota
11th December 2007, 11:45
Maybe if you try to explain your exact goal (not an intermediate goal but the ultimate one), we'll be able to help you better.

dude
11th December 2007, 12:28
What I currently trying to do is a Windows Explorer-like application. So it has two panes. The left shows directory structure. The right-one is for the contents of a directory.

It's generally easy to implement, except a one specific reqiurement: the customer wants the directory tree to be showing the My Documents folder at the top of the hierarchy, alongside the system logical drives . It's suuposed to let user an easy access to his personal files. I know it sounds kinda silly, but it is what I currently need.

Without that specific requirement it would take me about several hours to implement the task using QDirModel (without getting deep into Interview framework). Also using old-style QTreeWidget would take me to the result in 1-2 days.

But I was hoping to follow the new Trolltech approch, i.e. Interveiw framework, and actually failed with that. If you provide me with an small piece of code, I would be very grateful for your help.

wysota
11th December 2007, 12:58
It's generally easy to implement, except a one specific reqiurement: the customer wants the directory tree to be showing the My Documents folder at the top of the hierarchy, alongside the system logical drives . It's suuposed to let user an easy access to his personal files. I know it sounds kinda silly, but it is what I currently need.

So I was right - the extra item points to some existing folder, correct?


Also using old-style QTreeWidget would take me to the result in 1-2 days.
True, but then you'd have to implement some functionality to introduce additional things to your application and you'd be spending days biting your nails thinking how to do this while QDirModel would support it out of the box. Convinience classes are nice for simple things, but the more complex the subject, the more problems with them and the more benefit of using a model approach.


If you provide me with an small piece of code, I would be very grateful for your help.

Let's see... I don't have time to write a complete and tested solution, but here are some hints.
Two essential functions are mapToSource() and mapFromSource() in your proxy model. A basic functionality of mapToSource() can look like this:

QModelIndex Proxy::mapToSource(const QModelIndex &ind) const {
QModelIndex mydoc = ((QDirModel*)sourceModel())->index("/home/wysota/Dokumenty");
if(ind.parent().isValid()){
return ind; // this could just work... if not, you need to map it to the source model
}
// parent not valid - drives level
if(ind.row()==0) return mydoc; // return fixed index for the documents folder
return sourceModel()->index(ind.row()-1, ind.column());
}

If you provide those two methods and the rowCount() like this:

int Proxy::rowCount(const QModelIndex &index) const {
int origrows = sourceModel()->rowCount(mapToSource(index));
return index.isValid() ? origrows : origrows+1;
}

you should have the basic functionality already working.

jpn
11th December 2007, 13:00
Isn't "My Documents" a link to a documents directory in QDir::home()? Therefore you could just fake an item and actually return children for QDirModel::index(QDir::homePath())..

wysota
11th December 2007, 13:03
Isn't "My Documents" a link to a documents directory in QDir::home()? Therefore you could just fake an item and actually return children for QDirModel::index(QDir::homePath())..

That's exactly what I'm suggesting. There are two ways to do it - either subclass QDirModel and manipulate indexes there or implement a proxy and manipulate indexes there.

dude
11th December 2007, 13:36
So I was right - the extra item points to some existing folder, correct?
Yes


Convinience classes are nice for simple things, but the more complex the subject, the more problems with them and the more benefit of using a model approach.
Mayb it's true when you're deep into that model/view. And I'm currently not. As for me, the Qt's docs on this theme are not too clear and lack in-depth examples.


Let's see... I don't have time to write a complete and tested solution, but here are some hints.

Thanks indeed. There's no need for a full working solution :) I'll try to use your code and give a feedback. Thank you


Isn't "My Documents" a link to a documents directory in QDir::home()? I'm not too sure on this. QAssistant says that function return that path:

C:/Documents and Settings/Username But I actually need C:/Documents and Settings/Usernam\MyDocuments.


Therefore you could just fake an item and actually return children for QDirModel::index(QDir::homePath())..
If that's true about home(), coutld you please give a mini-example? :)

Zandru
11th December 2007, 14:10
did you have a look at QFileDialog?

it allows you to customize the "shortcut URLs" on the left side, by simple drag&drop.

Maybe this would make your customer happy?

mchara
12th December 2007, 07:06
Hi,
I've already tried to write model you need

My model didn't used modelProxy(i didn't know something like this exists) so i've subclassed QDirModel with own setWinFilePath etc. methods(those original aren't virtual).

I actually left it behind because of some strange behavior i couldn't debug(it wasn't as important as time consuming).
I had also custom iconProvider(that's in use and generates thumbnails for images for standard QDirModel) so i had to refresh model after thumbnails changes(because QDirModel have internally cached data). Model crashes on refreshing after entering/leaving my documents (i think the fact that same modelIndex can be retrieved via 2 different paths causes some recursion in QDirModels internal cache). Maybe you could figure it out - i would be glad to get it back in use.


Maybe you can even use it as is, if you don't need refreshes.

Attached model returns same values as QDirModel
change theRefreshing = true to false in CWinDirModelPrivate constructor to get behavior with my computer & myDocuments. It's because i blocked added feature instead of removing whole class hoping i'll fix it later, but there's still lots of other things to do so its pending.

Oh, almost forgot - im getting myDocuments path by


QString CWinDirModel::getMyDocsPath()const
{
WCHAR docs[1024];
LPITEMIDLIST doc = new ITEMIDLIST;

SHGetSpecialFolderLocation(qApp->activeWindow()->winId(), CSIDL_PERSONAL ,&doc);
SHGetPathFromIDList(doc, docs);
char cast[1024];
for(int i=0; i < 1024;i++)
cast[i] = docs[i];
return QString(cast);
}