How to initializa the operation of QProgressBar, I already declare her maximum, minimum, range and values.
I want to assimilate the progress of QProgressBar with the "sleep_for" function.
Current code:
void MainPrograma::on_pushCorre_clicked()
{
QPlainTextEdit *printNaTela = ui->plainTextEdit;
printNaTela->moveCursor(QTextCursor::End);
printNaTela->insertPlainText("corrida iniciada\n");
QProgressBar *progresso = ui->progressBar;
progresso->setMaximum(100);
progresso->setMinimum(0);
progresso->setRange(0, 100);
progresso->setValue(0);
progresso->show();
WORD wEndereco = 53606;
WORD wValor = 01;
WORD ifValor = 0;
EscreveVariavel(wEndereco, wValor);
//How to assimilate QProgressBar to this function:
std::this_thread::sleep_for(std::chrono::milliseconds(15000));
//StackOverFlow help me please
EscreveVariavel(wEndereco, ifValor);
use a QTimer
and in the slot update the value of the progressbar
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
t = new QTimer(this);
t->setSingleShot(false);
c = 0;
connect(t, &QTimer::timeout, [this]()
{ c++;
if (c==100) {
c=0;
}
qDebug() << "T...";
ui->progressBar->setValue(c);
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
t->start(100);
}
I'm not sure about your intentions with such sleep: are you simulating long wait? do you have feedback about progress during such process? Is it a blocking task (as in the example) or it will be asynchronous?
As a direct answer (fixed waiting time, blocking) I think it is enough to make a loop with smaller sleeps, like:
EscreveVariavel(wEndereco, wValor);
for (int ii = 0; ii < 100; ++ii) {
progresso->setValue(ii);
qApp->processEvents(); // necessary to update the UI
std::this_thread::sleep_for(std::chrono::milliseconds(150));
}
EscreveVariavel(wEndereco, ifValor);
Note that you may end waiting a bit more time due to thread scheduling and UI refresh.
For an async task you should pass the progress bar to be updated, or some kind of callback that does such update. Keep in mind that UI can only be refreshed from main thread.
Related
I want to repeat the action performed in a SLOT until a qpushbutton is down.So I've done the following connections:
connect(ui->button,SIGNAL(pressed()),this,SLOT(button_hold()));
connect(ui->button,SIGNAL(released()),this,SLOT(button_released()));
and I've implemented the SLOTS in the following way
void My_class::button_hold(){
//CLASS ATTRIBUTE key_is_released , i
QThread::msleep(200);
int wait_lock = 500;
i++; //GOAL OF THE SLOT
QCoreApplication::processEvents(QEventLoop::AllEvents);
while(!key_is_released){
QThread::msleep(wait_lock);
i++;
cout<<i<<endl;
if(wait_lock > 50) wait_lock -= 50;
QCoreApplication::processEvents(QEventLoop::AllEvents);
}
key_is_released = false;
}
void My_class::button_released(){
key_is_released = true;
}
The goal is to repeat the action in the button_hod() Slot (In the example it's to increase i) even more quickly decreasing wait_lock and keeping the button down.The issue is that if i click button two times or more in a quick way the loop never ends,like if processEvents() would not work.The firts msleep is used in order to do not enter the loop (i is increased just once)if the button is just clicked and it is not keep down too long.If i do not click quickly but I keep down the button the method works well.
What am I doing wrong?
Reentering the event loop is a bad idea, and leads to spaghetti code.
Qt provides a wonderful state machine system with UML semantics. It's often best to model the system you're designing as a state machine. Here, there are two states: an idle state, and an active state: the value is incremented periodically while active.
The state machine decouples the Ui particulars (a button) from the core functionality: that of a periodically incrementing value.
Ideally, you would also factor out the state machine and the value to a controller class, and only connect the controller and the Ui from main() or a similar function.
// https://github.com/KubaO/stackoverflown/tree/master/questions/48165864
#include <QtWidgets>
#include <type_traits>
class Ui : public QWidget {
Q_OBJECT
int m_value = -1;
QStateMachine m_machine{this};
QState m_idle{&m_machine}, m_active{&m_machine};
QVBoxLayout m_layout{this};
QPushButton m_button{"Hold Me"};
QLabel m_indicator;
QTimer m_timer;
void setValue(int val) {
if (m_value == val) return;
m_value = val;
m_indicator.setNum(m_value);
}
void step() {
if (m_value < std::numeric_limits<decltype(m_value)>::max())
setValue(m_value + 1);
}
public:
Ui(QWidget * parent = {}) : QWidget(parent) {
m_layout.addWidget(&m_button);
m_layout.addWidget(&m_indicator);
m_machine.setInitialState(&m_idle);
m_idle.addTransition(&m_button, &QPushButton::pressed, &m_active);
m_active.addTransition(&m_button, &QPushButton::released, &m_idle);
m_machine.start();
m_timer.setInterval(200);
connect(&m_timer, &QTimer::timeout, this, &Ui::step);
connect(&m_active, &QState::entered, [this]{
step();
m_timer.start();
});
connect(&m_active, &QState::exited, &m_timer, &QTimer::stop);
setValue(0);
}
};
int main(int argc, char ** argv) {
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"
The problem was that clicking fast let say 10 times ,the release slot was called yet 10 times before entering in the loop, so key_is_released would be never updated again to true.Instead the button_hold slot was not yet been called for ten times.So adding a counter, increasing it in the button_hold slot and decreasing it in the release slot and adding a second condition counter>0 besides !key_is_released in the while loop fixes all.(It enters the loop only if not all the release slot have been run)
I am not a multithreading expert.
I know that the GUI should be managed by the main thread, however I'd need 2 things to be done by the mainthread simultanuously.
The situation is the following:
The user clicks on a pushbutton (to take a selfie), a count down timer starts (3 seconds). The user can see in a QLabel the numbers 3-2 changing every second. Meanwhile the user can see the camera data in another QLabel of the same window.
In other words the mainthread should do 2 things:
update QLabel1 to always show the timer
update QLabel2 with the live videostram from the camera
I am having some difficulties to achieve this. Could someone help me out?
I am not necessarily asking for an easy trick/work around. I'd like to use multithreading that way I can improve my knowledge about this technique and not just use a one time easy/quick workaround...
Thank you
My current code:
What I tried: when the user clicks the button called btnTakeSnap a new thread is started and in that thread the timer starts counting down and updating the labelTimeSnap (this is a QLabel in which I load "fancy" images with numbers 3-0). Once the timer reaches 0 a picture is taken.
But I don't see my QLabel being updated with the timer. It is only when 0 is reached that suddenly the number 0 gets displayed in my QLabel.
Any advice?
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
snapIndex=1;
QString fileName = "../somePicture.jpg";
QImage imageFrame;
connect(ui->btnTakeSnap, SIGNAL(clicked()), this, SLOT(startTimerWorker()) );
}
void MainWindow::startTimerWorker()
{
timerSnapThread = new QThread(this);
MainWindow *workerTimerSnap = new MainWindow();
connect(timerSnapThread, &QThread::started, workerTimerSnap, &MainWindow::updateTimer);
workerTimerSnap->moveToThread(timerSnapThread);
timerSnapThread->start();
}
void MainWindow::updateTimer()
{
int selectedTimer;
if(ui->rdBtntimer1s->isChecked())
{selectedTimer = 1000;}
if(ui->rdBtntimer3s->isChecked())
{selectedTimer = 3000;}
QString filename;
QImage image;
//timer
if(selectedTimer == 3000) //3 seconds
{
QElapsedTimer t;
t.start();
while (t.elapsed() < selectedTimer)
{
if(t.elapsed()==0)
{
filename = "../../testImages/timer3.png";qDebug()<<"3";
image.load(filename);
image= image.scaled(ui->labelTimeSnap->width(), ui->labelTimeSnap->height(),Qt::KeepAspectRatio);
ui->labelTimeSnap->setPixmap(QPixmap::fromImage(image));
}
if(t.elapsed()==1000)
{
filename = "../../testImages/timer2.png";qDebug()<<"2";
image.load(filename);
image= image.scaled(ui->labelTimeSnap->width(), ui->labelTimeSnap->height(),Qt::KeepAspectRatio);
ui->labelTimeSnap->setPixmap(QPixmap::fromImage(image));
}
if(t.elapsed()==2000)
{
filename = "../../testImages/timer1.png";qDebug()<<"1";
image.load(filename);
image= image.scaled(ui->labelTimeSnap->width(), ui->labelTimeSnap->height(),Qt::KeepAspectRatio);
ui->labelTimeSnap->setPixmap(QPixmap::fromImage(image));
}
}
takeSnap();
}
if(selectedTimer == 1000)
{
QElapsedTimer t;
t.start();
while (t.elapsed() < selectedTimer)
{
if(t.elapsed()==0)
{
filename = "../../testImages/timer1.png";
qDebug()<<"1";
image.load(filename);
image= image.scaled(ui->labelTimeSnap->width(), ui->labelTimeSnap->height(),Qt::KeepAspectRatio);
ui->labelTimeSnap->setPixmap(QPixmap::fromImage(image));
}
if(t.elapsed()==1000)
{
filename = "../../testImages/timer1.png";
qDebug()<<"0";
image.load(filename);
image= image.scaled(ui->labelTimeSnap->width(), ui->labelTimeSnap->height(),Qt::KeepAspectRatio);
ui->labelTimeSnap->setPixmap(QPixmap::fromImage(image));
}
}
takeSnap();
}
}
void MainWindow::takeSnap()
{
static int i=0;
cv::VideoCapture cap(CV_CAP_ANY);
cv::Mat imgFrame;
cap >> imgFrame;
//BGR-> RGB
cv::cvtColor(imgFrame, imgFrame, CV_BGR2RGB);
//Mat -> QPixMap
QImage img;
img = QImage((uchar*)imgFrame.data, imgFrame.cols, imgFrame.rows, QImage::Format_RGB888);
QPixmap pixmap = QPixmap::fromImage(img);
int w = ui->labelSnap1->width();
int h = ui->labelSnap1->height();
if(i==0)
{ui->labelSnap1->setPixmap(pixmap.scaled(w,h,Qt::KeepAspectRatio));}
if(i==1)
{ui->labelSnap2->setPixmap(pixmap.scaled(w,h,Qt::KeepAspectRatio));}
if(i==2)
{ui->labelSnap3->setPixmap(pixmap.scaled(w,h,Qt::KeepAspectRatio));}
i++;
if(i==3){i=0;}
showNextSnap();
}
You can use signals to communicate threads together.
define a signal in your second thread like this:
signals:
void changeLabelOnMain(QString text);
emit your signal in second thread:
emit changeLabelOnMain("some text");
connect your signal to a slot in your main :
SecondClassName secondObject= new SecondClassName();
connect(secondObject, &SecondClassName::changeLabelOnMain, this, &MainClassName::YourSlotName);
this is a simple example of making threads communicate together.
I have a function that should send data to a raspberry pi for a given period of time depending on the parameter.
//headerfiles
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QUdpSocket udpSocket;
// movement Timer
QTimer* movementTimer;
private slots:
void sendDatagram(); // Sends to the RaspberryPi
void processFrameAndUpdateGUI();
void turnLeft(double time);
void turnRight(double time);
void goStraight(double time);
void MovRobot();
};
// MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
movementTimer = new QTimer(this);
//send datagram sends data to tehe PI.
connect(movementTimer, SIGNAL(timeout()), this, SLOT(sendDatagram()));
MovRobot(); // MovRobot() function
}
// controls robot to turn left for specified time
void TurnLeft(double time)
{
s = 1; // sets the value to be sent to the PI.
movementTimer->setInterval(time);
movementTimer->setSingleShot(true);
movementTimer->start();
}
//sendDatagram() slot
void MainWindow::sendDatagram() {
QString datagramOutput = "start," +
QString::number(w) + ',' + QString::number(a) + ',' +
QString::number(s) + ',' + QString::number(d) + ',' +
QString::number(ui->motorSpeedSlider->value()) + ',' +
QString::number(dispenserSignal);
datagramOutput += ",end";
QByteArray datagram;
QDataStream out(&datagram,QIODevice::WriteOnly);
out << datagramOutput;
udpSocket.writeDatagram(datagram,QHostAddress("192.168.0.104"),12345);
}
//MovRobot Function;
void MainWindow :: MovRobot() {
if (ui->pushButton_3->isChecked()) {
MapArea(); // This function maps the area....
// final_plan is a vector<Point2i> that stores positions on the map for
// the robot to move to
for (int i = 0; i < final_plan.size(); i++) {
for (int j = final_plan.size() - 1; j>=0; j--) {
do {
Mat src;
bool bsuccess = cap.read(src);
if (!bsuccess) {
ui->label_34->setText("Status: Can't read frame.");
ui->pushButton_3->setChecked(0);
}
GetRobotPosition(src);
// AngleToGoal calculates the angle of the robot relative
// to the final goal.
double tempAngle = AngletoGoal(final_plan[i][j]);
if (tempAngle>=0) {
turnLeft(TimeToTurn(tempAngle));
}
else {
turnRight(TimeToTurn(tempAngle));
}
double tempDistance = DistancetoGoal(final_plan[i][j]);
goStraight(tempDistance);
} while (DistancetoGoal(final_plan[i][j])<20);
}
}
}
}
The timer should only send the data over to the PI for the duration it in time sendDatagram is the function that sends the data over to the Pi. Is there anything I'm missing here. The timer doesn't start inside the TurnLeft() function and doesn't run at all. Am I going about this wrong?
EDIT: 13/03/2016:
My apologies for the late reply. I've been quite sick the past few days. I've added the relevant parts of the code. MovRobot() is the main function responsible for the movement and this is called in the constructor for MainWindow. I have debugged and stepped through the program and yes, TurnLeft() is called. However, the sendDatagram() slot doesn't actually send anything in the function. To confirm sendDatagram() was actually working, I used another timer to continuously send information to the PI on the robot to control the arm.
// Header File
QTimer* tmrTimer;
private slot:
void processFrameAndUpdateGUI();
// MainWindow Constructor:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(tmrTimer, SIGNAL(timeout()), this, SLOT(processFrameAndUpdateGUI()));
tmrTimer->start(10);
}
void MainWindow::processFrameAndUpdateGUI() {
sendDatagram();
}
The sendDatagram() slot is pretty much the same with the exception of me changing what values are being sent to the PI and this seems to work perfectly.
However, my original problem is, I would like to send the data to the robot with for a specified amount of time as that makes the robot turn x degrees. This is why I've made movementTimer() single shot.
Stepping through my program, I know this line is called within my TurnLeft function.
movementTimer->start();
but the sendDatagram() slot itself doesn't actually send anything to the PI.
Based on your code. The program never called TurnLeft(). So the timer never started. It will be better to start the timer in constrator instead.
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(MySlot()));
timer->start(1000);
I would try setting the timer to start in the constructor.
Almost done with that application I've been working on BUT, now I have one more problem. I created a QProgressBar and connected it to a QTimer. It goes up one percent per second but surpasses the actual progress. I have yet to program in the former however I set up the timer to go up one every second. Here is my problem the progress bar goes up to one percent then stops. It hits the if statement every second I know that, but it doesn’t go any higher then 1%.
Edit:
Sorry meant to add the code.
#include "thiwindow.h"
#include "ui_thiwindow.h"
#include <QProcess>
#include <fstream>
#include <sstream>
int ModeI;
ThiWindow::ThiWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ThiWindow)
{
ui->setupUi(this);
std::ifstream ModeF;
ModeF.open ("/tmp/Mode.txt");
getline (ModeF,ModeS);
std::stringstream ss(ModeS);
ss >> ModeI;
ModeF.close();
SecCount = new QTimer(this);
Aproc = new QProcess;
proc = new QProcess;
connect(SecCount, SIGNAL(timeout()), this, SLOT(UpdateProcess()));
connect(Aproc, SIGNAL(readyRead()), this, SLOT(updateText()));
connect(proc, SIGNAL(readyRead()), this, SLOT(updateText()));
SecCount->start(1000);
if (ModeI==1)
Aproc->start("gksudo /home/brooks/Documents/Programming/AutoClean/LPB.pyc");
else
proc->start("/home/brooks/Documents/Programming/AutoClean/LPB.pyc");
ui->progressBar->setValue(0);
}
ThiWindow::~ThiWindow()
{
delete ui;
}
void ThiWindow::updateText()
{
if (ModeI==1){
QString appendText(Aproc->readAll());
ui->textEdit->append(appendText);}
else{
QString appendText(proc->readAll());
ui->textEdit->append(appendText);}
}
void ThiWindow::UpdateProcess()
{
SecCount->start(1000);
int Count=0;
float Increments;
int Percent_limit;
if (ModeI==1){
Increments = 100/5;
Percent_limit = Increments;
if (Count<Percent_limit) {
Count += 1;
ui->progressBar->setValue(Count);
}
}
}
If you need more let me know.
Thanks,
Brooks Rady
You are always incrementing zero. int Count=0; This have to be removed from this function and moved for example to constructor where timer is started and declare it in header file ( shown in last two code snipets )
void ThiWindow::UpdateProcess()
{
SecCount->start(1000);
int Count=0; // Count is 0
float Increments;
int Percent_limit;
if (ModeI==1){
Increments = 100/5;
Percent_limit = Increments;
if (Count<Percent_limit) {
Count += 1; // Count is 0 + 1
ui->progressBar->setValue(Count); // progressBar is 1
}
}
}
You have to declare Count in header file. Count will be stored as long as ThiWindows exists. Not only for a few miliseconds in your example ( Count was destroyed when your UpdateProccess functions finish and then recreated again when it was called again )
class ThiWindow : public QMainWindow {
Q_OBJECT
public:
// whatever you have
private:
int Count;
}
Count should be initialized before Timer starts
SecCount = new QTimer(this);
Aproc = new QProcess;
proc = new QProcess;
connect(SecCount, SIGNAL(timeout()), this, SLOT(UpdateProcess()));
connect(Aproc, SIGNAL(readyRead()), this, SLOT(updateText()));
connect(proc, SIGNAL(readyRead()), this, SLOT(updateText()));
Count = 0; // << move Count variable here
SecCount->start(1000);
In my game I need a stopwatch to measure and show the elapsed time.
For this purpose I made a simple widget:
ZuulStopwatchWidget::ZuulStopwatchWidget(QWidget *parent) :
QWidget(parent)
{
num = new QLCDNumber(this); // create the display
num->setDigitCount(9);
time = new QTime();
time->setHMS(0,0,0,0); // set the time
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(showTime()));
i=0;
QString text = time->toString("hh:mm:ss");
num->display(text);
//num->setStyleSheet("* { background-color:rgb(199,147,88);color:rgb(255,255,255); padding: 7px}}");
num->setSegmentStyle(QLCDNumber::Flat); //filled flat outline
//setStyleSheet("* { background-color:rgb(236,219,187)}}");
layout = new QVBoxLayout(this);
layout->addWidget(num);
setMinimumHeight(70);
}
ZuulStopwatchWidget::~ZuulStopwatchWidget()
{
// No need to delete any object that has a parent which is properly deleted.
}
void ZuulStopwatchWidget::resetTime()
{
time->setHMS(0,0,0);
QString text = time->toString("hh:mm:ss");
num->display(text);
i=0;
stopTime();
}
void ZuulStopwatchWidget::startTime()
{
//flag=0;
timer->start(1);
}
void ZuulStopwatchWidget::stopTime()
{
timer->stop();
}
void ZuulStopwatchWidget::showTime()
{
QTime newtime;
//if(flag==1)
//i=i-1;
i=i+1;
newtime=time->addMSecs(i);
QString text = newtime.toString("mm:ss:zzz");
num->display(text);
}
But when I run my game the CPU usage is at about 13% on a 2,5Ghz i5. I know this is not problematic but it sure is ridiculous for a stupid clock.
Am I doing it completely wrong or is this common practice ?!
Many thanks in advance.
Start(1) sets the timer to trigger every millisecond
You then want to format a string and print it on the screen 16times faster than the screen is probably updating anyway