Insert text in while(1) loop to texteditor in QT - c++

Am trying to print "Some text" to QTextBrowser, continuously for "n" time. Where "n" is integer. For this I have used QTimer::SingleShot for timing. Once the timeout is triggered a FLAG is set to false and this "FLAG" is monitored in while loop to break when FLAG is false and it shall insert the text till FLAG is set to FALSE. Initial value for FLAG is true.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
FLAG = true;
}
void MainWindow::on_pushButton_clicked()
{
ui->pushButton->setEnabled(false);
RunTheTimer();
int counter = 0;
do
{
ui->textBrowser->insertPlainText(QString("Inside While loop %1 \n").arg(counter++));
counter++;
}while(FLAG);
FLAG = true;
}
void MainWindow::RunTheTimer()
{
ui->textBrowser-> insertPlainText("Timer Started");
QTimer::singleShot(60000, this, SLOT(Update()));// One Minute
}
void MainWindow::Update()
{
ui->textBrowser-> insertPlainText("Timeout signal triggered");
ui->pushButton->setEnabled(true);
FLAG = false;
}
MainWindow::~MainWindow()
{
delete ui;
}
Application is getting Hang, When I click Pushbutton.
After debugging I observed, timeout is not triggering once the execution is entered to while(1) loop and application is not able to insert any text inside while(1) loop. Why this behavior? What am I doing wrong?
Thanks.

You are not returning control to the event loop, many things in Qt are not designed to work without an event loop, Have a look at this page from the Qt wiki, in your case:
QTextBrowser won't be able to show the newly added text, since this requires the widget to be able to receive paint events (and this is impossible without an event loop).
The timer that sets your flag to false won't be able to fire, since your program is always busy executing your while loop, and it won't be able to do anything else (unless it gets out from that while loop and this is impossible if it does not set your flag to false. . .).
Instead of using an endless loop, if you want to execute something as repeatedly as possible, you can use a QTimer and set its interval property to 0, this is a special value that causes the timer to timeout as soon as the event loop finishes processing all events in the event queue.
Using the above approach instead of your endless loop, you can use another timer to stop the above timer after a specific amount of time, and you don't have to worry about events not arriving and timers not firing since the event loop is always executing now.
Here is a possible implementation of the above approach:
#include <QtWidgets>
int main(int argc, char* argv[]){
QApplication a(argc, argv);
//set up GUI
QWidget widget;
QVBoxLayout layout(&widget);
QTextBrowser textBrowser;
QPushButton button("Add Text");
layout.addWidget(&textBrowser);
layout.addWidget(&button);
//timer with 0 interval instead of while loop
QTimer workTimer;
workTimer.setInterval(0);
int counter=0;
QObject::connect(&workTimer, &QTimer::timeout, [&]{
//add text to textBrowser whenever the workTimer fires
textBrowser.append(QStringLiteral("Additional Text %1").arg(counter++));
});
//when the button is clicked
QObject::connect(&button, &QPushButton::clicked, [&]{
//start work timer
workTimer.start();
button.setEnabled(false);
//stop work timer after 5 seconds
QTimer::singleShot(5000, [&]{
workTimer.stop();
button.setEnabled(true);
});
});
widget.show();
return a.exec();
}

Related

How to start a loop on a QThread

How can I start a loop on a different thread by pressing a QPushButton?
The main idea is, when pushButtonStart is clicked, start a loop on a QThread, and when pushButtonStop is clicked, stops the loop in the QThread.
The loop can be done by QTimer, for loop or while loop, but he needs a way to stop by pressing a button.
I have this code to create a new timer and set up a connection when it fires. This is in the Start code.
checkTimer = new QTimer(this);
connect( checkTimer, SIGNAL(timeout()), this, SLOT(checkTimerFired()) );
checkTimer->start(3000);
My "stop running" button sets programCreated to false, and checkTimerFired starts with this:
if (!programCreated) {
checkTimer->stop();
return;
}
That should be the Qt-specific things you need. The rest is simple C++.
To do the loop, I use QTimer, which run the slot pressButton multiple times until stop(); function of QTimer is executed. Of course, to use this method I have to recode the program, to improve the algorithm.
QTimer loop:
void MainPrograma::on_pushTurnOn_clicked()
{
tierLoop = new QTimer(this);
timerLoop->setInterval(5000);
timerLoop->setSingleShot(true);
connect(timerLoop, SIGNAL(timeout()), SLOT(on_pushTurnOn_clicked()));
QPushButton *ThirdButton = uimain->QPushButton_3;
Nmb2 = 2
Nmb4 = 4;
if(Busy==1){
ThirdButton->setEnabled(false);
}
if(Busy==0){
timerLoop->start(); //start the loop
StartFunction(Nmb2, Nmb4);
if(X==1){
ThirdButton->setEnabled(false);
}
if(X==0){
ThirdButton->setEnabled(true);
}
}
}
void MainPrograma::on_pushTurnOff_clicked()
{
timerLoop->stop(); //stop the loop
}
QTimer declaration on .h:
private:
QTimer *timerLoop;
I still do not understand how to use QThread... But the loop is already answered!

Change interval timeouts of QTimer with slider

timer = new QTimer(this);
timer->setInterval(50);
QPushButton *start = new QPushButton("Start/Stop", this);
start->setText("Start/Stop");
layout->addWidget(start);
connect(start, &QPushButton::clicked, this, [this]() {
if (!timer->isActive()) {
timer->start();
} else {
timer->stop();
}
});
connect(timer, &QTimer::timeout, this, [&slider, sliderDisplay]() {
//increment slider value until at max, then reset to min
}
How can I use another slider to change the interval of timeouts emitted by my QTimer? I've tried using setInterval but it seems that I can't change the interval once it is set. Is the best way to do this just delete this QTimer and create a new QTimer with the specified interval every time?
OP has claimed that
How can I use another slider to change the interval of timeouts emitted by my QTimer? I've tried using setInterval but it seems that I can't change the interval once it is set.
I wondered a bit about that claim because I could've sworn that you can change the interval at any time.
The only restriction, I would consider: the new set interval might not be considered before the next timeout happens. (Usually, I use single-shot timers or timers with small intervals so that you wouldn't notice the difference.)
While playing with my demo, I got the impression that a call of QTimer::setInterval() restarts the interval.
Unfortunately, the doc. of QTimer::setInterval() doesn't mention this behavior explicitly, except:
Setting the interval of an active timer changes its timerId().
Thanks to #Scopchanov who had the look into source code (I was too lazy to).
QTimer::setInterval():
void QTimer::setInterval(int msec)
{
inter = msec;
if (id != INV_TIMER) { // create new timer
QObject::killTimer(id); // restart timer
id = QObject::startTimer(msec, Qt::TimerType(type));
}
}
So, in fact, setting the interval while the timer is running, it kills the currently running timer and restarts a new one.
My MCVE for demonstration – testQTimer.cc:
// Qt header:
#include <QtWidgets>
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("Test QTimer");
QFormLayout qForm;
QSlider qSliderInterval(Qt::Horizontal);
qSliderInterval.setRange(100, 1000);
qForm.addRow("Interval: ", &qSliderInterval);
QSlider qSliderStep(Qt::Horizontal);
qSliderStep.setRange(1, 10);
qForm.addRow("Step: ", &qSliderStep);
QPushButton qBtnStartStop("Start / Stop");
qForm.addRow("Timer:", &qBtnStartStop);
QSlider qSliderAnim(Qt::Horizontal);
qForm.addRow("Animation:", &qSliderAnim);
qSliderAnim.setRange(0, 100);
qWinMain.setLayout(&qForm);
qWinMain.show();
// setup timer
QTimer qTimer;
qTimer.setInterval(qSliderInterval.value());
// install signal handlers
QObject::connect(&qSliderInterval, &QSlider::valueChanged,
[&](int value) { qTimer.setInterval(value); });
QObject::connect(&qBtnStartStop, &QPushButton::clicked,
[&]() {
if (qTimer.isActive()) qTimer.stop();
else qTimer.start();
});
QObject::connect(&qTimer, &QTimer::timeout,
[&]() {
int value = qSliderAnim.value() + qSliderStep.value();
if (value > qSliderAnim.maximum()) {
value -= qSliderAnim.maximum() - qSliderAnim.minimum();
}
qSliderAnim.setValue(value);
});
// runtime loop
return app.exec();
}
Output:
Qt Version: 5.13.0

How to close the programme when running a qtconcurrent from another thread in qt

I am running a programme that has multithreading . The programme firstly has a main / UI thread running in it. In this programme I have a worker and handler class.
The worker class is having a simulate function which simply generates the random number. The simulate function continuously generates the number without blocking any thread i.e. via Qtconcurrent.
From the main/UI thread I have put this worker class into new thread. The handler class is running in main /UI thread and is responsible to communicate with worker class running in other thread via signal slot.
So far everything is ok.
Problem starts when i try to close the programme by simply clicking on app cross button. The
programme sort of hangs it does not close. However when i dont put worker in another class and run worker class from same main /UI thread then there is no problem and programme exits with 0.
So my question is how to stop Qtconcurrent is another thread and finally close the another thread aswell.
Thank You.
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QThread l_newThread;
Worker* l_worker = new Worker();
handler * l_handler = new handler();
l_worker->moveToThread(&l_newThread);
QObject::connect(&l_newThread, &QThread::started, l_worker, &Worker::Init);
QObject::connect(l_handler,&handler::toStop_Signal,&l_newThread, &QThread::quit);
QObject::connect(l_worker, &Worker::toStop_Signal_Worker, l_handler,&handler::toStop_Slot);
QObject::connect(&app,&QCoreApplication::aboutToQuit, l_worker, &Worker::stop);
// QObject::connect(&app,&QCoreApplication::aboutToQuit, &l_newThread, &QThread::quit);
l_newThread.start();
// l_worker->Init();
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
int result = app.exec();
l_newThread.wait();
return result;
}
worker.cpp
#include "worker.h"
Worker::Worker(QObject *parent) : QObject(parent)
{
}
void Worker:: Init()
{
m_simulation = true;
simulate();
}
void Worker::simulate()
{
QtConcurrent::run([this]{
QRandomGenerator generator;
while (m_simulation) {
qint32 t = generator.bounded(0,100);
qDebug() << t;
qDebug() << "sleeping for 1 second";
QThread::sleep(1);
}
if (!m_simulation) {
qDebug() << "Killing the concurrent thread";
// QThread::currentThread()->exit();
emit toStop_Signal_Worker();
}
});
}
void Worker::stop()
{
m_simulation = false;
}
handler.cpp
#include "handler.h"
handler::handler(QObject *parent) : QObject(parent)
{
}
void handler::toStop_Slot()
{
emit toStop_Signal();
}
results
QML debugging is enabled. Only use this in a safe environment.
19
sleeping for 1 second
55
sleeping for 1 second
70
sleeping for 1 second
69
sleeping for 1 second
Killing the concurrent thread
What probably happens here: the signal toStop_Signal which is meant to quit l_newThread is never delivered, because when it's emitted the event loop is already dead and gone. Hence, your program is stuck waiting for the thread in l_newThread.wait();.
I don't fully get why you start this thread at all, just to use QtConcurrent::run right after and span yet another thread ...
Anyway, once you're sure your worker has stopped (and you are, according to the output you posted), you can safely quit the (basically useless) thread directly in your main:
int result = app.exec();
l_newThread.exit(); //just quit it
l_newThread.wait();
return result;
Then you can get rid of this connection:
QObject::connect(l_handler,&handler::toStop_Signal,&l_newThread, &QThread::quit);
and (I guess) of the handler altogether.

QT "Tick" widget loop

I'm trying to understand the correct way to update a widget at frame-time.
The specific problem I'm trying to solve is to set the remaining time of a timer on a label.
I created and started the timer
MainTimer = new QTimer(this);
MainTimer->setSingleShot(true);
MainTimer->start(5000);
and on the QML I have a label, UI_MainTimerLabel, that I can access through ui->UI_MainTimerLabel->setNum(int).
Since the QTimer doesn't provide a OnTimerUpdate signal or callback method, I suppose I have to create some kind of loop to read the timer's value and set it to the label.
Should I do it through a QThread?
QThread::create([&]() {
while(true)
{
ui->UI_RemainingTimer->setNum(MainTimer->remainingTime());
}
})->start();
(note: I know that this won't work, but it's not a problem since I'm just trying to understand the concept)
Should I use a 0-timed QTimer?
UpdateTimer = new QTimer(this);
//{binding the UpdateTimer end signal to a ui->UI_RemainingTimer->SetNum(MainTimer->RemainingTimer() function}
UpdateTimer->start(0);
Should I use a QEventLoop (but I have yet to fully understand what is their correct usage)?
Should I use a user-created "MyTimerLabel" widget that self-updates (in which virtual overridden method?)?
Or is there some other correct way to manage a frame-time update, that I couldn't understand? (I'm trying to get the general correct approach, not the solving approach of this specific problem, though)
Thanks in advance
Is it necessary to update the GUI at every moment? No, each frame is updated every 30ms so something appropriate is to update half of that time, that is 15 ms. So the second timer is set to that period by calculating the remaining time showing it in the GUI:
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTimer main_timer;
main_timer.setSingleShot(true);
QTimer update_timer;
QLabel label;
label.setAlignment(Qt::AlignCenter);
label.resize(640, 480);
QObject::connect(&update_timer, &QTimer::timeout, [&main_timer, &update_timer, &label](){
int rem = main_timer.remainingTime();
if(rem <0){
label.setNum(0);
update_timer.stop();
}
else{
label.setNum(rem);
}
});
label.show();
main_timer.start(5000);
update_timer.start(15);
return a.exec();
}

I just don't understand threads in Qt

Okay, so, here's the deal.
I'm currently writing a small chat messaging simulation / project using SysV IPC, and I use Qt for my client app. What I want is a background thread that would wait on a message queue and send a signal to a GUI thread whenever a new message comes. I have attempted to write the code using QThread inheritance, but it doesn't seem to work, the messages are not shown, and I think I'm missing something here.
As for the code:
ipcEventListener.h:
class IPCEventListener : public QThread
{
Q_OBJECT
public:
IPCEventListener();
void run();
messageWrapper mw;
signals:
void sendChatMsg(MSG_CHAT_MESSAGE cm);
};
ipcEventListener.cpp
IPCEventListener::IPCEventListener()
{
}
void IPCEventListener::run()
{
mutex.lock();
int n = msgrcv(myQueueId, &mw, sizeof(mw)-sizeof(long), 0, IPC_NOWAIT);
mutex.unlock();
if (n>0)
{
snip...
else if (mw.resp.type == MESSAGE)
{
emit sendChatMsg(mw.chatMsg);
}
}
exec();
}
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
listener = new IPCEventListener(this);
connect(this->listener, SIGNAL(sendChatMsg(MSG_CHAT_MESSAGE)), this, SLOT(message_received(MSG_CHAT_MESSAGE)));
connect(this->ui->pushButton, SIGNAL(clicked()), this, SLOT(on_pushButton_clicked()));
listener->start();
ui->comboBox->addItem("Client");
ui->comboBox->addItem("Room");
}
void MainWindow::message_received(MSG_CHAT_MESSAGE cm)
{
QString formattedMessage = "";
formattedMessage.append("[");
formattedMessage.append(cm.send_time);
formattedMessage.append("] ");
if (cm.msg_type == PRIVATE) formattedMessage.append("[PRIV:] ");
formattedMessage.append(cm.sender);
formattedMessage.append(": ");
formattedMessage.append(cm.message);
formattedMessage.append("\n");
ui->textEdit->append(formattedMessage);
}
What am I missing?
(PS: I know the code probably breaks about a hundred thousand of code conventions, but the deadline is pretty soon and I have to resort to kludges. It's just a school project, though).
You have a logical error in your code. You treat void IPCEventListener::run() as a method which is in a loop and is executing again ang again ang again but it's not. QThread::run() is method where you only initialize your thread and execute exec() function, to start event loop. It means that in current version of your application, you try to receive message just once and then your thread is just waiting for some events, without doing anything with them.
So what you need is an inifite loop in which you will try to receive messages. And don't forget to stop this loop while program closing.