I tried to outsource the code for the connection to a local PostgreSQL server from my "main.cpp" file into a seperate class called "database.cpp".
The connection worked just fine, when I had the code in the "main.cpp":
main.cpp
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
QSqlQuery query(db);
qint32 declareConnection()
{
db.setHostName("127.0.0.1");
db.setPort(5432);
db.setDatabaseName("postgres");
db.setUserName("postgres");
db.setPassword("password");
return 0;
}
qint32 createUser(QString username, QString password)
{
if (db.open())
{
db.transaction();
query.prepare("INSERT INTO users (name, password) VALUES (?, ?);");
query.bindValue(0, username);
query.bindValue(1, toMD5(password));
query.exec();
query.finish();
db.commit();
db.close();
}
return 0;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Declare Database: " << declareConnection();
qDebug() << "Create User: " << createUser("Testuser", "Testpassword");
return a.exec();
}
But after I put the functions in the "database.cpp", the prepare.query() fails every time I try to execute it.
Here's my current code:
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Database postgre;
qDebug() << "Declare Database: " << postgre.declareConnection();
qDebug() << "Create User: " << postgre.createUser("Testuser", "Testpassword");
return a.exec();
}
Database.h
class Database : public QObject
{
Q_OBJECT
public:
explicit Database(QObject *parent = 0);
qint32 declareConnection();
qint32 createUser(QString username, QString password);
QSqlDatabase db();
};
Database.cpp
QSqlDatabase Database::db()
{
return QSqlDatabase::database();
}
qint32 Database::declareConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("127.0.0.1");
db.setPort(5432);
db.setDatabaseName("postgres");
db.setUserName("postgres");
db.setPassword("password");
return 0;
}
qint32 Database::createUser(QString username, QString password)
{
if (db().open())
{
db().transaction();
QSqlQuery query(db());
query.prepare("INSERT INTO users (name, password) VALUES (?, ?);");
query.bindValue(0, username);
query.bindValue(1, toMD5(password));
query.exec();
query.finish();
db().commit();
db().close();
}
return 0;
}
The declaration of the database seems to work just fine, but when it comes to the "createUser()" function, there is always that problem with the "query.prepare()" command.
The database is open, so that works.
Also the transaction is in an open state, when I check in debug.
The prepare is false.
The query.exec() says: Syntaxerror at end of line LINE 1: EXECUTE ^.
QPSQL: Unable to create query.
WARNING: No open transaction. (But was open???)
The problem is here
void Database::declareConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
you declared local variable db on a stack.
The decision is:
class Database : public QObject
{
...
QSqlDatabase & db() { return m_db; }
private:
QSqlDatabase m_db;
};
void Database::declareConnection()
{
m_db = QSqlDatabase::addDatabase("QPSQL");
...
}
Related
I have a problem with QT Creator.
I am writing a program and need a database for it.
I chose SQLITE and when I open the connection to the database directly in the MainWindow's constructor, everything is ok. But as soon as I use my class Database or methods from it, the connection to the database no longer works and I get the error
"Error open QSqlError("", "Driver not loaded", "Driver not loaded").
Does QtSql have problems with instances, methods or pointers?
I would be very grateful for tips, help or references to solutions, since I've been stuck for about 12 hours now.
my mainwindow.cpp when it works.
#include "HeaderLibary.h"
#include "ui_mainwindow.h"
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setMaximumHeight(MAX_WINDOW_HEIGHT);
this->setMinimumHeight(MIN_WINDOW_HEIGHT);
this->setMaximumWidth(MAX_WINDOW_WIDTH);
this->setMinimumWidth(MIN_WINDOW_WIDTH);
QFont font;
font.setPixelSize(FONT_SIZE);
font.setBold(true);
font.setFamily(FONT_FAMILY);
QString buffer;
QGridLayout *windowLayout = new QGridLayout;
matrix *inputMatrix = new matrix("input",MAXLENGTH,false,QLINEEDIT_HEIGHT,QLINEEDIT_WIDTH,font,5,5,true,1);
inputMatrix->set_matrix();
this->inputWidgets = inputMatrix->getMatrixWidgets();
buffer = "Hello User, welcome to AES - Advanced Encryption Standard!";
QLabel *title = inputMatrix->setLabel(buffer,MAX_WINDOW_WIDTH,LABEL_HIGHT);
font.setPointSize(FONT_SIZE_TEXT);
buffer = "With AES you can encrypt messages of up to 16 characters or 16 Bytes."
"\n\nPlease enter your message in this matrix. One character per field in numbered order.";
QLabel *text = inputMatrix->setLabel(buffer,MAX_WINDOW_WIDTH,LABEL_HIGHT);
QPushButton *startBtn = inputMatrix->setButton("Start",BTN_WIDTH,BTN_HEIGHT);
connect(startBtn,SIGNAL(clicked()),this,SLOT(start_clicked()));
windowLayout->addWidget(title,0,0,1,2,Qt::AlignCenter);
windowLayout->addWidget(text,1,0,1,2,Qt::AlignCenter);
windowLayout->addLayout(inputMatrix->getLayout(),2,0,1,2,Qt::AlignCenter);
windowLayout->addWidget(startBtn,3,1,1,1,Qt::AlignRight);
QWidget *widget = new QWidget();
widget->setLayout(windowLayout);
setCentralWidget(widget);
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("AES_DB.db");
if(!db.open())
qDebug()<< db.lastError();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::start_clicked()
{
QSqlDatabase db;
QSqlQuery qry;
QString tableName = "orginalMsgLetters";
QString buffer= QString("DROP TABLE %1;").arg(tableName);
if(!qry.exec(buffer))
qDebug()<< qry.lastError();
buffer = QString("CREATE TABLE IF NOT EXISTS %1 (id INTEGER NOT NULL PRIMARY KEY, itemValue VARCHAR(20));").arg(tableName);
if(!qry.exec(buffer))
qDebug()<< qry.lastError();
bool check = false;
int index = 0;
while(!check){
if(inputWidgets->at(index)->text() == ""){
QMessageBox::information(this,tr("Error"), tr("Please fill out each box to continue"));
return;
}
else if(inputWidgets->at(index)->text() == "ü" || inputWidgets->at(index)->text() == "ä" || inputWidgets->at(index)->text() == "ö" || inputWidgets->at(index)->text() == "ß"){
QMessageBox::information(this,tr("Error"), tr("Please don't use Ü/ü, Ä/ä, Ö/ö or ß "));
return;
}
else {
if(index+1 == MAXLENGTH)
check = true;
else
index++;
}
}
for (int i = 0; i < MAXLENGTH; ++i) {
buffer=QString("INSERT INTO %1 (id,itemValue)VALUES (%2,%3);").arg(tableName).arg(i).arg(this->inputWidgets->at(i)->text());
if(!qry.exec(buffer))
qDebug()<<qry.lastError();
buffer=QString("SELECT * FROM %1 WHERE id=%2").arg(tableName).arg(i);
if(!qry.exec(buffer))
qDebug()<<qry.lastError();
while(qry.next())
qDebug()<< qry.value(1).toString();
}
db.close();
}
my mainwindow.h
#pragma once
#include "HeaderLibary.h"
#include "Config_File.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QList<QLineEdit*>* inputWidgets;
//Database *db = new Database();
QSqlDatabase *db = new QSqlDatabase;
QSqlQuery *qry = new QSqlQuery;
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void start_clicked();
private:
Ui::MainWindow *ui;
};
my Database.cpp
When I use any method of this in my mainwindow.cpp to initialize the database it won't work and i get the error."Driver not loaded"
#include "database.h"
Database::Database(){
}
Database::~Database(){
}
void Database::deleteTable(QString tableName){
QSqlQuery qry;
QString buffer = QString("DROP TABLE %1;").arg(tableName);
qry.prepare(buffer);
qry.exec();
}
void Database::connect(){
QSqlDatabase db;
db.addDatabase("QSQLITE");
db.setDatabaseName("AES_DB.db");
if(!db.open())
qDebug()<< "Error open "<< db.lastError();
}
void Database::addValueToTable(QString tableName,int id, QString value){
QSqlQuery qry;
QString buffer = QString("INSERT INTO %1 (id,itemValue)VALUES (%2,%3);").arg(tableName).arg(id).arg(value);
qry.prepare(buffer);
if(!qry.exec()){
qDebug()<< qry.lastError();
qDebug()<< "\n insert Error \n";
}
}
void Database::createTable(QString tableName){
QSqlQuery qry;
QString buffer = QString("CREATE TABLE IF NOT EXISTS %1 (id INTEGER NOT NULL PRIMARY KEY, itemValue VARCHAR(20));").arg(tableName);
if(!qry.exec(buffer)){
qDebug()<< qry.lastError();
qDebug()<< "\n create Error \n";
}
}
void Database::close(){
QSqlDatabase db;
db.close();
}
QString Database::getValueFromTable(QString tableName,int id){
QSqlQuery qry;
QString string = QString("SELECT * FROM %1 WHERE id=%2").arg(tableName).arg(id);
qry.prepare(string);
if(!qry.exec(string)){
qDebug()<< qry.lastError();
qDebug()<< "\n get Error \n";
}
while(qry.next())
return qry.value(1).toString();
}
My database.h
#pragma once
#include "Config_File.h"
#include "HeaderLibary.h"
class Database
{
private:
public:
Database();
~Database();
void connect();
void addValueToTable(QString tableName,int id, QString value);
void deleteTable (QString tableName);
void createTable(QString tableName);
QString getValueFromTable(QString tableName,int id);
void close();
};
'''
[1]: https://i.stack.imgur.com/48zP0.png
Why don't you use
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("AES_DB.db");
...in your Database::connect() method?
I'm creating a QSqlRecord object and then I set the values to that QSqlRecord object. But even if I insert the QSqlRecord object to the QSqlTableModel object, the function of inserting records, returns false.
I have this C++ code and it create a QSqlRecord object and set the values. It setting the values in the correct indexed order as how the table was created.
/* Insert data */
int column_index = 0; /* Index 0 is the ID column */
QSqlRecord record;
qDebug() << CALIBRATION_COLUMNS.at(column_index).first;
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, 1); /* ID */
qDebug() << CALIBRATION_COLUMNS.at(column_index).first;
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, calibration_id);
qDebug() << CALIBRATION_COLUMNS.at(column_index).first;
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, calibration_comment);
qDebug() << CALIBRATION_COLUMNS.at(column_index).first;
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, calibration_date_time);
for(int i = 0; i < 12; i++){
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, min_adc[i]);
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, max_adc[i]);
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, bias_adc[i]);
}
for(int i = 0; i < 5; i++){
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, min_dadc[i]);
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, max_dadc[i]);
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, bias_dadc[i]);
}
for(int i = 0; i < 2; i++)
record.setValue(CALIBRATION_COLUMNS.at(column_index++).first, pulses_per_revolution_encoder[i]);
/* -1 means append record */
qDebug() << calibration_model->insertRecord(-1, record);
qDebug() << calibration_model->lastError().text();
qDebug() << "Submit:";
if(!calibration_model->submitAll()){
qDebug() << calibration_model->lastError().text();
return DATABASE_STATUS_COULD_NOT_INSERT_ROW;
}
return DATABASE_STATUS_OK;
But even if I insert the record, this function calibration_model->insertRecord(-1, record); returns false but the calibration_model->submitAll() returns true.
Output:
"ID"
"calibration_id"
"calibration_comment"
"calibration_date_time"
false
"No Fields to update"
Submit:
So tell me. What I'm I doing wrong here?
I'm getting the error No Fields to update, but what does that mean? I have an empty table and I just want to append with one row.
Not sure why you're getting that error. I have a small example for QSqlTableModel. Let me put it here. Maybe you could compare with your code.
main.cpp
#include <QApplication>
#include "mysqltablemodel.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("mydb");
if(!db.open()) {
qDebug() << db.lastError().text();
return 0;
}
QSqlQuery query(db);
if(!query.exec("DROP TABLE IF EXISTS mytable")) {
qDebug() << "create table error: " << query.lastError().text();
return 0;
}
if(!query.exec("CREATE TABLE IF NOT EXISTS mytable \
(id integer primary key autoincrement, name varchar(15), salary integer)")) {
qDebug() << "create table error: " << query.lastError().text();
return 0;
}
MySqlTableModel *model = new MySqlTableModel(0, db);
model->setTable("mytable");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
QSqlRecord rec = model->record();
rec.setValue(1, "peter");
rec.setValue(2, 100);
qDebug() << model->insertRecord(-1, rec);
rec.setValue(1, "luke");
rec.setValue(2, 200);
qDebug() << model->insertRecord(-1, rec);
if(model->submitAll()) {
model->database().commit();
} else {
model->database().rollback();
qDebug() << "database error: " << model->lastError().text();
}
query.exec("SELECT name, salary FROM mytable");
while (query.next()){
QString name = query.value(0).toString();
int salary = query.value(1).toInt();
qDebug() << name << salary;
}
return app.exec();
}
mysqltablemodel.h
#ifndef MYSQLTABLEMODEL_H
#define MYSQLTABLEMODEL_H
#include <QSqlTableModel>
#include <QSqlRecord>
#include <QSqlError>
#include <QSqlQuery>
#include <QDebug>
class MySqlTableModel : public QSqlTableModel
{
Q_OBJECT
public:
MySqlTableModel(QObject *parent = 0, QSqlDatabase db = QSqlDatabase());
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole ) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QHash<int, QByteArray> roles;
};
#endif // MYSQLTABLEMODEL_H
mysqltablemodel.cpp
#include "mysqltablemodel.h"
MySqlTableModel::MySqlTableModel(QObject *parent, QSqlDatabase db): QSqlTableModel(parent, db) {}
QVariant MySqlTableModel::data ( const QModelIndex & index, int role ) const
{
if(index.row() >= rowCount()) {
return QString("");
}
if(role < Qt::UserRole) {
return QSqlQueryModel::data(index, role);
}
else {
return QSqlQueryModel::data(this->index(index.row(), role - Qt::UserRole), Qt::DisplayRole);
}
}
QHash<int, QByteArray> MySqlTableModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Qt::UserRole + 1] = "name";
roles[Qt::UserRole + 2] = "salary";
return roles;
}
I am working in Ubuntu and want to store data in QSettings. The during the run, data is persistent, once the application restarts the data is no longer read. I am reading that an additional sync call of linux is required, but I am unable to find where this is defined.
During a run of program, I have successfully written the data to QSettings object and read the value from stored QSettings object.
I have a user class which can read and write from/to QJSonObject
class User
{
public:
User() {}
User(const QString& ruser_name, const QImage& rdisplay_image)
{
user_name = ruser_name;
display_image = rdisplay_image;
}
QString get_user_name() const { return user_name; }
void write(QJsonObject &json) const
{
json["user_name"] = user_name;
json["display_image"] = getImageInJsonValueFormat(display_image);
}
void read(const QJsonObject &json)
{
if (json.contains("user_name") && json["user_name"].isString())
user_name = json["user_name"].toString();
if (json.contains("display_image"))
display_image = getImageFromJsonValueFormat(json["display_image"]);
}
private:
QString user_name;
QImage display_image;
};
There are two global functions to work with image and Json
QJsonValue getImageInJsonValueFormat(const QImage& image)
{
QByteArray image_ByteArray;
if (image.isNull() != true)
{
QBuffer image_buffer(&image_ByteArray);
image_buffer.open(QIODevice::WriteOnly);
image.save(&image_buffer, "png");
auto const encoded = image_buffer.data().toBase64();
return {QLatin1String(encoded)};
}
return QLatin1String("");
}
QImage getImageFromJsonValueFormat(const QJsonValue& imageJsonValue)
{
QImage image;
auto const encoded = imageJsonValue.toString().toLatin1();
image.loadFromData(QByteArray::fromBase64(encoded), "png");
return image;
}
Also, three functions, but read the list from QSettings, to create default list and to store list to QSettings.
QMap<QString, User> Build_user_list()
{
QMap<QString, User> user_list;
User User1("baur",QImage("/home/Downloads/bechambaur.png"));
user_list["baur"] = User1;
User User2("Muller",QImage("/home/Downloads/GerdMuller.png"));
user_list["Muller"] = User2;
return user_list;
}
void StoreUserList(const QMap<QString, User> user_list)
{
QJsonObject userListObject;
QJsonArray userListArray;
QMapIterator<QString,User> iterator(user_list);
while (iterator.hasNext())
{
iterator.next();
QJsonObject userJsonObject;
User userObj = iterator.value();
userObj.write(userJsonObject);
userListArray.append(userJsonObject);
}
userListObject["user_list"] = userListArray;
QJsonDocument userListDoc(userListObject);
QSettings user_settings;
user_settings.setValue("QSettingsApplication", userListDoc);
user_settings.sync();
}
QMap<QString, User> GetStoredList()
{
QMap<QString, User> user_list;
QSettings user_settings;
QJsonDocument userListDocRead = user_settings.value("QSettingsApplication", "").toJsonDocument();
QJsonObject userListObjectRead = userListDocRead.object();//["userlist"];
if (userListObjectRead.contains("user_list"))
{
if(userListObjectRead["user_list"].isArray())
{
QJsonArray userlistarray = userListObjectRead["user_list"].toArray();
user_list.clear();
for(int index = 0; index < userlistarray.size(); ++index)
{
QJsonObject json_user_object = userlistarray[index].toObject();
User userObject;
userObject.read(json_user_object);
user_list[userObject.get_user_name()] = userObject;
}
}
}
return user_list;
}
With this application ran for the subsequent times is not reading the value from QSettings which it reads successfully during the run
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Why below list is empty after first successful run?
QMap<QString, User> user_listRead = GetStoredList();
if (user_listRead.size() == 0)
{
QMap<QString, User> user_list = Build_user_list();
StoreUserList(user_list);
QMap<QString, User> user_listRead = GetStoredList();
user_listRead = GetStoredList();
// This is is not empty, it reads contents successfully. What's wrong?
}
MainWindow w;
w.show();
return a.exec();
}
I expect data to be persistent. Where it is going wrong?
Entire file contents are
#include "mainwindow.h"
#include <QApplication>
#include <QString>
#include <QImage>
#include <QJsonObject>
#include <QBuffer>
#include <QSettings>
#include <QJsonArray>
#include <QJsonDocument>
QJsonValue getImageInJsonValueFormat(const QImage& image)
{
QByteArray image_ByteArray;
if (image.isNull() != true)
{
QBuffer image_buffer(&image_ByteArray);
image_buffer.open(QIODevice::WriteOnly);
image.save(&image_buffer, "png");
auto const encoded = image_buffer.data().toBase64();
return {QLatin1String(encoded)};
}
return QLatin1String("");
}
QImage getImageFromJsonValueFormat(const QJsonValue& imageJsonValue)
{
QImage image;
auto const encoded = imageJsonValue.toString().toLatin1();
image.loadFromData(QByteArray::fromBase64(encoded), "png");
return image;
}
class User
{
public:
User() {}
User(const QString& ruser_name, const QImage& rdisplay_image)
{
user_name = ruser_name;
display_image = rdisplay_image;
}
QString get_user_name() const { return user_name; }
void write(QJsonObject &json) const
{
json["user_name"] = user_name;
json["display_image"] = getImageInJsonValueFormat(display_image);
}
void read(const QJsonObject &json)
{
if (json.contains("user_name") && json["user_name"].isString())
user_name = json["user_name"].toString();
if (json.contains("display_image"))
display_image = getImageFromJsonValueFormat(json["display_image"]);
}
private:
QString user_name;
QImage display_image;
};
QMap<QString, User> Build_user_list()
{
QMap<QString, User> user_list;
User User1("baur",QImage("/home/Downloads/bechambaur.png"));
user_list["baur"] = User1;
User User2("Muller",QImage("/home/Downloads/GerdMuller.png"));
user_list["Muller"] = User2;
return user_list;
}
void StoreUserList(const QMap<QString, User> user_list)
{
QJsonObject userListObject;
QJsonArray userListArray;
QMapIterator<QString,User> iterator(user_list);
while (iterator.hasNext())
{
iterator.next();
QJsonObject userJsonObject;
User userObj = iterator.value();
userObj.write(userJsonObject);
userListArray.append(userJsonObject);
}
userListObject["user_list"] = userListArray;
QJsonDocument userListDoc(userListObject);
QSettings user_settings("userlist.conf", QSettings::NativeFormat);//(QSettings::IniFormat,QSettings::UserScope,"A","B");
user_settings.setValue("QSettingsApplication", userListDoc.toVariant());
user_settings.sync();
}
QMap<QString, User> GetStoredList()
{
QMap<QString, User> user_list;
QSettings user_settings("userlist.conf", QSettings::NativeFormat);
QVariant qvarval = user_settings.value("QSettingsApplication");
QJsonDocument userListDocRead = user_settings.value("QSettingsApplication").toJsonDocument();
QJsonObject userListObjectRead = userListDocRead.object();//["userlist"];
if (userListObjectRead.contains("user_list"))
{
if(userListObjectRead["user_list"].isArray())
{
QJsonArray userlistarray = userListObjectRead["user_list"].toArray();
user_list.clear();
for(int index = 0; index < userlistarray.size(); ++index)
{
QJsonObject json_user_object = userlistarray[index].toObject();
User userObject;
userObject.read(json_user_object);
user_list[userObject.get_user_name()] = userObject;
}
}
}
return user_list;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMap<QString, User> user_listreRead;
// Why below list is empty after first successful run?
QMap<QString, User> user_listRead = GetStoredList();
if (user_listRead.size() == 0)
{
QMap<QString, User> user_list = Build_user_list();
StoreUserList(user_list);
user_listreRead = GetStoredList();
// This is is not empty, it reads contents successfully. What's wrong?
}
MainWindow w;
w.show();
return a.exec();
}
Contents of userlist.conf are
[General]
QSettingsApplication=#Variant(\0\0\0\b\0\0\0\x1\0\0\0\x12\0u\0s\0\x65\0r\0_\0l\0i\0s\0t\0\0\0\t\0\0\0\x2\0\0\0\b\0\0\0\x2\0\0\0\x12\0u\0s\0\x65\0r\0_\0n\0\x61\0m\0\x65\0\0\0\n\0\0\0\f\0M\0u\0l\0l\0\x65\0r\0\0\0\x1a\0\x64\0i\0s\0p\0l\0\x61\0y\0_\0i\0m\0\x61\0g\0\x65\0\0\0\n\0\0\0\0\0\0\0\b\0\0\0\x2\0\0\0\x12\0u\0s\0\x65\0r\0_\0n\0\x61\0m\0\x65\0\0\0\n\0\0\0\b\0\x62\0\x61\0u\0r\0\0\0\x1a\0\x64\0i\0s\0p\0l\0\x61\0y\0_\0i\0m\0\x61\0g\0\x65\0\0\0\n\0\0\0\0)
I have written code for a server which accepts connections from different clients. Each client is serviced in different threads. Each thread accesses a database to get data and then updates this data to all the clients connected to server.
1) For the first time when UI asks data from server, it responds properly, but after that server does not read the socket i.e. Server's readyread() doesn't get invoked. Funnily enough, this works fine in mac and linux, this issue is seen only on windows
2) I was able to verify that when the DB module emits a signal which is caught by the threads, the hang occurs, Because everything worked fine when I removed the emit.
Here, I am attaching all the needed .h and .cpp codes
Defn.h
#ifndef DEFN_H
#define DEFN_H
struct PresetData{
QString ID;
QString name;
QString value;
QString source;
};
#endif // DEFN_H
main.cpp
#include <QCoreApplication>
#include "myserver.h"
#include "mydb.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyDB db;
MyServer server(&db);
server.startServer();
return a.exec();
}
mydb.h
#ifndef MYDB_H
#define MYDB_H
#include <QObject>
#include <QtSql>
#include "Defn.h"
class MyDB : public QObject
{
Q_OBJECT
public:
explicit MyDB(QObject *parent = 0);
signals:
void dataAvailable(QString ID, QString name, QString value, QString source);
public slots:
void onUpdateData(QString ID, QString name, QString value, QString source);
void onGetData(QString ID, QString name, QString value, QString source);
private:
QSqlDatabase m_db;
};
#endif // MYDB_H
mydb.cpp
#include "mydb.h"
MyDB::MyDB(QObject *parent) :
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setConnectOptions();
m_db.setDatabaseName("D:/MySimulator/New Folder/TCPServer1/DB.db");
if (m_db.open()){
qDebug() << "DB opened succesfully" ;
}else{
qDebug() << "DB Opening failed" ;
}
QStringList tables = m_db.tables();
if (tables.contains("Presets", Qt::CaseInsensitive)){
qDebug() << "DB Contains Data" ;
return;
}
}
void MyDB::onGetData(QString ID, QString name, QString value, QString source)
{
qDebug() << "onGetData" ;
QString queryString = "SELECT Value from 'Presets' where ID = \'" + ID + "\'";
QSqlQuery q;
bool result = q.exec(queryString);
if (result){
if (q.next()){
value = q.value(q.record().indexOf("Value")).toString();
qDebug() << " Retrieved Value = " << value ;
emit dataAvailable(ID, name, value, source);
}else{
qDebug("Empty Result");
}
}else{
qDebug("NO Result");
}
}
void MyDB::onUpdateData(QString ID, QString name, QString value, QString source)
{
qDebug() << "onUpdateData" ;
QString queryString = "UPDATE 'Presets' SET Value = \'" + value + "'\ WHERE ID = \'" + ID + "\'";
QSqlQuery q;
QSqlDatabase::database().transaction();
bool result = q.exec(queryString);
if (result){
QSqlDatabase::database().commit();
onGetData(ID, name, "", "000");
}else{
qDebug("NO Result");
}
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QDebug>
#include "Defn.h"
#include "mydb.h"
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(int ID, MyDB* db, QObject * parent = 0);
void run();
void parseInput(QString string);
signals:
void error(QTcpSocket::SocketError socketError);
void updateData(QString ID, QString name, QString value, QString source);
void getData(QString ID, QString name, QString value, QString source);
public slots:
void readyRead();
void disconnected();
void onDataAvailable(QString ID, QString name, QString value, QString source);
private:
QTcpSocket* socket;
int socketDescriptor;
MyDB* db;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include "qtcpserver.h"
#include "qabstractsocket.h"
MyThread::MyThread(int ID, MyDB* db, QObject * parent ):
QThread(parent)
{
this->socketDescriptor = ID ;
this->db = db;
}
void MyThread::run()
{
// thread starts here.
qDebug() << socketDescriptor << "Starting Thread" ;
socket = new QTcpSocket();
if (!socket->setSocketDescriptor(this->socketDescriptor)){
emit error(socket->error());
return;
}
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
connect(this, SIGNAL(getData(QString, QString , QString , QString )), this->db, SLOT(onGetData(QString , QString , QString , QString )));
connect(this, SIGNAL(updateData(QString , QString , QString , QString )), this->db, SLOT(onUpdateData(QString , QString , QString , QString )));
connect(this->db, SIGNAL(dataAvailable(QString , QString , QString , QString )), this, SLOT(onDataAvailable(QString , QString , QString , QString )));
qDebug() << socketDescriptor << "Client Connected" ;
exec();
}
void MyThread::readyRead()
{
QByteArray data = socket->readAll();
qDebug() << socketDescriptor << "Data in: " << data;
parseInput(data);
}
void MyThread::disconnected()
{
qDebug() << socketDescriptor << "Disconnected" ;
socket->deleteLater();
exit(0);
}
void MyThread::parseInput(QString dataFromTCP)
{
qDebug() << socketDescriptor << ":" <<"parseInput " << dataFromTCP;
if (dataFromTCP.isEmpty())
return;
QStringList list1 = dataFromTCP.split("\n", QString::SkipEmptyParts);
qDebug() << socketDescriptor << ":" << "list1 BEGIN";
for (int i = 0 ; i < list1.count(); i++)
{
qDebug() << i<< ":" << list1.at(i);
}
qDebug() << socketDescriptor << ":" << "list1 END";
if (list1.count() < 1){
return;
}
QString strMessage = "";
for (int i = 0 ; i < list1.count() ; i++)
{
strMessage = list1[i];
QStringList list2 = strMessage.split(" ", QString::SkipEmptyParts);
qDebug() << socketDescriptor << ":" << "list2 BEGIN";
for (int i = 0 ; i < list2.count(); i++)
{
qDebug() << i<< ":" << list2.at(i);
}
qDebug() << socketDescriptor << ":" << "list2 END";
if (list2.count() < 1){
break;
}
QString ID = list2[1];
QString source = QString::number(socketDescriptor) ;
if (list2[0] == "GET"){
emit getData(ID, "", "", source);
}
else if (list2[0] == "UPD"){
QString value = list2[2];
emit updateData(ID, "", value, source);
}
}
}
void MyThread::onDataAvailable(QString ID, QString name, QString value, QString source)
{
if( (QString::number(socketDescriptor) == source) || ("000" == source ) ) {
qDebug() << socketDescriptor << " : On Data Available " << ID << name << value ;
QString data = "DATA " + ID + " " + value + " " + "\n" ;
QByteArray ba;
ba.append(data);
socket->write(ba);
}
}
myserver.h
#ifndef MYSERVER_H
#define MYSERVER_H
#include <QDebug>
#include <QObject>
#include <QTCPServer>
#include <QTCPSocket>
#include "mythread.h"
#include "mydb.h"
class MyServer: public QTcpServer
{
Q_OBJECT
public:
explicit MyServer(MyDB* pdb, QObject* parent = 0);
void startServer();
signals:
public slots:
protected:
void incomingConnection(qintptr socketDescriptor);
private:
MyDB* pdb ;
};
#endif // MYSERVER_H
myserver.cpp
#include "myserver.h"
MyServer::MyServer(MyDB* pdb, QObject* parent ):
QTcpServer(parent)
{
this->pdb = pdb;
}
void MyServer::startServer()
{
if (!this->listen(QHostAddress::Any, 1234)){
qDebug() << "Could not Start Server " << this->errorString();
}
else{
qDebug() << " Server Running... ";
}
}
void MyServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << socketDescriptor << " Connecting... ";
MyThread *thread = new MyThread(socketDescriptor, pdb, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
Here the signal about which I mentioned above is dataAvailable from "mydb.cpp". If I comment out that line then server responds to client messages. But if that signal is emitted then after the initial response, the server seems to hang and no longer reacts to incoming messages from the client.
The same code is working perfectly fine in mac and linux. But it is having this problem in Windows only.
Could someone let me know what is it that I am doing wrong that it is failing only in Windows?
Thanks in advance for helping me out.
EDIT:
The objective of this code is that whenever a thread causes an update call to the database, EVERY thread including the one that called the update gets informed about the change. So it is EXPECTED that other thread that runs at that time also receives a signal.
This is what is expected of the server:
Be able to allow TCP connections from multiple clients simultaneously.
If any client requests info, it gets the required data over the TCP connection.
If any client updates info, all clients including the updating client, gets a notifications over the TCP connection.
Well, for starters, your code is completely not thread-safe. You create a single instance of MyDB in your main() function, then call it from threads without protecting its data member. Also, signals get emitted, updating data without any protection. What if two threads happen to be running at the same time?
Secondly, and this is more important: whenever you emit dataAvailable() you call functions in other thread objects in your own thread. This is the code path when data arrives:
MyThread::parseInput() emits
MyThread::getData(), which is connected to
MyDB::onGetData(), which emits
MyDb::dataAvailable, which is connected to (drumroll....)
MyThread::onDataAvailable, which eventually calls
socket->write()
So if data arrives in thread #1, you're going to send data from MyThread object #2, #3, #4, etc from .... thread #1. Depending on the OS, this is bad news. I don't know enough about Windows threads but I do know this code is terminally broken.
If all you want to do is update a database and relay the data you can dispense with the threads and use a sequential program that handles sockets using the regular Qt signals and slots just fine.
#include <QtGui>
#include <QtSql>
#include <QApplication>
class ABC {
public:
QSqlDatabase db;
QSqlQuery memberQuery;
ABC() {
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("test");
qDebug() << "Database open test : " << db.open();
}
void database() {
qDebug() << "Inside database method..." << endl;
QSqlQuery localQuery ;
qDebug() << "Using local Query : " << localQuery.exec("create table if not exists alu (ini int)");
localQuery.clear();
qDebug() << "Using memeber query object: " << memberQuery.exec("create table if not exists alu (ini int)");
memberQuery.clear();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ABC ob;
ob.database();
return a.exec();
}
Following result is found after executing this code.
Database open test : true
Inside database method...
Using local Query : true
QSqlQuery::exec: database not open
Using memeber query object: false
My question is why can't I use a member object QSqlQuery instead of a local one?
You need to initialise your QSqlQuery using the QSqlQuery(QSqlDatabase db) constructor.
To do this you will need to use initialiser lists in the constructor and have already setup the DB connection, or use pointers to initialise the QSqlQuery later.
With an ABC constructor taking an initialised DB (create the DB in main):
ABC(QSqlDatabase _db) :
db(_db),
memberQuery(db)
{
Q_ASSERT(db.isOpen());
}
Or
class ABC
{
QSqlDatabase db;
QSqlQuery* memberQueryPtr;
public:
ABC() : db(QSqlDatabase::addDatabase("QSQLITE")),memberQueryPtr(0)
{
db.setDatabaseName("test");
if (!db.open())
return;
memberQueryPtr = new QSqlQuery(db);
}
~ABC()
{
delete memberQueryPtr; memberQueryPtr = 0;
}
// ...
};