I've created a Qt project which displays a circle on a widget.
Then I have a method which redraws the circle at different positions every time I call the method.
What I want is to run that method in a for loop, say ten times, and be shown each of the 10 positions that the circle is redrawn in every one second.
Something along the lines of:
void method::paintEvent(QPaintEvent * p)
{
//code
for(int i=0; i<10;i++)//do this every second
{
method(circle[i]); //co-ordinates change
circle[i].pain( & painter); //using QPainter
}
//code
}
I've read about QTimer, but do not know how to use it. And the sleep function does not work.
As you've guessed, QTimer is the correct mechanism to use here. How to go about setting it up?
Here's one option:
class MyClass : public QObject
{
public:
MyClass():i(0)
{
QTimer::singleShot(1000,this,SLOT(callback()));//or call callback() directly here
} //constructor
protected:
unsigned int i;
void paintEvent(QPaintEvent * p)
{
//do your painting here
}
public slots:
void callback()
{
method(circle[i]); //co-ordinates change
//circle[i].pain( & painter); //don't use QPainter here - call update instead
update();
++i;//increment counter
if(i<10) QTimer::singleShot(1000,this,SLOT(callback()));
}
}
All you need to do is to trigger an update() from the timer event. The update() method schedules a paintEvent on the widget.
It is invalid to paint on a widget outside of the paintEvent - that's the mistake that all other answers did at the time I posted this answer. Merely calling the paintEvent method is not a workaround. You should call update(). Calling repaint() would also work, but do so only when you have understood the difference from update() and have a very good reason for doing so.
class Circle;
class MyWidget : public QWidget {
Q_OBJECT
QBasicTimer m_timer;
QList<Circle> m_circles;
void method(Circle &);
void paintEvent(QPaintEvent * p) {
QPainter painter(this);
// WARNING: this method can be called at any time
// If you're basing animations on passage of time,
// use a QElapsedTimer to find out how much time has
// passed since the last repaint, and advance the animation
// based on that.
...
for(int i=0; i<10;i++)
{
method(m_circles[i]); //co-ordinates change
m_circles[i].paint(&painter);
}
...
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) {
QWidget::timerEvent(ev);
return;
}
update();
}
public:
MyWidget(QWidget*parent = 0) : QWidget(parent) {
...
m_timer.start(1000, this);
}
};
Try something like this:
class MyDrawer : public QObject
{
Q_OBJECT
int counter;
QTimer* timer;
public:
MyDrawer() : QObject(), counter(10)
{
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(redraw()));
timer->start(1000);
}
public slots:
void redraw()
{
method(circle[i]); //co-ordinates change
circle[i].pain( & painter);
--counter;
if (counter == 0) timer->stop();
}
};
Don't forget to run moc on this file, although if your class is a QObject you probably already do it.
Try this.
for(int i=0; i<10;i++)//do this every second
{
method(circle[i]); //co-ordinates change
circle[i].pain( & painter);
sleep(1000);// you can also use delay(1000);
}
Use sleep() The function called sleep(int ms) declared in which makes the program wait for the time in milliseconds specified.
Related
I have a slot function
void MobilePlatform::slot_lineFollow(bool checked)
{
while(checked){
...
QCoreApplication::processEvents();
}
if(!checked){
....
}
}
This function is a function that is requested from a QCheckBox and when the checkbox is pressed the variable checked are true and go into the while loop, and when the checkbox is not pressed the variable checked are false and go out from the while loop and goes into the if block, this is normal, but what I don't understand is, why in the next step goes to QcoreApplication::processEvents() and when it goes out, the variable checked are true.
Knows anybody why these happens?
Thanks in advance!
You've got bitten by the fallout from reentering the event loop. Now you know why it's not a good idea. Never block in the main thread, and never reenter the event loop. Invert the flow of control to accurately reflect the asynchronous reality:
class MobilePlatform : public QWidget {
Q_OBJECT
using self = MobilePlatform;
using base_class = QWidget;
using spin_handler = void (self::*)();
QBasicTimer m_spinTimer;
...
Q_SIGNAL void spinSignal();
void spin(spin_handler);
void despin(spin_handler);
void follower();
Q_SLOT void slot_lineFollow(bool checked);
protected:
void timerEvent(QTimerEvent *) override;
public:
...
};
void MobilePlatform::timerEvent(QTimerEvent *ev) {
if (ev->timerId() == m_spinTimer.timerId())
Q_EMIT spinSignal();
else
base_class::timerEvent(ev);
}
void MobilePlatform::spin(spin_handler handler) {
if (!m_spinTimer.isActive())
m_spinTimer.start(0, this);
connect(this, &self::spinSignal, this, handler, Qt::UniqueConnection | Qt::DirectConnection);
Q_ASSERT(m_spinTimer.isActive());
}
void MobilePlatform::despin(spin_handler handler) {
static const auto signal = QMetaMethod::fromSignal(&self::spinSignal);
Q_ASSERT(signal.isValid());
disconnect(this, &self::spinSignal, this, handler);
auto const hasHandlers = isSignalConnected(signal);
if (!hasHandlers)
m_spinTimer.stop();
Q_ASSERT(m_spinTimer.isActive() == hasHandlers);
}
void MobilePlatform::follower() {
/* code that runs when following */
...
}
void MobilePlatform::slot_lineFollow(bool checked) {
if (checked()) {
spin(&self::follower);
} else {
despin(&self::follower);
...
}
}
Is it possible to implement function interrupt in Qt (5.x).
For example if I have a button and want something to execute on the thread (which is running infinite loop) when this button is clicked, I could say something like this:
in thread...
forever
{
if(button_is_pressed_flag)
{
do something...
}
}
is there a better way?
The infinite loop should be an event loop, and then it can automatically process cross-thread slot calls without you worrying about the details.
The idiom to run code "continuously" on an event loop is the zero-duration timer.
Let's say you start with code that looks like this:
class MyThread : public QThread {
bool button_is_clicked_flag = false;
void run() override {
forever{
if (button_is_clicked_flag) {
onButtonClick();
button_is_clicked_flag = false;
}
doWork();
}
}
void onButtonClick();
void doWork();
public:
using QThread::QThread;
void setButtonClickedFlag();
}
int main(int argc, char **argv) {
...
MyThread t;
t.start();
...
}
It is required for doWork() not to take too long - nor too short. If it took ~5ms on modern hardware, it'd be just about a right tradeoff between overhead and latency for a general-purpose application. If you need lower latency reaction in the worker thread, then doWork() must do less work. It probably doesn't make much sense for doWork() to take much less than 1ms.
And whenever doWork() doesn't have anything to do, e.g. if it's done with the computation it was supposed to perform, it should stop the timer that keeps it alive.
You should transform it to look as follows:
class MyWorker : public QObject {
Q_OBJECT
QBasicTimer m_timer;
void doWork();
void timerEvent(QTimerEvent *event) {
if (event->timerId() == m_timer.timerId())
doWork();
}
public:
explicit MyWorker(QObject *parent = nullptr) : QObject(parent) {
m_timer.start(0, this);
}
Q_SLOT void onButtonClick() {
// Ensure we're invoked correctly
Q_ASSERT(QThread::currentThread() == thread());
...
}
}
class Window : public QWidget {
Ui::Window ui;
public:
Q_SIGNAL void buttonClicked();
explicit Window(QWidget *parent = nullptr) : QWidget(parent) {
ui.setupUi(this);
connect(ui.button, &QPushButton::clicked, this, &Window::buttonClicked);
}
};
class SafeThread : public QThread {
Q_OBJECT
using QThread::run; // final method
public:
~SafeThread() { quit(); wait(); } // we're safe to destroy - always
};
int main(int argc, char **argv) {
...
MyWorker worker;
SafeThread thread;
Window window;
// onButtonClick will be executed in worker->thread()
connect(&window, &Window::buttonClicked, &worker, &MyWorker::onButtonClick);
worker.moveToThread(&thread);
thread.start();
window.show();
return app.exec();
}
The event loop that runs in QThread::run will continuously invoke doWork via the timer event handler. But whenever a cross-thread slot call needs to be made to an object living in that thread, the event loop will deliver the internal QMetaCallEvent representing the slot call to QObject::event, which will then execute the call.
Thus, when you set a breakpoint in onButtonClick, there will be QObject::event nearby on the call stack.
You could start a thread and then immediately wait on a std::condition_variable, then when the button is clicked (the event being called on the main thread), notify the condition variable and the thread would awake.
However, this is a bit strange. What are you trying to do? call an asynchronous task upon a button click? In that case, perhaps it would be better just to start one from the button click event with std::packaged_task or std::async.
For the project I am working on in Qt I need to make several things happen at the same time. One of these events is to take a temperature reading and display that reading in a text edit box along with a time stamp. The temp and time stamp do not display until the while loop i wrote finishes. I know the while loop is blocking it so I am trying to write a thread to display the time and temp, but can not figure out how to write to the gui from the thread.
Here is where I start the thread and the while loop
QThread cThread;
timeTempObject cObject;
cObject.DoSetup(cThread);
cObject.moveToThread(&cThread);
cThread.start();
while(flowTime > 0)
{
// set zero pin to be high while flowtime is more than 0
digitalWrite(0,1);
displayCurrentTime();
// set second pin LED to flash according to dutyCycle
digitalWrite(2,1);
delay(onTime);
// displayCurrentTime();
ui->tempTimeNoHeatMode->append(temp);
digitalWrite(2,0);
delay(offTime);
flowTime--;
}
noheatmode.h
namespace Ui {
class noheatmode;
}
class noheatmode : public QWidget
{
Q_OBJECT
public:
explicit noheatmode(QWidget *parent = 0);
~noheatmode();
private slots:
void on_startButtonNoHeatMode_clicked();
void on_noHeatModeBack_clicked();
public slots:
void displayCurrentTime();
private:
Ui::noheatmode *ui;
};
#endif // NOHEATMODE_H
timetempobject.h for the thread
class timeTempObject : public QObject
{
Q_OBJECT
public:
explicit timeTempObject(QObject *parent = 0);
void DoSetup(QThread &cThread);
public slots:
void DoWork();
};
#endif // TIMETEMPOBJECT_H
timetempobject.cpp
timeTempObject::timeTempObject(QObject *parent) :
QObject(parent)
{
}
void timeTempObject::DoSetup(QThread &cThread)
{
connect(&cThread,SIGNAL(started()),this,SLOT(DoWork()));
}
void timeTempObject::DoWork()
{
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(displayCurrentTime()));
// delay set to space out time readings, can be adjusted
timer->start(1500);
// Gets the time
QTime time = QTime::currentTime();
// Converts to string with chosen format
QString sTime = time.toString("hh:mm:ss:ms");
// displays current time in text edit box
Ui::noheatmode* noheatmode::ui->tempTimeNoHeatMode->append(sTime);
}
How do I alter my thread so it can write to the text editor in my gui?
Since QTextEdit::append is a slot, it's very easy to call it from other threads:
void tempTimeObject::DoWork() {
...
QMetaObject::invokeMethod(ui->tempTimeNoHeatMode, "append",
Qt::QueuedConnection, Q_ARG(QString, temp));
...
}
If you wished to execute arbitrary code, it boils down to "how to execute a functor in a given thread", with the thread being the main thread. The answers to this question provide multiple ways of doing it.
The simplest way on Qt 5 would be:
void tempTimeObject::DoWork() {
...
{
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, qApp, [=](QObject *){
ui->tempTimeNoHeatMode->append(text);
... // other GUI manipulations
});
} // here signalSource emits the signal and posts the functor to the GUI thread
...
}
Using Qt I create a QMainWindow and want to call a function AFTER the windows is shown. When I call the function in the constructor the function (a dialog actually) get's called before the window is shown.
If you want to do something while the widget is made visible, you can override QWidget::showEvent like this:
class YourWidget : public QWidget { ...
void YourWidget::showEvent( QShowEvent* event ) {
QWidget::showEvent( event );
//your code here
}
After analyzing the solutions above, it turns that all of them, including the heavily upvoted ones, are faulty.
Many recommend something like this:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
DoSomething();
}
void MyWidget::DoSomething() {
// ...
}
This works as long as there is no QCoreApplication::processEvents(); in DoSomething. If there is one, it processes all events in the queue, including the QShowEvent which called MyWidget::showEvent in the first place. When it gets to the original QShowEvent, it calls MyWidget::showEvent again, causing an infinite loop.
If this happens, there are three solutions:
Solution 1. Avoid calling processEvents in MyWidget::DoSomething, instead call update or repaint when necessary. If DoSomething calls something else, these functions should avoid processEvents also.
Solution 2. Make DoSomething a slot, and replace direct call to DoSomething() by
QTimer::singleShot(0, this, SLOT(DoSomething()));
Since zero interval timer fires only when after all events in the queue are processed, it will process all events, including the original QShowEvent, remove them from the queue, and only then call DoSomething. I like it the most.
Since only zero interval timer fires only when after all events in the queue are processed, you should not try to "improve" it by lengthening the interval, for instance
QTimer::singleShot(50, this, SLOT(DoSomething())); // WRONG!
Since 50 ms is usually enough time for processing events in the queue, that would usually work, causing an error which is hard to reproduce.
Solution 3. Make a flag which prevents calling DoSomething the second time:
class MyWidget : public QWidget {
// ...
};
void MyWidget::showEvent(QShowEvent* event) {
if (is_opening)
return;
is_opening = true;
QWidget::showEvent(event);
DoSomething();
is_opening = false;
}
void MyWidget::DoSomething() {
// ...
}
Here, is_opening is a boolean flag which should be initialized as false in constructor.
try this:
in mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void showEvent(QShowEvent *ev);
private:
void showEventHelper();
Ui::MainWindow *ui;
}
in mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
void MainWindow::showEvent(QShowEvent *ev)
{
QMainWindow::showEvent(ev);
showEventHelper();
}
void MainWindow::showEventHelper()
{
// your code placed here
}
Follow Reza Ebrahimi's example, but keep this in mind:
Do not omit the 5th parameter of connect() function which specifies the connection type; make sure it to be QueuedConnection.
I.E.,
connect(this, SIGNAL(window_loaded), this, SLOT(your_function()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
I believe that you'd achieve what you need if you do it this way.
There are several types in signal-slot connections: AutoConnection, DirectConnection, QueuedConnection, BlockingQueuedConnection (+ optional UniqueConnection). Read the manual for details. :)
Assuming you want to run your code in the UI thread of the window after the window has been shown you could use the following relatively compact code.
class MainWindow : public QMainWindow
{
// constructors etc omitted.
protected:
void showEvent(QShowEvent *ev)
{
QMainWindow::showEvent(ev);
// Call slot via queued connection so it's called from the UI thread after this method has returned and the window has been shown
QMetaObject::invokeMethod(this, "afterWindowShown", Qt::ConnectionType::QueuedConnection);
}
private slots:
void afterWindowShown()
{
// your code here
// note this code will also be called every time the window is restored from a minimized state
}
};
It does invoke afterWindowShown by name but that sort of thing is fairly common practice in Qt. There are ways of avoiding this but they're a bit more verbose.
Note that this code should work for any QWidget derived class, not just QMainWindow derived classes.
In theory it might be possible for a very quick user to invoke some sort of action on the UI of the displayed window before afterWindowShown can be called but it seems unlikely. Something to bear in mind and code defensively against perhaps.
I found a nice answer in this question which works well, even if you use a Sleep() function.
So tried this:
//- cpp-file ----------------------------------------
#include "myapp.h"
#include <time.h>
#include <iosteream>
MyApp::MyApp(QWidget *parent)
: QMainWindow(parent, Qt::FramelessWindowHint)
{
ui.setupUi(this);
}
MyApp::~MyApp()
{
}
void MyApp::showEvent(QShowEvent *event) {
QMainWindow::showEvent(event);
QTimer::singleShot(50, this, SLOT(window_shown()));
return;
}
void MyApp::window_shown() {
std::cout << "Running" << std::endl;
Sleep(10000);
std::cout << "Delayed" << std::endl;
return;
}
//- h-file ----------------------------------------
#ifndef MYAPP_H
#define MYAPP_H
#include <QtWidgets/QMainWindow>
#include <qtimer.h>
#include <time.h>
#include "ui_myapp.h"
class MyApp : public QMainWindow
{
Q_OBJECT
public:
MyApp(QWidget *parent = 0);
~MyApp();
protected:
void showEvent(QShowEvent *event);
private slots:
void window_shown();
private:
Ui::MyAppClass ui;
};
#endif // MYAPP_H
I solved it without a timer using Paint event. Works for me at least on Windows.
// MainWindow.h
class MainWindow : public QMainWindow
{
...
bool event(QEvent *event) override;
void functionAfterShown();
...
bool functionAfterShownCalled = false;
...
}
// MainWindow.cpp
bool MainWindow::event(QEvent *event)
{
const bool ret_val = QMainWindow::event(event);
if(!functionAfterShownCalled && event->type() == QEvent::Paint)
{
functionAfterShown();
functionAfterShownCalled = true;
}
return ret_val;
}
The best solution for me is count once paint event:
.H
public:
void paintEvent(QPaintEvent *event);
.CPP
#include "qpainter.h"
#include <QMessageBox> // example
int contPaintEvent= 0;
void Form2::paintEvent(QPaintEvent* event)
{
if (contPaintEvent ==0 )
{
QPainter painter(this);
QMessageBox::information(this, "title", "1 event paint"); // example
// actions
contPaintEvent++;
}
}
Reimplement method void show() like this:
void MainWindow::show()
{
QMainWindow::show();
// Call your special function here.
}
I'm learning Qt and I was reading about Threads, Events and QObjects from Qt wiki, and followed the wiki recommendations on how to handle some work in a while condition but its not working for my specific case. Here's a simple example of what I'm currently trying to achieve.
class FooEvents : public FooWrapper {
public virtual serverTime(..) { std::cout << "Server time event\n"; }
public virtual connected(..) { std::cout << "Connected event\n"; }
}
class Foo : public QObject {
private:
FooAPI *client;
public:
Foo(FooEvents *ev, QObject *parent = 0) : client(new FooApi(ev)) { .. }
private slots:
void processMessages() {
if (state is IDLE)
reqFooAPiServerTime();
select(client->fd()+1, ...);
if (socket is ready for read)
client.onReceive();
}
public:
void connect(...) {
if (connection) {
QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(processMessages()));
timer.start(1000); // I don't get the output from FooEvents
}
}
}
This is a very simple but I think it illustrates my case. Why is this not working and what other alternatives to I have to handle this case? Thanks.s
Edit: The processMessages is being called every second but I don't get any output from the events
Where is timer declared and defined?
If it's local to Foo::connect() it'll be destroyed before it ever has a chance to fire. Presumably it just needs to be a member object of the Foo class.
Also keep in mind that QObject provides it's own simple interface to a timer - just override the protected virtual timerEvent() function and call QObject's startTimer() to start getting those timer events. In this case instead of having a slot to receive the timer events, they will just end up at the overridden timerEvent() function:
protected:
void timerEvent(QTimerEvent *event) {
processMessages();
}
public:
void connect( /* ... */ ) {
// ...
startTimer(1000);
}
This won't work, because processMessages() is not a SLOT.
So Declare processMessages() as a private slot and then try.
You don't declare the timer neither the slot. In the header you must declare:
class ... {
QTimer timer;
...
private slots:
void processMessages();
...
};
Then remember to make the SIGNAL-SLOT connection and configure the timer:
connect(&timer, SIGNAL(timeout()), this, SLOT(processMessages()));
timer.setInterval(1000);
timer.start();
Also timer.start(1000); would be valid...
ANOTHER POSSIBILITY
Other possibility would be to use the timer associated with each Q_OBJECT and overload the timerEvent:
class ... {
Q_OBJECT
...
protected:
void timerEvent(QTimerEvent *event);
...
};
Then you must implement the timer event as this:
void MyClass::timerEvent(QTimerEvent *event) {
processMessages();
}
And you can configure the timer with a simple call to startTimer(1000);