Why is not Qt label refreshed? - c++

This is a method that I call from a button click
void ChangeLabelText(QLabel* myLabel)
{
int countNumber = 0;
for(int i = 0; i < 9999; i++)//outer loop
{
for(int k = 0; k < 65000; k++)//inner loop
{
countNumber++;
}
myLabel->setText(QString::number(countNumber));
}
}
When the code runs text of the label is set at the end of the outer loop, but I expected it to set label's text every time inner loop finishes. What might be causing it?

Your code executed in the main thread and in the main thread th UI update happens on events callbacks. What you need is to force repaint your ui. You can do it by calling repaint() or by asking aplication to process events with QCoreApplication::processEvents(). You need to make it after changing label.

Related

widget is only updated at the end of the for loop. I need it to be updated inside the for loop incrementally

How can I use this "for loop" bellow to have the window updated immediatly. Sleep(5) is only to see if the items are filled instantly
I am using Qt in a simple " class MainWindow : public QMainWindow" with one button and one plainTextEdit and one textEdit
for (int i=0; i < 10 ; i++ )
{
sentFrame = "toto"+i;
ui->alarmSent->addItem(sentFrame.toHex()); // filled at the end of for loop
ui->sentTest->insertPlainText(sentFrame.toHex()); // filled at the end of for loop
sleep(5);
}
You should not use the sleep method in the GUI because it blocks for a long time the eventloop where the GUI lives, a workaround is to use QTimer::singleShot() + QEventLoop:
for (int i=0; i < 10 ; i++ )
{
sentFrame = "toto"+i;
ui->alarmSent->addItem(sentFrame.toHex());
ui->sentTest->insertPlainText(sentFrame.toHex());
QEventLoop loop;
QTimer::singleShot(5*1000, &loop, &QEventLoop::quit);
loop.exec();
}

Updating QChart from QLineSeries in a running while loop

I want to make my QChart dynamically update whenever a point is added to the QLineSeries object attached to it, but it seems that this update only occurs after the while loop I am running has finished. I am using said while loop in interface.cpp that calls a function updatePlot() which adds the data point to the line series, but this only updates the chart after the while loop has completely finished. Pseudo code of what is happening here:
qtwindow.cpp
// Constructor that initializes the series which will be passed into the interface
AlgoWindow::AlgoWindow( ..., TradingInterface* interface, ... ) {
...
QLineSeries* series = new QLineSeries();
QLineSeries* benchmark = new QLineSeries();
QChart* chart = new QChart();
chart->addSeries(series);
chart->addSeries(benchmark);
// Also creates custom axes which are attached to each series
...
}
// Slot connected to a button signal
void AlgoWindow::buttonClicked() {
// Runs the backtest
interface->runbacktest(..., series, benchmark, ...);
}
interface.cpp
void TradingInterface::runbacktest(..., QtCharts::QLineSeries* algoplot, QtCharts::QLineSeries* benchplot) {
// Runs a huge while loop that continuously checks for events
while (continue_backtest) {
if (!eventsqueue.isEmpty()) {
// Handle each event for the bar
} else {
// All events have been handled for the day, so plot
updatePlot(algoplot, benchplot);
}
}
}
void TradingInterface::updatePlot(QtCharts::QLineSeries *algoseries,
QtCharts::QLineSeries *benchseries) {
// Get the date and the information to put in each point
long date = portfolio.bars->latestDates.back();
double equitycurve = portfolio.all_holdings.rbegin().operator*().second["equitycurve"];
double benchcurve = benchmarkportfolio.all_holdings.rbegin().operator*.second["equitycurve"];
// Append the new points to their respective QLineSeries
algoseries->append(date * 1000, equitycurve*100);
benchseries->append(date * 1000, benchcurve*100);
}
This gives me no errors and the while loop completes, but the lines are only plotted after runbacktest() exits. It then plots all the data correctly, but all at once.
What I need to happen is for the QChart to update every time the lines are added, which my guess was to use some form of custom signal-slot listener but I have no clue how to go about that. If the graph will not update until after the function completes, is it even possible within the QChart framework?
Also, I have already tried QChart::update() and QChartView::repaint(). Both produced the same results as without.
EDIT: I tried setting up a new thread that emits a signal back to the main thread whenever the data is completed but it seems to have changed nothing. The QChart still does not update until after all the data has been inputted. I added a couple lines to help debug and it seems like the function which emits the signal runs consistently just fine, but the slot function which receives the signal only runs after the thread has finished. Not only that, but slowing the signals down with a sleep does not make it plot slowly (like I thought), as the QChart still refuses to update until after the final update to addData().
Either remove your while loop and perform the work one step at a time with a timer.
Or run your runbacktest function in another thread and send a signal to update the QChart in the UI's thread when the data is ready.
Either way you need to give control back to the event loop so that the chart can be repainted.
The Qt idiom for running an operation “continuously” is to use a zero-duration “timer”. It’s not a timer really, but Qt calls it one.
You can do the operation in chunks that take approximately a millisecond. For this, invert the control flow. Qt doesn't provide too much syntactic sugar for it, but it's easy to remedy.
Convert this code, which maintains a loop:
for (int i = 0; i < 1000; ++i) {
doSomething(i);
}
into this lambda, which is invoked by the event loop:
m_tasks.addTask([this](i = 0) mutable {
doSomething(i);
++i;
return i < 1000;
});
assuming:
class Controller : public QObject {
Tasks m_tasks;
...
};
where the Tasks class maintains a list of tasks to be executed by the event loop:
class Tasks : public QObject {
Q_OBJECT
QBasicTimer timer;
std::list<std::function<bool()>> tasks;
protected:
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() != timer.timerId())
return;
for (auto it = tasks.begin(); it != tasks.end(); ) {
bool keep = (*it)();
if (!keep)
it = tasks.erase(it);
else
++it;
}
if (tasks.empty())
timer.stop();
}
public:
using QObject :: QObject;
template <typename F> void addTask(F &&fun) {
tasks.emplace_back(std::forward(fun));
if (!timer.isActive())
timer.start(0, this);
}
};

C++ Break a loop from another function

I'm developing a video recording software but I am stuck on an issue.
I want to stop the recording when the button Stop is clicked but nothing happens when I click it...
Here is my core (simplified):
MySoftware.hpp:
bool b_Stop = false;
MySoftware.cpp
MainWindow::MainWindow() : QWidget() {
qpb_StartCapture = new QPushButton("Start Capture", this);
QObject::connect(qpb_StartCapture, SIGNAL(clicked()), this, SLOT(startCapture()));
qpb_StopCapture = new QPushButton("Stop Capture", this);
QObject::connect(qpb_StopCapture, SIGNAL(clicked()), this, SLOT(stopCapture()));
}
void MainWindow::startCapture() {
b_Stop = false;
// CAMERAS INITIALIZATION
while (!b_Stop) {
for (int i = 0; i < v_cp_Cameras.size(); i++) {
// IMAGE CAPTURE
}
}
// IMAGES PROCESSING
}
void MainWindow::stopCapture() {
b_Stop = true;
}
The way I see it is that startCapture is probably called from your event loop. This blocks any other events from being processed. Try putting your loop into a separate thread and see if that works.
You're having the image capture inside of a for loop that doesn't check whether or not the stop button is activated. You're checking the while loop, which probably never starts over. put if (b_stop) {break;} inside the for loop and it might work.

How to get current row of QTableWidget if I clicked on its child?

I have created a QTableWidget in which I've used setCellWidget(QWidget*). I've set QLineEdit in the cell widget. I've also created a delete button and clicking that button sends a signal to the function deleteRow. I've also used a function currentRow() to get the current row, but it returns -1 because of the QLineEdit. The code snippet is below.
void createTable() {
m_table = new QTableWidget(QDialog); //member variable
for (int i = 0; i < 3; i++)
{
QLineEdit *lineEdit = new QLineEdit(m_table);
m_table->setCellWidget(i, 0, lineEdit);
}
QPushButton *deleteBut = new QPushButton(QDiaolg);
connect(deleteBut, SIGNAL(clicked()), QDialog, SLOT(editRow()));
}
editRow() {
int row = m_table->currentRow(); // This gives -1
m_table->remove(row);
}
In above scenario I click in the QLineEdit and then click on the button delete. Please help me out with a solution.
Just tried it here, it seems that currentRow of the table returns -1 when clicking the button right after program start, and when first selecting a cell, then selecting the QLineEdit and then clicking the button, the correct row is returned.
I would do the following as a workaround: Save the row number in the QLineEdit, e.g. by using QObject::setProperty:
QLineEdit *lineEdit = new QLineEdit(m_table);
lineEdit->setProperty("row", i);
m_table->setCellWidget(i, 0, lineEdit);
Then, in the editRow handler, retrieve the property by asking the QTableWidget for its focused child:
int row = m_table->currentRow();
if (row == -1) {
if (QWidget* focused = m_table->focusWidget()) {
row = focused->property("row").toInt();
}
}
The accepted solution, as is, would not work if rows might get deleted while the program runs. Thus the approach would require to update all the properties. Can be done, if this is a rare operation.
I got away with an iteration approach:
for(unsigned int i = 0; i < table->rowCount(); ++i)
{
if(table->cellWidget(i, relevantColumn) == QObject::sender())
{
return i;
}
}
return -1;
Quick, dirty, but worked, and in my case more suitable, as rows got deleted often or changed their positions, only buttons in the widget were connected to the slot and the slot was never called directly. If these conditions are not met, further checks might get necessary (if(QObject::sender()) { /* */ }, ...).
Karsten's answer will work correctly only if QLineEdit's property is recalculated each time a row is deleted, which might be a lot of work. And Aconcagua's answer works only if the method is invoked via signal/slot mechanism. In my solution, I just calculate the position of the QlineEdit which has focus (assuming all table items were set with setCellWidget):
int getCurrentRow() {
for (int i=0; i<myTable->rowCount(); i++)
for (int j=0; j<myTable->columnCount(); j++) {
if (myTable->cellWidget(i,j) == myTable->focusWidget()) {
return i;
}
}
return -1;
}

QProgressDialog bar resets to zero when completes

I am using QProgressDialog which obviously just shows progress and it increments along the way. When it reaches 100%, the progress bar on it resets to zero instead of showing 100% there after.
progress is member variable of the class.
QSharedPointer<QProgressDialog> progress;
It is used in on_clicked event. Note I am using Sleep() for simulation, I know it doesn't belong there. The problem is when it reaches 100%, the progress bar shows zero progress and I want to stick to 100%.
void MainWindow::on_pushButtonConvert_clicked()
{
int numFiles = 10;
progress = (QSharedPointer<QProgressDialog>) new QProgressDialog("Copying files...", "Abort Copy", 0, numFiles, this);
progress->setWindowModality(Qt::WindowModal);
progress->setAutoClose( false );
for (int i = 0; i < numFiles; i++) {
progress->setValue(i);
if (progress->wasCanceled())
break;
//... copy one file
Sleep(500);
}
progress->setValue(numFiles);
}
I figured it out, I had to call:
progress->setAutoReset( false );