I want to create a simple clock in my Qt program: QLabel which is updated once a second.
QLabel name: label_clock
My clock "script":
while (true)
{
QString time1 = QTime::currentTime().toString();
ui->label_clock->setText(time1);
}
But when I pase it into my program, you already know that it will stop executing it at this script - while will always give true, so rest of code under script will never execute -> program crashes.
What should I do to make this script work? I wanna create a simple clock which is being updated once a second.
You can use QTimer for this. Try something like this:
QTimer *t = new QTimer(this);
t->setInterval(1000);
connect(t, &QTimer::timeout, [&]() {
QString time1 = QTime::currentTime().toString();
ui->label_clock->setText(time1);
} );
t->start();
Of course you should enable c++11 support (add to your pro file CONFIG += c++11).
Related
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);
}
};
I am trying to send a signal in special time like every day in 22:23 o'clock with Qt Library.
I want to use something like this function:
QTimer::singleShot("22:23", this, SLOT(updateCaption()));
I don't want to use if statements in my code and check time. i mean:
if( time == "22:23" )
emit mysignal();
how is it possible?
Here's how I'd do this.
First, get the datetime of the next time you want to emit the signal:
auto then = QDateTime::currentDateTime();
auto setTime = QTime::fromString("22:23", "hh:mm");
if(then.time() > setTime){
then = then.addDays(1);
}
then.setTime(setTime);
calculate the time difference from now
auto diff = QDateTime::currentDateTime().msecsTo(then);
and start a singleshot timer at that time, to start the regular daily timer:
QTimer::singleShot(diff, [this]{
auto t = new QTimer(QCoreApplication::instance());
connect(t, &QTimer::timeout, [this]{
emit mysignal();
});
t->start(24 * 3600 * 1000);
});
Note that this will be an hour off when the daylight saving time changes.
I've done a brief search and found this solution, looks pretty smooth:
QDateTime now = QDateTime::currentDateTime();
QDateTime timeoftheaction = ...;
QTimer *timer=new QTimer;
connect(timer, SIGNAL(timeout()),object,SLOT(action);
timer->start(now.secsTo(timeoftheaction)*1000);
Of course you can write a function and call it with the single line
I am trying to get a text edit box to display the current time every 5 seconds using QTimer. I am having the current time figured in a separate method and then having the QTimer call that method and display the current time. I can not for the life of me figure out how to pass the variable from the setCurrentTime method to the QTimer. Im sure it a really easy fix but I cant figure it out. Here is my code.
void noheatmode::setCurrentTime()
{
QTime time = QTime::currentTime();
QString sTime = time.toString("hh:mm:mm");
// ui->tempTimeNoHeatMode->append(sTime);
}
void noheatmode::on_timeButton_clicked()
{
QTimer *timer =new QTimer(this);
connect(timer,SIGNAL(timeout()), this, SLOT(setCurrentTime()));
timer->start(5000);
ui->tempTimeNoHeatMode->append(sTime);
}
If I got your problem right, you just have minutes variable instead of a seconds. Just change "hh:mm:mm" to "hh:mm:ss"
void noheatmode::setCurrentTime()
{
QTime time = QTime::currentTime();
QString sTime = time.toString("hh:mm:ss");
ui->tempTimeNoHeatMode->append(sTime);
}
With your code:
void noheatmode::on_timeButton_clicked()
{
QTimer *timer =new QTimer(this);
connect(timer,SIGNAL(timeout()), this, SLOT(setCurrentTime()));
timer->start(5000);
ui->tempTimeNoHeatMode->append(sTime);
}
This means that the function within SLOT will be called every 5000 milliseconds which = 5 seconds. What could be done then is that you set your function setCurrentTime() to update your text box every time it is called.
Example:
void Class::setCurrentTime()
{
QTime times = (QTime::currentTime());
QString currentTime=times.toString("hh:mm:ss");
ui->label->setText(currentTime);
//Assuming you are using a label to output text, else substitute for what you are using instead
//Every time this function is called, it will receive the current time
//and update label to display the time received
}
So I have an GUI application which was created using a GridLayout. I made a bunch of QLineEdit widgets which need to be updated constantly with new values as they come in. What is the best and most efficient way to do this? Seems like a silly question but I have done some research online and can't seem to find the best and most efficient solution. Thanks in advance.
I tried doing QTimer, it compiles...but never seems to timeout for some reason(I have debug print statement in run() that never gets printed.
time = new QLineEdit();
operationMode->setFixedWidth(150);
layout->addWidget(time, 4,7);
/*Test*/
qDebug() << "Here 1";
QTimer* timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(10 * 100); // 1 seconds
connect(timer, SIGNAL(timeout()), this, SLOT(run()));
setLayout(layout);
}
void MainWindow::run(void)
{
qDebug() << "Here 2";
time->setText(QTime::currentTime().toString());
qDebug() << "Here 3";
//qApp->processEvents();
}
Got my QTimer to work, apparently I needed to run timer->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