I have a QAbstractItemModel and a QItemDelegate and here is my problem. The Delegate does nothing. Its subroutines are being called but nothing happens.
Here is what I would like to see in my table.
Text : QComboBox : Text : Text : QProgressBar
where : is a column seperator.
Delegate.
#ifndef DELEGATEACTION_H
#define DELEGATEACTION_H
#include <QVariant>
#include <QItemDelegate>
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QProgressBar>
class DelegateAction : public QItemDelegate
{
Q_OBJECT
public:
explicit DelegateAction(QObject *parent = 0);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
#endif // DELEGATEACTION_H
#include "DelegateAction.h"
DelegateAction::DelegateAction(QObject *parent) :
QItemDelegate(parent)
{
}
QWidget * DelegateAction::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget* editor = 0;
switch (index.column())
{
case 0:
default:
{
editor = new QLabel();
break;
}
case 1:
{
QComboBox* combo = new QComboBox(parent);
combo->addItem("Test");
combo->addItem("Test 2");
editor = combo;
break;
}
case 4:
{
editor = new QProgressBar(parent);
break;
}
}
editor->installEventFilter(const_cast<DelegateAction*>(this));
return editor;
}
void DelegateAction::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QVariant value = index.model()->data(index, Qt::DisplayRole);
switch (index.column())
{
case 0:
default:
{
QLabel* label = static_cast<QLabel*>(editor);
label->setText(value.toString());
break;
}
case 1:
{
QComboBox* combo = static_cast<QComboBox*>(editor);
combo->setCurrentIndex(combo->findText(value.toString()));
break;
}
case 4:
{
QProgressBar* progress = static_cast<QProgressBar*>(editor);
progress->setValue(value.toInt());
break;
}
}
}
void DelegateAction::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QVariant value;
switch (index.column())
{
case 0:
default:
{
value = static_cast<QLabel*>(editor)->text();
break;
}
case 1:
{
value = static_cast<QComboBox*>(editor)->currentText();
break;
}
case 4:
{
value = static_cast<QProgressBar*>(editor)->value();
break;
}
}
model->setData(index, value);
}
void DelegateAction::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
Model.
#ifndef MODELACTIONS_H
#define MODELACTIONS_H
#include <QAbstractTableModel>
#include <Unit.h>
class ModelAction : public QAbstractTableModel
{
Q_OBJECT
public:
explicit ModelAction(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
void sort(int column, Qt::SortOrder order);
void setUnits(const QList<Unit*>* units);
private:
const QList<Unit*>* units;
bool ascending[5];
};
#endif // MODELACTIONS_H
#include "ModelAction.h"
ModelAction::ModelAction(QObject *parent) :
QAbstractTableModel(parent),
units(0)
{
}
int ModelAction::rowCount(const QModelIndex &parent) const
{
if (units == 0)
{
return 0;
}
else
{
return units->length();
}
}
int ModelAction::columnCount(const QModelIndex &parent) const
{
return 5;
}
QVariant ModelAction::data(const QModelIndex &index, int role) const
{
if (index.isValid() == false)
{
return QVariant();
}
if (role == Qt::TextAlignmentRole)
{
if (index.column() == 0 || index.column() == 2)
{
return int(Qt::AlignLeft | Qt::AlignVCenter);
}
else
{
return int(Qt::AlignRight | Qt::AlignVCenter);
}
}
else if (role == Qt::DisplayRole)
{
if (index.column() == 0)
{
// Unit's id.
return index.row() + 1;
}
else if (index.column() == 1)
{
return "bob";
// Unit's Action.
//return mechs.at(index.row())->getWeight();
}
else if (index.column() == 2)
{
// Unit's Action start.
//return mechs.at(index.row())->getTechnology();
}
else if (index.column() == 3)
{
// Unit's Action end.
//return Currency::numberToCurrency(mechs.at(index.row())->getPurchaseValue());
}
else if (index.column() == 4)
{
// Unit's Action progress.
//return Currency::numberToCurrency(mechs.at(index.row())->getSellValue());
}
}
return QVariant();
}
QVariant ModelAction::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
{
return QVariant();
}
if (orientation == Qt::Horizontal)
{
if (section == 0)
{
return "Id";
}
else if (section == 1)
{
return "Action";
}
else if (section == 2)
{
return "Begin Time";
}
else if (section == 3)
{
return "End Time";
}
else if (section == 4)
{
return "Progress";
}
}
return QVariant();
}
void ModelAction::sort(int column, Qt::SortOrder order)
{
// MechCompare compare;
// compare.column = column;
// ascending[column] = !ascending[column];
// compare.ascending = ascending[column];
// qSort(mechs.begin(), mechs.end(), compare);
// reset();
}
void ModelAction::setUnits(const QList<Unit *> *units)
{
this->units = units;
reset();
}
Qt::ItemFlags ModelAction::flags(const QModelIndex &index) const
{
switch (index.column())
{
case 0:
default:
{
return Qt::NoItemFlags;
break;
}
case 1:
{
return Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
}
}
bool ModelAction::setData(const QModelIndex &index, const QVariant &value, int role)
{
switch (index.column())
{
case 1:
{
}
}
}
The only issue I'm aware of is the ModelAction::setData() function is incomplete. I have to go back and edit the data classes that this model displays before I can complete that subroutine. Still the comboboxes and progressbars should still display, just not do anything.
At this point I only see the id numbers and my test text "bob" for each row in the table. The QComboBox and QProgressBar are not rendered at all.
Any help will be appreciated.
Jec
The delegate functions you implemented are for editors. They are not displayed when you are not editing an item. It seems you may want QAbstractItemView::setIndexWidget instead of the delegate.
The createEditor method is only called after certain events. For example, when you double-click a cell,... As buck pointed out, you've got to use setIndexWidget.
Related
I have a QAbstractItemModel tree model for a Qt3D Entity tree:
qt3dentitytreemodel.h:
#define QT3DENTITYTREEMODEL_H
#include <QAbstractItemModel>
#include <Qt3DCore/QEntity>
class Qt3DEntityTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
Q_PROPERTY(Qt3DCore::QEntity * rootEntity READ rootEntity WRITE setRootEntity NOTIFY rootEntityChanged)
explicit Qt3DEntityTreeModel(QObject *parent = nullptr);
void setRootEntity(Qt3DCore::QEntity *rootEntity);
Qt3DCore::QEntity * rootEntity() const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
signals:
void rootEntityChanged(Qt3DCore::QEntity *rootEntity);
private:
Qt3DCore::QEntity *rootEntity_;
};
#endif // QT3DENTITYTREEMODEL_H
qt3dentitytreemodel.cpp:
#include "qt3dentitytreemodel.h"
Qt3DEntityTreeModel::Qt3DEntityTreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
void Qt3DEntityTreeModel::setRootEntity(Qt3DCore::QEntity *rootEntity)
{
qDebug() << "Qt3DEntityTreeModel::setRootEntity" << rootEntity;
auto old = rootEntity_;
rootEntity_ = rootEntity;
if(rootEntity_ != old)
emit rootEntityChanged(rootEntity);
}
Qt3DCore::QEntity * Qt3DEntityTreeModel::rootEntity() const
{
return rootEntity_;
}
QModelIndex Qt3DEntityTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if(!rootEntity_) return {};
if(column != 0) return {};
if(!parent.isValid())
{
if(row == 0) return createIndex(0, 0, rootEntity_);
}
else
{
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(parent.internalPointer());
if(!entity) return {};
return createIndex(row, column, entity->childNodes().at(row));
}
return {};
}
QModelIndex Qt3DEntityTreeModel::parent(const QModelIndex &child) const
{
if(!rootEntity_) return {};
if(!child.isValid()) return {};
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(child.internalPointer());
if(!entity) return {};
auto parent = entity->parentNode();
if(!parent) return {};
auto grandParent = parent->parentNode();
if(!grandParent) return createIndex(0, 0, parent);
return createIndex(grandParent->childNodes().indexOf(parent), 0, parent);
}
QVariant Qt3DEntityTreeModel::data(const QModelIndex &index, int role) const
{
if(!rootEntity_) return {};
if(!index.isValid()) return {};
if(role != Qt::DisplayRole && role != DisplayRole) return {};
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(index.internalPointer());
if(!entity) return {};
QString data = entity->metaObject()->className();
if(!entity->objectName().isEmpty())
data += QString(" \"%1\"").arg(entity->objectName());
return data;
}
QVariant Qt3DEntityTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return {};
}
int Qt3DEntityTreeModel::rowCount(const QModelIndex &parent) const
{
if(!parent.isValid()) return 1;
auto entity = reinterpret_cast<Qt3DCore::QEntity*>(parent.internalPointer());
if(!entity) return 0;
return entity->childNodes().count();
}
int Qt3DEntityTreeModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
Qt::ItemFlags Qt3DEntityTreeModel::flags(const QModelIndex &index) const
{
return {};
}
which works fine when displayed in a QTreeView (QtWidgets):
auto treeView = new QTreeView();
auto treeModel = new Qt3DEntityTreeModel(treeView);
treeModel->setRootEntity(rootEntity);
treeView->setModel(treeModel);
treeView->resize(640, 480);
treeView->show();
however when used in a QtQuick Controls TreeView, I cannot see the item text, and I cannot even expand the root tree item:
Item {
...
Qt3DEntityTreeModel {
id: entityTreeModel
rootEntity: root
}
TreeView {
model: entityTreeModel
TableViewColumn {
title: "Name"
width: 200
}
}
Scene3D {
id: scene3d
Entity {
id: root
...
}
}
}
I also tried to explicitly define a role in Qt3DEntityTreeModel:
qt3dentitytreemodel.h:
enum Roles {
DisplayRole = Qt::UserRole + 1
};
QHash<int, QByteArray> roleNames() const override;
qt3dentitytreemodel.cpp:
QVariant Qt3DEntityTreeModel::data(const QModelIndex &index, int role) const
{
if(!rootEntity_) return {};
if(!index.isValid()) return {};
if(role != Qt::DisplayRole && role != DisplayRole) return {};
...
}
QHash<int, QByteArray> Qt3DEntityTreeModel::roleNames() const
{
QHash<int, QByteArray> result;
result.insert(DisplayRole, QByteArrayLiteral("display"));
return result;
}
and define the respective role property in the column of the QtQuick TreeView:
TreeView {
model: entityTreeModel
TableViewColumn {
title: "Name"
role: "display"
width: 200
}
}
but still shows nothing.
Is there something else to do to be able to use a custom abstract item model with QtQuick's TreeView?
I think I found the explanation: the model is created before the Qt3D entity tree is "ready".
If I create the model in C++ after the view is loaded, then set the model as a context property, it displays correctly in the QML's TreeView:
QQuickView view;
view.resize(500, 500);
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.setSource(QUrl("qrc:/main.qml"));
auto rootObject = view.rootObject();
auto rootEntity = rootObject->findChild<Qt3DCore::QEntity*>("theRootEntity");
qDebug() << "rootEntity:" << rootEntity;
auto treeModel = new Qt3DEntityTreeModel(&app);
treeModel->setRootEntity(rootEntity);
view.rootContext()->setContextProperty("theModel", treeModel);
view.show();
The fundamental problem is that my model doesn't monitor updates to the Qt3D entity tree, and it does not reflect such updates.
when i am deleting Treeitem from the the tree model the destructor for the tree item is not being called.
This is my code for deleting the tree item from the model.
void TreeModel::removeItem(TreeItem *node)
{
const int row = node->row();
QModelIndex idx = createIndex(row, 0, node);
TreeItem* itm = getItem(idx);
beginRemoveRows(idx.parent(), row, row);
node->parent()->removeChild(row);
endRemoveRows();
}
The code for Treeitem RemoveChild.
void TreeItem::removeChild(int row)
{
childItems.removeAt(row);
}
The code for tree item header file.
#include <QList>
#include <QVariant>
#include <QVector>
#include "Container.h"
class TreeItem
{
public:
explicit TreeItem( Container *data , TreeItem *parent = 0 );
~TreeItem();
TreeItem *parent();
void appendChild(TreeItem *child);
TreeItem *child(int iNumber);
int childCount() const;
int childNumber() const;
Container data() const ;
Container* GetContainer();
bool setData(Container* data , QVariant value);
void setContainer( Container* data);
bool insertChildren(int position, int count );
bool removeChildren( int position , int count );
void removeChild(int row);
void removeChild(TreeItem* itm);
std::string getChildName(int row);
std::string getName();
int row() const;
void insertChild(int pos, TreeItem *child);
private:
QList<TreeItem*> childItems;
Container* itemData;
TreeItem* parentItem;
};
The code for the tree item Cpp file.
/////////////////////////////////////////////
//////////////////////////////////////////////////////
TreeItem::TreeItem( Container *data, TreeItem *parent )
{
parentItem = parent;
itemData = new Container;
*itemData = *data;
}
TreeItem::~TreeItem()
{
qDebug() << itemData->GetName().c_str();
if (itemData != nullptr)
{
delete itemData;
qDebug() << "deleting Item Data";
}
qDeleteAll(childItems);
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
TreeItem *TreeItem::child(int iNumber)
{
return childItems.value(iNumber);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*> (this));
return 0;
}
Container TreeItem::data() const
{
return *itemData;
}
bool TreeItem::setData( Container* data , QVariant value )
{
//*itemData = *data; // Do Not !!!! uncomment this as it will set the
value of default container constructor.
itemData->SetName(value.toString().toStdString() );
return true;
}
bool TreeItem::insertChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
Container cont;
TreeItem *item = new TreeItem(&cont, this);
childItems.insert(position, item);
return true;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position > childItems.count())
return false;
for (int row = 0; row < count; ++row)
{
delete childItems.takeAt(position);
}
return true;
}
void TreeItem::setContainer( Container* cont)
{
*itemData = *cont;
}
void TreeItem::appendChild(TreeItem *node)
{
childItems.append( node );
}
int TreeItem::row() const
{
if (parentItem)
return parentItem->childItems.indexOf( const_cast<TreeItem*>(this) );
return 0;
}
void TreeItem::removeChild(int row)
{
childItems.removeAt(row);
}
void TreeItem::insertChild(int pos, TreeItem *child)
{
childItems.insert(pos, child);
child->parentItem = this;
}
void TreeItem::removeChild(TreeItem* itm)
{
childItems.removeOne(itm);
}
std::string TreeItem::getChildName(int row)
{
return childItems.value(row)->getName();
}
std::string TreeItem::getName()
{
return itemData->GetName();
}
Container* TreeItem::GetContainer()
{
return itemData;
}
The Header file for the TreeModel Class///////////////////////////////////
#pragma once
#include <QAbstractItemModel>
#include <QString>
#include <QMimedata.h>
#include <Qdatastream.h>
class TreeItem;
class Container;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel(const QString &header, Container *data, QObject *parent = 0);
~TreeModel();
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role = Qt::EditRole) override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &parent) const override;
bool insertRows(int position, int rows, const QModelIndex &parent);
// bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Container* GetContainer(const QModelIndex &index);
void SetContainer(const QModelIndex &index, Container* cont);
////////////////////// Drag And Drop Actions ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Qt::DropActions supportedDropActions() const override;
Qt::DropActions supportedDragActions() const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
void setupModelData(const QStringList &lines, TreeItem *parent);
void removeItem(TreeItem *item);
bool FindChild(std::string stdstrChildName);
TreeItem *getItem(const QModelIndex &index) const;
TreeItem *getRoot();
private:
//void setupModelData(const Container &cont, TreeItem *parent);
TreeItem *rootItem;
};
The Cpp file for the TreeModel
#include "TreeModel.h"
#include "TreeItem.h"
#include <qcoreapplication.h>
#include <qdebug.h>
#include "Container.h"
TreeItem *TreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item)
return item;
}
return rootItem;
}
TreeModel::TreeModel(const QString &header, Container *data, QObject
*parent) : QAbstractItemModel(parent)
{
qDebug() << "First level done";
rootItem = new TreeItem( data);
}
TreeModel::~TreeModel()
{
delete rootItem;
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
TreeItem *item = getItem(index);
return QString::fromStdString(item->data().GetName());
//return QVariant::fromValue(item->data());
}
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return QVariant::fromValue(rootItem->data());
return QVariant();
}
bool TreeModel::setData(const QModelIndex &index, const QVariant &val, int
role)
{
if (role != Qt::EditRole)
return false;
Container c = val.value<Container>();
TreeItem *item = getItem(index);
bool result = true;
item->setData(&c, val);
if (result)
emit dataChanged(index, index, { role });
return result;
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem *parentItem = getItem(parent);
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
bool TreeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &val, int role)
{
if (role != Qt::EditRole || orientation != Qt::Horizontal)
return false;
Container c = val.value<Container>();
bool result = rootItem->setData(&c, val);
if (result)
emit headerDataChanged(orientation, section, section);
return result;
}
bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows);
endInsertRows();
return success;
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsDropEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 1;
TreeItem *parentItem = getItem(parent);
//qDebug() << "the child count = " << parentItem->childCount() << parentItem->data().GetName().c_str();
return parentItem->childCount();
}
int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
return 1;
}
Container* TreeModel::GetContainer(const QModelIndex &index)
{
TreeItem *item = getItem(index);
return item->GetContainer();
}
void TreeModel::SetContainer(const QModelIndex &index, Container* Cont)
{
TreeItem *item = getItem(index);
item->setContainer(Cont);
}
static const char s_treeNodeMimeType[] = "application/x-treenode";
QStringList TreeModel::mimeTypes() const
{
return QStringList() << s_treeNodeMimeType;
}
QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData;
QByteArray data; //a kind of RAW format for datas
//QDataStream is independant on the OS or proc architecture
//serialization of C++'s basic data types, like char, short, int, char *, etc.
//Serialization of more complex data is accomplished
//by breaking up the data into primitive units.
QDataStream stream(&data, QIODevice::WriteOnly);
QList<TreeItem *> nodes;
//
foreach(const QModelIndex &index, indexes) {
TreeItem *node = getItem(index);
if (!nodes.contains(node))
nodes << node;
}
stream << QCoreApplication::applicationPid();
stream << nodes.count();
foreach(TreeItem *node, nodes) {
stream << reinterpret_cast<qlonglong>(node);
}
mimeData->setData(s_treeNodeMimeType, data);
return mimeData;
}
bool TreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction
action, int row, int column, const QModelIndex &parent)
{
//Q_ASSERT(action == Qt::MoveAction);
//Q_UNUSED(column);
//test if the data type is the good one
if (!mimeData->hasFormat(s_treeNodeMimeType)) {
return false;
}
QByteArray data = mimeData->data(s_treeNodeMimeType);
QDataStream stream(&data, QIODevice::ReadOnly);
qint64 senderPid;
stream >> senderPid;
if (senderPid != QCoreApplication::applicationPid()) {
// Let's not cast pointers that come from another process...
return false;
}
TreeItem *parentNode = getItem(parent);
// Q_ASSERT(parentNode);
int count;
stream >> count;
if (row == -1) {
// valid index means: drop onto item. I chose that this should insert
// a child item, because this is the only way to create the first child
of an item...
// This explains why Qt calls it parent: unless you just support
replacing, this
// is really the future parent of the dropped items.
if (parent.isValid())
row = 0;
else
// invalid index means: append at bottom, after last toplevel
row = rowCount(parent);
}
//qDebug() << "The row" << row << parentNode->data().GetName().c_str() ;
for (int i = 0; i < count; ++i) {
// Decode data from the QMimeData
qlonglong nodePtr;
stream >> nodePtr;
TreeItem *node = reinterpret_cast<TreeItem *>(nodePtr);
// Adjust destination row for the case of moving an item
// within the same parent, to a position further down.
// Its own removal will reduce the final row number by one.
if (node->row() < row && parentNode == node->parent())
--row;
// Remove from old position
// qDebug() << "The remove item " << node->data().GetName().c_str();
removeItem(node);
// Insert at new position
//qDebug() << "Inserting into" << parent << row;
beginInsertRows(parent, row, row);
parentNode->insertChild(row, node);
endInsertRows();
++row;
}
return true;
}
void TreeModel::removeItem(TreeItem *node)
{
const int row = node->row();
QModelIndex idx = createIndex(row, 0, node);
TreeItem* itm = getItem(idx);
beginRemoveRows(idx.parent(), row, row);
node->parent()->removeChild(row);
endRemoveRows();
}
Qt::DropActions TreeModel::supportedDropActions() const
{
return Qt::MoveAction;
}
Qt::DropActions TreeModel::supportedDragActions() const
{
return Qt::MoveAction;
}
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
position++;
}
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount() - 1);
indentations << position;
}
}
else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
Container c;
// Append a new node to the current parent's list of children.
parents.last()->appendChild(new TreeItem(&c, parents.last()));
}
++number;
}
}
TreeItem *TreeModel::getRoot()
{
return rootItem;
}
childItems.removeOne(itm); and childItems.removeAt(row); just remove TreeItem * from your QList<TreeItem*> childItems; so memory is not freed.
You should explicitly call delete, once they are removed as you did with delete childItems.takeAt(position); for example.
I'm trying to create a widget representing a table and update some data.
In order to do this I am following the Qt Model/View tutorial.
I've created classes (that you can find at the end of the post)
EmittersTableModel that inherits from QAbstractTableModel
EmittersTableWidget that inherits from QTableView
I havethe EmittersTableModel object as private member of EmittersTableWidget. In its constructor I instantiate the model and use the setModel() method.
Then, when I try to update data, I call the EmittersTableWidget::setRadioData() method, and I emit the datachanged() signal.
I've verified with the debugger that:
emit dataChanged(topLeft, bottomRight) is called
EmittersTableModel::rowCount() is called
EmittersTableModel::columnCount() is called
EmittersTableModel::flags() is never called
EmittersTableModel::data() is never called.
It seems for me that I'm doing all that tutorial says (use setModel(), implement needed virtual functions, emit the signal).
What I'm missing?
EmittersTableModel.h
#include <QAbstractTableModel>
#include <QVector>
#include "Radio.h"
typedef QMultiMap<QString, MapScenario::Core::RadioPtr> PlayerRadioMap;
class EmittersTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
EmittersTableModel(QObject *parent);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const ;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
void setRadioData(const PlayerRadioMap &xRadioMap);
private:
typedef struct
{
QString xName;
MapScenario::Core::RadioPtr pxRadio;
} TableRowData;
PlayerRadioMap m_xRadioMap;
QVector<TableRowData> m_xDataVector;
};
EmittersTableModel.cpp
#include "EmittersTableModel.h"
EmittersTableModel::EmittersTableModel(QObject *parent)
:QAbstractTableModel(parent)
{
}
int EmittersTableModel::rowCount(const QModelIndex & /*parent*/) const
{
return m_xDataVector.size() - 1;
}
int EmittersTableModel::columnCount(const QModelIndex & /*parent*/) const
{
return 8;
}
QVariant EmittersTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case 0 :
{
return m_xDataVector.at(index.row()).xName;
} break;
case 1 :
{
return m_xDataVector.at(index.row()).pxRadio->getName();
} break;
}
return QString("Row%1, Column%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
Qt::ItemFlags EmittersTableModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
QVariant EmittersTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal) {
switch (section)
{
case 0:
return QString("Player");
case 1:
return QString("Emitter");
case 2:
return QString("Freq.");
case 3:
return QString("Power");
case 4:
return QString("Modulation");
case 5:
return QString("Freq. Hopp.");
case 6:
return QString("Silent");
case 7:
return QString("Rec. Power");
}
}
}
return QVariant();
}
void EmittersTableModel::setRadioData(const PlayerRadioMap &xRadioMap)
{
m_xDataVector.clear();
PlayerRadioMap::const_iterator xIt;
for (xIt = xRadioMap.begin(); xIt != xRadioMap.end(); ++xIt)
{
TableRowData xData;
xData.xName = xIt.key();
xData.pxRadio = xIt.value();
m_xDataVector.append(xData);
}
if (false == m_xDataVector.empty())
{
QModelIndex topLeft = createIndex(0, 0);
QModelIndex bottomRight = createIndex(m_xDataVector.size() - 1, 7);
emit dataChanged(topLeft, bottomRight);
}
}
EmittersTableWidget.h
#include <QTableView>
#include <QHeaderView>
#include <QMultiMap>
#include <boost/smart_ptr.hpp>
#include "EmittersTableModel.h"
#include "Scenario.h"
#include "Radio.h"
namespace MapScenario
{
namespace Core
{
class Player;
}
}
/** Class for the player properties table model window */
class EmittersTableWidget : public QTableView
{
Q_OBJECT
public:
EmittersTableWidget(QWidget *xParent = 0);
~EmittersTableWidget();
public slots:
void refreshScenarioDataSlot(const MapScenario::Core::ScenarioPtr pxScenario);
private:
EmittersTableModel *m_pxModel;
void getTransmitterMap(const MapScenario::Core::ScenarioPtr pxScenario, PlayerRadioMap *pxRadioMap) const;
void sendDataToTableModel(const PlayerRadioMap &xRadioMap);
};
EmittersTableWidget.cpp
#include "EmittersTableWidget.h"
#include "Player.h"
#include "CoreException.h"
using MapScenario::Core::ScenarioPtr;
using MapScenario::Core::Radio;
using MapScenario::Core::PlayerPtr;
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
EmittersTableWidget::EmittersTableWidget(QWidget *xParent)
: QTableView(xParent)
{
m_pxModel = new EmittersTableModel(0);
setModel(m_pxModel);
horizontalHeader()->setVisible(true);
verticalHeader()->setVisible(false);
setShowGrid(true);
setGridStyle(Qt::NoPen);
setCornerButtonEnabled(false);
setWordWrap(true);
setAlternatingRowColors(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSortingEnabled(true);
}
EmittersTableWidget::~EmittersTableWidget()
{
delete m_pxModel;
}
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SLOTS SECTION //
///////////////////////////////////////////////////////////////////////////////
void EmittersTableWidget::refreshScenarioDataSlot(const ScenarioPtr pxScenario)
{
PlayerRadioMap xRadioMap;
getTransmitterMap(pxScenario, &xRadioMap);
sendDataToTableModel(xRadioMap);
}
void EmittersTableWidget::getTransmitterMap(const ScenarioPtr pxScenario, PlayerRadioMap *pxRadioMap) const
{
QVector<QString> xNameList;
QVector<QString>::const_iterator xNameIt;
QStringList::const_iterator xRadioIt;
pxScenario->getPlayersNameList(xNameList);
for (xNameIt = xNameList.begin(); xNameIt != xNameList.end(); ++xNameIt)
{
QStringList xRadioList;
PlayerPtr pxPlayer = pxScenario->getPlayer(*xNameIt);
pxPlayer->getRadioNameList(xRadioList);
for (xRadioIt = xRadioList.begin(); xRadioIt != xRadioList.end(); ++xRadioIt)
{
pxRadioMap->insert(pxPlayer->getName(), pxPlayer->getRadio(*xRadioIt));
}
}
}
void EmittersTableWidget::sendDataToTableModel(const PlayerRadioMap &xRadioMap)
{
m_pxModel->setRadioData(xRadioMap);
}
I've found my problem.
In the tutorial that I've seen it was supposed that row and column numbers are Always constant. Instead I start with zero rows, and then I add or remove them when I need. In order to do this, I need to re-implement following methods:
virtual bool insertRows(int row, int count, const QModelIndex &parent)
virtual bool insertColumns(int column, int count, const QModelIndex &parent)
virtual bool removeRows(int row, int count, const QModelIndex &parent)
virtual bool removeColumns(int column, int count, const QModelIndex &parent)
as explained in QAbstractItemModel page in subclassing section. Now I insert and remove rows when needed and table is updated correctly.
So what I have here is my delegate code for a QAbstractTableModel. The 2nd column (index = 1), is always a comboBox. That comboBox has a list of actions that a given Unit in my simulation can perform. Upon clicking certain actions like Build or Train, I want a Dialog to pop up giving the user a set of choices. After making there choice, the delegate is supposed to update the model's data and move on.
Here is the snag I've run into. If I try to call a dialog from setModelData(), setEditorData(), those functions are const so I can't assign the results of the dialog to anything.
What I have tried to do is to connect the signal indexChanged(QString) from the QComboBox created in createEditor(). This at least allowed me to assign the result and call the dialog only when the appropriate choice was made. However I keep getting a seggy fault and the trace in QTCreator will not land on C code (33 calls of assembly). I'm unsure as to why I got that seggy.
When I tried that approach, the table no longer had the combobox once the dialog appeared. It reverted to its previous state and as soon as the program left the slot I made for QComboBox::indexChanged(QString), the seggy happened.
DelegateAction.h
#ifndef DELEGATEACTION_H
#define DELEGATEACTION_H
#include <QVariant>
#include <QStyledItemDelegate>
#include <QString>
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QProgressBar>
#include <QMouseEvent>
#include <JECMessageTable.h>
#include <UnitBase.h>
#include <ModelListUnit.h>
class DelegateAction : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit DelegateAction(QObject *parent = 0);
~DelegateAction();
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
public slots:
void setUnits(QList<Unit*>* units);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
//bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
private slots:
void comboChanged(QString string);
void comboDestroyed();
private:
bool comboUpdated;
const UnitBase* selectedUnit;
ModelListUnit *model; // The model for the JECMessageTable.
QList<Unit*>* units; // The current list of units.
};
#endif // DELEGATEACTION_H
DelegateAction.cpp
#include "DelegateAction.h"
DelegateAction::DelegateAction(QObject *parent) :
QStyledItemDelegate(parent),
selectedUnit(0),
model(0)
{
}
DelegateAction::~DelegateAction()
{
delete model;
}
QWidget * DelegateAction::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget* editor = 0;
switch (index.column())
{
case 0:
default:
{
editor = new QLabel();
break;
}
case 1:
{
QComboBox* combo = new QComboBox(parent);
combo->addItem("Idle");
combo->addItem("Gather");
combo->addItem("Train");
combo->addItem("Build");
combo->addItem("Upgrade");
editor = combo;
// connect(combo, SIGNAL(currentIndexChanged(QString)), this, SLOT(comboChanged(QString)));
// connect(combo, SIGNAL(destroyed()), this, SLOT(comboDestroyed()));
break;
}
case 4:
{
editor = new QProgressBar(parent);
break;
}
}
editor->installEventFilter(const_cast<DelegateAction*>(this));
return editor;
}
void DelegateAction::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QVariant value = index.model()->data(index, Qt::DisplayRole);
switch (index.column())
{
case 0:
default:
{
QLabel* label = static_cast<QLabel*>(editor);
label->setText(value.toString());
break;
}
case 1:
{
QComboBox* combo = static_cast<QComboBox*>(editor);
combo->setCurrentIndex(combo->findText(value.toString()));
break;
}
case 4:
{
QProgressBar* progress = static_cast<QProgressBar*>(editor);
progress->setValue(value.toInt());
break;
}
}
}
void DelegateAction::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QVariant value;
switch (index.column())
{
case 0:
default:
{
value = static_cast<QLabel*>(editor)->text();
break;
}
case 1:
{
QString string = static_cast<QComboBox*>(editor)->currentText();
switch (Action::getType(string))
{
case Action::INVALID:
{
return;
}
case Action::IDLE:
{
return;
}
case Action::GATHER:
{
return;
}
case Action::BUILD:
{
return;
}
case Action::TRAIN:
{
// Summon the build choice dialog.
if (units == 0)
{
return;
}
JECMessageTable messageBox(this->model, "Test");
messageBox.exec();
if (messageBox.result() == QDialog::Accepted)
{
//selectedUnit = this->model->getSelectedUnit();
}
else
{
//selectedUnit = 0;
}
return;
}
case Action::MORPH:
{
return;
}
case Action::UPGRADE:
{
}
}
break;
}
case 4:
{
value = static_cast<QProgressBar*>(editor)->value();
break;
}
}
model->setData(index, value);
}
void DelegateAction::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
QSize DelegateAction::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() == 4)
{
return QSize(option.rect.width(), option.rect.height());
}
else
{
return QStyledItemDelegate::sizeHint(option, index);
}
}
void DelegateAction::paint(QPainter *painter, const QStyleOptionViewItem &item, const QModelIndex &index) const
{
if (index.column() == 4 && index.isValid() == true)
{
QStyleOptionProgressBarV2 progress;
progress.minimum = 0;
progress.maximum = 100;
progress.progress = index.data().toInt();
progress.rect = QRect(item.rect.x(), item.rect.y(), item.rect.width(), item.rect.height());
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progress, painter);
}
else
{
// Painting the cells normally.
QStyledItemDelegate::paint(painter, item, index);
}
}
void DelegateAction::setUnits(QList<Unit*> *units)
{
this->units = units;
if (units != 0)
{
if (units->length() > 0)
{
model = new ModelListUnit(&units->at(0)->getUnitBase()->getOptionUnit());
}
}
}
void DelegateAction::comboChanged(QString string)
{
// comboUpdated = true;
// switch (Action::getType(string))
// {
// case Action::INVALID:
// {
// return;
// }
// case Action::IDLE:
// {
// return;
// }
// case Action::GATHER:
// {
// return;
// }
// case Action::BUILD:
// {
// return;
// }
// case Action::TRAIN:
// {
// // Summon the build choice dialog.
// if (units == 0)
// {
// return;
// }
// JECMessageTable messageBox(model, "Test");
// messageBox.exec();
// if (messageBox.result() == QDialog::Accepted)
// {
// selectedUnit = model->getSelectedUnit();
// }
// else
// {
// selectedUnit = 0;
// }
// return;
// }
// case Action::MORPH:
// {
// return;
// }
// case Action::UPGRADE:
// {
// }
// }
}
void DelegateAction::comboDestroyed()
{
disconnect(this, SLOT(comboChanged(QString)));
disconnect(this, SLOT(comboDestroyed()));
}
//bool DelegateAction::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
//{
// QStyledItemDelegate::editorEvent(event, model, option, index);
// if (comboUpdated == true && index.isValid() == true)
// {
// comboUpdated = false;
// if (selectedUnit != 0)
// {
// units->at(index.row());
// }
// }
//}
I figured this out. If anyone is interested in something similar, I sub classed QTableView.
Inside QTableView i used QTableView::setIndexWidget() to use QComboBoxes and QProgressBars. Basically my subclass had a QList of both types of widgets and they corresponded to a particular row in the table.
derive ComboBoxEditor from QComboBox
keep a Action object in ComboBoxEditor (data stored in model index)
when delegate's comboChanged() is called, you can access the Action via sender(), that is ComboBoxEditor.
Please refer to the official example, StarDelegate.
http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html
I have subclassed a QAbstractTableModel to represent data from a QMap. This QMap has QLists of QSqlRecords and this map is modified by some other part of my code. I want to use this model with a QTableView to display the sql records in this map for each key. Here is my code.
//mymodel.h
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
void setRecordMap(QMap<int, QList<QSqlRecord>> *map);
void setSelectedSerMsgIndex(QModelIndex *index);
private:
QMap<int, QList<QSqlRecord>> *recordMap;
QModelIndex *selectedSerendibMsgIndex;
};
//mymodel.cpp
MyModel::MyModel(QObject *parent) : QAbstractTableModel(parent)
{
}
int MyModel::rowCount(const QModelIndex &parent) const
{
if(recordMap->isEmpty())
return 0;
int row = selectedSerendibMsgIndex->row();
return recordMap->value(row).size();
}
int MyModel::columnCount(const QModelIndex &parent) const
{
if(recordMap->isEmpty())
return 0;
int row = selectedSerendibMsgIndex->row();
return recordMap->value(row).at(0).count();
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
if(recordMap->isEmpty())
return QVariant();
if (!index.isValid())
return QVariant();
int row = selectedSerendibMsgIndex->row();
if (index.row() >= recordMap->value(row).size())
return QVariant();
if (role == Qt::DisplayRole)
{
return recordMap->value(row).value(index.row()).value(index.column()); /* QVariant("hello");*/
}
else
{
return QVariant();
}
}
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
recordMap = map;
}
void MyModel::setSelectedSerMsgIndex(QModelIndex *index)
{
selectedSerendibMsgIndex = index;
}
Sorry for the huge post. But the problem is, I cannot see the data from the map. I am guessing it is because there's something wrong with my implementation of the data() method. But I can't figure out what it is. Please be kind enough to help me. Thank you.
try changing this:
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
recordMap = map;
}
to this:
void MyModel::setRecordMap(QMap<int, QList<QSqlRecord>> *map)
{
beginResetModel();
recordMap = map;
endResetModel();
}