QFileSystemModel custom icons? - c++

In my project, I have a QTreeView displaying a location on my drive. I need to change all the icons of the files to a custom icon but leave the folders alone.
I reimplemented QFileSystemModel and I was able to change ALL the icons. Any way to limit the change to only files instead of folders?
QVariant MyQFileSystemModel::data(const QModelIndex& index, int role) const
{
if(role == Qt::DecorationRole)
return QPixmap(":/icons/TAG_Int.png");
return QFileSystemModel::data(index, role);
}
This:
Becomes:
How can I only change the icon of the files?
Thanks for your time :)

I answered my own question:
QVariant MyQFileSystemModel::data( const QModelIndex& index, int role ) const {
if( role == Qt::DecorationRole )
{
QFileInfo info = MyQFileSystemModel::fileInfo(index);
if(info.isFile())
{
if(info.suffix() == "dat")
return QPixmap(":/icons/File_Icon.png");//I pick the icon depending on the extension
else if(info.suffix() == "mcr")
return QPixmap(":/icons/Region_Icon.png");
}
}
return QFileSystemModel::data(index, role);
}

Try to get filename and check is it a file or not. So it should be something like that:
QVariant MyQFileSystemModel::data(const QModelIndex& index, int role) const
{
if(role == Qt::DecorationRole)
{
QString name = index.data();//get filename
QFileInfo info(name);
if(info.isFile()) //check
return QPixmap(":/icons/TAG_Int.png");//return image if file
}
return QFileSystemModel::data(index, role); //if not, standard processing
}

Related

Add virtual column to Qt SQL model using a proxy

I display an SQL table in a view using a QSqlTableModel.
I want to display an additional status column based on the row data, for that I use a custom QIdentityProxyModel where I increase the columnCount and return data for that new virtual column which does not exist in the QSqlTableModel.
int MyProxyModel::columnCount(const QModelIndex& parent) const
{
return sourceModel() ? (sourceModel()->columnCount() + 1) : 0;
}
QVariant MyProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (section == columnCount()-1 &&
orientation == Qt::Horizontal &&
role == Qt::DisplayRole)
{
return tr("MyHeader");
}
return QIdentityProxyModel::headerData(section, orientation, role);
}
QVariant MyProxyModel::data(const QModelIndex &proxyIndex, int role) const
{
qDebug() << "data " << proxyIndex.row() << proxyIndex.column();
// ...never called for my extra column (== columnCount()-1)
if (proxyIndex.column() == columnCount()-1 && role == Qt::DisplayRole)
return QString("I am virtual");
return QIdentityProxyModel::data(proxyIndex, role);
}
Edit: I changed the code for something more simple regarding to the comments. I still have the same problem.
My problem is that the view never asks data for my virtual column, it calls data() for all other columns of the actual SQL table but not the last virtual one, what have I missed ?
Also, the header data is working well for my extra column, the problem is only with the data. The view draws the extra column, but content is empty (even alternating row background is not painted).
Thx !
The view needs to get the QModelIndex objects for the virtual column, so I also needed to override the index function in the proxy :
QModelIndex MyProxyModel::index(int row, int column, const QModelIndex &parent) const
{
if (column == columnCount()-1)
return createIndex(row, column);
return QIdentityProxyModel::index(row, column);
}
I didn't mind the parent because I only have a table (from database), though I do not know how it could be dealt with if needed because createIndex does not allow to specify a parent.
The m_mySqlTableColumnCount member is unnecessary. You'd have to ensure it's always correct by listening to the source model's signals that update the column count. Alas, it's unnecessary. You want to pass the column count request through to the source model:
int MyProxyModel::columnCount(const QModelIndex& parent) const
{
return sourceModel() ? (QIdentityProxyModel::columnCount() + 1) : 0;
}
Then:
QVariant MyProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (section == columnCount()-1 &&
orientation == Qt::Horizontal &&
role == Qt::DisplayRole)
{
return tr("MyHeader");
}
return QIdentityProxyModel::headerData(section, orientation, role);
}
QVariant MyProxyModel::data(const QModelIndex &proxyIndex, int role) const
{
if (proxyIndex.column() == columnCount()-1) {
qDebug() << proxyIndex.row() << proxyIndex.column();
...
}
return QIdentityProxyModel::data(proxyIndex, role);
}

How to add CheckBox on QTreeView + QFileSystemModel

I want to select some different folders in the treeview. There are two solution in QT like this:
QTreeView + QFileSystemModel, But how add the treebox in it? I dotn't know at all. In the same time, QFileSystemModel is asychronised, so after you choose a folder, then expand the directory, you will find the sub-folder were not choosen. How can I solve the this problem?
QTreeView + QDirModel, there is a good model and it work well:
http://www.programmershare.com/2041913/
But QDirModel is synchronised. So we have to wait a long time when choose a big folder. We can accept a long time, but how I can know the selection is finished?
Thanks anyway.
Your example should be tweaked a bit to use QFileSystemModel.
The trick is to declare the checkedIndexes set as mutable and update it inside the CFileSystemModel::data method.
QVariant CFileSystemModel::data(const QModelIndex &index, int role) const
{
if(role == Qt::CheckStateRole)
{
if (checkedIndexes.contains(index))
{
return checkedIndexes.contains(index) ? Qt::Checked : Qt::Unchecked;
}
else
{
int checked = Qt::Unchecked;
QModelIndex parent = index.parent();
while (parent.isValid())
{
if (checkedIndexes.contains(parent))
{
checked = Qt::Checked;
break;
}
parent = parent.parent();
}
if (checked == Qt::Checked)
{
checkedIndexes.insert(parent);
}
return checked;
}
}
else
{
return QFileSystemModel::data(index, role);
}
}
When you open a directory node in a view, QFileSystemModel starts to load new contents. After they are loaded, the view retieves new data using CFileSystemModel::data function, which checks if new nodes anchestors were checked and returns proper Qt::CheckStateRole value (and also updates the checkedIndexes set).

Display image in QTreeView

I'm trying to show an image in a QTreeView together with some other data. To to this I've created an QAbstractItemModel. In the data method, I return some strings for column index 0 and 1 and for the third I wish to return an image. This image however, is not being displayed.
When I return the image as a decoration it displays fine but I wish to add click listeners to the image that will trigger some events. I also wish to position my image to the far right in the treeview and this seems to require a custom delegate. For these reasons I've created a separate column for the image instead.
Image is created in constructor like this: mEditImage = QImage(":/images/myImage.png");
MyModel
QVariant MyModel::data(const QModelIndex &index, int role) const {
if (!index.isValid()) {
return QVariant();
}
if(role == Qt::FontRole) {
return fontData(index);
}
if(role == Qt::ForegroundRole) {
return foreGroundData(index);
}
MyModelItem *item = getItem(index);
/*
* Use decoration to display image. Probably needs a
* custom delegate to be able to position the image correctly.
* (http://www.qtcentre.org/threads/49639-decoration-position-and-alignment)
* (https://qt-project.org/forums/viewthread/24493)
*
* Will use extra column for now instead. Might help with click
* listeners as well.
*
* if(role == Qt::DecorationRole && item->parent() != mRootItem) {
return index.column() == 1 ? mEditImage : QVariant();
}*/
if(role == Qt::SizeHintRole) {
return mEditImage.size();
}
if (role == Qt::DisplayRole) {
QString id = QString::number(item->id());
QString name = item->name();
if(item->parent() != mRootItem && index.column() == 2) {
return mEditImage;
}
if(item->parent() == mRootItem){
return index.column() == 0 ? name : "";
} else {
return index.column() == 0 ? id : name;
}
} else if(role == Qt::BackgroundRole) {
return QVariant();
}
I've had a look here:
http://www.qtcentre.org/threads/29550-How-do-I-display-a-picture-on-a-QTableView-cell
How to set an image for a row?
I've tried changing the image to a QPixmap and QIcon and also tried embedding it in a QLabel (which could not be converted to a QVariant) without luck. Changing the image to a QString displays the string so the row/column logic seems fine. Removing the SizeHintRole logic doesn't make any difference either.
Any help in understanding how to display image data in a QTreeView would be helpful. I seem to be going at this from the wrong direction.
Cheers.
It doesn't matter, how to store image. QPixmap is prefferable for speed drawing.
QVariant MyModel::data(const QModelIndex &index, int role) const
{
item = itemFromIndex( index ); // Your item implementation
...
case Qt::DisplayRole:
return item->getText();
case Qt::DecorationRole:
return item->getImage();
}
Read Qt docs about roles, it's good - http://doc.qt.io/qt-4.8/qt.html#ItemDataRole-enum

How to return node name from a QTreeView

I've been working on a simple QTreeView of a local directory. The goal is allow the user to browse to his/her directory and select the correct csv file.
I've created a QFileSystemModel and displayed it with a QTreeView. I'm confused how to get the filename from the currently selected node.
I've read through the documentation and I've found the following signal/slot pairing:
connect(tree, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTreeWidgetEvent(QModelIndex)));
But I'm not sure what to do with the QModelIndex once activated. I know you're supossed to index the QTreeView with this index, but I'm not sure how.
Any help is greatly appreciated.
EDIT: Adding code so people can see what I'm doing.
QFileSystemModel *model = new QFileSystemModel;
model->setRootPath("/");
tree = new QTreeView;
tree->setModel(model);
tree->setRootIndex(model->index("/home/Missions/"));
tree->setColumnWidth(0, 350);
connect(tree, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTreeWidgetEvent(QModelIndex)));
WhatEverClassInheritingQObject::handleTreeWidgetEvent(const QModelIndex& index)
{
const QString valuablePathAskedFor(fileSystemModel->fileName(index));
...
}
you can retrieve the path as a QString in your setData method via filePath() method based just on the QModelIndex, so it will be called each time user has checked (or unchecked) some directory or file in your model being displayed, and then you need to store all these paths in some conatainer and implement method to return this:
bool MyQFileSystemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.column() != 0 || role != Qt::CheckStateRole)
return QFileSystemModel::setData(index, value, role);
int newCheckState = value.toInt();
QString filePath = filePath(index);
if (newCheckState == Qt::Checked || newCheckState == Qt::PartiallyChecked )
checkedPaths.insert(filePath);
else
checkedPaths.remove(filePath);
emit dataChanged(index, index.child(index.row(),0));
return true;
}
class MyQFileSystemModel : public QFileSystemModel
{
Q_OBJECT
public:
//...
QSet<QString> getChecked() const { return checkedPaths; }
private:
QSet<QString> chackedPaths;
//...
};

qlistview with checkboxes using custom model

I have subclassed filesystemmodel to include checkboxes in listview , which is working fine. My problem is whenever I click an item the text of that item disappears and when i click another item the text of previously selected item becomes visible. Can anyone please tell me the reason behind it.
Here is the code i implemented.
Please tell me what i am missing here,
Thanks
#include "custommodel.h"
#include <iostream>
using namespace std;
CustomModel::CustomModel()
{
}
QVariant CustomModel::data(const QModelIndex& index, int role) const
{
QModelIndex parent=this->parent(index);
if(role == Qt::DecorationRole)
{
if(this->filePath(parent)=="")
{
return QIcon(":/Icons/HardDisk.png");
}
else if(this->isDir(index))
{
QDir dir(this->filePath(index));
QFileInfoList files = dir.entryInfoList(QDir::NoDotAndDotDot |
QDir::Files | QDir::Dirs);
for(int file = 0; file < files.count(); file++)
if(files.count()>0)
return QIcon(":/Icons/FullFolder.png");
if(files.count()==0)
return QIcon(":/Icons/EmptyFolder.png");
}
else{
QFileInfo fi( this->filePath(index));
QString ext = fi.suffix();
if(ext=="jpeg"||ext=="jpg"||ext=="png"||ext=="bmp")
return QIcon(filePath(index));
}
}
if (role == Qt::CheckStateRole && !(this->filePath(parent)==""))
return checklist.contains(index) ? Qt::Checked : Qt::Unchecked;
return QFileSystemModel::data(index, role);
}
Qt::ItemFlags CustomModel::flags(const QModelIndex& index) const
{
return QFileSystemModel::flags(index)| Qt::ItemIsUserCheckable;
}
bool CustomModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (role == Qt::CheckStateRole) {
if (value == Qt::Checked)
checklist.insert(index);
else
checklist.remove(index);
emit dataChanged(index, index);
return true;
}
return QFileSystemModel::setData(index, value, role);
}
Not sure whether it's relevant, but I found the following note at:
http://doc.trolltech.com/4.6/qt.html#ItemFlag-enum
"Note that checkable items need to be given both a suitable set of flags and an initial state, indicating whether the item is checked or not. This is handled automatically for model/view components, but needs to be explicitly set for instances of QListWidgetItem, QTableWidgetItem, and QTreeWidgetItem."
As far as I can make out, your code looks correct -- but maybe try setting the ItemIsUserCheckable flag on the base QFileSystemModel class (in your custom constructor), and see if the inherited data() and setData() methods work with role=Qt::CheckStateRole. If you need to maintain a list of what's currently checked for some other reason, then go ahead and do so in your derived setData(), but still call QFileSystemModel::setData() as well.
Meanwhile, I'm looking for why my QTreeView doesn't update the timestamp when I modify a file (unless I quit and restart my app, kind of defeats the purpose!) ... looks like the dataChanged() signal isn't getting emitted.