I've a model which has items with QDate as Qt::DisplayRole/Qt::EditRole.
It works fine - view renders those items correctly and provides nice date editor. The only drawback is that I'd like to change date format being displayed.
Is there simpler way than preparing delegate with custom painting + QItemEditorFactory + QItemEditorCreatorBase etc?
It looks for me like the triumph of form over content.
If you need just to display a date in specific cell you should subclass QStyledItemDelegate and override it's QString displayText(const QVariant& value, const QLocale& locale) const;
class DateItemDelegate :
public QStyledItemDelegate
{
public:
DateItemDelegate (QObject* parent) :
QStyledItemDelegate(parent) {};
QString displayText(const QVariant& value, const QLocale& locale) const
{
if (value.type() == QVariant::DateTime)
{
return value.toDateTime().toString(Qt::ISODate);
}
}
};
As for me you should use custom painting in case you want to display multiple data in one cell.
Thx for all comments.
Summing up: there is no way to do it without custom delegate
Related
I change the text of a column of QTableView by using displaytext function in QStyledItemDelegate class.
QString Msg_NameGIdDelegate::displayText(const QVariant &value, const QLocale &locale) const
{
return Diag_Utility::getMsgNameStr(value.toInt());
}
How I can get the text of each cell in this column after delegate. If I use the following code I get the text before delegation.
for(int i=0; i<ui->msgCount_tableView->model()->rowCount();i++)
qDebug()<<ui->msgCount_tableView->model()->index(i,6).data().toString();
Looks like you change delegate text but not change model data and you are fetching model data and you want delegate data.
QAbstractItemDelegate *QAbstractItemView::itemDelegate(const QModelIndex &index) function will return your delegate so you can get displayText.
If this solution doesn't work you change model's data by model()->setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)function instead of delegate's displayText and you can fetch model->data()
I would like to know if there is any macro or way how to register Qt model as property of QObject.
For example, I have AnimalModel (http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel).
I Know I can pass it to root context of QuickView
QuickView view;
view.rootContext()->setContextProperty("myModel", &model);
In case I have QObject registered via Qml macros, I can pass this object to view too:
view.rootContext()->setContextProperty("obj", pDataObject);
But what If I want to have QObject which holds model of any data?
For example:
class DataObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
...
AnimalModel m_modelAnimals;
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
};
Every example I found until now shows how to pass QAbstractListModel to root context. But none how to use it as QObject property.
(I know there is QQmlListProperty but QQmlListProperty doesn't support partial refresh. It's always necessary to rebuild all Qml objects)
//Is this possible in any way?
//Q_PROPERTY(AnimalModel modelAnimals READ modelAnimals NOTIFY modelAnimalsChanged)
Yes it is, didn't you try? Of course, it will not be a AnimalModel but a AnimalModel *, but as long as the model inherits QAbstractListModel, that's all you need. You don't even need the NOTIFY part, as changes, internal to the model will be automatically reflected anyway. modelAnimalsChanged only makes sense when you replace the entire model with a different model, and naturally, to shut up QML's warnings about using a property without a notify signal. A cleaner way to do the latter when the model object doesn't change is to just return a AnimalModel * from a slot or a Q_INVOKABLE.
If you want a truly flexible model, you can make one that stores QObject *, then from QML you can create arbitrary objects with arbitrary properties, and add to the model. Then from the model you have a single object role which returns the object, and you can query and use the object to retrieve the properties it holds. Whereas a "classical" list model implementation will define a model with a static, fixed schema, using this approach allows to have "amorphous" objects in the model with different properties.
Naturally, this requires some type safety, for example have a property int type for each object in such a model, and based on it you can determine the available properties for the object. My usual approach is to have a Loader for a delegate, and have it pass the object as a data source to different QML UI implementations visualizing that object type that it instantiates. This way you have both different objects in the model, and different QML items as view delegates.
The last step to making the ultimate "jack of all trades" list/model object is to implement QQmlListProperty and Q_CLASSINFO("DefaultProperty", "container") for it, allowing you to both compose the list/model dynamically, or using QML's declarative syntax. Also note that with this solution, you can add to or remove from such a model, even remove declaratively instantiated objects.
Also, depending on your usage scenario, you may have to either qmlRegisterType() or qmlRegisterUncreatableType() for the model.
OK, on a second glance, it looks like by "model of any data" you didn't mean schema-less models but simply different schema models. In that case, instead of returning an AnimalModel *, you can use a QAbstractListModel * or even a QObject * - it will work in QML anyway, as it employs dynamism through the meta system. But at any rate, schema-less models are that much more powerful and flexible, and they don't need C++ code to be defined, it can all work from QML alone.
class List : public QAbstractListModel {
Q_OBJECT
QList<QObject *> _data;
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
Q_PROPERTY(QQmlListProperty<QObject> content READ content)
Q_PROPERTY(QObject * parent READ parent WRITE setParent)
Q_CLASSINFO("DefaultProperty", "content")
public:
List(QObject *parent = 0) : QAbstractListModel(parent) { }
int rowCount(const QModelIndex &p) const { Q_UNUSED(p) return _data.size(); }
QVariant data(const QModelIndex &index, int role) const {
Q_UNUSED(role)
return QVariant::fromValue(_data[index.row()]);
}
QHash<int, QByteArray> roleNames() const {
static QHash<int, QByteArray> roles = { { Qt::UserRole + 1, "object" } };
return roles;
}
int size() const { return _data.size(); }
QQmlListProperty<QObject> content() { return QQmlListProperty<QObject>(this, _data); }
public slots:
void add(QObject * o) { insert(o, _data.size()); }
void insert(QObject * o, int i) {
if (i < 0 || i > _data.size()) i = _data.size();
beginInsertRows(QModelIndex(), i, i);
_data.insert(i, o);
o->setParent(this);
sizeChanged();
endInsertRows();
}
QObject * take(int i) {
if ((i > -1) && (i < _data.size())) {
beginRemoveRows(QModelIndex(), i, i);
QObject * o = _data.takeAt(i);
o->setParent(0);
sizeChanged();
endRemoveRows();
return o;
} else qDebug() << "ERROR: take() failed - object out of bounds!";
return 0;
}
QObject * get(int i) {
if ((i > -1) && (i < _data.size())) return _data[i];
else qDebug() << "ERROR: get() failed - object out of bounds!";
return 0;
}
void internalChange(QObject * o) { // added to force sort/filter reevaluation
int i = _data.indexOf(o);
if (i == -1) {
qDebug() << "internal change failed, obj not found";
return;
} else {
dataChanged(index(i), index(i));
}
}
signals:
void sizeChanged();
};
Then, after you qmlRegisterType<List>("Core", 1, 0, "List"); you can use it pretty much any way you want to - it will hold any QObject or derived, naturally including QMLs QtObject It can directly be used as a model to drive a ListView. You can populate it dynamically using the slots or declarative, like this:
List {
QtObject { ... }
QtObject { ... }
List {
QtObject { ... }
QtObject { ... }
}
}
It will also handle object ownership, and you can easily nest it, producing in essence a compartmentalized tree model - note that you can't declaratively do that with QML's ListModel. You may want to add a parentChanged signal and implement a setter that emits it if you want to bind against a changing parent, it was not necessary in my case.
As of how to use it with a view, you can either use the objectName property or an int type property or basically any means to discern between different object types, and use a Loader for the delegate:
Loader {
// using component in order to capture context props and present to the variable delegate
sourceComponent: Qt.createComponent(obj.objectName + ".qml")
// if that is not needed simply use
// source: obj.objectName + ".qml"
// or setSource to pass specific properties to delegate properties
// Component.onCompleted: setSource(obj.objectName + ".qml", {/*prop list*/})
}
Update:
Here is also the gist of the implementation for a simple and just as dynamic and generic sorting and filtering proxy to go with this model for enhanced usability.
I have problems migrating from QTreeWidget to QtreeView. Things that were obvious and trivial with QTreeWidget seem to be impossible with view. Specifically: I have a main window with a treeview in it. TreeView uses the model I've implemented, but not directly – through QSortFilterProxyModel that is set as a tree's model. Now, the user activates an item in the tree and main windows receives a signal itemActivated(QModelIndex item). How can I tell which item of the underlying data was activated? Data is a vector, so with TreeWidget I could just store an item's vector index in the QTreeWidgetItem, but QModelIndex doesn’t even have setData API.
How can I tell which item of the underlying data was activated?
By inverting the proxy model:
// supposing to connect this to your itemActivated signal
void onItemActivated(const QModelIndex &index)
{
QModelIndex originalIndex = proxyModel->mapToSource(index);
originalModel->doSomething(originalIndex);
}
You can define custom roles in your source model, returning the underlying data or an identifier (if there's one) as variant. This has the advantage it works with any number of proxy models in between, as the data will be passed through the models unaltered and now mapping of indexes is required.
Assuming a model listing contacts, with a value struct/class Contact holding the data.
This requires Contact to be registered via Q_DECLARE_METATYPE.
class ContactModel ... {
...
enum Role {
ContactRole=Qt::UserRole,
ContactIdRole
};
QVariant data(...) const {
...
const Contact& contact = ...get from datastructure...
...
switch (role) {
...
case ContactRole:
return QVariant::fromValue( contact );
case ContactIdRole:
return contact.id;
}
}
...
And in the code receiving the index:
void SomeWidget::indexSelected(const QModelIndex& index)
{
const int id = index.data(ContactModel::ContactIdRole).toInt();
// look up Contact, do something with it
//or:
const Contact contact = index.data(ContactModel::ContactRole).value<Contact>();
// do something with the contact
...
}
The index can be from the contact model itself, or any proxy on top of it - the code here doesn't have to care.
The model to stores your data. The data is no longer owned by items/QModelIndex in the view. QModelIndex is only a unique identifier passed between the view and the model (in this case via QSortFilterProxyModel). The model should inherit QAbstractItemModel which has some pure virtual functions that need to be defined (you can copy boilerplate from http://qt-project.org/doc/qt-4.8/itemviews-simpletreemodel.html). You will e.g. have to define QAbstractItemModel::data( const QModelIndex & index, int role = Qt::DisplayRole) which defines which data that corresponds to a particular QModelIndex.
The QSortFilterProxyModel sits between the view and the model, but does not change the principles for the model. See the other answer on this question for how to deal with the QModelIndex conversion.
To conclude: QAbstractItemModel::data( const QModelIndex & index) will give you the data for a particular QModelIndex once you have defined it.
I have a standard treeview which is viewing a subclass of QStandardItemModel.
The items in the model are also subclassed form QStandardItem. The items have an additional object pointer which I use to store a pointer to an instance of my data class, a "stage" (itself a QObject). All items have either a pointer to a stage or subclass of it, or a NULL pointer in the _object.
class MyStandardItem : public QStandardItem
{
public:
MyStandardItem();
MyStandardItem(const QString &text);
MyStandardItem(const QIcon &icon, const QString &text);
~MyStandardItem();
void object(QObject *object) {_object = object;}
QObject *object(){return _object;}
private:
QObject *_object;
};
I want to move items around in the treeview, subject to some restrictions. I have given the treeview the correct policies:
view->setAcceptDrops(true);
view->setDragEnabled(true);
view->setDropIndicatorShown(true);
view->setDragDropMode(QAbstractItemView::InternalMove);
And in my model I provide the following:
Qt::DropActions MyStandardItemModel::supportedDropActions() const
{
return Qt::MoveAction;
}
Qt::ItemFlags MyStandardItemModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
MyStandardItem *item = dynamic_cast<MyStandardItem*>(itemFromIndex(index));
if(!item || !item->object())
{
return defaultFlags;
}
Stage *stage = dynamic_cast<Stage*>(item->object());
switch (stage->type())
{
case Stage::STAGEA:
return Qt::ItemIsDropEnabled | defaultFlags;
break;
case Stage::STAGEB:
case Stage::STAGEC:
return Qt::ItemIsDragEnabled | defaultFlags;
break;
}
return defaultFlags;
}
The dragging behaviour looks ok. But when I then click on a dragged item in the treeview, the object pointer of the selected item is junk:
void Project::model_clicked(const QModelIndex& index)
{
MyStandardItem *item = static_cast<MyStandardItem*>(_tree_model->itemFromIndex(index));
if(!item || !item->isValid())
return;
QObject *object = item->object();
if(!object)
return;
// object is junk
Stage *stage = static_cast<Stage*>(object);
// and of course stage is junk
}
do I need to implement dropMimeData or something similarly special for the drop for my subclassed MyStandardItem? Since I'm only moving I expected the object pointers to be intact.
If I do need to implement dropMimeData, what is the mimetype of the dragged data? I know I can see it using the model selection, but logically I should be able to get the data from the mimedata.
Your help most appreciated!
Well I found the answer to my own question.
the data is "moved" by Qt inserting into the required position in the model and then deleting it.
This means it is necessary to implement a clone() member to be used by dropMimeData (which must be reimplemented too as far as I can see)
This means widgets must be stored as pointers within objects to allow easy movement within the tree (otherwise the data has to be manually copied between widgets as there's no default copy for QObjects (by design)
I've treeview in which I'd like to have displayed files selected by user via file_dialog.getOpenFileNames(); file_dialog is QFileDialog.
I did create model class:
class File_Display_Model : public QAbstractItemModel
{
Q_OBJECT
private:
QStringList* selected_files_;
public:
explicit File_Display_Model(QObject *parent = nullptr,QStringList* selected_files = nullptr);
int File_Display_Model::columnCount( const QModelIndex & parent ) const
{
selected_files_->count();
}
QVariant File_Display_Model::data(const QModelIndex & index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
else
{
if (role == Qt::DisplayRole) {
if (index.row() == index.column())
{
return 0;
}
else
{
return selected_files_->at(role);
}
}
return QVariant();
}
}
QModelIndex File_Display_Model::index(int row, int column, const QModelIndex & parent ) const
{
/*DUMMY - HERE I JUST DON'T KNOW WHAT TO RETURN*/
return QModelIndex();
}
QModelIndex File_Display_Model::parent(const QModelIndex & index) const
{
return QModelIndex();
}
int File_Display_Model::rowCount( const QModelIndex & parent ) const
{
selected_files_->count();
}
};
And I also provided this class as a model to tree view. There is a problem with a index method in this class - I don't know what to return.
Could someone please help me and guide me how to make it work so files selected by an user are displayed in a treeview?
First of all there is no reason for using a QStringList*. Qt uses implicit sharing so it is efficient to pass it as an argument (dont forget that QStringList is nothing more than a QList<QString>).
Second you should review the excellent Qt Model/View Programming documentation.
Row and Column Count
You are trying to create a tree model so you should read carefully the tree model example. Notice that the rowCount and columnCount functions have as argument a model index.
The rowCount() function simply returns the number of child items for
the item that corresponds to a given model index, or the number of
top-level items if an invalid index is specified
and for the column count
Since each item manages its own column data, the columnCount()
function has to call the item's own columnCount() function to
determine how many columns are present for a given model index. As
with the rowCount() function, if an invalid model index is specified,
the number of columns returned is determined from the root item
So you have to think how your stringlist will be represented as a tree model. How columns will you have and what will be stored there for every level? How will be the rows hierarchy? Why are you using as column count the number of strings?
Model Index
When you reimplement the index() function you just have to check if the provided row and column are valid and if yes you should call the createIndex function. Again it all depends on what data your model contains and how you have structured them. Since you want to implement a tree model you have to take under consideration the parent item as well.
When reimplementing this function in a subclass, call createIndex() to
generate model indexes that other components can use to refer to items
in your model.