Qt5 execute submitAll() multiple times,the error occurred.
void UserManageWork::submitAll()
{
bool res = true;
DB.open();
res = relTableModel->submitAll(); //QSqlRelationalTableModel
DB.close();
emit submitAllFinished(res);
}
It does not occur the first, but often the second.
The QSqlRelationalTableModel is loaded using the child thread, it does not work in the ui thread, I may now know why the error occurs, as #chehrlic said, there is an open QSqlQuery in QSqlRelationalTableModel, I should not execute DB.close() at this point.
My question now is, if I want to use QSqlRelationalTableModel in the child thread and load the model with tableview through the signal-slot, how should I manage the opening and closing of the database connection in the child thread.
Related
I need to update the content of a field on my QWidget via a JSON file (updated in real time). I've read about functions readLine() and readAll() of QFile, but when I try a loop like :
while(true):
jsfile.readLine()
creation of objects, update of values, display etc ...
I lost the focus on my window. But I want to keep the control of the application with my buttons and obviously to watch the evolution of the JSON values.
I have thought that Qt manages itself the events and keeps the focus on the current window, but like I've said, it's not the case.
Is there a good solution (multi threads maybe) to use my window while the application reads the file (with new informations in real time)?
(With the constraint "real time" I can't read the whole file every time and I've no choice about the format of this file)
Update
I tried the thread method.
So, I choose to create my thread instance into the main (with my main window) and connect here. But, when I run the program, I've this error :
no matching member function for call to 'connect'
Reader reader;
QObject::connect(controler, SIGNAL(ready()),
reader, SLOT(received()));
According to this error, I've thought that the reason was main don't inherits of Object, and so, I've move the connection ans the creation of thread instance into my main window.
Reader reader;
QObject::connect(reader, SIGNAL(newobject(QJsonObject)),
this, SLOT(displayJSON(QJsonObject)));
With this one, I've the same error while I've already connect lot of widget into this class without any error.
What can be the problem ?
Update 2
I've a solution when I give as argument my main window (controler) in reader's constructor and connect into this one but, if possible, I would an explanation for the previous problem.
The current problem that I have is that signals are emit well but slots are executed after the end the application (so after the end of the thread's execution and not during)
This isn't really the subject of this topic so we can close this one.
You can use QThread (Qt documentation: QThread) class to create a thread, which will read your file. The main thread will execute your GUI application and it will be available during file reading.
You can find a simple example in documentation for creating your thread:
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
You can modify this example for your purpose. For example, WorkerThread for your task may be something like this:
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {
while(!stopFlag)
{
// read JSON file to QByteArray. Use QFile and QTextStream
// use QJsonDocument to read JSON content
// find what is new in JSON
emit signalSomethingNew(/*parameters*/);
QThread::currentThread()->msleep(/*timeout*/);
}
}
signals:
void signalSomethingNew(/*parameters*/);
};
At the end you must implement slot on your QWidget for signalSomethingNew(/*parameters*/) and make connection:
connect(yourThread, &WorkingThread::signalSomethingNew, youWidget, &YouWidget::yourSlot);
For working with JSON data: QJsonDocument
I'm interpreting your question as "my application is unresponsive whilst doing work" rather than "my focus jumped to another window" - please comment if you meant something different.
You have a choice of options:
Create and run a background QThread to do the work. Have it emit signals (connected to your widgets using Qt::QueuedConnection - the default) when it has results to display.
This is a good solution when the worker has a lot of computation to do, or needs all the input to be read before it can start. It works very well when the target system has processors available with no other work to do.
Use a QSocketNotifier to signal your GUI thread when some of the input becomes available (note that the name is misleading - it actually works on all kinds of file descriptor, not just sockets).
This is appropriate when the algorithm is simple and incremental - i.e. if a small chunk of input can be read and processed quickly.
Incorporate periodic calls to processEvents() in your algorithm:
auto *const dispatcher = QThread::currentThread()->eventDispatcher;
while (line = json.readLine()) {
doSomethingWith(line);
if (dispatcher)
dispatcher->processEvents();
}
This won't work unless you can modify the algorithm like this - if the loop is in somebody else's (closed) code, then you'll need one of the other solutions.
I'm running a C++ program, build with Qt, that never can stop.
The program always fetches data from database and if there is a result sends an SMS.
I'm able to connect to database, but after some hours (+/- 10), it doesn't work anymore.
I don't know if the problem is because I lose connection with database or because my computer goes standby...
I'm not able in Qt to see database status: db.open() always returns true when tested inside while loop.
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName("");
db.setPort();
db.setDatabaseName("");
db.setUserName("");
db.setPassword("");
if (db.open())
{
while (true)
{
// MySQL Request
// If data -> send SMS
}
}
There's always the possibility to loose a DB connection for whatever reason. You just can't rely on it. You have to check your connection inside the loop and implement some kind of re-connection scheme if the connection gets lost. As far as I know Qt doesn't do that for you.
Qt provides an event driven framework; events occur and the program reacts to those events.
When you have a never ending loop, events are queued, waiting until they can be processed. In your case, this is never going to happen, so the queue of events will just keep increasing, taking up resources such as memory.
There are two possible ways of solving this. The first is to call QApplication::processEvents every now and again in your loop.
However, the better method would be to remove the while(true) and instead use a QTimer which will periodically call a function to process any available data.
Assuming you have a class, derived from QObject, here's skeleton code using QObject's own timer
class MyObject : public QObject
{
Q_OBJECT
public:
MyObject(QObject *parent = 0);
protected:
// this will be called periodically from the timer
void timerEvent(QTimerEvent *event);
private:
m_timerId = 0; // C++ 11 initialisation
};
MyObject::MyObject(QObject *parent)
: QObject(parent)
{
m_timerId = startTimer(50); // 50-millisecond timer
}
void MyObject::timerEvent(QTimerEvent *event)
{
if(event->timerId() == m_timerId)
{
// MySQL Request
// If data -> send SMS
}
}
I have a Qt GUI class preferencesWindow that, obviously, is responsible for handling the user preferences. I have some fields that manage the connection to a database server. When a field is left, dbsChanged() method is called. Below is some code I managed to write:
void preferencesWindow::dbsChanged() {
QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases);
QStringList databases = loader.result();
if (databases.length()) {
this->ui.database->show();
this->ui.nodb_label->hide();
this->ui.database->clear();
this->ui.database->addItems(databases);
this->ui.okButton->setDisabled(false);
this->ui.validationStatus->setPixmap(QPixmap(":/icon/tick.png"));
} else {
this->ui.database->hide();
this->ui.nodb_label->show();
this->ui.okButton->setDisabled(true);
this->ui.validationStatus->setPixmap(QPixmap(":/icon/error.png"));
}
}
QStringList preferencesWindow::get_databases() {
QSqlDatabase test_connection;
if (QSqlDatabase::contains("PREFEREMCES_LIVE_TEST_CONNECTION"))
test_connection = QSqlDatabase::database("PREFEREMCES_LIVE_TEST_CONNECTION");
else test_connection = QSqlDatabase::addDatabase("QMYSQL", "PREFEREMCES_LIVE_TEST_CONNECTION");
test_connection.setHostName(this->ui.serverAddress->text());
test_connection.setUserName(this->ui.username->text());
test_connection.setPassword(this->ui.password->text());
test_connection.setDatabaseName(this->ui.database->currentText());
test_connection.setPort(this->ui.serverPort->value());
test_connection.open();
qDebug() << "Error: " << test_connection.lastError();
QSqlQuery show_databases = test_connection.exec("show databases");
QStringList databases;
while (show_databases.next()) {
databases.append(show_databases.value(0).toString());
}
QSqlDatabase::removeDatabase("PREFERENCES_LIVE_TEST_CONNECTION");
return databases;
}
Since get_databases can take a long time, I thought that putting in on a separate thread as you can see in these two lines:
QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases);
QStringList databases = loader.result();
could solve the problem. It runs on a separate thread, but it still freezes the GUI (while working).
How should I rewrite this entire process? I though of some solutions, but I am not really sure about their performance, and I don't want to work uselessly...
It freezes the GUI because even though the get_databases call is in a separate thread, you still wait for the results which causes the freeze.
I don't know how to do it in Qt, but the normal thing would be to open a dialog saying "please wait" or something with a cancel button, and have the worker thread send a signal to the parent (GUI) thread when done.
The QFuture will wait until the thread sets the result when your call loader.result(). You have to wait for that value later.
I guess you could store the future object as member of preferencesWindow and send yourself a signal, when finishing get_databases. So you give your application time to process other events during this wait time.
You can use QFutureWatcher to monitor that status of the QFuture object, like written in the documentation:
// Instantiate the objects and connect to the finished signal.
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
// Start the computation.
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
In my project i have several Objects which gather some data and put them into a database, now for some reasons the database link could fail, so i made signal for it and bound it to a slot which will write what happened in a log file and try to reconnect to the database.
Now the problem is, sometimes it happens that a signal trigger the slot and then try to reconnect to the database while a previous connection is on his way. And as the Objects send several requests in a very small interval this problem happens always, because as soon as a request fails the signal is emitted to re-establish the connection.
So what I want is a proper way to prevent any slot to call the "connectToDatabase()" method when another slot already did it so that the method will end properly.
here is the slot.
void mainFrame::reconnectDatabase(QSqlQuery *failedQuery)
{
log_it("QUERY:\""+failedQuery->lastQuery()+"\" ERROR:\""+failedQuery->lastError().text()+"\"");
log_it("Re-connecting to Database...");
QSqlDatabase db = QSqlDatabase::database();
if(!db.open())
log_it("Reconnecting database operation failed! REASON: \""+db.lastError().text()+"\"");
}
log_it() is my function for writing logs.
You could accomplish this by having a boolean check indicating that an attempt to connect to the database is already in progress.
void MyClass::slotConnectToDatabase()
{
if (m_connectingToDatabase)
return;
m_connectingToDatabase = true;
// connect to database
m_connectingToDatabase = false;
}
When connectToDatabase is called for the first time, you can set a boolean variable to true which shows thatconnectToDatabase is called. In connectToDatabase method you can check for this variable and return if it is true :
void connectToDatabase()
{
if(!connected)
connected = true;
else
return;
//...
}
I've a dialog displaying progress bar + some other data, and I also have a cancel button on this dialog. While this dialog is displayed there is potentially heavy computation going on, which is show on progress bar. This computation is started from withing this dialog code so I have:
Counting_Progress_Dialog::Counting_Progress_Dialog(QWidget *parent) :
QDialog(parent)
{
setupUi(this);
thread_ = new Threaded;//THIS IS THE THREAD IN WHICH COMPUTATION IS BEING PERFORMED
connect(thread_,SIGNAL(counter_value(int)),this,SLOT(update_progress_bar(int)));
connect(this,SIGNAL(rejected()),thread_,SLOT(terminate()),Qt::QueuedConnection);//
HERE I'M CONNECTING REJECTED ON DIALOG TO TERMINATE ON THREAD
}
void Counting_Progress_Dialog::start()
{
thread_->start(QThread::LowestPriority);
}
and I do invoke this in part of the program:
void My_Class::dummy_()
{
auto old_priority = this->thread()->priority();
this->thread()->setPriority(QThread::HighestPriority);
Counting_Progress_Dialog progress;
progress.start();//this will start thread
progress.exec();//this will enter it's event loop
progress.wait();//this will wait until thread is finished
this->thread()->setPriority(QThread::NormalPriority);
}
But despite all this, when I press cancel on my dialog, the whole application freezes. What am I doing wrong? How to make it behave correctly?
UPDATED:
void Counting_Progress_Dialog::wait()
{
thread_->wait();
}
I see that you are connecting using 2 different strategies. But if thread_ and this(counting dialog) are really within two separated threads then the connection will always be Qt::QueuedConnection. Well that's not the issue.
progress.exec();//this will enter it's event loop
Calling exec() suspend the execution of dummy_() until the dialog have to return. And when the dialog return your thread is terminated. So I don't see the purpose of
progress.wait();//this will wait until thread is finished
By the way which function is that? the only one I know is Qthread::wait(). I am pretty confident the issue is here...
edit:
progress.wait() is not the issue... But it is possible that the events sent by the thread are causing trouble in some way. Use the debugger or some qDebug() to see if update_progress_bar is called after you push cancel.