I am trying to add a QOpenGLWidget to my QGraphicsScene, but the application crashes when I call initializeOpenGLFunctions(). I am pretty sure that the context of OpenGLView is null and that is why it is crashing (provides no logs) for two reasons:
When I print it, it outputs 0x0
When I try to enable QOpenGLDebugLogger it outputs that there is no current context.
I thought that QOpenGLWidget would have an OpenGLContext out of the box. Any idea why the context is not getting set? Am I missing something in my initialization?
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setOption(QSurfaceFormat::DebugContext);
QSurfaceFormat::setDefaultFormat(format);
OpenGLView view = new OpenGLView();
header
class OpenGLView : public QOpenGLWidget, protected QOpenGLFunctions
{
}
#include "OpenGLView.h"
OpenGLView::OpenGLView(QWidget *parent) : QOpenGLWidget(parent) {
initializeGL();
}
void OpenGLView::initializeGL() {
initializeOpenGLFunctions(); // crashes
// ...
}
void OpenGLView::paintGL() {
// ...
}
void OpenGLView::resizeGL(int w, int h) {
// ...
}
It is because you called initializeGL() in the constructor. By that time, the context has not been initialized. The context is first initialized when the widget is shown. Details taken from Qt's source code below:
void QOpenGLWidgetPrivate::initialize()
{
Q_Q(QOpenGLWidget);
if (initialized)
return;
...
QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
ctx->setFormat(requestedFormat);
if (shareContext) {
ctx->setShareContext(shareContext);
ctx->setScreen(shareContext->screen());
}
if (Q_UNLIKELY(!ctx->create())) {
qWarning("QOpenGLWidget: Failed to create context");
return;
}
...
context = ctx.take();
initialized = true;
q->initializeGL();
}
bool QOpenGLWidget::event(QEvent *e)
{
Q_D(QOpenGLWidget);
switch (e->type()) {
...
case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too
if (d->initialized && window()->windowHandle()
&& d->context->shareContext() != QWidgetPrivate::get(window())->shareContext())
{
// Special case: did grabFramebuffer() for a hidden widget that then became visible.
// Recreate all resources since the context now needs to share with the TLW's.
if (!QCoreApplication::testAttribute(Qt::AA_ShareOpenGLContexts))
d->reset();
}
if (!d->initialized && !size().isEmpty() && window()->windowHandle()) {
d->initialize();
if (d->initialized)
d->recreateFbo();
}
break;
...
}
return QWidget::event(e);
}
Related
I make an http operation(get,post etc...) by using QNetworkAccessManager. I run a few "get" operation in paralel. For this , I use QtConcurrent::run(this,&RestWebservice::GetHTTPData) to make multi HTTP operations.
My problem is When I close the app before HTTP operation does not complete , App is crashed.Application Output write this line QMutex: destroying locked mutex then write The program has unexpectedly finished.
I guest problem occurs in this line
void RestWebservice::get()
{
// mutex.lock();
m_networkManager.get(m_networkrequest);
// mutex.unlock();
}
But I am not sure because QtCreater Debugger is not good like VS.By the way , GetHTTPData is in different class.
MY CODE for start network Operation:(MobileOperation.cpp).For exapmle getUserAccount metod start a http operation.
void MobileOperations::getWorkOrderListT(int ekipId) {
MyGlobal::Metods metod=MyGlobal::EkipIsEmriListesi;
QString parameters="{EkipId}";
QMap<QString,QVariant> paramlist;
paramlist["EkipId"]=ekipId;
GetHTTPData(metod,parameters,paramlist);
if(m_workorder.IsSuccess==true)
{
// emit successupdatewo();
if(m_workorder.workorders.count()>0)
{
InsertWo(json.workorder->workorders);
emit processstop("İş Emri Listesi Güncellendi");
// QThread::sleep(2);
}
else
{
emit processstop(json.workorder->ReturnMessage);
}
emit successworkstart();
}
else
{
emit processstop("Bağlantı Başarısız Oldu");
}
}
void MobileOperations::getUserAccount(QString kullaniciAdi, QString sifre,bool isremember)
{
json.user=m_user;
QtConcurrent::run(this,&MobileOperations::getUserAccountT,kullaniciAdi,sifre,isremember);
// getUserAccountT(kullaniciAdi,sifre,isremember);
processstart("Baglaniyor");
}
void MobileOperations::GetHTTPData(MyGlobal::Metods MetodName, QString Parameters, QMap<QString, QVariant> paramlist)
{
try
{
parameter=new HttpRequest();
parameter->url=m_url;
parameter->metodname=MetodName;
parameter->resource=m_path;
parameter->appid=m_appid;
parameter->apppass=m_apppass;
parameter->parametersname=Parameters;
parameter->params=paramlist;
rest= new RestWebservice(parameter->GenerateHTTPQuery(),MetodName);
// json=new JSonParser();
// loop=new QEventLoop();
loop=new QEventLoop();
QObject::connect(rest,SIGNAL(sendhttpdata(QByteArray,MyGlobal::Metods)),&json,SLOT(onGetData(QByteArray,MyGlobal::Metods)));
QObject::connect(&json,SIGNAL(serilazitionCompleted()),loop,SLOT(quit()));
rest->get();
loop->exec();
}
catch(std::string &exp)
{
qDebug()<<"Sonlandırıldı";
}
}
MY CODE of classes For HTTP operatins :
#include "restwebservice.h"
#include <QJsonDocument>
#include<QJsonArray>
#include <QJsonObject>
#include<QJsonValue>
#include<QList>
#include <QThread>
RestWebservice::RestWebservice(QNetworkRequest request,
MyGlobal::Metods metod,
QObject* parent):QObject(parent),m_networkrequest(request),m_metodname(metod)
{
connect(&m_networkManager, SIGNAL(finished(QNetworkReply*)),this, SLOT(onResult(QNetworkReply*)));
// connect(&m_networkManager,SIGNAL())
}
void RestWebservice::get()
{
// mutex.lock();
m_networkManager.get(m_networkrequest);
// mutex.unlock();
}
void RestWebservice::post(QString request)
{
QByteArray requestA= request.toUtf8();
m_networkManager.post(m_networkrequest,requestA);
}
void RestWebservice::onResult(QNetworkReply* reply)
{
try
{
if (reply->error() != QNetworkReply::NoError)
{
qDebug()<<reply->error()<<":"<<reply->errorString();
MyGlobal::NetworkStatus=reply->errorString();
emit sendhttpdata(m_data,m_metodname);
return;
// throw(reply->errorString().toStdString());
}
QByteArray data = reply->readAll();
reply->deleteLater();
m_data=data;
MyGlobal::NetworkStatus="Tablolar Yüklendi";
emit sendhttpdata(m_data,m_metodname);
}
catch(std::string exp)
{
qDebug()<<"Exception:"<<QString::fromStdString(exp);
}
catch(std::exception &exp)
{
qDebug()<<"Exception:"<<QString::fromStdString(exp.what());
}
}
void RestWebservice::onError()
{
qDebug()<<"Hata VAR";
}
HttpRequest::HttpRequest(QObject *parent) :
QObject(parent)
{
}
QNetworkRequest HttpRequest::GenerateHTTPQuery()
{
// QString path="";
QString path=QString("/%1/%2/%3/%4/%5").arg(resource).arg(MyGlobal::getMetodName(metodname)).arg(appid).arg(apppass).arg(parametersname);
foreach (QString param, params.keys()) {
path.replace("{"+param+"}",params[param].toString());
}
QUrl m_url(url);
m_url.setPath(path);
m_request.setUrl(m_url);
m_request.setRawHeader("Content-Type","application/json;charset=utf-8");
// m_request.setRawHeader("SOAPAction","http://tempuri.org/IMobileClient/UserAuth");
qDebug()<<m_url.url();
return m_request;
}
QNetworkRequest HttpRequest::GenerateHTTPQueryPost()
{
// QString path="";
QString path=QString("/%1/%2").arg(resource).arg(MyGlobal::getMetodName(metodname));
QUrl m_url(url);
m_url.setPath(path);
m_request.setUrl(m_url);
m_request.setRawHeader("Content-Type","application/json;charset=utf-8");
// m_request.setRawHeader("SOAPAction","http://tempuri.org/IMobileClient/UserAuth");
qDebug()<<m_url.url();
return m_request;
}
Is you mutex a member of your class. In that case the mutex is destructed before it is unlocked (as I presume the containing class goes out of scope), which causes the message you see. The destructor of the mutex is called when the class is destructed, while the lock is held. This is a problem. Typically you will have to find a way to not block indefinitely during your network request.
I have several fields in a widget, that each can affect the behavior of an item, and changing some of them will change others.
I read somewhere that the editingFinished() signal of a line edit is triggered only by user actions - and not by code changes... Is that true ?
connect(m_lineEdit1, SIGNAL(editingFinished()), this, SLOT(m_lineEdit1Changed()));
connect(m_lineEdit2, SIGNAL(editingFinished()), this, SLOT(m_lineEdit2Changed()));
connect(this, SIGNAL(someSignal()), this, SLOT(updateData()));
void m_lineEdit1Changed()
{
changedata1();
emit someSignal();
}
void m_lineEdit2Changed()
{
changedata2();
emit someSignal();
}
void updateData()
{
m_lineEdit1.setText(fromdata);
m_lineEdit2.setText(fromdata);
}
If I change m_lineEdit1, and update the entire widget (which changes, through code, m_lineEdit2), I hit a breakpoint in m_lineEdit2Changed()
This leads to an infinite loop of updates...
What can I do to get around it ?
Blocking signals is a bit of a sledgehammer of an approach. You can use a sentinel class to explicitly prevent recursion:
#define SENTINEL_STRINGIFY(x) #x
#define SENTINEL_TOSTRING(x) SENTINEL_STRINGIFY(x)
#define SENTINEL_AT __FILE__ ":" SENTINEL_TOSTRING(__LINE__)
class Sentinel {
Q_DISABLE_COPY(Sentinel);
static QMutex m_mutex;
static QSet<QString> m_sentinels;
QString const m_sentinel;
bool const m_ok;
static bool checkAndSet(const QString & sentinel) {
QMutexLocker lock(&m_mutex);
if (m_sentinels.contains(sentinel)) return false;
m_sentinels.insert(sentinel);
return true;
}
public:
explicit Sentinel(const char * sentinel) :
m_sentinel(sentinel), m_ok(checkAndSet(m_sentinel)) {}
~Sentinel() {
if (!m_ok) return;
QMutexLocker lock(&m_mutex);
m_sentinels.remove(m_sentinel);
}
bool operator()() const { return m_ok; }
};
QMutex Sentinel::m_mutex;
QSet<QString> Sentinel::m_sentinels;
...
void Foo::m_lineEdit1Changed()
{
Sentinel s(SENTINEL_AT);
if (!s) return; // exit if this method is on the call stack
...
changedata1();
emit someSignal();
}
This is thread-safe and can be used from any thread.
A technique to avoid this problem is to use the QObject::blockSignals() function.
In your example you would do:
void updateData()
{
m_lineEdit1.blockSignals(true);
m_lineEdit1.setText(fromdata);
m_lineEdit1.setText(fromdata);
m_lineEdit1.blockSignals(false);
}
The blockSignals() call prevents the object sending any signals while you are changing the data in the line edit.
I am trying to multiple request with QNetworkAccessManager even I connect slots QNetworkAccessManager cant emit finished() signal.
Here is the code I implemented.
void GetNetwork::getAction(QStringList *urls, QList<QByteArray> *result)
{
uint size=urls->size();
multiGetResult=result;
getUrls=urls;
setFlags();
for(uint x=0;x<size;x++)
{
int test=caluculateIndex(x);
getNAM[test]->get(QNetworkRequest(QUrl(urls->at(x))));
}
//qDebug()<<reply->readAll();
while (!waitWithFlag()) QThread::msleep(15);
delete threadFlag;
}
bool GetNetwork::setMultipleGet(uint number)
{
int diff=number-(getNAM.size()-1);
if(((getNAM.size()-1)+diff)<0)
return false;
for(int i=0;i<diff;i++)
{
getNAM.append(new QNetworkAccessManager(this));
connect(getNAM[getNAM.size()-1],SIGNAL(finished(QNetworkReply*)),this,SLOT(handleMultiRequest(QNetworkReply*)));
}
for(int i=diff;i<0;i++)
{
disconnect(getNAM[getNAM.size()-1],SIGNAL(finished(QNetworkReply*)),this,SLOT(handleMultiRequest(QNetworkReply*)));
delete getNAM[getNAM.size()-1];
getNAM.remove(getNAM.size()-1);
}
return true;
}
void GetNetwork::handleMultiRequest(QNetworkReply * reply)
{
int index=getUrls->indexOf(reply->url().toString());
if(reply->error()!=QNetworkReply::NoError||index==-1)
{
QString error=QString("Network Error file:%1 line:%2 error:%3")
.arg(__FILE__)
.arg(__LINE__)
.arg(reply->errorString());
emit errorOccured(error);
return;
}
multiGetResult->insert(index,reply->readAll());
threadFlag[index]=true;
}
What's wrong in these codes? I cant figure it out.
Thank you.
First of all you don't need a separate thread for QNetworkAccessManager as it internally runs in a separate thread (asynchronous) since Qt 4.8.1.
Secondly you are only connecting the last instance of QNetworkAccessManager with a finished slot, instead of doing that, connect each instance of QNetworkAccessManager with handleMultiRequest Slot and keep increasing the count whenever the slot is invoked. You don't need sleep and all that stuff, it is all event driven.
So,
void GetNetwork::handleMultiRequest(QNetworkReply * reply)
{
int index=getUrls->indexOf(reply->url().toString());
if(reply->error()!=QNetworkReply::NoError||index==-1)
{
QString error=QString("Network Error file:%1 line:%2 error:%3")
.arg(__FILE__)
.arg(__LINE__)
.arg(reply->errorString());
emit errorOccured(error);
return;
}
count++;
if(count == num_get_requests)
{
emit
allDone()
}
}
and I changed fucntions like Adnan idea
void GetNetwork::handleMultiRequest(QNetworkReply * reply)
{
int index=getUrls->indexOf(reply->url().toString());
if(reply->error()!=QNetworkReply::NoError||index==-1)
{
QString error=QString("Network Error file:%1 line:%2 error:%3")
.arg(__FILE__)
.arg(__LINE__)
.arg(reply->errorString());
emit errorOccured(error);
return;
}
multiGetResult->insert(index,reply->readAll());
threadDoneCounter++;
if(threadDoneCounter==getUrls->size())
emit threadsAreDone();
}
and
void GetNetwork::getAction(QStringList *urls, QList<QByteArray> *result)
{
uint size=urls->size();
multiGetResult=result;
getUrls=urls;
threadDoneCounter=0;
for(uint x=0;x<size;x++)
{
int test=caluculateIndex(x);
getNAM[test]->get(QNetworkRequest(QUrl(urls->at(x))));
}
QEventLoop eLoop;
connect(this,SIGNAL(threadsAreDone()),&eLoop,SLOT(quit()));
eLoop.exec();
disconnect(this,SIGNAL(threadsAreDone()),&eLoop,SLOT(quit()));
}
I have to create an 2 custom events.
I followed this link & made my code :--
Is there a cleaner way to register Qt custom events?
Is it the right way to create & post & pass some data(Qstring) to the customized event ?
===========================================================
Edit code as per Kuba Ober sugession :---
Mainwindow.h :--
UpdateEvent *myUpdateEvent ;
ClearEvent *myClearEvent ;
Mainwindow.c :---
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
i =0;
myUpdateEvent = new UpdateEvent("hello");
myClearEvent = new ClearEvent("bye");
QCoreApplication::postEvent(this, myUpdateEvent);
QCoreApplication::postEvent(this, myClearEvent);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
qDebug() << "oo";
if (UpdateEvent::is(event)) {
UpdateEvent *tempUpdateEvent = static_cast<UpdateEvent *>(event);
qDebug() << tempUpdateEvent->value();
}
else if (ClearEvent::is(event)) {
ClearEvent *tempClearEvent = static_cast<ClearEvent *>(event);
qDebug() << tempClearEvent->value();
}
return true;
}
event.h file :--
template <typename T> class StringEvent : public QEvent
{
QString m_str;
public:
explicit StringEvent(const QString val) : QEvent(staticType()), m_str(val)
{
}
QString setvalue(QString val)
{
m_str = val;
}
QString value() const
{
return m_str;
}
static QEvent::Type staticType()
{
static int type = QEvent::registerEventType();
return static_cast<QEvent::Type>(type);
}
static bool is(const QEvent * ev)
{
return ev->type() == staticType();
}
};
class UpdateEvent : public StringEvent<UpdateEvent>
{
public:
explicit UpdateEvent(QString val): StringEvent(val)
{
}
};
class ClearEvent : public StringEvent<ClearEvent>
{
public:
explicit ClearEvent(QString val): StringEvent(val)
{
}
};
why eventFilter is not triggered ? And i am not able to see debug message on postevent ?
I can only comment on the code smell of your event implementation, since it is at this point unclear why would you need to send events to an edit control within a window. The latter is bad design.
Your Event class is needlessly complicated. You should be setting up all values inside the event during construction, and they should be accessible through read-only accessors afterwards. The extra event type seems to be an unnecessary gimmick as well.
Here's how I'd do it, using a metafactory pattern (a name I just coined, perhaps there's a better/existing name for it?). This solves the need for explicit derived class constructor injection that'd be otherwise needed.
I've split up the metafactory into small constituent classes for ease of understanding.
// A type-identifier-generating wrapper for events
template <typename D> class EventWrapper : public QEvent {
public:
EventWrapper() : QEvent(staticType()) {}
static QEvent::Type staticType() {
static QEvent::Type type = static_cast<QEvent::Type>(registerEventType());
return type;
}
static bool is(const QEvent * ev) { return ev->type() == staticType(); }
static D* cast(QEvent * ev) { return is(ev) ? static_cast<D*>(ev) : 0; }
};
// The generic event metafactory for C++98 (doesn't need C++11)
template <typename D, template <typename> class Carrier> class EventMF {
class EventFwd;
class Final;
class FinalWrapper : public EventWrapper<EventFwd>, public virtual Final {};
public:
// EventFwd is a class derived from Event. The EventWrapper's cast()
// will cast to a covariant return type - the derived class. That's OK.
typedef Carrier<FinalWrapper> Event;
private:
class EventFwd : public Event {};
class Final {
friend class FinalWrapper;
friend class Carrier<FinalWrapper>;
private:
Final() {}
Final(const Final &) {}
};
};
// A string carrier
template <typename B> class StringData : public B {
QString m_str;
public:
explicit StringData(const QString & str) : m_str(str) {}
QString value() const { return m_str; }
};
// A string event metafactory
template <typename D> class StringEventMF : public EventMF<D, StringData> {};
class Update : public EventMF<Update, StringData> {}; // using generic metafactory
class Clear : public StringEventMF<Clear> {}; // using specific metafactory
#if 0
// This should fail at compile time as such derivation would produce classes with
// duplicate event types. That's what the Final class was for in the matafactory.
class Error : public Update::Event { Error() : Update::Event("") {} };
#endif
int main(int, char**)
{
// Test that it works as expected.
Update::Event update("update");
Clear::Event clear("clear");
Q_ASSERT(Update::Event::staticType() != Clear::Event::staticType());
Q_ASSERT(Update::Event::staticType() == Update::Event::cast(&update)->staticType());
qDebug() << Update::Event::cast(&update)->value();
Q_ASSERT(Update::Event::cast(&clear) == 0);
qDebug() << Clear::Event::cast(&clear)->value();
Q_ASSERT(Clear::Event::cast(&update) == 0);
}
The Metafactory::Event classes are the custom event classes that derive from QEvent.
The class hierarchy for Update::Event looks as follows (going from the least derived to most derived class):
EventWrapper<EventMF<...>::EventFwd>, EventMF<...>::Final (multiple inheritance)
EventMF<Update, StringData<Update>>::FinalWrapper
StringData<Update> = EventMF<Update, StringData<Update>>::Event
with EventMF<...> being shorthand for EventMF<Update, StringData<Update>.
The line Update::Event update("update") constructs a custom string-carrying event instance, with the constructors called from the last to the first in the list above.
Since EventMF<...> is a metafactory that works at time of compilation only, there is absolutely no need for its instance to exist at runtime. Thus EventMF<...>::EventMF constructor is never called. You can enforce this invariant by deleting the constructor (declaring it private for C++98).
The use within an event handler would look like:
void MainWindow::customEvent(QEvent *event)
{
...
if (Update::Event::is(event)) {
qDebug() << Update::Event::cast(event)->value();
...
}
else if (Clear::Event::is(event)) {
...
}
...
}
I am trying to create a class which should handle all the data from and to a sqlite database. However, I am pretty new to QT and C++ and wondering about the declaration of the database object in the class. I could need some tips on what I am doing right and wrong and how it normally should or could be done. My goal was, to create a single QSqlDatabase for the class and use it for every function within the class.
At the moment, I have the following code:
main.cpp
#include "mainwindow.h"
#include "database.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Database db;
MainWindow w;
if(db.createStructure())
{
w.show();
}
return a.exec();
}
database.h
#ifndef DATABASE_H
#define DATABASE_H
#include <QObject>
#include <QSqlDatabase>
class Database : public QObject
{
Q_OBJECT
public:
explicit Database(QObject *parent = 0);
// FUNCTIONS
bool createStructure();
signals:
public slots:
private:
// VARIABLES
QSqlDatabase m_db;
// FUNCTIONS
bool open();
void close();
bool transaction();
bool commit();
};
#endif // DATABASE_H
database.cpp
#include "database.h"
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QList>
Database::Database(QObject *parent) :
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setHostName("localhost");
m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
}
// PRIVATE
bool Database::open()
{
return m_db.open();
}
void Database::close()
{
return m_db.close();
}
bool Database::transaction()
{
return m_db.transaction();
}
bool Database::commit()
{
return m_db.commit();
}
// PUBLIC
bool Database::createStructure()
{
bool prepared;
QList<QString> commands;
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
if (!Database::open())
{
return false;
}
else
{
if (!Database::transaction())
{
Database::close();
return false;
}
else
{
foreach(QString command, commands)
{
QSqlQuery query;
prepared = query.prepare(command);
if(!prepared)
{
if (!Database::commit())
{
Database::close();
return false;
}
else
{
Database::close();
return false;
}
}
else
{
if(!query.exec())
{
if (!Database::commit())
{
Database::close();
return false;
}
else
{
Database::close();
return false;
}
}
}
}
if (!Database::commit())
{
Database::close();
return false;
}
else
{
Database::close();
return true;
}
}
}
}
This code is working.
However, the QSQLITE database is not added a single time to the m_db object, but every time a function in the class is called, because the...
Database::Database(QObject *parent) :
QObject(parent)
{
m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setHostName("localhost");
m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
}
...codeblock is executed every time.
The current default connection is just replaced and since the new one is the same, that doesn’t have any effect on the program, but it doesn’t look like a neat solution.
So I tried to replace this codeblock with a declare-function I can call from main.cpp once...
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Database db;
MainWindow w;
db.declare(“QSQLITE”, “localhost”, QCoreApplication::applicationDirPath() + "/events.db");
if(db.createStructure())
{
w.show();
}
return a.exec();
}
database.cpp
void Database::declare(QString driver, QString host, QString path)
{
m_db = QSqlDatabase::addDatabase(driver);
m_db.setHostName(host);
m_db.setDatabaseName(path);
}
...but the values for the m_db object are of course only available within the declare-function and not for the other functions I call afterwards.
My best guess for a solution would be to declare the QSqlDatabase in main.cpp and give it to the function it should call:
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSqlDatabase qdb = QSqlDatabase::addDatabase("QSQLITE");
qdb.setHostName("localhost");
qdb.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
Database db;
MainWindow w;
if(db.createStructure(qdb))
{
w.show();
}
return a.exec();
}
database.cpp
bool Database::open(QSqlDatabase qdb)
{
return qdb.open();
}
void Database::close(QSqlDatabase qdb)
{
return qdb.close();
}
bool Database::transaction(QSqlDatabase qdb)
{
return qdb.transaction();
}
bool Database::commit(QSqlDatabase qdb)
{
return qdb.commit();
}
bool Database::createStructure(QSqlDatabase qdb)
{
bool prepared;
QList<QString> commands;
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
if (!Database::open(qdb))
{
return false;
}
else
{
if (!Database::transaction(qdb))
{
Database::close(qdb);
return false;
}
else
{
foreach(QString command, commands)
{
QSqlQuery query;
prepared = query.prepare(command);
if(!prepared)
{
if (!Database::commit(qdb))
{
Database::close(qdb);
return false;
}
else
{
Database::close(qdb);
return false;
}
}
else
{
if(!query.exec())
{
if (!Database::commit(qdb))
{
Database::close(qdb);
return false;
}
else
{
Database::close(qdb);
return false;
}
}
}
}
if (!Database::commit(qdb))
{
Database::close(qdb);
return false;
}
else
{
Database::close(qdb);
return true;
}
}
}
}
Is it possible to somehow store a reusable QSqlDatabase object in a class? If so, how?
Really appreciate your help!
EDIT 1
Some code created from the designer I am using a function in.
mainwindows.cpp
void MainWindow::on_pushButton_24_clicked()
{
Database db;
bool b = db.createStructure();
QMessageBox::information(this, "test", QString(b));
}
I'll stick to your original code for the explanations.
Disclaimer: I didn't compile any of my suggestions, forgive me if there are syntax errors.
First of all, what you are probably looking for is the Singleton Pattern (which I don't really like that much anymore, but for your purpose one could argue that it can be considered appropriate):
You have to have the following in your class definition:
class Database : public QObject
{
Q_OBJECT
public:
static Database* instance();
private:
static Database* m_instance;
Database();
~Database() {}; // it can be necessary to have this public in some cases, if
// you ever get a linker error related to deletion, this is
// probably the reason.
public:
// FUNCTIONS
...
};
And the following in your .cpp file:
// init singleton pointer to NULL
Database* Database::m_instance = NULL;
Database* Database::instance()
{
if( !m_instance )
{
m_instance = new Database();
}
return m_instance;
}
You can then access that singleton using e.g.
if( Database::instance()->createStructure() )
{
w.show();
}
What does this do? At the start of the program, the line
Database* Database::m_instance = NULL;
initialises your m_instance variable to NULL. The first time you call Database::instance() it realizes that m_instance is still NULL and creates a new object and makes m_instance point to that object. From that point on, the pointer to that object will always be returned, but no more Database object will be created.
In your createStructure() function you commit() your database even when there is an error. The usual procedure is to commit() upon success and rollback() upon failure.
Before fixing that, be sure to read the next point though:
The third thing I would recommend is getting used to being suspicious whenever you see multiple occurrences of the same lines a lot. That usually cries for a sub function.
I'm talking about
Database::close();
return false;
Take a look at how I rewrote your createStructure() method by introducing another method and leaving out else{ } where it was not necessary:
bool Database::createStructure()
{
QStringList commands;
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
commands.append("CREATE TABLE...;");
if (!Database::open()) return false;
// at this point you can be sure the database is open
if (!Database::transaction())
{
Database::close();
return false;
}
// at this point you can be sure the database is open and a transaction was started
if (!Database::executeCommands(commands))
{
// an error occurred - we need to rollback what we did so far
Database::rollback();
Database::close();
return false;
}
// everything was executed properly, but the transaction is still active
// => commit the changes we've made
bool committed = Database::commit();
// no matter if the commit was successful or not, close the database,
// then return the result we've stored
Database::close();
return committed;
}
bool Database::executeCommands(const QStringList& commands)
{
// This method simply executes the queries and is relieved from
// transaction-related code.
foreach(QString command, commands)
{
QSqlQuery query;
bool prepared = query.prepare(command);
if(!prepared) return false;
if(!query.exec()) return false;
}
return true;
}
This could be further refactored, it is just an example of making your code easier to follow and thus usually less error-prone.