C++ Break a loop from another function - c++

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.

Related

Is there a better way to connect an array of QPushButton's to a slot so each button has a unique identifier when pushed?

I am creating a checkers board as part of a university assignment. There has been very little teaching around the GUI side of things. So I may have overcomplicated this project a bit and become stuck while trying to access the clicks from the board GUI.
I have read about lambda's in the documentation https://doc.qt.io/qt-5/qsignalmapper.html for signal mappers, but, I am not sure if it is right for the application I am coding. I have tried to implement the signal mapping so that when a particular button is pushed on the checker's board, the slot can identify it with its unique signal.
If there is a better way, or if lambdas are the best way, could you please explain how to implement this?
At the moment, my current code can't access the clicks made. I also get an error for the connect() function:
no known conversion from 'void QAbstractButton::* (bool)' to 'const char*' for the second argument.
connect(&checkers[i], &QPushButton::clicked, signalMapper, &QSignalMapper::map);
However, this compiles but does not work:
connect(&checkers[i], SIGNAL(clicked), signalMapper, SLOT(map));
Below is more complete code for greater context.
Constructor and board set function:
// Constructor
GameWindow::GameWindow(QWidget *parent):
QDialog(parent), ui(new Ui::GameWindow)
{
ui->setupUi(this);
BoardSetup(4);
}
/* Sets up the initial gameboard display with the other game information displays also being
* initialised. The game board is an array of QpushButtons that have there Icon
* changed according to where the pieces are on the board.
* Called by the GameWindow Constructor*/
void GameWindow::BoardSetup(int size)
{
{
QSignalMapper *signalMapper = new QSignalMapper(this->checkers);
connect(signalMapper, &QSignalMapper::mappedInt, this, &GameWindow::SquareClicked );
bool col=1;
for (int j=0;j<size;j++)
{
col=!col;
for(int i=0; i<size; i++)
{
if(col == 1) // logically check to see what Color the square needs to be.
{
checkers[i+j*size].setGeometry(QRect(width/size*j,width/size*i,width/size,width/size));
checkers[i+j*size].setFixedSize(QSize(width/size,width/size));
checkers[i+j*size].setStyleSheet("background-color: black");
}
else if(col==0)
{
checkers[i+j*size].setGeometry(QRect(width/size*j,width/size*i,width/size,width/size));
checkers[i+j*size].setFixedSize(QSize(width/size,width/size));
checkers[i+j*size].setStyleSheet("background-color: white");
}
col=!col;
signalMapper->setMapping(&checkers[i], i);
connect(&checkers[i], &QPushButton::clicked, signalMapper, &QSignalMapper::map);
}
}
Grid = new QGridLayout;
Grid->setGeometry(QRect(0,0,width,width));
for(int j=0; j<size; j++)
{
for(int i=0; i<size; i++)
{
Grid->addWidget(&checkers[i+j*size], j, i, Qt::AlignmentFlag::AlignCenter);
}
}
GridGroup = new QGroupBox();
GridGroup->setLayout(Grid);
ui->BoardGrid->addWidget(GridGroup);
ui->BoardGrid->setGeometry(QRect(0,0,width,width));
ui->BoardGrid->addWidget(GridGroup);
setWindowTitle("Checkers Game");
ui->P1PieceCount->setFontPointSize(40);
ui->P1PieceCount->setAlignment(Qt::AlignCenter);
ui->P2PieceCount->setFontPointSize(40);
ui->P2PieceCount->setAlignment(Qt::AlignCenter);
ui->TurnCount->setFontPointSize(40);
ui->TurnCount->setAlignment(Qt::AlignCenter);
ui->Player2Name->setAlignment(Qt::AlignCenter);
CheckersBoard(size);
SetInitialDisplay(size);
}
return;
}
How I am trying to access the clicks.
GetClicks() and WaitForClick() functions:
/* This function will get the clicks that are required to make the human move.
* Called by GameWindow::HumanMove */
void GameWindow::GetClicks()
{
connect(this, &GameWindow::SquareClicked, this, &GameWindow::WaitForClick);
}
/* This function will get the clicks that are required to make the human move.
* Called by GameWindow::GetClicks via signal SquareClicked */
void GameWindow::WaitForClick(int square)
{
firstclick = !firstclick;
unsigned int test=square;
if (firstclick ==0 && test != StartPos)
{
StartPos = square;
checkers[square].setChecked(1);
}
else if (firstclick == 1 && test == StartPos)
{
checkers[square].setChecked(0);
}
else if (firstclick==1 && test!= StartPos)
{
EndPos = square;
}
}
The StartPos and EndPos clicked are used in a possible move function to validate the move. It is then updated to the GUI.
Any help on how best to implement this signal and slot mechanism would be greatly appreciated.
Have a look at Qt calculator example?
https://doc.qt.io/qt-5/qtwidgets-widgets-calculator-example.html
you can connect multiple buttons to the same slot.
in slot, find out which button sent the signal using QObject::sender().
Once have the button, extract the position using MyButton::Pos().
(MyButton is the widget used for each of the board button)

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

tbb concurrent_bounded_queue multiple threads access

I have the following code:
tbb::concurrent_bounded_queue<Image> camera_queue_;
camera_queue_.set_capacity(1);
struct Image
{
int hour_;
int minute_;
int second_;
int msec_;
QImage image_;
Image(){hour_ = -1; minute_ = -1; second_ = -1; msec_ = -1; image_ = QImage();}
Image& operator=(Image const& copy)
{
this->hour_ = copy.hour_;
this->minute_ = copy.minute_;
this->second_ = copy.second_;
this->msec_ = copy.msec_;
this->image_ = copy.image_;
return *this;
}
};
In a Qt Thread :
ThreadA:
tbb::concurrent_bounded_queue<Image> image_queue_;
image_queue_.set_capacity(1);
Image cur_image_;
void Worker::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.pop(cur_image_);
image_queue_.push(cur_image_);
}
emit imageReady();
}
emit finished();
}
Image Worker::getCurrentImage()
{
Image tmp_image;
image_queue_.pop(tmp_image);
return tmp_image;
}
In Another Thread:
ThreadB:
Producer::Producer(){
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Producer::operate, work_, &Worker::process);
QObject::connect(work_, &Worker::imageReady, this, &Producer::displayImage);
QObject::connect(this, &Producer::stopDecode, work_, &Worker::stop);
workerThread_->start();
emit operate();
}
void Producer::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.push(GetImage());
}
}
}
void Producer::displayImage()
{
Image tmp = std::move(work_->getCurrentImage());
widget_->showImage(tmp.image_);
}
However, In main thread, I have a function that enables user to click a button to get current image:
bool Producer::SaveImage()
{
Image img = std::move(work_->getCurrentImage());
std::string fileName = std::to_string(img.hour_) + "-" + std::to_string(img.minute_) + "-" + std::to_string(img.second_) + "-" + std::to_string(img.msec_/1000) + ".jpg";
std::string outFileName = folder + "/" + fileName;
return img.image_.save(QString::fromStdString(outFileName));
}
The problem is:
When user does not click the button to invoke Producer::SaveImage(), the Image Decoding and Showing runs smoothly. But when user invoke Producer::SaveImage(), the whole program will get stuck (Caton phenomenon ?). The GUI response becomes not that smooth. The more user invokes SaveImage, the slower the GUI response becomes.
Can anyone help to explain why ? Is there a way to solve that ?
Why do you want to use concurrent queue? It looks like there is a syncronization mechanism in place and you rely mostly on it instead of using concurrent_queue for synchronisation and communication as it is supposed for.
The issue is that when you set capacity = 1, both operations of concurrent_bounded_queue will block until there is enough space of items in the queue. E.g. if the queue contains an item already, the push operation will block. And since you control your threads with another notification mechanism, you might catch a deadlock.
In particular, try to swap the operations like below:
camera_queue_.pop(cur_image_);
emit imageReady();
image_queue_.push(cur_image_);
This should prepare the thread which receives images (if I understand this correctly) and it will block on its image_queue_.pop() method, then this thread will put new image and unblocks the recipient. There might be other issues similar to this, so, please rethink all of your synchronization.

SFML help (Using the Sleep function in <Windows.h>)

I need some help with my SFML/C++ code in Visual C++ 2010 Express Edition. I am trying to get my text (livesLeft) to "You Win!" Before the program sleeps. This is making the player know that he won. But instead, the program goes right to sleep and changes the text right as it is closing, so you only see it change for a few milliseconds. I can't even read it.
bool play = true;
bool win = false;
bool touchFinish = false;
int lives = 3;
sf::Font arial;
if(arial.loadFromFile("Fonts/arial.ttf") == 0)
{
return 1;
}
sf::Text livesLeft;
livesLeft.setFont(arial);
livesLeft.setCharacterSize(30);
livesLeft.setString("Lives: ");
livesLeft.setPosition(0, 0);
livesLeft.setColor(sf::Color::White);
sf::RectangleShape finish;
finish.setSize(sf::Vector2f(200, 600));
finish.setPosition(700, 0);
finish.setFillColor(sf::Color::Green);
Those are my variables in use and this is my code where I'm trying to change the text:
if(player.getGlobalBounds().intersects(finish.getGlobalBounds()))
{
livesLeft.setString("You Win!");
touchFinish = true;
}
if(touchFinish)
{
win = true;
}
if(win)
{
Sleep(5000);
play = false;
}
I also forgot to add in that I do have the rendering at the end:
window.clear();
window.draw(livesLeft);
window.draw(finish);
window.draw(player);
window.draw(obs1);
window.draw(obs2);
window.draw(obs3);
window.draw(obs4);
window.draw(obs5);
window.draw(obs6);
window.draw(obs7);
window.draw(obs8);
window.draw(obs9);
window.draw(obs10);
window.draw(obs11);
window.draw(obs12);
window.draw(obs13);
window.display();
This is most likely due to the fact that you aren't calling the display method for you window. Telling the text to change simply prepares the next framebuffer to display the new text, but because you never tell the window to update its display before sleeping, it never displays the new framebuffer.
Here is a quick example of a simple program using SFML. Notice the window.display() method at the end of the main game loop.
You effectively want to be doing this:
if(win)
{
window.display();
Sleep(5000);
play = false;
}
The reason why you need to update the display before hand again is because Sleep(5000); blocks the thread, essentially meaning that it sits at that call for 5000ms. Also, if you want to keep the previous items on the screen, you'll want to redraw those before window.display(); as well, since these won't be in the next framebuffer.

qslider sliderReleased value

I m trying to make a media player . The time status of the track is shown using QSlider. Track seek should happen when the user releases the slider somewhere on the QSlider. I had a look on the list of QSlider signals. The one that seems to fit is sliderReleased() which happens when the user releases the slider, but it does not get the latest value of where slider is. So to get sliders latest value I used sliderMoved() and stored reference.
Signal and slot connection
connect(this->ui->songProgress, SIGNAL(sliderMoved(int)), this,
SLOT(searchSliderMoved(int)));
connect(this->ui->songProgress, SIGNAL(sliderReleased()), this,
SLOT(searchSliderReleased()));
Functions
void MainWindow::searchSliderMoved(int search_percent)
{
value=this->ui->songProgress->value();
std::cout<<"Moved: Slider value"<<value<<std::endl;
}
Now I am Using the "value" variable inside searchSliderReleased for seeking
void MainWindow::searchSliderReleased()
{
std::cout<<"Released: Slider value"<<value<<std::endl;
emit search(value);//seek the track to location value
}
But the problem with above technique is that sliderMoved signal does not give the value where the slider is dropped but it give the location from where the slider was moved. How do I obtain the value where the slider was dropped?
You can use the valueChanged(int) signal of the slider, but set the flag
ui->horizontalSlider->setTracking(false);
Just finished building a DiectShow media player with QSlider here at work. Here's a few slot snippets on how I did it.
Widget::sliderPressed()
{
if( isPlaying() )
{
m_wasPlaying = true;
pause();
}
else
{
m_wasPlaying = false;
}
// Set a flag to denote slider drag is starting.
m_isSliderPressed = true;
}
Widget::sliderReleased()
{
if( m_wasPlaying )
{
play();
}
m_wasPlaying = false;
m_isSliderPressed = false;
}
Widget::valueChanged( int value )
{
if( m_isSliderPressed )
{
// Do seeking code here
}
else
{
// Sometime Qt when the user clicks the slider
// (no drag, just a click) Qt will signals
// pressed, released, valueChanged.
// So lets handle that here.
}
}
Had the same problem and calling sliderPosition() instead of value() directly when handling sliderReleased() worked for me.
I prefer to save the last value set by our app, like this pseudocode:
// Save our last value
lastSoftwareVal = 0;
// Set value from program
slider_set_value(value){
lastSoftwareVal = value;
slider.setValue(value)
}
// Slider on change callback
slider_on_change(value){
if(value == lastSoftwareVal){
// Value was changed by our app
return false;
}
// User has changed the value, do something
}