How to set ListView model from C++? - c++

I want to create tab with ListView from C++. Part of adding tab is done :D I want to have one model per ListView and be able to control model from C++ side.
So far I have done this:
C++ part:
SmsModel *model = new SmsModel();
model->createDummyData(phoneNumber);
QObject* tab = findTab(phoneNumber);
if (tab == nullptr)
{
QObject* pRoot = mAppEngine->rootObjects()[0];
QObject* m_pTabView= pRoot->findChildren<QObject*>("conversationTabView").first();
if (m_pTabView)
{
QVariant returnedValue;
QVariant title = phoneNumber;
QQmlContext *context = new QQmlContext(mAppEngine, mAppEngine);
context->setContextProperty(QString("myModel"), model);
QQmlComponent *component = new QQmlComponent(mAppEngine, QUrl("qrc:/ChatView.qml"), this);
QObject *object = component->create(context);
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
QObject *p = object->findChild<QObject*>("chatView");
p->setProperty("model", context->contextProperty("myModel"));
qDebug() << p->property("model");
object->setProperty("active", QVariant(true));
component->setParent(m_pTabView);
QMetaObject::invokeMethod(m_pTabView, "addTab",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, title),
Q_ARG(QVariant, QVariant::fromValue(component)));
object->setProperty("anchors.fill", "parent");
}
QML part:
import QtQuick 2.5
import QtQml.Models 2.2
import QtQuick.Window 2.2
import org.example 1.0
Rectangle {
color: "#E0E0E0"
ListView {
objectName: "chatView"
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
clip: true
model: myModel
delegate: bubbleDelegate
Component {
id: bubbleDelegate
Rectangle {
implicitWidth: messageText.implicitWidth + 2*messageText.anchors.margins
implicitHeight: messageText.implicitHeight + 2*messageText.anchors.margins
anchors.left: model.received ? parent.left : undefined
anchors.right: model.received ? undefined : parent.right
anchors.margins: 5
id: bubble
smooth: true
radius: 10
color: model.received ? "#673AB7" : "#E040FB"
Text {
id: messageText
anchors.fill: parent
anchors.margins: 5
text: model.message
wrapMode: Text.WordWrap;
horizontalAlignment: model.received ? Text.AlignLeft : Text.AlignRight
color: "white"
}
}
}
}
}
When I run my application, there is no data in GUI and I have following error
ReferenceError: myModel is not defined.
Thank you for every response.

In order to use a C++ object as a list model you must have a class that inherits from QAbstractListModel. Take a look at the example here.

Related

Qt QML TableView Header not displaying [duplicate]

I am creating a Table using the new qml tableview (Qt 5.12).
I am able to create a model in C++ and able to populate the model in tabular format along with scrollbar.How do I add column headers to this table?
Code:
import QtQuick 2.12
import QtQuick.Controls 2.5
import Qt.labs.qmlmodels 1.0
//import QtQuick.Controls.Styles 1.4
import TableModel 0.1
Rectangle {
id:table
border.width: 3
border.color: 'dark blue'
QtObject{
id:internals
property int rows:0
property int col:0
property int colwidth:0
property var columnName:[]
}
function setRows(num){ internals.rows = num}
function setCols(num){ internals.col = num}
function setColWidth(num){internals.colwidth = num}
function setColNames(stringlist){
if(stringlist.length > 1)
internals.col = stringlist.length
dataModel.setColumnName(stringlist);
}
function addRowData(stringlist){
var len = stringlist.length
if(len >0)
{
dataModel.addData(stringlist)
}
}
TableModel {
id:dataModel
}
TableView{
id:tbl
anchors.top: headerCell
anchors.fill: parent
//columnSpacing: 1
//rowSpacing: 1
clip: true
ScrollBar.horizontal: ScrollBar{}
ScrollBar.vertical: ScrollBar{}
model:dataModel
Component{
id:datacell
Rectangle {
implicitWidth: 100
implicitHeight: 20
color: 'white'
border.width: 1
border.color: 'dark grey'
Text {
id:txtbox
anchors.fill: parent
wrapMode: Text.NoWrap
clip: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: display
}
}
}
}
function init(){
console.log("Calling init")
tbl.delegate= datacell
}
}
Currently TableView does not have headers so you should create it, in this case use Row, Column and Repeater.
On the other hand you must implement the headerData method and you must do it Q_INVOKABLE.
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
// ...
Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// ...
TableView {
id: tableView
model: table_model
// ...
Row {
id: columnsHeader
y: tableView.contentY
z: 2
Repeater {
model: tableView.columns > 0 ? tableView.columns : 1
Label {
width: tableView.columnWidthProvider(modelData)
height: 35
text: table_model.headerData(modelData, Qt.Horizontal)
color: '#aaaaaa'
font.pixelSize: 15
padding: 10
verticalAlignment: Text.AlignVCenter
background: Rectangle { color: "#333333" }
}
}
}
Column {
id: rowsHeader
x: tableView.contentX
z: 2
Repeater {
model: tableView.rows > 0 ? tableView.rows : 1
Label {
width: 60
height: tableView.rowHeightProvider(modelData)
text: table_model.headerData(modelData, Qt.Vertical)
color: '#aaaaaa'
font.pixelSize: 15
padding: 10
verticalAlignment: Text.AlignVCenter
background: Rectangle { color: "#333333" }
}
}
}
The complete example you find here.
If you're using Qt 5.15, you can use a HorizontalHeaderView for column labels.
https://doc.qt.io/qt-5/qml-qtquick-controls2-horizontalheaderview.html
There's also VerticalHeaderView for row labeling.
https://doc.qt.io/qt-5/qml-qtquick-controls2-verticalheaderview.html
I'm new to the QML. I came to the answer of eyllanesc so many times through my struggle with the new TableView (qt 5.12+), so I wanna thank you and share what helped me even more.
It's this video:
Shawn Rutledge - TableView and DelegateChooser: new in Qt 5.12
part of Qt Virtual Tech Summit 2019
The discussed code
It's a bit long but he covers
the differences between old and new TableView
how to create universal model for the views
resizable headers
different representation per column type - DelegateChooser
sortable columns
column reorder

How can I customize the child items in a TreeView of QML?

QML:
import QtQuick 2.2
import QtQuick.Controls 1.5
import QtQml.Models 2.2
import filesystem_browser 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
ItemSelectionModel
{
id: sel
// This model is comming from C++' class DisplayFileSystemModel.
model: treeViewModel
}
TreeView {
id: view
anchors.fill: parent
anchors.margins: 2 * 12
model: treeViewModel
rootIndex: root
selection: sel
TableViewColumn
{
title: "Name"
role: "display"
resizable: true
}
itemDelegate:
Rectangle
{
id: dd
color: "pink"
height: 20
Rectangle
{
height: 20; width: 40; color: "green"; anchors.right: parent.right
border.width: 1
}
border.width: 1
Text
{
anchors.verticalCenter: parent.verticalCenter
text: styleData.value
}
}
}
}
C++
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
QStandardItemModel model;
QStandardItem *item1 = new QStandardItem("File");
item1->appendRows({new QStandardItem("New"),
new QStandardItem("Open"),
new QStandardItem("Open Recent"),
new QStandardItem("Close"),
new QStandardItem("Save..."),
new QStandardItem("Save As..."),
new QStandardItem("Import Audio File..."),
new QStandardItem("Print")
});
QStandardItem *item3 = new QStandardItem("Edit");
item3->appendRows({new QStandardItem("Undo"),
new QStandardItem("Redo"),
new QStandardItem("Cut"),
new QStandardItem("Copy"),
new QStandardItem("Paste"),
new QStandardItem("Delete"),
new QStandardItem("Select All")
});
model.appendRow(item1);
model.appendRow(item3);
qmlRegisterUncreatableType<DisplayFileSystemModel>("filesystem_browser", 1, 0,
"FileSystemModel", "Cannot create");
engine.rootContext()->setContextProperty("treeViewModel", &model);
engine.rootContext()->setContextProperty("root", model.indexFromItem(model.invisibleRootItem()));
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
This results in:
I want to customize the child items and the parent item individually.
For example:
File //Parent item
New.. // Child item
Save.. // Child item
This current code puts the same customisation on the parent as well as the children.
My previous comment about using the row or column value was close, but incorrect. A quick look at the docs shows us that there is another property that gives us the depth of an item. So I think you can achieve what you want by simply doing something like this:
color: styleData.depth ? "blue" : "pink"

QML Treeview: How to get QModelIndex of childs

I have a QML TreeView with a QStandardItemModel and use a ItemSelectionModel to manage the selection. The ItemSelectionModel wants a QModelIndex for its select function. How can I obtain the QModelIndex of children in my view?
The tree looks like this:
file 1
task 1
task 2
file 2
task 1
I want to select task2 when I click on it (I can have a MouseArea in the delegate) (so that the TreeView highlights it), and in order to do this, I must call ItemSelectionModel.select with the QModelIndex of task 2. But I don'
t know how I can get the QModelIndex of task2.
QStandardItemModel is derived from QAbstractItemModel and therefore provides an index function:
virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const
but to use this function I need to know the index of the parent. How can I get it from the view?
To obtain the child you must first have the parent, so in the case of your scheme you must obtain "file1" and for this you must obtain his parent, and this parent is the rootIndex of the TreeView, so the sequence is: rootIndex -> file1 -> task1.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardItemModel>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStandardItemModel model;
QStandardItem *item1 = new QStandardItem("file1");
item1->appendRows({new QStandardItem("task1"), new QStandardItem("task2")});
QStandardItem *item2 = new QStandardItem("file2");
item2->appendRows({new QStandardItem("task1")});
model.appendRow(item1);
model.appendRow(item2);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("tree_model", &model);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQml.Models 2.11
Window {
visible: true
width: 640
height: 480
TreeView {
id: treeView
anchors.fill: parent
model: tree_model
selectionMode: SelectionMode.MultiSelection
selection: ItemSelectionModel {
id: ism
model: tree_model
}
TableViewColumn {
title: "Name"
role: "display"
width: 300
}
Component.onCompleted: {
expandAll()
var ix1 = tree_model.index(0, 0, treeView.rootIndex)
var ix = tree_model.index(0, 0, ix1)
ism.select(ix, ItemSelectionModel.Select)
}
}
// https://forum.qt.io/topic/75395/qml-treeview-expand-method-not-working
function expandAll() {
for(var i=0; i < tree_model.rowCount(); i++) {
var index = tree_model.index(i,0)
if(!treeView.isExpanded(index)) {
treeView.expand(index)
}
}
}
}
Update:
To get the index of the item pressed you must use styleData.index:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQml.Models 2.11
Window {
visible: true
width: 640
height: 480
TreeView {
id: treeView
anchors.fill: parent
model: tree_model
selectionMode: SelectionMode.MultiSelection
selection: ItemSelectionModel {
id: ism
model: tree_model
}
TableViewColumn {
title: "Name"
role: "display"
width: 300
}
itemDelegate: Item {
Text {
anchors.verticalCenter: parent.verticalCenter
color: styleData.textColor
elide: styleData.elideMode
text: styleData.value
}
MouseArea{
anchors.fill: parent
onClicked: {
var ix = tree_model.index(0, 0, styleData.index)
ism.select(ix, ItemSelectionModel.Select)
}
}
}
}
}

Accessing qml objects from loaded qml using cpp code

I have a main.qml which loads Page1.qml using loaders. How can I find object 'whiteArea' within Page1.qml from my cpp code?
I am currently using the following to fetch an object and would like to obtain the loaded qml as well like this as well.
QObject * object = engine.rootObjects().at(0)->findChild<QObject *> ("yourObjectName");
main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
import myplugin 1.0
ApplicationWindow {
id:app
visible: true
width: 640
height: 480
title: qsTr(" World")
objectName: "Appwindow"
property ApplicationWindow appwindow:app
Label {
objectName: "label"
text: qsTr(" World")
anchors.centerIn: parent
}
MyItemTest{
objectName: "myItem"
anchors.fill: parent
}
Rectangle{
objectName: "Rectangle"
id:rect
width: 50
height: 50
color: "yellow"
}
Button {
objectName: "MyButton"
id: btnClick
text : "btn"
Loader { id: pageLoader }
onClicked: {
pageLoader.source = "Page1.qml"
}
}
}
Page1.qml
import QtQuick 2.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import myplugin 1.0
Item {
Rectangle{
objectName: "whiteArea"
id:rect
width: 50
height: 50
color: "white"
}
}
From the Qt documentation:
The loaded object can be accessed using the item property.
So you should do some subsearch inside a loaded item, for example like this:
QObject * loader = engine.rootObjects().at(0)->findChild<QObject*>("loader");
qWarning() << loader;
QObject * item = qvariant_cast<QObject*>(QQmlProperty::read(loader,"item"));
qWarning() << item;
QObject *whiteArea = item->findChild<QObject *>("whiteArea");
qWarning() << whiteArea;
The output:
QQuickLoader(0x24918240, name = "loader")
QQuickItem(0x24919740)
QQuickRectangle(0x24919728, name = "whiteArea")
First of all , give Loader an object name property, like "loader".
then be sure at the time you running the below code , loader.item is set with"Page1.qml" then do something like this:
QObject* loader = m_engine->rootObjects()[0]->findChild<QObject*>("loader");
QObject* page= qvariant_cast<QObject *>(loader->property("item"));
QObject* whiteArea = page->findChild<QObject*>("whiteArea");

QML ListView data retreival from mysql database

I my test database, I have table named users, which represents loggable users. Now, I've succesfully connected to database through Qt/C++, exposed class to QML (also without problems), setup QML ListView to show this users with custom model and delegate (file main.qml) and when I run my app, five items are shown in ListView. Now, ListView's delegate is composed from Image and Text sections (Image section for loggable user image, which resides as BLOB in mysql database and Text section for user name, which also resides as VARCHAR in mysql database - both fields are in same table named users):
import QtQuick 2.3
Item
{
id: uePeopleItemDelegate
property Image uePersonImage
property string uePersonName
width: 256
height: 256
antialiasing: true
clip: true
Rectangle
{
id: ueRectangleMain
color: "#000000"
radius: 16
anchors.fill: parent
antialiasing: true
border.color: "#ffffff"
border.width: 4
clip: true
//opacity: 0.7
Grid
{
antialiasing: true
anchors.rightMargin: 12
anchors.leftMargin: 12
anchors.bottomMargin: 12
anchors.topMargin: 12
anchors.fill: parent
spacing: 4
rows: 2
columns: 1
}
Row
{
id: ueRowImage
anchors.rightMargin: 12
anchors.leftMargin: 12
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top
anchors.bottomMargin: 48
anchors.topMargin: 12
}
Image
{
id: uePersonleImage
x: 12
y: 12
width: 232
height: 196
antialiasing: true
fillMode: Image.PreserveAspectFit
source: ""
}
Column
{
id: ueColumnPeopleInfo
x: 12
y: 214
width: 232
height: 30
spacing: 0
}
Text
{
id: ueTextPersonName
x: 12
y: 214
width: 232
height: 30
color: "#ffffff"
text: uePersonName
font.bold: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 16
}
}
}
And here is screenshot of delegates:
If I delete/add user in database, the number of delegates changes according to number of table user records, which works perfect. But now I have two problems: In main.qml, I instantiate this ListView:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtMultimedia 5.0
import QtQuick.Layouts 1.0
import QtTest 1.1
import "gui/delegates"
ApplicationWindow
{
id: ueWindowMain
title: qsTr("TestApp")
width: Screen.desktopAvailableWidth
height: Screen.desktopAvailableWidth
visible: true
opacity: 1.0
contentOrientation: Qt.LandscapeOrientation
color: "black"
ListView
{
id: uePeopleListView
snapMode: ListView.SnapToItem
highlightRangeMode: ListView.ApplyRange
anchors.right: parent.right
anchors.rightMargin: 0
anchors.bottom: parent.top
anchors.bottomMargin: -128
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
orientation: ListView.Horizontal
flickableDirection: Flickable.HorizontalFlick
antialiasing: true
spacing: 16
delegate: UePeopleItemDelegate
{
id: uePersonDelegate
uePersonImage: "" // Q1: How do I retreive image (user image) from database?
uePersonName: "" // Q2: How do I retreive text (user name) from database?
}
model: uePeopleModel
}
}
How do I retreive data (image and text) to show it in delegate in lines 49 and 50? Here are also header and implementation of my model class:
#ifndef UEPEOPLEMODEL_H
#define UEPEOPLEMODEL_H
#include <QImage>
#include <QVariant>
#include <QStringList>
#include <QDebug>
#include <QHash>
#include <QByteArray>
#include <QtSql/QSqlError>
#include <QtSql/QSqlQueryModel>
#include <QtSql/QSqlRecord>
#include <QModelIndex>
#include "../settings/uedefaults.h"
class UePeopleModel : public QSqlQueryModel
{
Q_OBJECT
private:
QHash<int, QByteArray> m_ueRoleNames;
void ueGenerateRoleNames();
public:
UePeopleModel(QObject *parent=0);
~UePeopleModel();
QVariant data(const QModelIndex &index,
int role) const Q_DECL_OVERRIDE;
void ueRefresh();
inline QHash<int, QByteArray> ueRoleNames() const
{ return this->m_ueRoleNames; }
};
#endif // UEPEOPLEMODEL_H
and implementation:
#include "uepeoplemodel.h"
UePeopleModel::UePeopleModel(QObject* parent)
: QSqlQueryModel(parent)
{
QSqlDatabase db;
if(!QSqlDatabase::connectionNames().contains(UePosDatabase::UeDatabaseConnectionNames::DATABASE_CONNECTION_NAME_PEOPLE,
Qt::CaseInsensitive))
{
db=QSqlDatabase::addDatabase(UePosDatabase::DATABASE_DRIVER,
UePosDatabase::UeDatabaseConnectionNames::DATABASE_CONNECTION_NAME_PEOPLE);
} // if
db.setHostName(/*this->uePosSettings()->ueDbHostname()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_HOSTNAME);
db.setDatabaseName(/*this->uePosSettings()->ueDbName()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_NAME);
db.setUserName(/*this->uePosSettings()->ueDbUser()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_USERNAME);
db.setPassword(/*this->uePosSettings()->ueDbPassword()*/UePosDatabase::UeDatabaseConnectionParameters::DATABASE_PASSWORD);
if(db.open())
{
this->setQuery(UePosDatabase::UeSqlQueries::UeTablePeople::SQL_QUERY_GET_ALL_PEOPLE,
db);
this->ueGenerateRoleNames();
}
else
{
qDebug() << db.lastError().text();
}
} // default constructor
UePeopleModel::~UePeopleModel()
{
} // default destructor
QVariant UePeopleModel::data(const QModelIndex &index,
int role) const
{
QVariant value;
if(role<Qt::UserRole)
{
value=QSqlQueryModel::data(index,
role);
}
else
{
int iColumnIndex=role-Qt::UserRole-1;
QModelIndex modelIndex=this->index(index.row(),
iColumnIndex);
value=QSqlQueryModel::data(modelIndex,
Qt::DisplayRole);
} // if
return value;
// QVariant value=QSqlQueryModel::data(index,
// role);
// if(value.isValid()&&role==Qt::DisplayRole)
// {
// switch(index.column())
// {
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_ID:
// return value.toInt();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_NAME:
// return value.toString();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_APPPASSWORD:
// return value.toString();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_CARD:
// return value.toString();
// case UePosDatabase::UeTableIndexes::UeTablePeople::INDEX_IMAGE:
// {
// QImage image;
// image.loadFromData(value.toByteArray());
// return image;
// } // case
// default:
// return value;
// } // switch
// } // if
// return QVariant();
} // data
void UePeopleModel::ueRefresh()
{
this->setQuery(UePosDatabase::UeSqlQueries::UeTablePeople::SQL_QUERY_GET_ALL_PEOPLE);
} // ueRefresh
void UePeopleModel::ueGenerateRoleNames()
{
this->ueRoleNames().clear();
for(int iIndex=0; iIndex<this->record().count(); iIndex++)
{
this->ueRoleNames().insert(Qt::UserRole+1+iIndex,
this->record().fieldName(iIndex).toUtf8());
} // for
} // ueGenerateRoleNames
The default QML image provider can only work with URLs or file paths. If you want to load a QML image from a C++ image class, such as QPixmap or QImage you will have to go about and implement your own image provider. I've described one possible implementation strategy here:
After that it is fairly straightforward, the data() method won't be returning an image wrapped in a QVariant, just a custom image URL targeted at the custom image provider, and that's what you are going to use in the QML delegate. Naturally, inside the database, you will still have a blob, and you will still use the fromData() method to construct the image, however the image provider will be used to associate the custom image source string to the actual image. Naturally, you must find a way to control the lifetime of the actual image so you don't get memory leaks. My suggestion is to implement something like the Pixmap item from the answer listed above, and make it a part of your delegate - this way the image will be deleted when no longer needed. You will however also need to implement setData() for the Pixmap and pass the QPixmap pointer in text form and store it in pix.
It might be easier and wiser to NOT store the image in the database, but as a regular file, and just store the relative path to it in the database, it will save you some trouble. As for the question "how to retrieve data" - I'd say go there and actually read the documentation...