QML TableView get data from specific cell (selected row + specific column) - qt

I have QML TableView with QSqlQueryModel. I need to select any row and get data from every column of table to separate TextField.
Here is abonentstable.h:
#pragma once
#include <QObject>
#include <QSqlQueryModel>
class AbonentsSqlModel : public QSqlQueryModel
{
Q_OBJECT
public:
explicit AbonentsSqlModel(QObject *parent = 0);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const { return m_roleNames; }
private:
void generateRoleNames();
QHash<int, QByteArray> m_roleNames;
};
abonentstable.cpp:
#include "abonentstable.h"
#include <QSqlRecord>
#include <QSqlField>
AbonentsSqlModel::AbonentsSqlModel(QObject *parent) : QSqlQueryModel(parent)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("data_base.sqlite");
db.open();
}
void AbonentsSqlModel::setQuery(const QString &query, const QSqlDatabase &db)
{
QSqlQueryModel::setQuery(query, db);
generateRoleNames();
}
void AbonentsSqlModel::generateRoleNames()
{
m_roleNames.clear();
for( int i = 0; i < record().count(); i ++) {
m_roleNames.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
}
}
QVariant AbonentsSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value;
if(role < Qt::UserRole) {
value = QSqlQueryModel::data(index, role);
}
else {
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
return value;
}
Table.qml:
TableView {
id: table
model: abonents
....
TableViewColumn {
delegate: Text {
text: " " + model.name + " " + model.surname
font.pointSize: 20
}
width: 575
}
TableViewColumn {
delegate: Text {
text: " " + model.phone
font.pointSize: 20
}
width: 575
}
TableViewColumn {
delegate: Text {
text: " " + model.ip_address
font.pointSize: 20
}
width: 525
}
}
And some text fields:
TextField {
id: leftText
}
TextField {
id: centerText
}
TextField {
id: rightText
}
This sqlite table has 4 columns, and I need to get data from selected row to those text fields: 2 columns to left, 1 to center and 1 to right.

When the user clicks a valid row it is emitted the clicked(int row) signal. Then, you can get the values for that row, format the text depending on your requirements and set the values in the three TextField. For example, in your case:
TableView {
id: table
model: abonents
... (your TableViewColumn components) ...
onClicked: {
leftText.text = abonents.get(row).name + " " + libraryModel.get(row).surname;
centerText.text = abonents.get(row).phone;
rightText.text = abonents.get(row).ip_address;
}
}
Please, let me please use my own answer to show you a complete example:
import QtQuick 2.2
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
ApplicationWindow {
id: window
visible: true
title: "Table View Example"
TableView {
y: 70
width: 500
TableViewColumn {
role: "title"
title: "Title"
width: 100
}
TableViewColumn {
role: "author"
title: "Author"
width: 100
}
TableViewColumn{
width: 300
delegate: Text {
text: model.title + " " + model.author
font.family: "Courier New"
font.pixelSize: 18
color: "red"
}
}
onClicked: {
leftText.text = libraryModel.get(row).title + " " + libraryModel.get(row).author;
centerText.text = libraryModel.get(row).title;
rightText.text = libraryModel.get(row).author;
}
model: libraryModel
ListModel {
id: libraryModel
ListElement {
title: "A Masterpiece"
author: "Gabriel"
}
ListElement {
title: "Brilliance"
author: "Jens"
}
ListElement {
title: "Outstanding"
author: "Frederik"
}
}
}
TextField {
id: leftText
}
TextField {
id: centerText
anchors.left: leftText.right
}
TextField {
id: rightText
anchors.left: centerText.right
}
}

Related

"Unable to open file"

This is one of my first projects using QML: I started making a simple game character and a few rooms, and to make the walls'hitboxes I stored them all into a file and included a FileIO class I saw in this site, adapted to return the entire file instead of just one line.
The program, though, tells me it isn't able to open the file (I have put it in the project's directory)
//main.qml (ignore line 5)
import QtQuick 2.15
import QtQuick.Shapes 1.12
import FileIO 1.0
// Whats this? UwU a byzantiuwm?!?!!? UwU i wove byzantiuwm, especiwwawwy in the #memes channew wewe byzantiuwm is the tawk of ze town! OwO
Rectangle {
width: 639
height: 480
id: sus // I hate myself
color: "#00ff00"
Shape {
property string d: "S"
property int room: 1
id: player
x: xPos
y: yPos
z: 1
visible: true
property int xPos: 297
property int yPos: 219
parent: sus
width: 45
height: 45
focus: true
Keys.onPressed:
{
if(event.key === Qt.Key_Down){
yPos+=3; d="S"
}
if(event.key === Qt.Key_Up){
yPos-=3; d="N"
}
if(event.key === Qt.Key_Left){
xPos-=3; d="W"
}
if(event.key === Qt.Key_Right){
xPos+=3; d="E"
}
event.accepted = true;
}
ShapePath{
strokeColor: "transparent"
fillColor: "transparent"
PathLine{x:45 ;y: 0}
PathLine{x:45 ;y:45}
PathLine{x: 0 ;y:45}
}
Image {
id: sprite
parent: player
width: parent.width
height: parent.height
smooth: false
source: "Player/Player_" + player.d + ".png"
}
}
Image {
id: background
x: 0
y: 0
z: 0
width: 639
height: 480
visible: true
smooth: false
parent: sus
source: "Rooms/" + player.room + ".png"
fillMode: Image.Stretch
}
Shape {
id: wallHitBox
x: 0
y: 0
z: 50
width: sus.width
height: sus.height
//visible: false
FileIO{
id: fileReader
source: "Rooms/Hitboxes_or_something.txt"
onError: {console.log(msg)}
}
property var the: fileReader.read()
ShapePath{
strokeColor: "transparent"
fillColor: "blue" //"transparent"
PathSvg{
path: wallHitBox.the[player.room];
}
}
}
}
//fileio.cpp
#include "fileio.h"
#include <QFile>
#include <QTextStream>
FileIO::FileIO(QObject *parent) :
QObject(parent)
{
}
QVector<QString> FileIO::read()
{
if (mSource.isEmpty()){
emit error("source is empty");
return QVector<QString>();
}
QFile file(mSource);
QVector<QString> fileContent;
if ( file.open(QIODevice::ReadOnly) ) {
QString line;
QTextStream t( &file );
do {
line = t.readLine();
fileContent.resize(fileContent.size()+1);
fileContent[fileContent.size()] = line;
} while (!line.isNull());
file.close();
} else {
emit error("Unable to open the file");
return QVector<QString>();
}
return fileContent;
}
bool FileIO::write(const QString& data)
{
if (mSource.isEmpty())
return false;
QFile file(mSource);
if (!file.open(QFile::WriteOnly | QFile::Truncate))
return false;
QTextStream out(&file);
out << data;
file.close();
return true;
}
#define FILEIO_H
#include <QObject>
class FileIO : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString source
READ source
WRITE setSource
NOTIFY sourceChanged)
explicit FileIO(QObject *parent = 0);
Q_INVOKABLE QVector<QString> read();
Q_INVOKABLE bool write(const QString& data);
QString source() { return mSource; };
public slots:
void setSource(const QString& source) { mSource = source; };
signals:
void sourceChanged(const QString& source);
void error(const QString& msg);
private:
QString mSource;
};
#endif // FILEIO_H
Can someone please help me?

QSqlQueryModel TableView custom delegate

I have a QSqlQueryModel and a TableView to display the data from the model. The code and output data results are fine. But, I just want to display an image in front of each row in the TableView. However, with my current QML code, the image is repeated along with the elements in my table columns. I have added example screenshots for reference
Current Output (Screenshot)
What I want
My Code is as below
Test.qml
import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Page {
id : somepageid
TableView{
id: testTable
model: QueryModel
height: 500
width: 400
delegate:
Row{
Image {
id: statusImg
height: 18
width: 18
source: "../../../Images/icons/tick.png"
}
Text {
text: display
}
}
}
}
QueryModel.cpp
#include "querymodel.h"
QueryModel::QueryModel(QObject *parent): QSqlQueryModel(parent)
{
}
void QueryModel::setQuery(const QString &query, const QSqlDatabase &db)
{
QSqlQueryModel::setQuery(query, db);
generateRoleNames();
}
void QueryModel::setQuery(const QSqlQuery &query)
{
QSqlQueryModel::setQuery(query);
generateRoleNames();
}
QVariant QueryModel::data(const QModelIndex &index, int role) const
{
QVariant value;
if(role < Qt::UserRole) {
value = QSqlQueryModel::data(index, role);
}
else {
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
return value;
}
QHash<int, QByteArray> QueryModel::roleNames() const
{
return {{Qt::DisplayRole, "display"}};
}
void QueryModel::callSql()
{
QSqlDatabase dbMysql = QSqlDatabase::database();
this->setQuery(this->tmpSql(), dbMysql);
}
QString QueryModel::tmpSql() const
{
return m_tmpSql;
}
void QueryModel::setTmpSql(QString tmpSql)
{
if (m_tmpSql == tmpSql)
return;
m_tmpSql = tmpSql;
emit tmpSqlChanged(m_tmpSql);
}
void QueryModel::generateRoleNames()
{
m_roleNames.clear();
for( int i = 0; i < record().count(); i ++) {
m_roleNames.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
}
}
A possible solution is to use a Loader:
// ...
delegate: Row{
Loader{
active: model.column === 0
sourceComponent: Image {
id: statusImg
height: 18
width: 18
source: "../../../Images/icons/tick.png"
}
}
Text {
text: model.display
}
}
// ...

Display several roles in one column of TableView

I have a SQLite 3 database with 4 columns and QML code with TableView that displays it:
TableView {
id: table
...
TableViewColumn {
role: "name"
width: 275
}
TableViewColumn {
role: "surname"
width: 300
}
TableViewColumn {
role: "phone"
width: 575
}
TableViewColumn {
role: "ip_address"
width: 525
}
model: abonents
}
It works fine, but I need to display the first two roles, name and surname, as a unique column in TableView.
Here is the code for my model and the main.
abonentstable.h:
#ifndef ABONENTSTABLE
#define ABONENTSTABLE
#include <QObject>
#include <QSqlQueryModel>
class AbonentsSqlModel : public QSqlQueryModel
{
Q_OBJECT
public:
explicit AbonentsSqlModel(QObject *parent = 0);
void setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase());
void setQuery(const QSqlQuery &query);
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const { return m_roleNames; }
signals:
public slots:
private:
void generateRoleNames();
QHash<int, QByteArray> m_roleNames;
};
#endif // ABONENTSTABLE
dbconnection.cpp:
#include "abonentstable.h"
#include <QSqlRecord>
#include <QSqlField>
AbonentsSqlModel::AbonentsSqlModel(QObject *parent) :
QSqlQueryModel(parent)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("data_base.sqlite");
db.open();
}
void AbonentsSqlModel::setQuery(const QString &query, const QSqlDatabase &db)
{
QSqlQueryModel::setQuery(query, db);
generateRoleNames();
}
void AbonentsSqlModel::setQuery(const QSqlQuery & query)
{
QSqlQueryModel::setQuery(query);
generateRoleNames();
}
void AbonentsSqlModel::generateRoleNames()
{
m_roleNames.clear();
for( int i = 0; i < record().count(); i ++) {
m_roleNames.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
}
}
QVariant AbonentsSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value;
if(role < Qt::UserRole) {
value = QSqlQueryModel::data(index, role);
}
else {
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
return value;
}
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QVariant>
#include <QSql>
#include <QSqlQueryModel>
#include <QObject>
#include "abonentstable.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
AbonentsSqlModel *abonentsSqlModel = new AbonentsSqlModel(0);
abonentsSqlModel->setQuery("SELECT * FROM abonents");
QQmlContext *context = engine.rootContext();
context->setContextProperty("abonents", abonentsSqlModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
I think you could join both values using the delegate component.
In your case:
TableView {
id: table
...
TableViewColumn {
width: 575
delegate: Text { text: model.name + " " + model.surname }
}
TableViewColumn {
role: "phone"
width: 575
}
TableViewColumn {
role: "ip_address"
width: 525
}
model: abonents
}
Here you have another example, just for testing if you want to work with it. The example is based on this Qt example.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.2
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
ApplicationWindow {
id: window
visible: true
title: "Table View Example"
TableView {
TableViewColumn {
role: "title"
title: "Title"
width: 100
}
TableViewColumn {
role: "author"
title: "Author"
width: 100
}
TableViewColumn{
width: 300
delegate: Text { text: model.title + " " + model.author }
}
TableViewColumn{
width: 300
delegate: Text {
text: model.title + " " + model.author
font.family: "Courier New"
font.pixelSize: 18
color: "red"
}
}
model: libraryModel
ListModel {
id: libraryModel
ListElement {
title: "A Masterpiece"
author: "Gabriel"
}
ListElement {
title: "Brilliance"
author: "Jens"
}
ListElement {
title: "Outstanding"
author: "Frederik"
}
}
}
}

How to assign SQL query output model from Qt to QML's TableView?

From C++ (Qt):
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
/*
* I have omitted the code about connection with the database and all for ease of
* viewing the code.
*/
// My own function for filling in the data in the `QSqlQueryModel`
QSqlQueryModel* model = new QSqlQueryModel;
QString binid = "B1";
QString query = "SELECT t1.BinId, t1.PartitionId, t2.UnitId, t2.ItemCount FROM Bin_Partitions AS t1 "
"INNER JOIN Partition_Units AS t2 ON t1.PartitionId = t2.PartitionId "
"WHERE t1.BinId ='" + binid + "'";
model->setQuery(query);
/*
* I can see that the query runs successfully because the following
* QTableView DOES get populated properly.
*/
// Use QTableView to visualize
QTableView *view = new QTableView;
view->setModel(model);
view->show();
QQmlApplicationEngine engine;
// Passing the same model to QML for displaying in the TableView.
engine.rootContext()->setContextProperty ("SQQL", model);
engine.load(QUrl(QStringLiteral("/home/.../main.qml")));
QObject *topLevel = engine.rootObjects ().value (0);
QQuickWindow *window = qobject_cast <QQuickWindow *> (topLevel);
return app.exec();
}
From QML:
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.2
Window
{
visible: true
width: 360
height: 360
color: "blue"
TableView
{
TableViewColumn{ role: "col1" ; title: "BinId" ; visible: true}
TableViewColumn{ role: "col2" ; title: "PartitionId" }
TableViewColumn{ role: "col3" ; title: "UnitId" }
TableViewColumn{ role: "col4" ; title: "ItemCount" }
model: SQQL
}
}
The TableView of QML shows up EMPTY!
I need help.
You have to subclass QSqlQueryModel and reimplement roleNames and data methods
MySqlModel.h
class MySqlModel: public QSqlQueryModel
{
Q_OBJECT
public:
MySqlModel(QObject *parent = 0) : QSqlQueryModel(parent) {}
enum Roles {
BinId = Qt::UserRole + 1,
PartitionId,
UnitId,
ItemCount
};
QHash<int, QByteArray> roleNames() const {
QHash<int, QByteArray> roles;
roles[BinId] = "binIdRole";
roles[PartitionId] = "partitionIdRole";
roles[UnitId] = "unitIdRole";
roles[ItemCount] = "itemCountRole";
return roles;
}
QVariant data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
QString fieldName;
switch (role) {
case BinId: fieldName = QStringLiteral("t1.BinId"); break;
case PartitionId: fieldName = QStringLiteral("t1.PartitionId"); break;
case UnitId: fieldName = QStringLiteral("t2.UnitId"); break;
case ItemCount: fieldName = QStringLiteral("t2.ItemCount"); break;
}
if (!this->record().isGenerated(fieldName))
return QVariant();
else {
QModelIndex item = indexInQuery(index);
if ( !this->query().seek(item.row()) )
return QVariant();
return this->query().value(fieldName);
}
return QVariant();
}
};
main.qml
Window {
visible: true
width: 640
height: 480
TableView {
anchors.fill: parent
model: SQQL
TableViewColumn{ role: "binIdRole" ; title: "BinId" ; visible: true}
TableViewColumn{ role: "partitionIdRole" ; title: "PartitionId" }
TableViewColumn{ role: "unitIdRole" ; title: "UnitId" }
TableViewColumn{ role: "itemCountRole" ; title: "ItemCount" }
}
}

QT QML import ListModel from C++ to QML

How can i change the model of a PathView with c++ code ?
i add an objectName to my pathView to find it, then i change the property like this, but when i do that, my list is empty :
QDeclarativeItem *listSynergie = myClass.itemMain->findChild<QDeclarativeItem*> ("PathViewInscription");
listSynergie->setProperty("model", QVariant::fromValue(dataList));
My data list is fill like this :
QList<QObject*> dataList;
dataList.append(new synergieListObject("Item 1", "1",false));
dataList.append(new synergieListObject("Item 2", "2",true));
dataList.append(new synergieListObject("Item 3", "3",false));
dataList.append(new synergieListObject("Item 4", "4",false));
This is the code of my PathView :
PathView {
objectName: "PathViewInscription"
Keys.onRightPressed: if (!moving) { incrementCurrentIndex(); console.log(moving) }
Keys.onLeftPressed: if (!moving) decrementCurrentIndex()
id: view
anchors.fill: parent
highlight: Image { source: "../spinner_selecter.png"; width: view.width; height: itemHeight+4; opacity:0.3}
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
dragMargin: view.width/2
pathItemCount: height/itemHeight
path: Path {
startX: view.width/2; startY: -itemHeight/2
PathLine { x: view.width/2; y: view.pathItemCount*itemHeight + itemHeight }
}
}
and the code of ListModel :
ListModel {
id: appModel
ListElement { label: "syn1"; value: "1"; selected:false}
ListElement { label: "syn2"; value: "2" ; selected:false}
ListElement { label: "syn3"; value: "3" ; selected:false}
}
what's wrong ?
Thanks !
EDIT :
the code of the appDelegate :
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors { horizontalCenter: parent.horizontalCenter }
text: label
smooth: true
}
MouseArea {
anchors.fill: parent
onClicked: view.currentIndex = index
}
}
}
the code of my object :
#ifndef SYNERGIELISTOBJECT_H
#define SYNERGIELISTOBJECT_H
#include <QObject>
class synergieListObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
public:
synergieListObject(QObject *parent=0);
synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent=0);
QString label() const;
void setLabel(const QString &label);
QString value() const;
void setValue(const QString &value);
bool selected() const;
void setSelected(const bool &selected);
signals:
void labelChanged();
void valueChanged();
void selectedChanged();
private:
QString m_label;
QString m_value;
bool m_selected;
};
#endif // SYNERGIELISTOBJECT_H
c++ my object :
#include "synergielistobject.h"
synergieListObject::synergieListObject(QObject *parent): QObject(parent)
{
}
synergieListObject::synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent): QObject(parent), m_label(label), m_value(value), m_selected(selected)
{
}
QString synergieListObject::label() const
{
return m_label;
}
void synergieListObject::setLabel(const QString &label)
{
if (label != m_label) {
m_label = label;
emit labelChanged();
}
}
QString synergieListObject::value() const
{
return m_value;
}
void synergieListObject::setValue(const QString &value)
{
if (value != m_value) {
m_value = value;
emit valueChanged();
}
}
bool synergieListObject::selected() const
{
return m_selected;
}
void synergieListObject::setSelected(const bool &selected)
{
if (selected != m_selected) {
m_selected = selected;
emit selectedChanged();
}
}
I have never used QdeclarativeItem to set model in QML . Try this instead
QDeclarativeContext *ctxt = view.rootContext(); ctxt->setContextProperty("model", QVariant::fromValue(dataList));
Declare the model as a property of the root. This way you can set model.Or add a function that takes model as argument and sets the model for the view. Then you can call this function from c++.

Resources