QProgressDialog bar resets to zero when completes - c++

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 );

Related

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.

Why is not Qt label refreshed?

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.

Invoke a method only once from a class

I have a shape class in which there is a method (hitTest(int,int)) that continuously checks if the mouse is inside its bounds or not. In another method, I keep on checking if the mouse has stayed there for more than 1 sec.
If it has, trigger a function (by notification/event) that runs animation
It it has not, then don't trigger the animation
If it has already triggered the animation and the animation is running but the mouse leaves the area during this, trigger an interrupt function (by notification/event)
//OnLoad _initHover = false;
void update() //called continously in the application per frame
{
if(hitTest(getMouseX(), getMouseY())){
if(!_initHover){
_initHover = true;
_hoverStartTime = getCurrentTime(); //start hover time
cout<<"Start hist test\n";
}
//If it has hovered over the video for 1.0 sec
if((ofGetElapsedTimef() - _hoverStartTime) > 1.0){
cout<<"Hitting continously for 1 sec\n";
notificationCenter->postNotification(new AnimationStartNotification);
}
}
else{
_initHover = false;
notificationCenter->postNotification(new AnimationInterruptNotification);
}
}
The above code runs fine but there's a logical issue I am facing while trying to use. There are multiple instances of the above Shape class and each class consequently has their update() method as well. The mouse cursor has which has animationStarthandler and animationStophandlers is a single class in the whole application.
Issue 1: So, even when one of the shape just notifies the animationStarthandler to fire, the other shape classes on which hit test is false set the animation to interrupt and the animation does not run.
Issue 2: When the hit test succeeds and the cursor has been in the area for more than 1 sec, the hit test will keep on sending the notification to start the animation (anim's duration 1.5 sec approx.) How do I restrict the hit test to fire the animation only once and keep on firing the same animation again and again?
If in the main method of my application, I directly try to fire the animation by calling the method playAnimation in the pointer class, I get the required result. But I want to give this hover timing and animation functionality to the ShapeClass itself. Any suggestions?
I think that you should consider adding a new boolean, which holds the information of the triggering of the animation (called in the code sample _animationTriggered). This prevents shapes that have not triggered the animation to stop it and the animation that triggered it to make it several times.
if(hitTest(getMouseX(), getMouseY()))
{
if(!_initHover)
{
_initHover = true;
_hoverStartTime = getCurrentTime();
cout<<"Start hist test\n";
}
if((ofGetElapsedTimef() - _hoverStartTime) > 1.0)
{
if (!_animationTriggered)
{
cout<<"Hitting continously for 1 sec\n";
notificationCenter->postNotification(new AnimationStartNotification);
_animationTriggered = true;
}
}
}
else
{
if ( _animationTriggered )
{
_initHover = false;
notificationCenter->postNotification(new AnimationInterruptNotification);
_animationTriggered = false;
}
}
Don't forget to initialie this new boolean in the same place as _initHover

Thread loading images faster until they disapear in Studio

I am pretty new to visual studio. I am trying to load images and display it on a UI with two buttons start and stop. Every time I complete my count of total frames the next time I press start my images load more faster than before and in the end images the speed is so fast that they just disappear or appear as black. The thread speed or loading of images time increases as with each cycle. How can I limit this.. or put a time interval between showing two images. Thank you.
there code is as follows :
void CmirrorImageDlg::OnBnClickedStart()
{
m_play = TRUE;
CString num = NULL;
num.Format(_T("Pause"));
m_start.EnableWindow(false);
m_stop.EnableWindow(true);
m_stop.SetWindowTextW(num);
m_pThread = AfxBeginThread(Operate, (LPVOID)this, THREAD_PRIORITY_NORMAL);
}
void CmirrorImageDlg::OnBnClickedStop()
{
m_play = FALSE;
CString num = NULL;
num.Format(_T("Resume"));
m_start.EnableWindow(true);
m_stop.EnableWindow(false);
m_start.SetWindowTextW(num);
m_count = 0;
//m_pThread = AfxBeginThread(Operate, (LPVOID)this, THREAD_PRIORITY_ABOVE_NORMAL);
// TODO: Add your control notification handler code here
}
UINT CmirrorImageDlg::Operate(LPVOID param)
{
CmirrorImageDlg* pDlg = (CmirrorImageDlg*) param;
CString test;
while ( pDlg->m_play && pDlg->m_count < TOTAL_FRAME_NUMBER)
{
test.Format(_T("images/%.4d.BMP"),pDlg->m_count);
pDlg->hbitmap = (HBITMAP)LoadImage(NULL,test,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
pDlg->inv_hbitmap = pDlg->GetInvertedBitmap(pDlg->hbitmap, TRUE);
pDlg->inv_hbitmap_1 = pDlg->GetInvertedBitmap(pDlg->hbitmap , FALSE);
//CBitmap map = LoadBitmapW(test);
//filePath = IDB_BITMAP1;
//filePath++;
//IDB_PICTURE2.LoadBitmapW(filePath);
pDlg->m_picture.SetBitmap(pDlg->hbitmap);
pDlg->m_picture_2.SetBitmap(pDlg->inv_hbitmap);
pDlg->m_picture_3.SetBitmap(pDlg->inv_hbitmap_1);
CString num = NULL;
num.Format(_T("%d"),pDlg->m_count);
pDlg->m_label.SetWindowTextW(num);
// TODO: Add your control notification handler code here
pDlg->m_count++;
}
if(pDlg->m_count >= TOTAL_FRAME_NUMBER)
{
CString num = NULL;
num.Format(_T("%d"),0);
pDlg->m_count = 0;
pDlg->m_play= false;
pDlg->m_label.SetWindowTextW(num);
pDlg->m_picture.SetBitmap(NULL);
pDlg->m_picture_2.SetBitmap(NULL);
pDlg->m_picture_3.SetBitmap(NULL);
CString num1 = NULL;
num.Format(_T("Stop"));
pDlg->m_start.EnableWindow(true);
pDlg->m_stop.EnableWindow(false);
pDlg->m_stop.SetWindowTextW(num);
num.Format(_T("Start"));
pDlg->m_start.SetWindowTextW(num);
//pDlg->m_pThread->SuspendThread();
}
return 0;
}
A reasonably simple option:
Set a WM_TIMER for your dialog to fire every 40 milliseconds (if you want 25 frames per second). If not playing, the timer handler will do nothing.
When the user hits play, you'll load the first image, and then set the 'playing' flag. Inside the timer handler, it will take the currently loaded image and blit it to the screen, then load the next image and return. When there are no more images in the sequence you clear your 'playing' flag.
Because you always display the current image when the timer fires, it doesn't matter how long it takes to load the next image (as long as you can do it faster than the playback).