Access data from Tree Model item - qt

This is my Table View structure.
How can i browse through the Table view and get SubChild as a TreeItem element.
For Example -> Group2$Child2$SubChild.
This line should return back subchild as TreeItem*.
The Header file for the TreeModel.
#pragma once
#include <QAbstractItemModel>
#include <QString>
#include <QMimedata.h>
#include <Qdatastream.h>
#include "Container.h"
class TreeItem;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel( const QString &header, const 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);
TreeItem *getItem(const QModelIndex &index) const;
TreeItem *getRoot();
private:
TreeItem *rootItem;
};
I would be happy to post any specific code required.

Related

QListView moveRow() from model not called

I'm having a Listview which should show a preview of an image. For this I created a the item "ListItem"
class ListItem
{
public:
ListItem();
ListItem(QString name, QImage image);
QImage* previewIcon();
void setPreviewIcon(QImage icon);
QImage *image();
void setImage(QImage* image);
void setImage(QImage image);
void setName(QString name);
void setChecked(bool checked);
QString name();
bool checked();
private:
QImage m_preview;
QImage m_image;
QString m_name;
bool m_checked;
};
This model stores the image it self and a preview of it. This works fine for inserting and removing items:
class ListModel: public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
bool removeRow(int row, const QModelIndex &parent = QModelIndex());
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool insertRow(int row, const QModelIndex &parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool appendItem(ListItem* item);
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override;
bool moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild);
Qt::DropActions supportedDropActions() const override;
QHash<int, QByteArray> roleNames() const override;
private:
ListItem* getItem(const QModelIndex &index) const;
private:
QList<ListItem*> m_scannedDocuments;
};
This is the setup of the QListView:
m_scannedDocumentsModel = new ListModel();
m_scannedDocuments = new QListView();
m_scannedDocuments->setModel(m_scannedDocumentsModel);
m_scannedDocuments->setDragDropMode(QAbstractItemView::InternalMove);
m_scannedDocuments->setMovement(QListView::Snap);
m_scannedDocuments->setDefaultDropAction(Qt::MoveAction);
Dragging and droping is fine for the preview, the name and if it is checked or not. The problem is the image "m_image". When I'm doing a move in the view, the view calls insertRows() and inserts a new item and removes the old item, but does not call moveRows.
Why moveRows() is not called?
Here you can find the full implementation:
ListModel.cpp
ListModel.h
Another approach would be to create a userRole for the image it self. Here I tried to reimplement roleNames() as
QHash<int, QByteArray> ListModel::roleNames() const {
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
roles[Qt::UserRole] = "Qt::UserRole";
return roles;
}
and check the roles in setdata()/data() but this didn't work either.
What is the best way to have a complex model and moving them in a listview?
As already answered in brief on the Qt interest list:
Because moveRows was added in Qt 5.x, and drag and drop in Qt 4, and still uses insert+remove for backwards compatibility.
I'd add that "moveRows" is I guess considered an "optimization" and not really a requirement for a model, and the view has no way to know if the move methods are really implemented. Since an editable model requires remove/insert functions, it's "safer" to call those by default.
You can re-implement the model's dropMimeData() method and call moveRows() yourself. But, the caveat is you should return false from the method if you do this. If you return true to a Qt::MoveAction, the view will still try to remove the row from the old position in the model (which is obviously not what you want).

Accessing item of QAbstractItemModel via Pointer gets NULL after several reads

I need to access items directly of an QAbstractListModel from QML.
I realized it by returning the address from a function from the model:
Function is
Q_INVOKABLE DataSourceObject *dataPointer(const QModelIndex &index);
#ifndef DATASOURCEMODEL_H
#define DATASOURCEMODEL_H
#include "datasourceobject.h"
#include <QAbstractListModel>
class DataSourceModel : public QAbstractListModel
{
Q_OBJECT
public:
enum datasourceRoles {
idRole = Qt::UserRole ,
nameRole,
unitRole,
valueRole
};
explicit DataSourceModel(QObject *parent = nullptr);
void addDataSourceObject(DataSourceObject *dataSourceObject);
Q_INVOKABLE QVariantMap get(int row) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Q_INVOKABLE DataSourceObject *dataPointer(const QModelIndex &index);
Qt::ItemFlags flags(const QModelIndex& index) const override;
QHash<int, QByteArray> roleNames() const override;
//bool checkIndex(const QModelIndex &index) const;
private:
QList<DataSourceObject*> m_DataSourceObjects;
};
#endif // DATASOURCEMODEL_H
#include "datasourcemodel.h"
#include <qdebug.h>
DataSourceModel::DataSourceModel(QObject *parent)
: QAbstractListModel(parent)
{
}
QVariantMap DataSourceModel::get(int row) const
{
return m_DataSourceObjects[row]->toMap();
}
void DataSourceModel::addDataSourceObject(DataSourceObject *dataSourceObject)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_DataSourceObjects << dataSourceObject;
endInsertRows();
}
int DataSourceModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_DataSourceObjects.count();
}
QVariant DataSourceModel::data(const QModelIndex &index, int role) const
{
if(index.row() < 0 || index.row() >= m_DataSourceObjects.count() || !index.isValid())
return QVariant();
DataSourceObject *dataSourceObject = m_DataSourceObjects[index.row()];
if (role == idRole)
return dataSourceObject->id();
else if (role == nameRole) {
return dataSourceObject->name();
}
else if (role == unitRole) {
return dataSourceObject->unit();
}
else if (role == valueRole)
return dataSourceObject->value();
return QVariant();
}
bool DataSourceModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
DataSourceObject *dataSourceObject = m_DataSourceObjects[index.row()];
if (data(index, role) != value) {
if(role == idRole)
dataSourceObject->setId(value.toInt());
else if(role == nameRole)
dataSourceObject->setName(value.toString());
else if(role == unitRole)
dataSourceObject->setUnit(value.toString());
else if(role == valueRole)
dataSourceObject->setValue(value.toDouble());
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
return false;
}
DataSourceObject *DataSourceModel::dataPointer(const QModelIndex &index)
{
qDebug() << m_DataSourceObjects[index.row()];
return m_DataSourceObjects[index.row()];
}
Qt::ItemFlags DataSourceModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEditable; // FIXME: Implement me!
}
QHash<int, QByteArray> DataSourceModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[idRole] = "id";
roles[nameRole] = "name";
roles[unitRole] = "unit";
roles[valueRole] = "value";
return roles;
}
I can access the item and it's propertys from QML and the GUI gets automatically updated with Q_PROPERTY on the gui.
text: dataSourceModel.dataPointer(dataSourceModel.index(88,0)).value
Object:
#ifndef DATASOURCEOBJECT_H
#define DATASOURCEOBJECT_H
#include <QString>
#include <QVariantMap>
class DataSourceObject : public QObject
{
Q_OBJECT
Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit DataSourceObject(QObject *parent = nullptr);
DataSourceObject(const QJsonObject &obj);
int id() const;
void setId(int id);
QString name() const;
void setName(const QString &name);
QString unit() const;
void setUnit(const QString &unit);
Q_INVOKABLE double value() const;
void setValue(double value);
QVariantMap toMap() const;
signals:
void valueChanged();
private:
int m_id;
QString m_name;
QString m_unit;
double m_value;
};
#endif // DATASOURCEOBJECT_H
The property "value" of the items get's changed during runtime in background.
After circa 80 changes of the propertys the pointer that points to the object returns NULL. So I assume the item in the model changed it's address.
How can I prevent the model to change the addresses of it's item.
But also when I call again Q_INVOKABLE DataSourceObject *dataPointer(const QModelIndex &index); to get the address again, the "old" address ist returned but a "QObject" instead of "DataSourceObject"

how can i Polutate table Widget with combo Box and spinBox delegates

How can i have different delegates in different columns of a table. for instance, i want spinbox delegate in 1st column and combobox in 2nd column.
i took an example of spinbox delegate from examples directories and tried to do the changes:
main.cpp
#include "test1.h"
#include <QApplication>
#include <QHeaderView>
#include <QStandardItemModel>
#include <QTableWidget>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QStandardItemModel model(4, 2);
QTableView tableView;
tableView.setModel(&model);
SpinBoxDelegate sdelegate;
ComboBoxDelegate comDel;
tableView.setItemDelegate(&sdelegate);
tableView.horizontalHeader()->setStretchLastSection(true);
for (int row = 0; row < 4; ++row)
{
for (int column = 0; column < 1; ++column)
{
tableView.setItemDelegate(&comDel);
QModelIndex index = model.index(0, 0, QModelIndex());
}
for (int column = 1; column < 2; ++column)
{
tableView.setItemDelegate(&sdelegate);
QModelIndex index = model.index(1, 1, QModelIndex());
}
}
tableView.setWindowTitle(QObject::tr("Spin Box & combo Box Delegate"));
tableView.show();
return app.exec();
}
test1.h my header file
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <QStyledItemDelegate>
class SpinBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
SpinBoxDelegate(QObject *parent = 0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) /*const Q_DECL_OVERRIDE*/;
void setEditorData(QWidget *editor, const QModelIndex &index) /*const Q_DECL_OVERRIDE*/;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) /*const Q_DECL_OVERRIDE*/;
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) /*const Q_DECL_OVERRIDE*/;
};
class ComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(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 // LINEEDIT_H
test1.cpp my cpp file
#include "test1.h"
#include <QSpinBox>
#include <QComboBox>
SpinBoxDelegate::SpinBoxDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */)/* const*/
{
QSpinBox *editor = new QSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(100);
return editor;
}
void SpinBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) /*const*/
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) /*const*/
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) /*const*/
{
editor->setGeometry(option.rect);
}
ComboBoxDelegate::ComboBoxDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QComboBox *editor = new QComboBox(parent);
//QStringList list ;
//list << "srikanth" << "dilip";
//editor->addItems(list);
editor->installEventFilter(const_cast<ComboBoxDelegate*>(this));
return editor;
}
void ComboBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString value = index.model()->data(index, Qt::DisplayRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->addItem(value);
}
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QString value = comboBox->currentText();
model->setData(index, value);
}
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}

Set color to a QTableView row

void MyWindow::initializeModelBySQL(QSqlQueryModel *model,QTableView *table,QString sql){
model = new QSqlQueryModel(this);
model->setQuery(sql);
}
With this method i can set a QSQlQueryModels to my QTableviews.
But How i can set color to a row based on a cell value?
The view draws the background based on the Qt::BackgroundRole role of the cell which is the QBrush value returned by QAbstractItemModel::data(index, role) for that role.
You can subclass the QSqlQueryModel to redefine data() to return your calculated color, or if you have Qt > 4.8, you can use a QIdentityProxyModel:
class MyModel : public QIdentityProxyModel
{
QColor calculateColorForRow(int row) const {
...
}
QVariant data(const QModelIndex &index, int role)
{
if (role == Qt::BackgroundRole) {
int row = index.row();
QColor color = calculateColorForRow(row);
return QBrush(color);
}
return QIdentityProxyModel::data(index, role);
}
};
And use that model in the view, with the sql model set as source with QIdentityProxyModel::setSourceModel.
OR
You can keep the model unchanged and modify the background with a delegate set on the view with QAbstractItemView::setItemDelegate:
class BackgroundColorDelegate : public QStyledItemDelegate {
public:
BackgroundColorDelegate(QObject *parent = 0)
: QStyledItemDelegate(parent)
{
}
QColor calculateColorForRow(int row) const;
void initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const
{
QStyledItemDelegate::initStyleOption(option, index);
QStyleOptionViewItemV4 *optionV4 =
qstyleoption_cast<QStyleOptionViewItemV4*>(option);
optionV4->backgroundBrush = QBrush(calculateColorForRow(index.row()));
}
};
As the last method is not always obvious to translate from C++ code, here is the equivalent in python:
def initStyleOption(self, option, index):
super(BackgroundColorDelegate,self).initStyleOption(option, index)
option.backgroundBrush = calculateColorForRow(index.row())
Your best bet is to define a custom model (QAbstractTableModel subclass). You probably want to have a QSqlQueryModel as a member in this custom class.
If it's a read-only model, you need to implement at least these methods:
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
and for well behaved models also
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
If you need the model to be able to edit/submit data, things get a bit more involved and you will also need to implement these methods:
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &index=QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index=QModelIndex());
What will actually change a row appearance lies in the return value of this method:
QVariant data(const QModelIndex &index, int role) const;
A dumb example:
QVariant MyCustomModel::data(const QModelIndex &index, int role) const
{
if ( !index.isValid() )
return QVariant();
int row = index.row();
int col = index.column();
switch ( role )
{
case Qt::BackgroundRole:
{
if(somecondition){
// background for this row,col is blue
return QVariant(QBrush (QColor(Qt::blue)));
}
// otherwise background is white
return QVariant(QBrush (QColor(Qt::white)));
}
case Qt::DisplayRole:
{
// return actual content for row,col here, ie. text, numbers
}
case Qt::TextAlignmentRole:
{
if (1==col)
return QVariant ( Qt::AlignVCenter | Qt::AlignLeft );
if (2==col)
return QVariant ( Qt::AlignVCenter | Qt::AlignTrailing );
return QVariant ( Qt::AlignVCenter | Qt::AlignHCenter );
}
}
}

QStandardItem + QComboBox

I am trying to put a QComboBox into a QStandardItem to be used in a QStandardItemModel. I have been looking around and I cannot find an answer, any ideas?
You don't store a QComboBox in a QStandardItemModel. Let's say you have the following choices:
A
B
C
D
and you have a list with two items in a QListView, the first value being A the second being D:
QListView* pView = new QListView();
QStandardItemModel* pModel = new QStandardItemModel();
pView->setModel(pModel);
pModel->appendRow(new QStandardItem("A"));
pModel->appendRow(new QStandardItem("D"));
What we created above is a list widget which will display the values of "A" and "D". Now, to the QComboBox. I assume you want that to edit the values of "A" and "D" in the list. For this, you need to create a QItemDelegate.
See http://doc.qt.io/qt-4.8/qitemdelegate.html
An attempt:
class ComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(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;
};
ComboBoxDelegate::ComboBoxDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItem("A");
editor->addItem("B");
editor->addItem("C");
editor->addItem("D");
return editor;
}
void ComboBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
QComboBox *cBox = static_cast<QComboBox*>(editor);
cBox->setCurrentIndex(cBox->findText(value));
}
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *cBox = static_cast<QComboBox*>(editor);
QString value = cBox->currentText();
model->setData(index, value, Qt::EditRole);
}
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
And then you need to set the delegate on the QListView to make it work, see:
pView->setItemDelegate(new ComboBoxDelegate(pView));

Resources