Adding markers/places to Qt QML Maps? - c++

Good day ! I am designing a Qt-based GUI platform that displays a map and allows users to add markers/pins on top of the map.
I am rendering the map in a QtQuickWidget using the following QML:
Plugin {
id: mapPlugin
name: "osm"
}
I want to allow the user to add an interactive pin on the map using a button on a form. The user can press and hold a point on the map which will open the form, where the user can name the place and press OK.
Example of interactive pin
Example of what I want to achieve: https://webmshare.com/play/5EXV8
I have tried using QQmlComponent and QQuickView but I was
unsuccessful
[http://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html]
Another way is to add objects within QML itself using MapItems but
this is very unintuitive. This is what my map.qml looks like:
https://gist.github.com/blackvitriol/7941688d6362162888630a28c79f8cd9
Project Structure: https://imgur.com/a/P8YAS
Can someone tell me how to allow the user to press and hold left click on the map, then add a marker to that point ?

A possible solution is to use MapItemView and create a model that stores the coordinates:
markermodel.h
#ifndef MARKERMODEL_H
#define MARKERMODEL_H
#include <QAbstractListModel>
#include <QGeoCoordinate>
class MarkerModel : public QAbstractListModel
{
Q_OBJECT
public:
using QAbstractListModel::QAbstractListModel;
enum MarkerRoles{positionRole = Qt::UserRole + 1};
Q_INVOKABLE void addMarker(const QGeoCoordinate &coordinate){
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_coordinates.append(coordinate);
endInsertRows();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override{
Q_UNUSED(parent)
return m_coordinates.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override{
if (index.row() < 0 || index.row() >= m_coordinates.count())
return QVariant();
if(role== MarkerModel::positionRole)
return QVariant::fromValue(m_coordinates[index.row()]);
return QVariant();
}
QHash<int, QByteArray> roleNames() const{
QHash<int, QByteArray> roles;
roles[positionRole] = "position";
return roles;
}
private:
QList<QGeoCoordinate> m_coordinates;
};
#endif // MARKERMODEL_H
main.qml
import QtQuick 2.0
import QtLocation 5.6
import QtPositioning 5.6
import QtQuick.Window 2.0
Rectangle {
width: Screen.width
height: Screen.height
visible: true
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: mapview
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75)
zoomLevel: 14
MapItemView{
model: markerModel
delegate: mapcomponent
}
}
Component {
id: mapcomponent
MapQuickItem {
id: marker
anchorPoint.x: image.width/4
anchorPoint.y: image.height
coordinate: position
sourceItem: Image {
id: image
source: "http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_red.png"
}
}
}
MouseArea {
anchors.fill: parent
onPressAndHold: {
var coordinate = mapview.toCoordinate(Qt.point(mouse.x,mouse.y))
markerModel.addMarker(coordinate)
}
}
}
main.cpp
#include "markermodel.h"
#include <QApplication>
#include <QQuickWidget>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QQuickWidget w;
MarkerModel model;
w.rootContext()->setContextProperty("markerModel", &model);
w.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
w.show();
return a.exec();
}
The complete example can be found in the following link.

Related

From random function in cpp to qml

I have a class and I can get data from the constructor to show it in the TableView in qml, but the problem is when I want to show data from anoher function from that class. For example:
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQmlContext>
#include "myclass.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyModel *myTable=new MyModel;
engine.rootContext()->setContextProperty("myTable", myTable);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
MyModel.h
struct Info
{
QString name;
QString address;
QString values;
QString salary;
};
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent=nullptr);
.
////some variables declared////
.
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override;
Q_INVOKABLE QHash<int,QByteArray> roleNames() const override;
private:
QList<Votes> m_model;
MyModel.cpp
MyModel::MyModel(QObject *parent)
: QAbstractTableModel{parent}
{
m_model.append({"name1","add1","value1","salary1"});
m_model.append({"name2", "add2","value2","salary2"});
m_model.append({"name3","add3","value3","salary3"});
} ////I can see this in TableView
void MyModel::mapTable(QVector<QStringList> mapGroup)
{
for(int i=0;i<mapGroup.count();i++)
{
m_model.append ({mapGroup[i][0], mapGroup[i][1], mapGroup[i][2], mapGroup[i][3});
}
} ////I cannot see this in TableView
...
MyModel.qml
import QtQuick 2.0
import QtQuick.Controls 1.4 as CI
CI.TableView
{
model: myTable
CI.TableViewColumn
{
role: "name"
title:"Name"
width: 100
}
CI.TableViewColumn
{
role: "add"
title:"Address"
width: 100
}
CI.TableViewColumn
{
role: "value"
title:"Value"
width: 100
}
CI.TableViewColumn
{
role: "salary"
title:"Salary"
width: 100
}
}
Dialog.qml
...
Rectangle
{
id: tableRec
width: parent.width/1.5
height: parent.height/2
MyModel
{
id: infoTable
width: tableRec.width
height: tableRec.height
anchors.fill: parent
rowDelegate: Rectangle
{
id: rowTable
height: 15
SystemPalette
{
id: myPalette
colorGroup: SystemPalette.Active
}
color:
{
var baseColor = styleData.alternate?myPalette.alternateBase:myPalette.base
return styleData.selected?myPalette.highlight:baseColor
}
}
itemDelegate: Item
{
Text
{
// anchors.centerIn: parent
anchors.verticalCenter: parent.verticalCenter
text: styleData.value
font.pixelSize: Math.round(infoTable.height)/16
}
}
}
How can I pass the data from mapTable function to TableView? I get that it has something to do with QAbstractTableModel, but I cannot make it work.
When you create a custom model based on an inner data model, you have to notify the views that you inserted/removed rows.
The model cannot know when you change its inner model. That's why you have to use beginInsertRows and endInsertRow in your mapTable method.
A quick example:
class MyModel: public QAbstractListModel
{
Q_OBJECT
public:
MyModel(QObject* parent=nullptr): QAbstractListModel (parent)
{
innerModel.append("Bob");
innerModel.append("Patrick");
innerModel.append("Alice");
}
Q_INVOKABLE int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid())
return 0;
return innerModel.size();
}
Q_INVOKABLE int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid())
return 0;
return 2;
}
Q_INVOKABLE QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
{
qDebug() << index << role;
switch (role)
{
case Qt::DisplayRole:
case Qt::UserRole + 1:
return innerModel.at(index.row());
case Qt::UserRole + 2:
return QString::number(index.row() + 1);
}
return QVariant();
}
Q_INVOKABLE QHash<int,QByteArray> roleNames() const override
{
QHash<int,QByteArray> roles;
roles.insert(Qt::UserRole + 1, "name");
roles.insert(Qt::UserRole + 2, "index");
return roles;
}
Q_INVOKABLE void add(QString const& name)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
innerModel.append(name);
endInsertRows();
}
private:
QList<QString> innerModel;
};
Item {
anchors.fill: parent
TextEdit {
width: 100
id: name
anchors.left: parent.left
anchors.top: parent.top
}
Button {
text: "save"
onClicked: theModel.add(name.text)
anchors.left: name.right
anchors.top: parent.top
}
ListView {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: name.bottom
model: theModel
delegate: Text {
text: name
}
}
}
If you comment beginInsertRows(QModelIndex(), rowCount(), rowCount()); and endInsertRows in the method add, you will not be able to see the new rows int the list because the model cannot know when its inner model has changed.

How to show headers from QSqlTableModel on TableView(QtQuick 2)?

I set headers to model but, i can't show this in my tableview, why? Maybe there is some other parameter besides modelData, which I could use to return the names of my headers to tableview qml?
I can't provide more reproduce example, because you're haven't my sql database. This code return only data to tableview. Maybe tableview(qt quick 2) can't return headers.
In class TableModel i don't override standart methods from QSqlTableModel.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlTableModel>
#include <QFontDatabase>
#include <QDebug>
#include "TableModel.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QSqlDatabase db;
db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("172.17.110.69");
db.setDatabaseName("usb_DB");
db.setUserName("usb_secure");
db.setPassword("QWer!234");
db.setPort(3306);
db.open();
qDebug()<<db.isOpen();
TableModel* model = new TableModel(&app,db);
model->setTable("usb_devices");
model->sort(0,Qt::SortOrder::AscendingOrder);
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
model->setHeaderData(0,Qt::Orientation::Horizontal,"Column0");
model->setHeaderData(1,Qt::Orientation::Horizontal,"Column1");
model->setHeaderData(2,Qt::Orientation::Horizontal,"Column2");
model->setHeaderData(3,Qt::Orientation::Horizontal,"Column3");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QQmlContext *context = engine.rootContext();
context->setContextProperty("tableModel", model);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.Material 2.12
import QtQuick.Window 2.11
ApplicationWindow {
id:appWindow
visible: true
TableView {
id: tableView
boundsBehavior:Flickable.StopAtBounds
reuseItems: true
clip: true
columnWidthProvider: function (column) { return 300; }
rowHeightProvider: function () { return 48; }
anchors.fill: parent
topMargin: columnsHeader.implicitHeight
model: tableModel
delegate:ItemDelegate{
id: delegateItem
TextField{
text: modelData
horizontalAlignment: Text.AlignHCenter
}
}
Row {
id: columnsHeader
y: tableView.contentY
z:2
Repeater {
model: tableView.columns > 0 ? tableView.columns : 1
Label {
id: labelRow
width: tableView.columnWidthProvider(index)
height: tableView.rowHeightProvider()
text: modelData
}
}
}
}
}
TableModel.h
#ifndef TABLEMODEL_H
#define TABLEMODEL_H
#include <QtSql/QSqlRelationalTableModel>
#include <QSqlRecord>
class TableModel : public QSqlRelationalTableModel
{
Q_OBJECT
using QSqlRelationalTableModel::QSqlRelationalTableModel;
public slots:
Q_INVOKABLE bool set(int, int, QString);
private:
QSqlRecord rec;
};
#endif // TABLEMODEL_H
Result
The problem is that if the Repeater model is a number "n" the modelData are the numbers from "0" to "n-1". The solution is to be able to access headerData from QML and for this Q_INVOKABLE must be used:
class TableModel : public QSqlRelationalTableModel
{
Q_OBJECT
public:
using QSqlRelationalTableModel::QSqlRelationalTableModel;
Q_INVOKABLE QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;
};
// ...
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return QSqlRelationalTableModel::headerData(section, orientation, role);
}
// ...
*.qml
// ...
Repeater {
model: tableView.columns > 0 ? tableView.columns : 1
Label {
id: labelRow
width: tableView.columnWidthProvider(index)
height: tableView.rowHeightProvider()
text: tableModel.headerData(modelData, Qt.Horizontal) // <---
}
}
// ...

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

How to populate listview with QStringList?

I am developing an app which is to find all the mp4 videos in the sdcard and list them in the listview. A button is made to play all the video in the list or clicking one of the video list is able to play that video.
I don't know how to implement the linking between the QStringlist in C++ and the folderModel in QML. Maybe it should use another way to populate the listview with QStringlist. I have implemented the search part of the mp4 files in C++ side, but don't know how to make the listview populated with that QStringlist which stored the filepath of mp4 videos. Please help.
The source code:
filemodel.cpp
#ifndef FILEMODEL_H
#define FILEMODEL_H
#include <QObject>
#include <QStringList>
#include <QDirIterator>
#include <QString>
class MyObject : public QObject{
Q_OBJECT
public:
explicit MyObject (QObject* parent = 0) : QObject(parent) {}
Q_INVOKABLE QStringList findfile();
};
QStringList MyObject::findfile( ) {
QStringList all_dirs;
QDirIterator it(dir, QStringList() << "*.mp4", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()){
all_dirs << it.next();
}
}
#endif // FILEMODEL_H
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyObject obj;
engine.rootCtontext()->setContextProperty("MyObject", &obj);
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.1
import Qt.labs.folderlistmodel 2.1
import QtMultimedia 5.0
import QtQuick.Controls.Styles 1.4
import Qt.labs.platform 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1 {
ListView {
width: 200; height: 400
FolderListModel {
id: folderModel
nameFilters: ["*.mp4"]
}
Component {
id: fileDelegate
Text { text: fileName }
}
model: folderModel
delegate: fileDelegate
}
Button {
id: button
width: parent.width
text: "Play"
background: Rectangle {
implicitHeight: 40
border.color: "#26282a"
border.width: 2
radius: 4
}
onClicked:
{
player.source = folderModel.get (0, "fileURL")
playTimer.start()
player.play()
swipeView.setCurrentIndex(1)
}
}
}
Page {
MediaPlayer {
id: player
}
VideoOutput {
id: video
anchors.fill: parent
source: player
}
}
}
function setImageIndex(i)
{
index = i;
if (index >= 0 && index < folderModel.count){
player.source = folderModel.get (index, "fileURL");
player.play();
}
else{
player.source = folderModel.get (index, "fileURL");
player.play();
}
}
Timer {
id: playTimer
interval: 2000
repeat: true
running: true
onTriggered: {
var source_name = player.source;
if(source_name.toString().indexOf(".mp4")>0){ //processing .mp4
if (player.status == MediaPlayer.EndOfMedia){
if (index + 1 < folderModel.count){
setImageIndex(index + 1);
}
else{
index = 0;
setImageIndex(index);
}
}
}
}
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("First")
}
TabButton {
text: qsTr("Second")
}
}
}
You should not use FolderListModel if you want to make your own filter from C ++, to do it there are several possibilities.
one of them is to implement your own model for it, we create a class that inherits from QAbstractListModel:
#ifndef FILEMODEL_H
#define FILEMODEL_H
#include <QAbstractListModel>
#include <QDirIterator>
#include <QUrl>
#include <QMetaType>
#include <QFuture>
#include <QtConcurrent>
struct File
{
Q_GADGET
Q_PROPERTY(QString name MEMBER name)
Q_PROPERTY(QUrl url MEMBER url)
public:
QString name;
QUrl url;
File(const QString& name=""){
this->name = QFileInfo(name).fileName();
this->url = QUrl::fromLocalFile(name);
}
};
Q_DECLARE_METATYPE(File)
class FileModel : public QAbstractListModel
{
enum dashBoardRoles {
NameRole=Qt::UserRole+1,
URLRole
};
Q_OBJECT
Q_PROPERTY(QString folder READ folder WRITE setFolder NOTIFY folderChanged)
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
public:
FileModel(QObject *parent=Q_NULLPTR):QAbstractListModel(parent){
}
Q_INVOKABLE QVariant get(int index){
return QVariant::fromValue(m_all_dirs[index]);
}
int rowCount(const QModelIndex &parent=QModelIndex()) const{
Q_UNUSED(parent)
return m_all_dirs.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const{
if(index.row()<0 && index.row()>= rowCount())
return QVariant();
File file = m_all_dirs[index.row()];
if(role == NameRole)
return file.name;
else if(role == URLRole)
return file.url;
return QVariant();
}
QHash<int, QByteArray> roleNames() const {
QHash <int,QByteArray> roles;
roles [NameRole]="fileName";
roles [URLRole]="url";
return roles;
}
QString folder() const{
return mFolder;
}
void setFolder(const QString &folder)
{
if(mFolder == folder)
return;
mFolder = folder;
emit folderChanged();
findFiles();
}
QStringList nameFilters() const{
return mNameFilters;
}
void setNameFilters(const QStringList &nameFilters){
if(mNameFilters == nameFilters)
return;
mNameFilters = nameFilters;
emit nameFiltersChanged();
findFiles();
}
signals:
void folderChanged();
void nameFiltersChanged();
private:
void findFiles(){
beginResetModel();
m_all_dirs.clear();
if(QDir(mFolder).exists()){
QFuture<QStringList> future = QtConcurrent::run([=]() {
QStringList files;
QDirIterator it(mFolder, mNameFilters, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()){
files<<it.next();
}
return files;
});
QStringList fullNames = future.result();
for(const QString& fullName: fullNames){
File file{fullName};
m_all_dirs << file;
}
}
endResetModel();
}
QString mFolder;
QList<File> m_all_dirs;
QStringList mNameFilters;
};
#endif // FILEMODEL_H
and then it is registered and used in the .qml
main.cpp
qmlRegisterType<FileModel>("com.eyllanesc.filemodel", 1,0, "FileModel");
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtMultimedia 5.8
import com.eyllanesc.filemodel 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Tabs")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page {
ListView {
id: lv
width: 200; height: 400
Component {
id: fileDelegate
Text { text: fileName
MouseArea{
anchors.fill: parent
onClicked: playMusic(index)
}
}
}
model: FileModel{
id: myModel
folder: "/home/eyllanesc"
nameFilters: ["*.mp4"]
}
delegate: fileDelegate
}
Button {
id: button
anchors.top: lv.bottom
width: parent.width
text: "Play"
background: Rectangle {
implicitHeight: 40
border.color: "#26282a"
border.width: 2
radius: 4
}
onClicked: playMusic(0)
}
}
Page {
MediaPlayer {
id: player
onStopped: {
if(status===MediaPlayer.EndOfMedia){
playMusic((lv.currentIndex+1) % lv.count)
}
}
}
VideoOutput {
id: video
anchors.fill: parent
source: player
}
}
}
function playMusic(index){
player.stop()
player.source = myModel.get(index).url
player.play()
swipeView.setCurrentIndex(1)
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page 1")
}
TabButton {
text: qsTr("Page 2")
}
}
}
The other solution is to use QQmlListProperty and expose those properties:
#ifndef FILEMANAGER_H
#define FILEMANAGER_H
#include <QDirIterator>
#include <QFileInfo>
#include <QFuture>
#include <QObject>
#include <QQmlListProperty>
#include <QUrl>
#include <QVector>
#include <QtConcurrent>
class File: public QObject{
Q_OBJECT
Q_PROPERTY(QString fileName READ fileName CONSTANT)
Q_PROPERTY(QUrl url READ url CONSTANT)
public:
File(const QString fullPath="", QObject *parent = nullptr):QObject(parent){
mFullPath = fullPath;
}
QString fileName() const
{
return QFileInfo(mFullPath).fileName();
}
QUrl url() const{
return QUrl::fromLocalFile(mFullPath);
}
private:
QString mFullPath;
};
class FileManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<File> files READ files NOTIFY filesChanged)
Q_PROPERTY(QString folder READ folder WRITE setFolder NOTIFY folderChanged)
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
public:
explicit FileManager(QObject *parent = nullptr):QObject(parent){}
QQmlListProperty<File> files(){
return QQmlListProperty<File>(this, this,
&FileManager::filesCount,
&FileManager::file);
}
QString folder() const
{
return mFolder;
}
void setFolder(const QString &folder)
{
if(mFolder == folder)
return;
mFolder = folder;
emit folderChanged();
findFiles();
}
int filesCount() const{
return mFiles.count();
}
File *file(int index) const{
return mFiles.at(index);
}
QStringList nameFilters() const{
return mNameFilters;
}
void setNameFilters(const QStringList &nameFilters){
if(mNameFilters == nameFilters)
return;
mNameFilters = nameFilters;
emit nameFiltersChanged();
findFiles();
}
signals:
void folderChanged();
void filesChanged();
void nameFiltersChanged();
private:
void findFiles( ) {
mFiles.clear();
if(QDir(mFolder).exists()){
QFuture<QStringList> future = QtConcurrent::run([=]() {
QStringList files;
QDirIterator it(mFolder, mNameFilters, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()){
files<<it.next();
}
return files;
});
for(const QString& fullName: future.result()){
File* file = new File(fullName);
mFiles << file;
}
}
emit filesChanged();
}
static int filesCount(QQmlListProperty<File>* list){
return reinterpret_cast<FileManager* >(list->data)->filesCount();
}
static File* file(QQmlListProperty<File>* list, int index){
return reinterpret_cast<FileManager* >(list->data)->file(index);
}
QVector<File *> mFiles;
QString mFolder;
QStringList mNameFilters;
};
#endif // FILEMANAGER_H
and then it is registered and used in the .qml
main.cpp
qmlRegisterType<FileManager>("com.eyllanesc.filemanager", 1,0, "FileManager");
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtMultimedia 5.8
import com.eyllanesc.filemanager 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Tabs")
FileManager{
id: manager
folder: "/home/eyllanesc"
nameFilters: ["*.mp4"]
}
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page {
ListView {
id: lv
width: 200; height: 400
Component {
id: fileDelegate
Text { text: fileName
MouseArea{
anchors.fill: parent
onClicked: playMusic(index)
}
}
}
model: manager.files
delegate: fileDelegate
}
Button {
id: button
anchors.top: lv.bottom
width: parent.width
text: "Play"
background: Rectangle {
implicitHeight: 40
border.color: "#26282a"
border.width: 2
radius: 4
}
onClicked: playMusic(0)
}
}
Page {
MediaPlayer {
id: player
onStopped: {
if(status===MediaPlayer.EndOfMedia){
playMusic((lv.currentIndex+1) % lv.count)
}
}
}
VideoOutput {
id: video
anchors.fill: parent
source: player
}
}
}
function playMusic(index){
player.stop()
player.source = manager.files[index].url
player.play()
swipeView.setCurrentIndex(1)
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page 1")
}
TabButton {
text: qsTr("Page 2")
}
}
}
Both examples can be found in the following links
https://github.com/eyllanesc/stackoverflow/tree/master/48534529-1
https://github.com/eyllanesc/stackoverflow/tree/master/48534529-2

Pass QStandardItemModel from C++ to QtQuick / QML TableView and display it

I'm currently trying to pass a QStandardItemModel to a QtQuick TableView and then display it. This is basically my code (just a simplified extract, so I hope I haven't added any extra mistakes here).
The C++ / Qt part:
foo.cpp
[...]
QStandardItemModel *Foo::getModel()
{
QStandardItemModel *model = new QStandardItemModel(this);
QList<QStandardItem*> standardItemList;
QList<QString> data;
data.append("Cat");
data.append("Dog");
data.append("Mouse");
foreach (QString cell, comInputData->getHeadings()) {
QStandardItem *item = new QStandardItem(cell);
standardItemList.append(item);
}
// we only add one row here for now; more will come later
model->appendRow(standardItemList);
standardItemList.clear();
return model;
}
[...]
main.cpp
Foo f;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myModel", f.getModel());
engine.load(QUrl(QStringLiteral("qrc:///InputView.qml")));
The QtQuick / QML part:
InputView.qml
TableView {
id: monitorInputVectorsTable
[... positioning and sizing ...]
model: myModel
}
I guess, I'm still missing some important parts in the QML part, am I? I found some examples with inline-model-defintions like this:
ListModel {
id: libraryModel
ListElement{ title: "A Masterpiece" ; author: "Gabriel" }
ListElement{ title: "Brilliance" ; author: "Jens" }
}
... displayed this way (the following added inside the TableView-item):
TableViewColumn{ role: "title" ; title: "Title" ; width: 100 }
TableViewColumn{ role: "author" ; title: "Author" ; width: 200 }
My guess: I need to add such a line as well. However, I could not figure out where to get the role from the C++ QStandardItemModel? Is it even necessary to set a role? At least the QWidgets examples with the "classic" QTreeView just set the model and everything was fine...
You can subclass QStandardItemModel and re-implement roleNames() to define your desired role names :
#include <QStandardItemModel>
class MyModel : public QStandardItemModel
{
public:
enum Role {
role1=Qt::UserRole,
role2,
role3
};
explicit MyModel(QObject * parent = 0): QStandardItemModel(parent)
{
}
explicit MyModel( int rows, int columns, QObject * parent = 0 ): QStandardItemModel(rows, columns, parent)
{
}
QHash<int, QByteArray> roleNames() const
{
QHash<int, QByteArray> roles;
roles[role1] = "one";
roles[role2] = "two";
roles[role3] = "three";
return roles;
}
};
After adding items you can set data to the model like :
model->setData(model->index(0,0), "Data 1", MyModel::role1);
model->setData(model->index(0,1), "Data 2", MyModel::role2);
model->setData(model->index(0,2), "Data 3", MyModel::role3);
Now in the qml you can access different columns by the role names :
TableView {
TableViewColumn {title: "1"; role: "one"; width: 70 }
TableViewColumn {title: "2"; role: "two"; width: 70 }
TableViewColumn {title: "3"; role: "three"; width: 70 }
model: myModel
}
Adding data to the model as in Nejats answer did not work in my case (Qt 5.5). The data had to be added as follows:
QStandardItem* it = new QStandardItem();
it->setData("data 1", MyModel::role1);
it->setData("data 2", MyModel::role2);
it->setData("data 3", MyModel::role3);
model->appendRow(it);
A complete example for the answer of Nejat with this modificaiton follows (also avilable here: https://github.com/psimona/so-qt-QStandardItemModel-Example)
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
// QStandardItemModel derived class
#include <QStandardItemModel>
class MyModel : public QStandardItemModel {
public:
enum Role {
role1=Qt::UserRole,
role2,
role3
};
explicit MyModel(QObject * parent = 0): QStandardItemModel(parent){}
explicit MyModel( int rows, int columns, QObject * parent = 0 )
: QStandardItemModel(rows, columns, parent){}
QHash<int, QByteArray> roleNames() const{
QHash<int, QByteArray> roles;
roles[role1] = "one";
roles[role2] = "two";
roles[role3] = "three";
return roles;
}
};
// Main
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyModel* model = new MyModel;
QStandardItem* it = new QStandardItem();
it->setData("data 1", MyModel::role1);
it->setData("data 2", MyModel::role2);
it->setData("data 3", MyModel::role3);
model->appendRow(it);
it = new QStandardItem();
it->setData("more data 1", MyModel::role1);
it->setData("more data 2", MyModel::role2);
it->setData("more data 3", MyModel::role3);
model->appendRow(it);
QQmlApplicationEngine engine;
// register cpp model with QML engine
engine.rootContext()->setContextProperty("myModel", model);
// Load qml file in QML engine
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: true
TableView {
anchors.fill: parent
TableViewColumn {title: "1"; role: "one"; width: 150 }
TableViewColumn {title: "2"; role: "two"; width: 150 }
TableViewColumn {title: "3"; role: "three"; width: 150 }
model: myModel
}
}