QML Treeview: How to get QModelIndex of childs - c++

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)
}
}
}
}
}

Related

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"

Qt Unit Testing C++ with QML qtquick

I am trying to implement a Qt unit test and I would like to "click" a qtquick Button in QML from C++. I am successfully able to use QCompare on the properties of one of my QML objects in test_case3 but I can't figure out how to create a click event in test_case4. My Files are below.
tst_case_1.cpp
void tst_case_1::test_case3()
{
QScopedPointer<MouseMemory> mouse(new MouseMemory);
QQmlEngine engine;
engine.rootContext()->setContextProperty("mouse", mouse.data());
QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
QObject *object = component.create();
QQuickItem *clear = object->findChild<QQuickItem*>("clear");
QVariant tmp = clear->property("text");
QCOMPARE(tmp.toString(), "Clear2");
delete object;
}
void tst_case_1::test_case4()
{
QScopedPointer<MouseMemory> mouse(new MouseMemory);
QQmlEngine engine;
engine.rootContext()->setContextProperty("mouse", mouse.data());
QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
QObject *object = component.create();
QQuickItem *clear = object->findChild<QQuickItem*>("clear");
/* INSERT CODE HERE
Implement QML QQuickItem Button click
/*
delete object;
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
id: root
visible: true
width: 500
height: 500
Row {
id: tools
Button {
id: clear
objectName: "clear"
text: "Clear"
onClicked: {
canvas.clear()
}
}
Button {
id: save
text: "Save"
onClicked: {
mouse.save()
}
}
}
Canvas {
id: canvas
anchors.top: tools.bottom
width: 500
height: 500
property int lastX: 0
property int lastY: 0
function clear() {
var ctx = getContext("2d")
ctx.reset()
canvas.requestPaint()
mouse.clear()
}
onPaint: {
var ctx = getContext("2d")
ctx.lineWidth = 2
ctx.strokeStyle = color.red
ctx.beginPath()
ctx.moveTo(lastX, lastY)
lastX = area.mouseX
lastY = area.mouseY
ctx.lineTo(lastX, lastY)
ctx.stroke()
mouse.test()
mouse.add(lastX, lastY)
}
MouseArea {
id: area
anchors.fill: parent
onPressed: {
canvas.lastX = mouseX
canvas.lastY = mouseY
}
onPositionChanged: {
canvas.requestPaint()
}
}
}
}

Mapping MapPolyline on the map QML. C++/Qt

On the instructions I should to display map on the screen and draw on it the line. Try to do this:
main.cpp
#include <QGeoPath>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
class PathController: public QObject{
Q_OBJECT
Q_PROPERTY(QGeoPath geopath READ geoPath WRITE setGeoPath NOTIFY geopathChanged)
public:
PathController(QObject *parent = 0) : QObject(parent) {}
QGeoPath geoPath() const {
return mGeoPath;
}
void setGeoPath(const QGeoPath &geoPath) {
if(geoPath != mGeoPath) {
mGeoPath = geoPath;
emit geopathChanged();
}
}
signals:
void geopathChanged();
private:
QGeoPath mGeoPath;
};
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QGeoPath path;
path.addCoordinate(QGeoCoordinate(55.006355, 92.860984));
path.addCoordinate(QGeoCoordinate(55.1, 93));
path.addCoordinate(QGeoCoordinate(56.1, 92.777));
PathController controller;
controller.setGeoPath(path);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("pathController", &controller);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
main.qml
import QtLocation 5.6
import QtPositioning 5.6
Window {
visible: true
width: 640
height: 480
Plugin {
id: osmMapPlugin
name: "osm"
}
Map {
anchors.fill: parent
plugin: osmMapPlugin
center: QtPositioning.coordinate(56.006355, 92.860984)
zoomLevel: 10
MapPolyline {
id: pl
line.width: 3
line.color: 'red'
}
}
Connections{
target: pathController
onGeopathChanged: {
var lines = []
for(var i=0; i < pathController.geopath.size(); i++){
lines[i] = pathController.geopath.coordinateAt(i)
}
pl.path = lines
}
}
}
However, get a blank map. The lines do not appear. Question - how to render lines? They do exist, just like "invisible".
Should be a great triangle
And if you call printf("%s" controller.geoPath().toString().toUtf8().constData()); after assignment - you will see exactly what I entered. But the map is empty for some reason. Help, please.
The error is caused because when a new QGeoPath is established through setGeoPath, it issues the signal to update but at that moment the .qml is not started so the connection is not made so onGeopathChanged is not called in qml since after the first change it never changed back.
A possible solution is to use Component.onCompleted so that it notifies next to onGeopathChanged that there is a change as I show in the following code:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtLocation 5.6
import QtPositioning 5.6
Window {
visible: true
width: 640
height: 480
Plugin {
id: osmMapPlugin
name: "osm"
}
Map {
anchors.fill: parent
plugin: osmMapPlugin
center: QtPositioning.coordinate(56.006355, 92.860984)
zoomLevel: 10
MapPolyline {
id: pl
line.width: 10
line.color: 'red'
}
}
function loadPath(){
var lines = []
for(var i=0; i < pathController.geopath.size(); i++){
lines[i] = pathController.geopath.coordinateAt(i)
}
return lines;
}
Connections{
target: pathController
onGeopathChanged: pl.path = loadPath()
}
Component.onCompleted: pl.path = loadPath()
}

Access to existing QML component from C++

I am a newbie in QML, and cannot resolve a simple issue. I want to get access to the QML components from C++, but I cannot.
The pointer test is always 0. What can be the reason?
The code is the following:
main.cpp
int main(int argc, char *argv[])
{
QGuiApplication &app=reg6::Bonder::BonderGuiApplication::instance();
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject* test=engine.rootObjects().first()->findChild<QObject*> ("cameraArea");
test->setProperty("color","black");
return app.exec();
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 1800
height: 900
SplitView
{
anchors.fill: parent
orientation: Qt.Vertical
SplitView {
Layout.fillHeight: true
SplitView {
orientation: Qt.Vertical
width:400
Layout.minimumWidth: 400
Layout.maximumWidth: 500
Camera {
id: cameraArea
height: 400
Layout.maximumHeight: 400
Layout.minimumHeight: 300
}
List {
id: listArea
}
}
Bonder {
id: mainArea
Layout.fillWidth: true
}
Properties {
id: propertiesArea
Layout.minimumWidth: 300
Layout.maximumWidth: 400
}
}
Error {
id: errorArea
Layout.minimumHeight: 100
height: 200
}
}
}
Camera.qml
import QtQuick 2.5
Rectangle {
color: "lightblue"
}
You have to set the objectName property also of the QML component to get a valid pointer to your QObject because T QObject::findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const needs the objectName not the ID

QML XML list view not working

I'm new to QML. I cannot get these code working.
Model.qml
import QtQuick 1.1
XmlListModel {
source: "./test.xml"
query: "/tag1/tag2"
onSourceChanged: {
console.log("source changed:" + source)
reload()
}
XmlRole { name: "id"; query: "id/string()" }
XmlRole { name: "name"; query: "name/string()" }
}
View.qml
import QtQuick 1.1
ListView {
width: 200
}
TheDelegate.qml
import QtQuick 1.1
Rectangle {
width: parent.width
height: 20
Text {
text: id + ": " + name
}
}
Main.qml
import QtQuick 1.1
Item {
id: container
Model {
id: resultModel
objectName: "resultModel"
}
View {
id: resultView
model: resultModel
delegate: TheDelegate {}
}
}
And in my main.cpp:
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QDeclarativeView view;
view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
view.setSource(QUrl::fromLocalFile("./TheMain.qml"));
view.show();
int rtnVal = app.exec();
return rtnVal;
}
When i run the project, it just shows nothing, no window displayed.
Thanks in advance.
EDIT:
I use the qmlviewer(4.8.4) to debug my qmls, and i get the warning:
TheDelegate.qml:18: ReferenceError: Can't find variable: name
But I have to name defined in the XmlRole.
You need to set Height as well for listview.
ListView {
width: 200
height: parent.height;
}