Persistent text in a QTreeView delegate during edit mode - c++

I'm using a QTreeView with the default delegate to display editable model data. When I double-click or press F2 on the field I want to change, I get the text edit box, but the existing text is erased when the editor appears. I want the existing text to remain but become selected. The "editable tree model" example from the Qt documentation has this behavior exactly, however I can't for the life of me figure out how it is accomplished. The example does not use a custom delegate as far as I can tell and there are no calls related to delegate behavior that I can find. Can this be done without a custom delegate?
Edit: here's my code for the reimplemented QAbstractItemModel::data():
QVariant projectModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
node* item = static_cast<node*>(index.internalPointer());
if (role == Qt::DisplayRole)
return QVariant(item->data(index.column()).c_str());
else if (role == Qt::ForegroundRole)
return item->text_color(index.column());
else if (role == Qt::BackgroundRole)
return item->background_color(index.column());
else if (role == Qt::CheckStateRole)
return item->check_state(index.column());
else if (role == Qt::DecorationRole)
return item->icon(index.column());
else if (role == Qt::TextAlignmentRole)
return item->text_alignment(index.column());
else
return QVariant();
}

Your model should return a data, that you want to see in ediitor, via Qt::EditRole. If data is invalid (QVariant::isValid() == false) then editor will request data via Qt::DisplayRole.

Related

Why can't you use setData() to set background color of a Cell in QTreeView?

I'm using the following code to try to change the background color of a cell at a given QModelIndex.
ui->TreeView->model()->setData(index, QVariant(QBrush (QColor(Qt::green))) , Qt::BackgroundRole);
where index is given by the dataChanged() signal.
This isn't working. Any ideas why?
Here's my reimplemented setData function.
bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
TreeItem *item = getItem(index); //gets item a given index
bool result = item->setData(index.column(), value);
if (result)
emit dataChanged(index, index);
return result;
}
And here is the setData method for the underlying item:
bool TreeItem::setData(int column, const QVariant &value)
{
if (column < 0 || column >= itemData.size())
return false;
itemData[column] = value;
return true;
}
Apologies for the vague question. I've managed to solve it by myself so I will post here in case anyone is ever stuck on a similar issue.
The problem for me was that I hadn't reimplemented QAbstractItemView's data() method to account for the new role.
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
TreeItem *item = getItem(index);
if (role == Qt::BackgroundRole)
return item->data(index.column());
//and so on...
AFAIK the data() method gives the treeview the data out of the model that it needs to present. Within this method I hadn't accounted for the case when role == Qt::BackgroundRole so the view was never given the appropriate information out of the model.

QFileSystemModel custom icons?

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
}

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

Show different levels of C++ DOM model in different QML views

I need to create a QML application with pages of items that are defined by a xml file. The xml must be parsed in C++. Each page of items will be a StackView page containing a ListView of the items. Each item on a page has several values defining text, colour, size, etc.
As a start, my DOM model creation is based on the Qt Simple DOM Model Example. The model is wrapped with a QAbstractItemModel.
I've exposed the C++ model to QML using the rootContext->setContextProperty.
I'm struggling with splitting the data between the StackView pages. I assume that I need to assign the different levels of the hierarchical model(page parent and item children) to different UserRoles in order to filter them to the QML views but I'm struggling to find any suitable examples of how to go about this.
So my question is:
Can you show me an example of assigning UserRoles to a C++ DOM Model and the associated data method for returning the item data by UserRole and hierarchical level?
or
Am I going in the wrong direction and there's a better way to achieve this?
OK, I've now got this going. After some more playing I decided to select the data by xml Node name - I used "Page" & "Item". If anyone else is looking at hierarchical models in C++ and views in QML the other key requirement I found to implementing a solution is the QML DelegateModel (formally VisualDataModel). Here are the snippets that directly answer the questions...
From DomModel.h
public:
enum menuRoles
{
PageNumberRole = Qt::UserRole + 1,
PageNameRole,
ItemNumberRole,
ItemNameRole
};
...
QVariant data(const QModelIndex &index, int role) const;
...
protected:
QHash<int, QByteArray> roleNames() const;
From DomModel.cpp
QVariant DomModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
else
{
DomItem *item = static_cast<DomItem*>(index.internalPointer());
QDomNode node = item->node();
if(node.nodeName() == "Page")
{
switch (role)
{
case PageNumberRole:
return node.attributes().namedItem("number").nodeValue();
break;
case PageNameRole:
return node.attributes().namedItem("name").nodeValue();
break;
default:
return QVariant();
break;
}
}
else if(node.nodeName() == "Item")
{
switch (role)
{
case ItemNumberRole:
return node.attributes().namedItem("number").nodeValue();
break;
case ItemNameRole:
return node.attributes().namedItem("name").nodeValue();
break;
default:
return QVariant();
break;
}
}
else
return QVariant();
}
}
...
QHash<int, QByteArray> DomModel::roleNames() const
{
// This tells the subscribing views what data roles are available
// Any changes must be reflected in the DomModel::data function
QHash<int, QByteArray> roles;
roles[PageNumberRole] = "pageNumber";
roles[PageNameRole] = "pageName";
roles[ItemNumberRole] = "itemNumber";
roles[ItemNameRole] = "itemName";
return roles;
}

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.