FredCpp FredCpp - 2 months ago 11
C++ Question

Qt - MainWindow freezes when running

I'm trying to get large data from a database but when running the main window freezes.

I am working under windows and according to this link Windows automatically set the program to a hanging state state after 5 seconds.

Is there a way to prevent freezing?

Here is the code:

void MainWindow::on_getDataButtonClicked()
{
ui->centralWidget->setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
try
{
Client client(user, password);

std::future<map<string, map<string, string> > > fut =
std::async(std::launch::async, &Client::get_data, &client);

// While not all data has been retrieved, set message to the status bar.
while (fut.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
{
ui->statusBar->showMessage("Getting data.");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ui->statusBar->showMessage("Getting data..");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ui->statusBar->showMessage("Getting data...");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}

map<string, map<string, string> > exported_strings = std::move(fut.get());

ui->statusBar->showMessage("%All data has been retrieved!");
}
catch (std::string& s)
{
QMessageBox::critical(this, "Error", QString::fromStdString(s));
}
catch (std::exception& e)
{
QMessageBox::critical(this, "Error", QString(e.what()));
}
catch (...)
{
QMessageBox::critical(this, "Error", "An unknown error has occurred.");
}
ui->centralWidget->setEnabled(true);
QApplication::restoreOverrideCursor();
}


On a side note, the main window does not freezes when debugging.

Answer

To avoid multithreading, here's what you should do:

  • Cut your code (virtually, just in thought) into chunks
  • Run your program and study these chunks, and see which of them causes the freezing (takes longest to execute)
  • Once you know what part causes the freezing, use QApplication::processEvents(); to force the MainWindow to become responsive.

Example: Let me illustrate with an example. Say you have this function

void MainWindow::on_getDataButtonClicked()
{
    //do some easy stuff
    for(long i = 0; i < 100000; i++)
    {
        //do some stuff
        //do some other stuff
    }
    //do some different stuff
}

Now while executing this nasty function, your window will definitely freeze until the whole function is finished. The "easy stuff" is not the problem, because it's fast. But the big loop is the problem. So all you have to do is tell Qt to reprocess the events that get the main window to become responsive again; Like this:

void MainWindow::on_getDataButtonClicked()
{
    //do some easy stuff
    for(long i = 0; i < 100000; i++)
    {
        QApplication::processEvents();
        //do some stuff
        //do some other stuff
    }
    QApplication::processEvents();
    //do some different stuff
}

How many times should you call processEvents();? Call it whenever you want the window to respond again. If in doubt, just put processEvents() like everywhere! Infest every line with that. It's not the best practice, but then you can remove them one by one and see where your program starts freezing again.

One additional piece of advice: Don't throw std::string exceptions. This is a very bad idea. Learn how to throw classes inherited from std::exception. You can still hold your string inside the std::exception object. For example, you can do: throw std::exception("This is very very bad");. There are many other classes you can throw. Find them here.

Comments