I created an in-memory SQLite database using Qt-C++
const QString m_DRIVER = "QSQLITE";
QSqlDatabase m_db;
m_db = QSqlDatabase::addDatabase(m_DRIVER, "mkddb");
m_db.setDatabaseName(":memory:");
My question is how I can access it in QML?
As long as You have the database connection established in some class of Yours, eg. Database Handler, You can expose it to QML in a very simple way. Here is a very brief concept of how such handler would look like:
DatabaseHandler.h
class DatabaseHandler : public QObject
{
Q_OBJECT
public:
explicit DatabaseHandler(QObject *parent = nullptr);
Q_INVOKABLE void addDatabaseEntry(QString queryString);
private:
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE", "mkddb");
QSqlQuery databaseQuery;
};
DatabaseHandler.cpp
DatabaseHandler::DatabaseHandler(QObject *parent) : QObject(parent)
{
database.setDatabaseName(":memory:");
database.open();
databaseQuery = QSqlQuery(database);
}
void DatabaseHandler::addDatabaseEntry(QString queryString)
{
databaseQuery.exec(queryString);
}
The object itself has to be a QObject with a Q_OBJECT macro as on the code above.
To expose the DatabaseHandler to QML, in eg. main.cpp You would have to write this:
qmlRegisterType<DatabaseHandler>("com.organization.databasehandler", 1, 0, "DatabaseHandler");
Then in QML code You can import the database handler and use its methods as long as they're declared with the macro Q_INVOKABLE like in the DatabaseHandler.h:
import com.organization.databasehandler 1.0
ApplicationWindow {
DatabaseHandler {
id: databaseHandler
}
Button {
onClicked: databaseHandler.addDatabaseEntry(/*Query to execute on the database*/)
}
}
Related
For example I have my custom class with model.
class DbWrapper : public QObject
{
Q_OBJECT
explicit DbWrapper(QObject *parent = nullptr);
Q_INVOKABLE void connect();
QSqlDatabase db;
MyModel* myModel;
}
void DbWrapper::connect()
{
///db.open()
myModel = new MyModel(0,db);
/// myModel.select() and another
}
and main.qml for example:
DbWrapper db();
db.connect();
QQmlContext *context = engine.rootContext();
context->setContextProperty("db", &db);
context->setContextProperty("journalModel", db.tableModel);
and in QML for TableView model: journalModel it's working. But I have some problem when me need connect to database after. for example from QML event button click.
onClicked{
db.connect()
}
But I can't review my model in QTableView, how i can solve it? MyModel this custom class from QSqlRelationTableModel.
I have an enum that I use in qml
class SettingManager : public QObject
{
Q_OBJECT
public:
enum BookKinds{
BookKind1=0,
BookKind2=1,
};
Q_ENUMS(BookKinds)
Q_PROPERTY(BookKinds bookKind READ bookKind WRITE setBookKind NOTIFY bookKindChanged)
explicit SettingManager(QObject *parent = nullptr);
void setBookKind(BookKinds dkob);
BookKinds bookKind();
signals:
void bookKindChanged();
};
in main.cpp I registerd SettingManager
qmlRegisterType<SettingManager>("Test",1,0,"SettingManager");
I use this in qml file
onCurrentIndexChanged:
{
if(tbarBookKindForDisplay.currentIndex==0)
{
settingManager.bookKind=BookManager.BookKind1;
}
else if(tbarBookKindForDisplay.currentIndex==1){
settingManager.bookKind=BookManager.BookKind2;
}
}
when CurrentIndex of TabBar changes below error occurs:
Error: Cannot assign [undefined] to int
You register the type as SettingManager but use it as BookManager. The correct code is:
settingManager.bookKind = SettingManager.BookKind1;
You should also use Q_ENUM instead of Q_ENUMS.
I was following an example on web to populate combobox, but it did not wok for me and I do not know why!. I have two classes stock and DbCon, stock has three private fields along with public accessor and mutators. DbCon has a Q_Property and two public function, one returns a database connection and the other creates and returns stock list as a QList<QObject*>. In main.cpp I have created a contextual property named "data" to access DbCon from QML.
in main.qml I have
....
ComboBox{
model: data.stockModel
textRole: "code"
}
....
in main.cpp
DbCon db;
engine.rootContext()->setContextProperty("data", &db);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
in dbcon.h
class DbCon:public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> stockModel READ stockModel)
public:
explicit DbCon(QObject *parent = 0);
QSqlDatabase db();
QList<QObject*> stockModel();
};
in implementation of QList<QObject*> stockModel() of dbcon.h
QList<QObject*> data;
....
while (query.next()) {
stock *s = new stock();
....
data.append(s);
}
return data;
and in stock.h
class stock : public QObject
{
Q_OBJECT
private:
QString m_name;
QString m_code;
int m_id;
public:
explicit stock(QObject *parent = 0);
QString name();
void setname(QString &name);
QString code();
void setcode(QString &code);
int id();
void setid(int &id);
};
When I run the application I get the following message in application output
QQmlExpression: Expression qrc:/main.qml:16:20 depends on non-NOTIFYable properties:
QQuickComboBox::data
and I do not get anything in combobox!
If I create another contextual property in main.cpp in this way
engine.rootContext()->setContextProperty("myModel", QVariant::fromValue(data));
and set myModel as model for combobox, it works fine. But I want to do it in this way because onCurrentIndexChanged I will call another function that returns another QList<QObject*> for a TableView of another qml file.
EDIT: Entrie qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width:600
height:600
property string contentSource
Column{
spacing:10
ComboBox{
model: data.stockModel
textRole: "code"
}
Loader {
id: content
width: parent.width
height:400
}
Row{
spacing:10
Button{
text: "Page1"
onClicked: content.source = "Page1.qml"
}
Button{
text: "Page2"
onClicked: content.source = "Page2.qml"
}
}
}
}
By changing data to dataStore in main.cpp and data.stockModel to dataStore.stockModel in main.qml I get following error
file:///C:/Qt/Qt5.7.0/5.7/mingw53_32/qml/QtQuick/Controls.2/ComboBox.qml:62:15: Unable to assign [undefined] to QString
You have two issues:
Your stockModel property should be NOTIFYable, which means that you should define the property with e.g. Q_PROPERTY(QList<QObject*> stockModel READ stockModel NOTIFY stockModelChanged) and provide a void stockModelChanged(const QList<QObject *> &) signal in the DbCon class.
stock::name() must be a property too, so you need to declare that with Q_PROPERTY(QString name READ name NOTIFY nameChanged) and provide a void nameChanged(const QString &) signal in the stock class as well.
Can you explain a methods for storing program data with global access ?
I found these keywords:
- using static class to store data
- pass QList by value
- pass Qlist by reference
- use 'friend' keyword
but I cannot find any real example of storing global QList, as they say, it is a bad design to use global variables. Also there is a mention that using pointers on a QList is a bad idea because of implicit sharing (?).
So where should I store my Qlist for accessing it from a different class in an other .cpp ? So I have:
mainwindow.h
QList <SceneCard> sceneList;
QString mTitle;
public slots:
QString setValue()
{
return mTitle;
}
mainwindow.cpp
MainWindow::AddScene()
{
sceneCard = new SceneCard(sNumber);
sceneList.append(sceneCard);
mTitle = "Nejat is right!"
}
void MainWindow::showSceneCard()
{
SceneDialog D;
connect(D,SIGNAL(getValue()),this,SLOT(setValue()));
D.exec();
}
scenedialog.h
#ifndef SCENEDIALOG_H
#define SCENEDIALOG_H
#include <QDialog>
#include <QList>
namespace Ui {
class SceneDialog;
}
class SceneDialog : public QDialog
{
Q_OBJECT
public:
SceneDialog(QWidget *parent = 0);
~SceneDialog();
signals:
QString getValue();
private:
Ui::SceneDialog *ui;
QString myText;
};
scenedialog.cpp
#include "scenedialog.h"
#include "ui_scenedialog.h"
#include <QDebug>
SceneDialog::SceneDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SceneDialog)
{
ui->setupUi(this);
myText = getValue();
qDebug() << myText; // myText is empty!!
}
You can put your list as a class member and use Qt's Signal/slot mechanism to access the list from other classes. Just make a signal in the target class, connect it to a slot in the class containing the list and make a connection between two objects of the classes. This way you can access any data member of other classes by connecting a signal to a slot returning that value and just emitting the signal and getting the return value.
For example if you have two classes like :
class A: public QObject
{
Q_OBJECT
public:
A(QObject *parent = 0);
~A();
signals:
int getValue();
private:
void someFunction()
{
int val = getValue();
}
};
class B
{
Q_OBJECT
public:
B(QObject *parent = 0);
~B();
public slots:
int getValue()
{
return someValue;
}
};
And connect the signal from an object of A to the slot in an object of B :
connect(a, SIGNAL(getValue()), b, SLOT(getValue()));
In class A you can access the value returned from getValue slot in B by just calling the signal and using the returned value.
Note that the two objects should be in the same thread for this to work. If they are in different threads then the connection type should be of type Qt::BlockingQueuedConnection :
connect(a, SIGNAL(getValue()), b, SLOT(getValue()), Qt::BlockingQueuedConnection);
Another way is two use static class members but it is not recommended unless you have a good reason to do it. If you have two classes like :
class A {
public:
static QList<int> list;
};
class B {
public:
void do_something();
};
You can access A's static data member from B like this:
void B::do_something()
{
int val = A::list[0];
...
};
I am using QT framework. I have been using SIGNAL-SLOT for a while. I like it. :-)
But I cannot make it work when I use QThread. I always create new thread using “moveToThread(QThread …)” function.
Any suggestion? :-)
The error message is:
Object::connect: No such slot connection::acceptNewConnection(QString,int) in ..\MultiMITU600\mainwindow.cpp:14
Object::connect: (sender name: 'MainWindow')
I have read about similar problems but those were not connected to QThread.
Thanks, David
EDITED: you asked for source code
Here is one:
Here is the code:
The main class which contains the signal and the new thread:
mainwindow header:
class MainWindow : public QMainWindow
{
…
QThread cThread;
MyClass Connect;
...
signals:
void NewConnection(QString port,int current);
…
};
The constructor of the above class: .cpp
{
…
Connect.moveToThread(&cThread1);
cThread.start(); // start new thread
….
connect(this,SIGNAL(NewConnection(QString,int)),
&Connect,SLOT(acceptNewConnection(QString,int))); //start measuring
…
}
The class that contains the new thread and SLOT
Header:
class MyClass: public QObject
{
Q_OBJECT
….
public slots:
void acceptNewConnection(QString port, int current);
}
And the .cpp file of the above class:
void MyClass::acceptNewConnection(QString port, int current){
qDebug() << "This part is not be reached";
}
Finally I use emit in the class where the connection was made:
void MainWindow::on_pushButton_3_clicked()
{
…
emit NewConnection(port, 1);
}
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
public slots:
void acceptConnection(QString port, int current) {
qDebug() << "received data for port " << port;
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0) : QMainWindow(parent) {
myClass.moveToThread(&thread);
thread.start();
connect(this, SIGNAL(newConnection(QString,int)), &myClass, SLOT(acceptConnection(QString,int)));
emit newConnection("test", 1234);
}
signals:
void newConnection(QString, int);
private:
QThread thread;
MyClass myClass;
};
output:
received data for port "test"
Is your void MainWindow::on_pushButton_3_clicked() slot connected to a signal?
Also, for the sake of the clarity and readability of your code, keep the established naming convention and use lower case for object instances and member objects and methods.