conn1 is the name of db connection not C++ variable. Variable name is m_insertDataBase - C++ ABC.
And line 4is unnecessary.Qt Code:
To copy to clipboard, switch view to plain text mode
conn1 is the name of db connection not C++ variable. Variable name is m_insertDataBase - C++ ABC.
And line 4is unnecessary.Qt Code:
To copy to clipboard, switch view to plain text mode
You also don't want to do the addDatabase() and setDatabaseName() calls every time, just once, when "m_insertDataBase" is not valid yet.
See QSqlDatabase:::isValid().
Also, as someone had already pointed out in another thread, don't concatenated an input string into a SQL Query, always use prepare() and bound values for proper escaping.
Cheers,
_
It didn't seem to work when passing the driver and the connection name with the line of code above this one. So I tried setting the connection name again but kept getting "conn1 was not declared in this scope". I know conn1 is the db connection name I declare it in the code above and in this line and I declare the instance variable in my header file. I only used the above code seeing if setting the db connection name would work like this I realize its unnecessary, I was just trying something to get it to work. I'm not sure if I need to declare the instance variable under public or not in the header file?
Added after 4 minutes:
You are talking about binding my Msg variable correct? the parameter I'm using in my sql statement?
I was using a prepare statement before but took it out to try to short my code and it only takes one parameter wasn’t sure how to tell if its using the right connection. I will go back to using the prepare statement as suggested to allow proper escaping. How do I set the conn1 connection since The prepare() fcn only takes one argument can't pass it the connection name with sql statement do I just set the connection by
Qt Code:
To copy to clipboard, switch view to plain text mode
I will look at documentation on isValid(). How do I only call those methods once when the instance is not valid? little explanation on how this works be great, you got a example?
Last edited by jfinn88; 24th August 2016 at 16:29.
Yes
You pass the QSqlDatabase handle to the QSqlQuery constructor.
Qt Code:
insertQuery.prepare(...);To copy to clipboard, switch view to plain text mode
Qt Code:
if (!m_insertDataBase.isValid() { // addDatabase, setDatabaseName }To copy to clipboard, switch view to plain text mode
Cheers,
_
I have two classes I need to use an instance variable of the class QSqlDatabase to set database connections. I created an instance variable of the QSqlDatabase class in both classes under private:
--------------- Header file----------------
Qt Code:
//Data struct for user event log struct userEventLogMsg{ //hold all values for a single list entry, QString id; QString username; QString eventmessage; QString datetime; }; //---Class UserEventDailyLog responsible for XMUI UserEvnetLog | Subed classed: QAbstractTableModel---// { Q_OBJECT public: ~UserEventLog(); enum userEventRoles {idRole= Qt::UserRole + 220, nameRole, msgRole, dateRole}; QHash<int, QByteArray> roleNames() const; Q_INVOKABLE void addEvent(const userEventLogMsg &msg); void dbConnect(); void sqlSelect(); private: QList<userEventLogMsg> m_userEventList; QSqlDatabase m_selectDataBase; };To copy to clipboard, switch view to plain text mode
Qt Code:
//---------CPP file--------------// // // UserEventLogModel // //---Constructor---// } //---Destructor---// UserEventLog::~UserEventLog() { } Q_UNUSED(parent); qDebug()<< m_userEventList.count(); return m_userEventList.count(); } QHash<int, QByteArray> UserEventLog::roleNames() const { QHash<int, QByteArray> roleNames; roleNames.insert(idRole, "id"); roleNames.insert(nameRole, "userName"); roleNames.insert(msgRole, "eventMessage"); roleNames.insert(dateRole, "dateTime"); qDebug()<< roleNames; return roleNames; } if (index.row() < 0 || index.row() >= m_userEventList.count()){ } QVariant text; if(role == idRole) { userEventLogMsg msg = m_userEventList.at(index.row()); text = msg.id; qDebug() << text; } else if(role == nameRole) { userEventLogMsg msg = m_userEventList.at(index.row()); text = msg.username; qDebug() << text; } else if(role == msgRole) { userEventLogMsg msg = m_userEventList.at(index.row()); text = msg.eventmessage; qDebug() << text; } if(role == dateRole) { userEventLogMsg msg = m_userEventList.at(index.row()); text = msg.datetime; qDebug() << text; } return text; } void UserEventLog::addEvent(const userEventLogMsg &msg) { m_userEventList.insert(0, msg); endInsertRows(); } void UserEventLog::dbConnect() { qDebug() << m_selectDataBase.isValid(); m_selectDataBase.setDatabaseName("/home/amet/userLog.db"); m_selectDataBase.open(); if(!m_selectDataBase.open()){ qDebug() <<"error in opening DB"; } else{ qDebug() <<"connected to DB" ; } } void UserEventLog::sqlSelect() { //m_selectDataBase = QSqlDatabase::database("conn2"); qDebug() << m_selectDataBase.isValid(); m_selectDataBase.open(); QSqlQuery selectQuery; selectQuery.prepare("SELECT id, userName, eventMessage, dateTime FROM userlogevents"); qDebug() <<selectQuery.lastError(); if(selectQuery.exec()){ qDebug()<<"sql statement exicuted fine"; } else{ qDebug() <<"Errors accured with sql statement"; qDebug() <<selectQuery.lastError(); } while (selectQuery.next()){ userEventLogMsg msg; UserEventLog model; msg.id = selectQuery.value(0).toString(); msg.username = selectQuery.value(1).toString(); msg.eventmessage = selectQuery.value(2).toString(); msg.datetime = selectQuery.value(3).toString(); model.addEvent(msg); } //m_selectDataBase.close(); //m_selectDataBase.removeDatabase("conn2"); } //----------------------------------------------------------------//To copy to clipboard, switch view to plain text mode
my other class that need a database connection to the same database
---------CPP file-----------
Qt Code:
void XMUI::hdpiWindow() { mUserEventLogModel = new UserEventLog(); mUserEventLogModel->dbConnect(); mUserEventLogModel->sqlSelect(); m_QmlEngine->rootContext()->setContextProperty("UserEventLog", mUserEventLogModel); } { m_insertDataBase.setDatabaseName("/home/amet/userLog.db"); m_insertDataBase.open(); if(m_insertDataBase.isOpen()){ qDebug() <<"connected to DB" ; } else{ qDebug() <<"error in opening DB"; m_insertDataBase.open(); } qDebug() << "insert Log Message called"; insertQuery.prepare("INSERT INTO userlogevents (firstName, lastName, userName, eventMessage, dateTime) VALUES('John', 'Doe', 'JohnnyD', ':eventMessage', datetime(current_timestamp))"); insertQuery.bindValue(":eventMessage", msg); insertQuery.exec(); //m_insertDataBase.close(); //m_insertDataBase.removeDatabase("conn1"); }To copy to clipboard, switch view to plain text mode
----header file--------
Qt Code:
{ Q_OBJECT public: explicit XMUI(QQuickWidget* _quickWidget); void hdpiWindow(); private: QSqlDatabase m_insertDataBase;To copy to clipboard, switch view to plain text mode
Added after 18 minutes:
I was passing the name of my connection not the instance variable, changed it to instance variable.
I moved the add and set methods inside an if condition to set them if the database is not valid. However I get confused because I need to connect to the database to begin with... If I'm only setting them when it not valid how do I initially set the connect the first time. set it in the constructor?Qt Code:
if (!m_insertDataBase.isValid() { // addDatabase, setDatabaseName }To copy to clipboard, switch view to plain text mode
Cheers,
_
Qt Code:
{ //qDebug() << m_insertDataBase.connectionNames(); if(!m_insertDataBase.isValid()){ qDebug() <<"error in opening DB"; m_insertDataBase.setDatabaseName("/home/amet/userLog.db"); qDebug() << m_insertDataBase.connectionNames(); } else{ qDebug() <<"connected to DB"; m_insertDataBase.open(); //qDebug() << m_insertDataBase.lastError(); } m_insertDataBase.open(); m_insertQuery.prepare("INSERT INTO userlogevents (firstName, lastName, userName, eventMessage, dateTime) VALUES('John', 'Doe', 'JohnnyD', ':eventMessage', datetime(current_timestamp))"); m_insertQuery.bindValue(":eventMessage", msg); m_insertQuery.exec(); qDebug() << m_insertQuery.lastError(); //m_insertDataBase.close(); //QSqlDatabase::removeDatabase("conn1"); }To copy to clipboard, switch view to plain text mode
Last edited by jfinn88; 24th August 2016 at 21:38.
You can call open inside the same if(), or after the whole if/else block.
There is no point in calling open() before it is valid and there is no point in passing the instance to QSqlQuery before it is opened.
There is also no point in using prepare if you are still just concenating strings instead of passing the msg value as via bindValue().
You also might want to think what your while loop is doing, in particular the life time of the "model" object and which class you are in.
Cheers,
_
I updated this right before you posted this. I'm using the bind value method now with the prepare method
Now that you point it out that makes sense will move open() call inside if() statement, since the if statement is checking for validation would you also pass the instance to QSqlQuery inside the if() block?
Qt Code:
//---------Inserts data into database (XMUI Global Funciton)--------------// { //qDebug() << m_insertDataBase.connectionNames(); if(!m_insertDataBase.isValid()){ qDebug() <<"error in opening DB"; m_insertDataBase.setDatabaseName("/home/amet/userLog.db"); qDebug() << m_insertDataBase.connectionNames(); } else{ qDebug() <<"connected to DB"; m_insertDataBase.open(); //qDebug() << m_insertDataBase.lastError(); } m_insertDataBase.open(); m_insertQuery.prepare("INSERT INTO userlogevents (firstName, lastName, userName, eventMessage, dateTime) VALUES('John', 'Doe', 'JohnnyD', :eventMessage, datetime(current_timestamp))"); m_insertQuery.bindValue(":eventMessage", msg); m_insertQuery.exec(); qDebug() << m_insertQuery.lastError(); //m_insertDataBase.close(); //QSqlDatabase::removeDatabase("conn1"); } //----------------------------------------------------------------//To copy to clipboard, switch view to plain text mode
Having trouble with connection to database... app crashes. I use a default connection for a user login and close it in the destructor, once the user logins in a series of method calls to insertLogMessage() takes place It seems like it never changes connection from default to conn1...
Tried placing some qdebug error checks:but I dont get any errors back: QSqlError("", "", "") I list the connection names usingQt Code:
qDebug() << m_insertDataBase.lastError(); & qDebug() << m_insertQuery.lastError();To copy to clipboard, switch view to plain text modeit shows the default connection and the conn1 connection | verified insert query pushes to database data is showing up now!Qt Code:
qDebug() << m_insertDataBase.connectionNames();To copy to clipboard, switch view to plain text mode
do I need to close or remove the database?
Now I need help fixing my function in my other class
Qt Code:
void UserEventLog::dbConnect() { if(!m_selectDataBase.isValid()){ qDebug() << "error in opening DB"; m_selectDataBase.setDatabaseName("/home/amet/userLog.db"); } else{ qDebug() <<"connected to DB" ; } m_selectDataBase.open(); } void UserEventLog::sqlSelect() { //---Check is Database is valid---// if(!m_selectDataBase.isValid()) { qDebug() << "error in opening DB"; m_selectDataBase.setDatabaseName("/home/amet/userLog.db"); m_selectDataBase.open(); } else{ qDebug()<<"connected to DB"; m_selectDataBase.open(); } //---Check if database is open---// if(!m_selectDataBase.open()) { qDebug() << "database was closed"; m_selectDataBase.open(); } else{ qDebug() << "database is open"; } //---Check if Sql Query Ran---// if(selectQuery.exec()){ qDebug()<<"sql statement exicuted fine"; } else{ qDebug() << "Errors accured with sql statement"; qDebug() << selectQuery.lastError(); } while (selectQuery.next()){ //---Instance of userEventLogMsg Class Struct---// userEventLogMsg msg; //---Instance of userEventlog Class---// UserEventLog model; //---Add query data to the msg class struct---// msg.id = selectQuery.value(0).toString(); msg.username = selectQuery.value(1).toString(); msg.eventmessage = selectQuery.value(2).toString(); msg.datetime = selectQuery.value(3).toString(); //---Use model object to access an call addEvent()---// model.addEvent(msg); } //m_selectDataBase.close(); //QSqlDatabase::removeDatabase("conn2"); }To copy to clipboard, switch view to plain text mode
Last edited by jfinn88; 24th August 2016 at 23:41.
No, I would leave the QSqlQuery outside.
No, you should be able to keep more than one connection at any given time.
Now I need help fixing my function in my other class
You likely want to use the m_selectDataBase for this query, no?
This doesn't look at all what I assume you want.
This creates a new model instance in every loop iteration, adds one record and then destroys the model (when it goes out of scope).
Cheers,
_
Okay, That makes sense played with it and I declare it before the if() block.
Okay, so I'm fine with leaving conn1, and conn2 connected.No, you should be able to keep more than one connection at any given time.
I can pass it with the sql statement correct ?You likely want to use the m_selectDataBase for this query, no?
Qt Code:
QSqlQuery selectQuery("SELECT id, userName, eventMessage, dateTime FROM userlogevents", m_selectDataBase);To copy to clipboard, switch view to plain text mode
you are correct I do not want to create a new object every time it goes threw while loop moved object declaration above while loop. Will calling addEvent () in the while loop be fine or should it be called once after the while loop is done putting data in struct?This doesn't look at all what I assume you want.
This creates a new model instance in every loop iteration, adds one record and then destroys the model (when it goes out of scope).
I’m still little confused how the virtual functions get called... from my understanding is when the instance variable of my class is created (to pass the model to C++ by setting rootContext item) it makes a call to the constructor and when the constructor gets called the virtual functions get called (trying to clear any confuse I still have with models and virtual functions) ?
Qt Code:
//---Instance of userEventLogMsg Class Struct---// userEventLogMsg msg; while (selectQuery.next()){ //---Add query data to the userEventLogMsg class struct---// msg.id = selectQuery.value(0).toString(); msg.username = selectQuery.value(1).toString(); msg.eventmessage = selectQuery.value(2).toString(); msg.datetime = selectQuery.value(3).toString(); //---Use model object to access an call addEvent()---// addEvent(msg); }To copy to clipboard, switch view to plain text mode
Also I want to call dbConnect and selectQuery() methods from QML (button click)
in the header file I declare the function as Q_INVOKABLE now I know I could register the QML type to make the class accessible in QML and import it in the QML to call the function but I think I would have to make the class global object? is there a simpler way to do this ?
I have seen an example with Q_INVOKABLE and setting the context property, but I get confused on what engine object/class I need to use to set it. I don't quit understand the rootContext methodology yet and have a QML engine already defined and in use that I don't want to mess up, Im not sure if replacing what is currently on the engine stack is the right way todo it? or if using a new engine object is correct... would I need to create an instance object of a qml engine in my userEvetnLog class? (how can I tell what object this is pointing to?)
Qt Code:
mUserEventLogModel = new UserEventLog(); m_QmlEngine->rootContext()->setContextProperty("UserEventLog", mUserEventLogModel);To copy to clipboard, switch view to plain text mode
In my QML I call my funtions
Qt Code:
Action { id: action_userEventLogBtn enabled:!inSequence onTriggered:{ //----Code to Load User Event Database into tableView-----// input_loader.filename = "" UserEventLog.dbConnect(); UserEventLog.sqlSelect(); weld_view.state = "USEREVENT" onLoaded: console.log("User Event Log");To copy to clipboard, switch view to plain text mode
update: Okay I got my functions working in QML used previously defined QQmlEngine and instance of UserEventLog class that was used to make my model available to QML
update: made a stupid mistake when calling my addEvent function and now just realized it. Was getting a segfault fixed by removing UserEventLog object and just calling addEvent since Im in the class.
Last edited by jfinn88; 25th August 2016 at 19:37.
Yes.
In every loop iteration.
You can basically keep your code, but instead of calling addEvent() on a different model you just call addEvent() of the current object.
When you create an object, the constructor for that class is called.
Once the object is created, calls to virtual functions always end up in the most specific subclass.
E.g. if you overwrite roleNames() a call to roleNames() will end up calling your implementation.
For what purpose?
I can see a use case of re-running the query on demand, but dbConnect?
dbConnect() is really internal, no? It has to be called before database access happens, so the class itself should know when to call it.
That is unnecessary.
Q_INVOKABLE methods and slots are already accessible as functions on an object set as a context property.
Registration of a type is only necessary if instances of that type need to be created from QML or if it has enum definitions where the QML code should be able to use the names.
You don't have to change anything in your code.
Cheers,
_
jfinn88 (26th August 2016)
Thank you for all the help anda_skoa,
Model seems to be working good now I will posted updated code
I'm now wanting to add some functionality to my tableView, Like to be able to search by date and userName... I want to pass text values back to my c++ function a would like to use the signal and slot mechanism to perform this action. However I get lost with what Object I need to pass the QObject::connect() method, for it to work.
I created two signals and two slots one for the date text field in qml and the other is for the userName text field in qml.
in C++ I created to slots to receive the qml signals and to out put the text data passed to the console using a debug() method.
I'm little confused on where to place the QObject::connect method in my project... main.cpp or sqliteModel.cpp in the constructor ?
I am unsure what objects to pass the connect() method for the signal to receive the slot ?
I would also Like to create a table in the database for each separate day for user event logging and delete old tables after 30 days
here is my code: (had to remove functions for model in cpp file to get it to fit)
=========== main.qml===============
Qt Code:
import QtQuick 2.5 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.3 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 Window { signal submitDateText(string text) signal submitUserNameText(string text) visible: true width: 760 height: 450 title: "User Event Log" TableView { width: 750; height: 350; anchors.centerIn: parent; horizontalScrollBarPolicy: 0 frameVisible: true model: sqliteModel TableViewColumn { role: "id" title: "id" width: 100 } TableViewColumn { role: "userName" title: "User Name" width: 200 } TableViewColumn { role: "eventMessage" title: "Event Message" width: 200 } TableViewColumn { role: "dateTime" title: "Date Time" width: 200 } } RowLayout { id: row1 x: 201 y: 403 anchors.horizontalCenter: parent.horizontalCenter; anchors.bottom: parent.bottom width: 750 height: 47; clip: false opacity: 0.9 Button { id: load_btn text: qsTr("Load") MouseArea{ anchors.fill: parent onClicked: { sqliteModel.dbConnect(); sqliteModel.sqlSelect(); } } } Label { id: userNameLabel text: qsTr("User Name") } TextField { id: userNameTextField placeholderText: qsTr("User Name") } Label { id: dateLabel width: 39 height: 17 text: qsTr("Date") } TextField { id: dateTextField width: 125 height: 25 placeholderText: qsTr("mm//dd/yyyy") } Button { id: searchBtn text: qsTr("Search") MouseArea{ anchors.fill: parent onClicked: { // emit the submitTextField signal submitDateText(dateTextField.text); submitUserNameText(userNameTextField.text); } } } Button { id: exit_btn text: qsTr("Exit") MouseArea{ anchors.fill: parent onClicked: close(); } } } }To copy to clipboard, switch view to plain text mode
=========== main.cpp===============
Qt Code:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QSqlDatabase> #include "sqlitemodel.h" #include <QUrl> int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); sqliteModel *model = new sqliteModel; QQmlApplicationEngine engine; QQmlContext *contxt = engine.rootContext(); contxt->setContextProperty("sqliteModel", model); return app.exec(); }To copy to clipboard, switch view to plain text mode
=========== sqliteModel.cpp==========
Qt Code:
#include "sqlitemodel.h" { // connect our QML signal to our C++ slot } sqliteModel::~sqliteModel() { } void sqliteModel::createDailyTable() { dbConnect(); int addOne; createTableQry.prepare("CREATE TABLE userlogevents1 AS SELECT * FROM userlogevents WHERE 0"); createTableQry.exec(); m_selectDataBase.close(); } void sqliteModel::deleteDailyTable() { dbConnect(); //---Selects all tables older than 30 days in database | Gets date created---// selectTables.prepare("SELECT usereventlog, create_date FROM sys.tables WHERE DATEDIFF(day, create_date, getdate()) > 30"); selectTables.exec(); QString selectTableResult; selectTableResult = selectTables.value(0).toString(); selectTableResult.append(selectTables.value(1).toString()); selectTableResult.append(selectTables.value(2).toString()); qDebug() << selectTableResult; //--- If the table is older than 30 days drop it---// deleteTableQry.prepare("DROP TABLE userlogevetns"); deleteTableQry.exec(); m_selectDataBase.close(); } { dbConnect(); searchDateQry.prepare("SELECT id, userName, eventMessage, dateTime FROM usereventlog WHERE userName = "+dateText); searchDateQry.exec(); m_selectDataBase.close(); } { dbConnect(); searchDateQry.prepare("SELECT id, userName, eventMessage, dateTime FROM usereventlog WHERE userName = "+ userNameText); searchDateQry.exec(); m_selectDataBase.close(); } { qDebug() << "c++: sqliteModel::searchDateText:" << dateText; } { qDebug() << "c++: sqliteModel::searchUserNameText:" << userNameText; }To copy to clipboard, switch view to plain text mode
=========== sqliteModel.h===============
Qt Code:
#ifndef SQLITEMODEL_H #define SQLITEMODEL_H #include <assert.h> #include <list> #include <QList> #include <QColor> #include <QObject> #include <QDebug> #include <QString> #include <QFileInfo> #include <QDateTime> #include <QQmlError> #include <QQmlApplicationEngine> #include <QQmlEngine> #include <QQmlContext> #include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlError> #include <QtSql/QSqlRecord> #include <QModelIndex> #include <QAbstractListModel> struct userEventLogMsg{ QString id; QString username; QString eventmessage; QString datetime; }; { Q_OBJECT public: ~sqliteModel(); enum userEventRoles {idRole= Qt::UserRole + 220, nameRole, msgRole, dateRole}; QHash<int, QByteArray> roleNames() const; Q_INVOKABLE void addEvent(const userEventLogMsg &msg); Q_INVOKABLE void dbConnect(); Q_INVOKABLE void sqlSelect(); void createDailyTable(); void deleteDailyTable(); public slots: private: QList<userEventLogMsg> m_msgList; QSqlDatabase m_selectDataBase; QSqlQuery m_selectQuery; };To copy to clipboard, switch view to plain text mode
I was able to pass the text form qml to my cpp slot by expose Qt slot to QML element. By using the context property I set for my model to be exposed to QML.
now my searchDateText fcn and searchUserName fcn can be called in qml by:
Qt Code:
Button { id: searchBtn text: qsTr("Search") MouseArea{ anchors.fill: parent onClicked: { sqliteModel.searchDateText(dateTextField.text); sqliteModel.searchUserNameText(userNameTextField.text); } } }To copy to clipboard, switch view to plain text mode
(?) I did the same for my dbConnect and sqlSelect fcns however I have to include Q_INVOKABLE when intializing them in the header file or for some reason or I cant call them in QML with out the Q_INVOKABLE keyword but my searchDateText & searchUserNameText fucntions do??? -> error: Property 'dbConnect' of object sqliteModel() is not a function
I would like to pass the QML text using a QML signal to a CPP slot using the QObject::connect() mainly for learning purposes... The objects that I need to pass the connect() method make more sense now, I need to pass a QML object for the qml signal() and pass a class object (sqliteModel) for the slot(), if I use the connect() method in my cpp class (in the constructor) I can use keyword "this" for the CPP object for the connect method?
Last edited by jfinn88; 26th August 2016 at 23:26.
You just call the slots directly.
Slots, like Q_INVOKABLE methods, can be called from QML.
Your QML code suggests that you want to pass both date and user name when clicking search, so a single slot with two arguments will do.
I guess you have two options:
- use an additional table that maps from a date to a table name for that date
- encode the date in the table name
Yes, exactly, though you probably want to use a single method if both inputs are to be used in the search.
They have to be slots or Q_INVOKABLE.
From the QML side's point of view there is actually no difference.
It is customary though to use Q_INVOKABLE when methods return something, as slots usually don't do that.
In your case all methods you have so far could be slots.
I would advise against that, you would put effort into learning something that you then don't want to use.
I.e. you don't want your C++ code to be dependent on specific QML objects or their specific signals.
Yes, but you really don't want to do that.
Theoretically yes, but the QML scene has not been loaded yet when the model's constructor runs, so the connect would have to happen from outside after both model and QML objects exist.
Cheers,
_
Bookmarks