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

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

Related

How to fix QML code editor auto-completion for integrated C++ methods in QT Creator?

All!
In my QTCreator for QML code auto-completion for integrated C++ methods doesn't work.
For instance, I did Myclass class and pass object pointer to QML engine by setContextProperty method.
QQmlApplicationEngine engine;
Myclass* confptr = new Myclass();
engine.rootContext()->setContextProperty("testCPP", confptr);
In QML code after I input ...
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column {
spacing: 10
Button {
onClicked: testCPP.
... there is no auto-completion popup. So I need copy method name manually from C++ code.
After running application it works properly but manual inputs disturbs me.
Can you give an advice, how I can solve this problem?
Myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
class Myclass : public QObject
{
Q_OBJECT
Q_PROPERTY(int engineRPM READ engineRPM WRITE setEngineRPM NOTIFY engineRPMChanged)
public:
Myclass();
int engineRPM() const;
signals:
void engineRPMChanged();
private:
int m_engineRPM;
public slots:
void setEngineRPM (int temp);
};
#endif // MYCLASS_H
Myclass.cpp
#include "myclass.h"
#include <QDebug>
Myclass::Myclass()
{
setEngineRPM(100);
}
void Myclass::setEngineRPM (int temp){
m_engineRPM = temp;
qDebug () << "Some text here" << temp;
}
int Myclass::engineRPM() const{
return m_engineRPM;
}
Main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "myclass.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Myclass* confptr = new Myclass();
confptr->setEngineRPM(1);
engine.rootContext()->setContextProperty("testCPP", confptr);
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();
}
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column {
spacing: 10
Button {
onClicked: testCPP.setEngineRPM(1000)
}
Text {
text: testCPP.engineRPM
}
}
}
I have installed:
Qt Creator 5.0.0 Based on Qt 5.15.2 (MSVC 2019, 64 bit)
OS: Windows 10 21H1 build 19043.1415

Control OSM map in QML qt

I am trying to control the map in Qt however I keep ending up with the following error:
QGeoTileRequestManager: Failed to fetch tile (291271,152514,19) 5 times, giving up. Last error message was: 'Permission denied'
I have functions in C++ that parse the messages and calculates the position:
Map.qml
import QtQuick 2.12
import QtQuick.Window 2.14
import QtQuick.Controls 2.15
import QtLocation 5.6
import QtPositioning 5.6
import QtQuick.Controls.Material 2.12
import QtQuick.Layouts 1.12
import Qt.labs.location 1.0
Page {
id: mainWindow
visible: true
function addMarker(latitude, longitude)
{
var Component = Qt.createComponent("qrc:/Marker.qml")
var item = Component.createObject(window, {
coordinate: QtPositioning.coordinate(latitude, longitude)
})
map.addMapItem(item)
}
Map {
id: map
width: mainWindow.width
height: mainWindow.height
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75)
Component.onCompleted: addMarker(59.91, 10.75)
zoomLevel: 60
}
Plugin {
id: mapPlugin
name: "osm" // "mapboxgl", "esri", ...
// specify plugin parameters if necessary
PluginParameter {
name: "osm.mapping.providersrepository.disabled"
value: "true"
}
PluginParameter {
name: "osm.mapping.providersrepository.address"
value: "http://maps-redirect.qt.io/osm/5.6/"
}
}
}
Setting the coordinates is done through Q_PROPERTY:
#include <QObject>
class Data : public QObject
{
Q_OBJECT;
Q_PROPERTY(double gnss_log READ gnss_long WRITE set_gnss_long NOTIFY gnss_long_changed);
Q_PROPERTY(double gnss_lat READ gnss_lat WRITE set_gnss_lat NOTIFY gnss_lat_changed);
public: signals:
void gnss_long_changed();
void gnss_lat_changed();
public slots:
void set_gnss_long(double);
void set_gnss_lat(double);
public:
Data();
double gnss_long();
double gnss_lat();
private:
double m_gnss_long;
double m_gnss_lat;
};
void Data::set_gnss_long(double curr_long)
{
// Checks whether updated baud rate changed
if (curr_long == m_gnss_long)
return;
m_gnss_long = curr_long;
qDebug() << m_gnss_long;
//Emits signal indicating change
emit gnss_long_changed();
}
double Data::gnss_long()
{
return m_gnss_long;
}
I when I run this, I get a blank screen with a bunch of the errors mentioned above.
Since the OP has not provided an MRE my answer will only show a demo. The logic is to create a QProperty that exposes the position, then a binding must be done:
#include <QGeoCoordinate>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QTimer>
#include <random>
class Data: public QObject{
Q_OBJECT
Q_PROPERTY(QGeoCoordinate gnssPosition READ gnssPosition WRITE setGnssPosition NOTIFY gnssPositionChanged)
public:
const QGeoCoordinate &gnssPosition() const{
return m_gnssPosition;
}
void setGnssLatitude(qreal latitude){
QGeoCoordinate coordinate(latitude, m_gnssPosition.longitude());
setGnssPosition(coordinate);
}
void setGnssLongitude(qreal longitude){
QGeoCoordinate coordinate(m_gnssPosition.latitude(), longitude);
setGnssPosition(coordinate);
}
void setGnssPosition(const QGeoCoordinate &newGnssPosition){
if (m_gnssPosition == newGnssPosition)
return;
m_gnssPosition = newGnssPosition;
emit gnssPositionChanged();
}
signals:
void gnssPositionChanged();
private:
QGeoCoordinate m_gnssPosition;
};
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
Data data_out;
data_out.setGnssPosition(QGeoCoordinate(59.91273, 10.74609));
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("data_out", &data_out);
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);
QTimer timer;
timer.setInterval(1000);
QObject::connect(&timer, &QTimer::timeout, &data_out, [&data_out](){
std::random_device rd;
std::mt19937 e2(rd());
std::uniform_real_distribution<> dist(-.05, .05);
QGeoCoordinate coord = data_out.gnssPosition();
coord.setLatitude(coord.latitude() + dist(e2));
coord.setLongitude(coord.longitude() + dist(e2));
data_out.setGnssPosition(coord);
// qDebug() << data_out.gnssPosition();
});
timer.start();
return app.exec();
}
#include "main.moc"
import QtQuick 2.15
import QtQuick.Window 2.15
import QtLocation 5.15
import QtPositioning 5.15
Window {
id: mainWindow
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property Component markerProvider: MapQuickItem {
anchorPoint.x: rect.width / 2
anchorPoint.y: rect.height / 2
sourceItem: Rectangle{
id: rect
width: 40
height: 40
color: "salmon"
}
}
function addMarker(coordinate){
var marker = markerProvider.createObject()
console.log(marker)
marker.coordinate = coordinate
map.addMapItem(marker)
}
Map {
id: map
width: mainWindow.width
height: mainWindow.height
plugin: mapPlugin
center: data_out.gnssPosition
zoomLevel: 12
}
Plugin {
id: mapPlugin
name: "osm" // "mapboxgl", "esri", ...
// specify plugin parameters if necessary
PluginParameter {
name: "osm.mapping.providersrepository.disabled"
value: "true"
}
PluginParameter {
name: "osm.mapping.providersrepository.address"
value: "http://maps-redirect.qt.io/osm/5.6/"
}
}
Connections{
target: data_out
function onGnssPositionChanged(){
addMarker(data_out.gnssPosition)
}
}
}

QQuickPaintedItem update function does not redraw when using it for loading a new QImage

I am displaying an image in QT C++ with QQuickPaintedItem, the first draw works fine however when I want to load a new image the paint function is not called. I narrowed it down to a simple example where a button is clicked and the new image should be loaded. I am using a stackview, so I have a .ui.qml file and a qml file.
The Header File:
#ifndef IMAGEITEM_H
#define IMAGEITEM_H
#include <QQuickPaintedItem>
#include <QQuickItem>
#include <QPainter>
#include <QImage>
class ImageItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
ImageItem(QQuickItem *parent = nullptr);
Q_INVOKABLE void setImage(const QImage &image);
void paint(QPainter *painter);
QImage image() const;
Q_INVOKABLE void toggleImage();
signals:
void imageChanged();
private:
QImage current_image;
};
#endif // IMAGEITEM_H
The Cpp file. Here the function "toggleImage" is the one being called when pressing the button. It gets called, but the paint function won't get called.
#include "imageitem.h"
ImageItem::ImageItem(QQuickItem *parent) : QQuickPaintedItem(parent)
{
//adapt this accordint to your location of the image!
this->current_image = QImage("file:/../../SimpleImageSwitcher/1.png");
}
void ImageItem::paint(QPainter *painter)
{
QRectF bounding_rect = boundingRect();
QImage scaled = this->current_image.scaledToHeight(bounding_rect.height());
QPointF center = bounding_rect.center() - scaled.rect().center();
if(center.x() < 0)
center.setX(0);
if(center.y() < 0)
center.setY(0);
painter->drawImage(center, scaled);
}
QImage ImageItem::image() const
{ return this->current_image;
}
void ImageItem::toggleImage()
{
qDebug() << "image changer called!";
QImage img = QImage("file:/../../SimpleImageSwitcher/2.png");
this->setImage(img);
emit imageChanged();
}
void ImageItem::setImage(const QImage &image)
{
this->current_image = image;
update();
}
The main cpp where I register the Image view and set the context property:
#include <QGuiApplication>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include "imageitem.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
ImageItem imageengine;
engine.rootContext()->setContextProperty("imageEngine",&imageengine);
qmlRegisterType<ImageItem>("imageView", 1, 0, "ImageItem");
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();
}
The ui.qml called HomeForm.ui.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import imageView 1.0
Page {
width: 600
height: 400
property alias button: button
property alias test: button
title: qsTr("Home")
ImageItem {
id: liveImageItem
height: parent.height
width: parent.width
}
Button {
id: button
text: "change"
anchors.verticalCenterOffset: -180
anchors.horizontalCenterOffset: -250
anchors.centerIn: parent
}
}
...and the main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import imageView 1.0
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Stack")
StackView {
id: stackView
initialItem: "HomeForm.ui.qml"
anchors.fill: parent
}
HomeForm {
button.onClicked: imageEngine.toggleImage()
}
}
When I tried to debug the functions and stepped in to the update() function it went out of the functions right after this line: Q_DECL_CONSTEXPR QRect() noexcept : x1(0), y1(0), x2(-1), y2(-1) {}. What does that mean? I read that a lot of times the width and the height of the Rectangle where the image is displayed must be set but I did so. Thank you for your help

Get user input from qml to c++

I'm trying to get user input from qml TextField to c++, but it only works if text property value is hardcoded (const value).
c++
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *serverField1 = rootObject->findChild<QObject*>("serverField1");
qDebug() << serverField1->property("text"); //Here I expect to get user input
Qml
ApplicationWindow {
id: applicationWindow
visible: true
width: 300
height: 550
TextField {
id: serverField1
objectName: "serverField1"
width: 200
height: 110
// text: "hardcoded value" //If text is const value, qDebug will get data from this property
}
}
You are asking for the text of the TextField when the window is displayed so what you will get is the text you have initially set, what you should do is get it every time it changes. I think that you have some class that will handle some processing with that data, that class will be called Backend, it must inherit from QObject so that it can have a qproperty and embed an object of that class to QML through setContextProperty, so every time it changes the text in QML will also change the text in the Backend class object.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlProperty>
#include <QDebug>
class Backend: public QObject{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
QString text() const{
return mText;
}
void setText(const QString &text){
if(text == mText)
return;
mText = text;
emit textChanged(mText);
}
signals:
void textChanged(const QString & text);
private:
QString mText;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Backend backend;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("backend", &backend);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
// test
QObject::connect(&backend, &Backend::textChanged, [](const QString & text){
qDebug() << text;
});
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TextField {
id: serverField1
x: 15
y: 46
width: 120
height: 45
topPadding: 8
font.pointSize: 14
bottomPadding: 16
placeholderText: "Server Ip"
renderType: Text.QtRendering
onTextChanged: backend.text = text
}
}
You can find the complete code in the following link.

Adding markers/places to Qt QML Maps?

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.