PDA

View Full Version : How to update items in a QTreeView



laugusti
11th August 2009, 16:09
Hi all,

I am a newbie using Qt and I am stuck with this problem. Let me resume ...

I am trying to display a tree containing nodes representing resource items.
Those items have several attributes (image id, ...) and booleans such as resourceVisible [true|false].

I am able to display the tree with icons representing my booleans (using a delegate). I also use a QSortFilterProxyModel to filter the rows.

Now I would like to update the model when the user click on the icon representing the boolean resourceVisible. But I can't have it work :-\

Here is my program

Structure defining a resource:


struct ResourceTreeItem {

ResourceTreeItem(std::string resourceID=""):m_resourceID(resourceID), m_resourceVisible(true), m_contentVisible(true),
m_boundaryBoxVisible(true), m_mosaicked(true), m_IDVisible(true) {}

ResourceTreeItem(const ResourceTreeItem& copy): m_resourceID(copy.m_resourceID), m_resourceVisible(copy.m_resourceVisible), m_contentVisible(copy.m_contentVisible),
m_boundaryBoxVisible(copy.m_boundaryBoxVisible), m_mosaicked(copy.m_mosaicked), m_IDVisible(copy.m_IDVisible) {}

std::string m_resourceID;
bool m_resourceVisible;
bool m_contentVisible;
bool m_boundaryBoxVisible;
bool m_mosaicked;
bool m_IDVisible;
};

A node (based on Editable Tree Model Example):


class ResourceTreeNode {
public:
ResourceTreeNode(const ResourceTreeItem& data, ResourceTreeNode* parent = 0);
~ResourceTreeNode();

QVariant data(int column) const;
bool setData(int column, const QVariant& value);
ResourceTreeNode* child(int number);
ResourceTreeNode* parent();

int childCount() const;
int childNumber() const;
bool insertChildren(int position, int count, ResourceTreeItem resourceTreeItem);
bool removeChildren(int position, int count);

private:
ResourceTreeNode* m_parent;
ResourceTreeItem m_resourceTreeItem;
QList<ResourceTreeNode*> m_childrens;

};

A custom model (also based on Editable Tree Model Example):


class QResourceTreeModel : public QAbstractItemModel
{
Q_OBJECT

public slots:
void itemClicked(const QModelIndex &index);

public:
static const int NB_COLUMN = 6;

QResourceTreeModel(QObject *parent = 0) : QAbstractItemModel(parent), m_root(0) {}
QResourceTreeModel(ResourceTreeNode* root, QObject *parent = 0) : QAbstractItemModel(parent), m_root(root) {}

void setRootNode(ResourceTreeNode* node);

QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex& index) const;


int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;

QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

private:
ResourceTreeNode* getNode(const QModelIndex& index) const;
ResourceTreeNode* m_root;
};

Basically I connect the signal clicked(const QModelIndex &) to my slot itemClicked(const QModelIndex &index) so I can update the corresponding cell in the tree.

Here is some key methods ...


QVariant ResourceTreeNode::data(int column) const
{
switch (column) {
case 0:
return QVariant(m_resourceTreeItem.m_resourceVisible);
case 1:
return QVariant(m_resourceTreeItem.m_resourceID.c_str());
case 2:
return QVariant(m_resourceTreeItem.m_contentVisible);
case 3:
return QVariant(m_resourceTreeItem.m_boundaryBoxVisible);
case 4:
return QVariant(m_resourceTreeItem.m_mosaicked);
case 5:
return QVariant(m_resourceTreeItem.m_IDVisible);
default:
return QVariant();
}

return QVariant();
}


bool ResourceTreeNode::setData(int column, const QVariant& value) {

if (column < 0 || column >= 5) {
return false;
}

switch (column) {
case 0:
m_resourceTreeItem.m_resourceVisible = value.toBool();
case 1:
m_resourceTreeItem.m_resourceID = value.toString().toStdString();
case 2:
m_resourceTreeItem.m_contentVisible = value.toBool();
case 3:
m_resourceTreeItem.m_boundaryBoxVisible = value.toBool();
case 4:
m_resourceTreeItem.m_mosaicked = value.toBool();
case 5:
m_resourceTreeItem.m_IDVisible = value.toBool();
default:
break;
}

return true;
}


void QResourceTreeModel::itemClicked(const QModelIndex &index) {
if ((index.isValid()) && (index.column() != 1)) {

/* The idea is to store false if it was true or true if it was false */

ResourceTreeNode* node = getNode(index);
bool result = node->setData(index.column(), QVariant(!node->data(index.column()).toBool()));

if (result) {
emit dataChanged(index, index);
}
}
}



My problem comes from the method ResourceTreeNode::getNode(index) in QResourceTreeModel::itemClicked(index). The returned node does not contain any ResourceTreeItem? I used a log in getNode() and the problem only happen when I call getNode() from my slot. Somebody have any hints?

caduel
11th August 2009, 16:18
i) the error is probably: you gave your model an index of the proxy model instead of its own (you need to call QAbstractProxyModel::mapToSource(); also it is a good idea to pace assert() in one's model to ensure the indexes passed are from the right model

ii) you should call dataChanged() in setData(); setData() can be called directly

iii) to feature checkboxes, have a look at QAbstractItemModel::data() and Qt::CheckStateRole, you do not need a custom delegate for that (although you can, of course, go that way, too.)

HTH

laugusti
11th August 2009, 16:45
Thank you for helping me ...

i) I will investigate on QAbstractProxyModel::mapToSource() as I didn't use it before.

ii) I'll move the signal dataChanged() but what do you mean by setData() should be called directly?

iii) I don't want checkboxes else I would have used the role as described in the model/view programming page. I need an icon to be shown when a resource is visible and another icon when the resource is hidden (a la Photoshop).

laugusti
11th August 2009, 18:08
With your inputs, I was able to fix my code.
As you said, it comes from the proxy model. I should connect the clicked() signal to the proxy model and not directly to the real model behind. Then use the mapToSource() method to retrieve the data using the right index.

Thanks, Lionel

caduel
11th August 2009, 18:32
I didn't say setData() should be called directly, but only that it could be called directly (by someone), since it is a public part of your model's api. And if someone should do that, you need to emit dataChanged() there - otherwise the view's won't reflect the change if made (only) via setData().

HTH

laugusti
12th August 2009, 09:03
My bad ... I didn't read correctly!
Now all updates go through setData() with the signal dataChanged() there.

Regards, Lionel