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.
Related
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) // <---
}
}
// ...
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.
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
I try to create a custom module for the Qml TreeView and I used Simpel Treeview Example as foundation, but I always get "unabe to assign [undefined] to QString".
Here some infos u might need:
Displayed view
Qml:
import QtQuick 2.0
import QtQuick.Controls 1.4
import cci.screenshotCreator.StructureDataModel 1.0
TreeView {
id: root
state: "closed"
TableViewColumn {
role: "folderName"
title: "Ordnerstruktur"
}
model: StructureDataModel{
id: dataModel
}
itemDelegate: Item {
height: 30
Rectangle {
id: rectangle
color: styleData.selected ? "lightgray" : "#ffffff"
border.width: styleData.selected ? 2 : 1
border.color: styleData.selected ? "lightblue" : "#ababab"
anchors.fill: parent
}
Text {
id: folderText
text: dataModel.folderName
fontSizeMode: Text.Fit
anchors.fill: parent
font.pointSize: 12
verticalAlignment: Text.AlignLeft
}
C++:
#include "structuredatamodel.h"
structureDataModel::structureDataModel(QObject *parent)
: QAbstractItemModel(parent)
{
QList<QVariant> rootData;
rootData << "/";
rootItem = new StructureItem(rootData);
setupModelData(rootItem);
}
structureDataModel::~structureDataModel()
{
delete rootItem;
}
QModelIndex structureDataModel::index(int row, int column, const QModelIndex &parent) const
{
if(!hasIndex(row, column, parent))
return QModelIndex();
StructureItem *parentItem;
if(!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<StructureItem*>(parent.internalPointer());
StructureItem *childItem = parentItem->child(row);
if(childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QHash<int, QByteArray> structureDataModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[structureRoles::folderName] = "folderName";
return roles;
}
QModelIndex structureDataModel::parent(const QModelIndex &index) const
{
if(!index.isValid())
return QModelIndex();
StructureItem *childItem = static_cast<StructureItem*>(index.internalPointer());
StructureItem *parentItem = childItem->parentItem();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int structureDataModel::rowCount(const QModelIndex &parent) const
{
StructureItem *parentItem;
if(parent.column() > 0)
return 0;
if(!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<StructureItem*>(parent.internalPointer());
return parentItem->childCount();
}
int structureDataModel::columnCount(const QModelIndex &parent) const
{
if(parent.isValid())
return static_cast<StructureItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}
QVariant structureDataModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(role != structureRoles::folderName)
return QVariant();
StructureItem *item = static_cast<StructureItem*>(index.internalPointer());
return item->data(index.column()).toString();
}
Qt::ItemFlags structureDataModel::flags(const QModelIndex &index) const
{
if(!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
QVariant structureDataModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == structureRoles::folderName)
return rootItem->data(section);
return QVariant();
}
void structureDataModel::setupModelData(StructureItem *parent)
{
QList<QVariant> data;
data << "Test";
StructureItem *child = new StructureItem(data, parent);
parent->appendChild(child);
}
The StructureItem is is the same as TreeItem (just a different name :) )
I made my own setupModelData so that model only have one row as root point.
Later I would like to have a editable treeview but for now a working read-only treeView should work right now.
I suspect that it has something to with the setupModelData or making the c++ visible for qml.
PS: I added qmlRegisterType("cci.screenshotCreator.StructureDataModel", 1, 0, "StructureDataModel"); to the main file to register the cpp class.
Minimal Application for testing:
https://pastebin.com/u/Klidrack (all files of my pastebin profile) :)
Oh, didn't saw it at first look.
There is diference between ListView's and TreeView's delegated components in QML.
In TreeView you have itemDelegate and all data are expanded through styleData properties.
in your case this should look like this:
itemDelegate: Item
{
height: 30
Rectangle
{
id: rectangle
color: styleData.selected ? "lightgray" : "#ffffff"
border.width: styleData.selected ? 2 : 1
border.color: styleData.selected ? "lightblue" : "#ababab"
anchors.fill: parent
}
Text
{
id: folderText
text: styleData.value
fontSizeMode: Text.Fit
anchors.fill: parent
font.pointSize: 12
verticalAlignment: Text.AlignLeft
}
}
I have a model on C++ end and a treeview
TreeView {
id: view
itemDelegate: Rectangle{
property int indexOfThisDelegate: model.index
Text {
text:???
font.pixelSize: 14
}
}
model: myModel
onCurrentIndexChanged: console.log("current index", currentIndex)
TableViewColumn {
title: "Name"
role: "type"
resizable: true
}
onClicked: console.log("clicked", index)
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
}
How can I get data from my TreeItem? The problem is that indexOfThisDelegate is integer instead of QModelIndex, so
I would like to have something like
Text {
text:model.getDescription(currentlyPaintedModelIndex)
font.pixelSize: 14
}
or should I have a mapping between integer and tree QModelIndex?
Ok, figured it out myself
In the model:
QHash<int, QByteArray> MenuTreeModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[TitleRole] = "Title";
return roles;
}
// of course it could me more complex with many roles
QVariant MenuTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
MenuTreeItem *item = itemForIndex(index);
if (role != TitleRole)
{
return QVariant();
}
QString str = item->data(index.column()).toString();
return item->data(index.column());
}
Our custom tree item (for example):
class MenuTreeItem
{
// item data, contains title
QList<QVariant> m_itemData;
};
In qml:
TreeView {
id: view
itemDelegate: Rectangle{
Text {
text:model.Title
font.pixelSize: 14
}
}
model: myModel
}