Reputation: 4335
I've been working on a simple dialog widget that should display GMT time at a rate of 10 Hz. Since the system I'm working on runs for days and days, it should be stable.
On some overnight runs, I've noticed that my "BAR" program is running at 100 % after several hours of execution. I'm flummoxed as to why this occurs, but I've been able to narrow it down to three functions:
I'm using a simple function called ace_time
to obtain time of day:
inline double ace_time(void)
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return (double) tv.tv_sec + 1e-6 * (double) tv.tv_usec;
}
And then I obtain the milliseconds, seconds, minutes, etc, from the return of this function. I then use QTime
to format it:
QTime time(hours, minutes, seconds, milliseconds);
QString timeStr = time.toString("hh:mm:ss.zzz");
And then set the text in my label:
clock->setText(timeStr);
I'm confused why I would get 100 % cpu usage, unless gettimeofday
, QTime
or setText
are doing things that I'm not expecting.
Have the experts here noticed any of these functions behaving strangely?
I'm using Qt 4.8, if that helps.
Looking forward to getting some ideas to solve this. Thanks!
Adding more code:
I want to have two bars. A top and a bottom bar. So I've written a BarBase
class and a TopBar
class. I also needed to write a custom QLayout
to help me with the layout. I highly doubt the layout manager is causing this because it is only called when the bar is resizing and the geometry needs to be recalculated.
class BarBase : public QWidget
{
public:
BarBase()
{
setFixedHeight(barHeight);
setContentsMargins(5, 0, 5, 0);
QPalette palette;
QColor color(50, 252, 50);
palette.setColor(QPalette::Background, color);
setAutoFillBackground(true);
setPalette(palette);
}
virtual ~BarBase();
protected:
QLabel *createWestLabel(const QString &);
QLabel *createCenterLabel(const QString &);
QLabel *createEastLabel(const QString &);
private:
QLabel *createLabel(const QString &, Qt::Alignment)
{
QLabel *label = new QLabel(str);
label->setAlignment(Qt::AlignVCenter | alignment);
//label->setFrameStyle(QFrame::Box | QFrame::Raised);
QFont font("Times");
font.setPixelSize(barHeight - 4);
font.setBold(true);
label->setFont(font);
return label;
}
};
And here is my class for the TopBar
only
class TopBar : public BarBase
{
Q_OBJECT
public:
TopBar()
{
Layout *layout = new Layout;
classification = createCenterLabel("Classification");
layout->addWidget(classification, Layout::Center);
hostname = createWestLabel("Hostname");
layout->addWidget(hostname, Layout::West);
layout->addWidget(createWestLabel(":"), Layout::West);
software = createWestLabel("Software");
layout->addWidget(software, Layout::West);
runMode = createEastLabel("SIM");
layout->addWidget(runMode, Layout::East);
layout->addWidget(createEastLabel(":"), Layout::East);
clock = createClockLabel("-dd::hh::mm::ss.z");
layout->addWidget(clock, Layout::East);
deadman = new QTimer;
connect(deadman, SIGNAL(timeout()), this, SLOT(updateLocalGMT()));
deadman->start(100); // 10 ms;
setLayout(layout);
setWindowTitle(tr("Top Bar"));
}
virtual ~TopBar();
public slots:
void updateLocalGMT()
{
double milliseconds = fmod(ace_time(), 86400.0) * 1000;
bool sign = (milliseconds >= 0.0);
if (!sign)
{
milliseconds = -milliseconds;
}
const int millisecondsToDays = 86400.0 * 1000.0;
const int millisecondsToHours = 3600.0 * 1000.0;
const int millisecondsToMinutes = 60 * 1000.0;
const int millisecondsToSeconds = 1000.0;
double days = floor(milliseconds / millisecondsToDays);
milliseconds -= days * millisecondsToDays;
double hours = floor(milliseconds / millisecondsToHours);
milliseconds -= hours * millisecondsToHours;
double minutes = floor(milliseconds / millisecondsToMinutes);
milliseconds -= minutes * millisecondsToMinutes;
double seconds = floor(milliseconds / millisecondsToSeconds);
milliseconds -= seconds * millisecondsToSeconds;
QTime time(hours, minutes, seconds, milliseconds);
/*
if (!time.isValid())
{
INFO("Invalid input to QTime [day, hour, min, sec, ms]: [%f %f %f %f %f]",
days, hours, minutes, seconds, milliseconds);
}
*/
QString timeStr = time.toString("hh:mm:ss.zzz");
timeStr = timeStr.left(timeStr.length() - 2); // to remove the last two z
timeStr.prepend((sign) ? "+" : "-");
timeStr.prepend("<code style='color:white'>");
timeStr.append("</code>");
// timeStr = timeStr.left(timeStr.length() - 2);
// qDebug() << currentTime;
clock->setText(timeStr);
}
private:
QLabel *classification;
QLabel *hostname;
QLabel *software;
QLabel *runMode;
QLabel *clock;
QLabel *createClockLabel(const QString &text)
{
QLabel *label = new QLabel(text);
label->setAlignment(Qt::AlignVCenter);
QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter);
font.setFixedPitch(true); // enforces monospace
font.setPointSize(18);
font.setBold(true);
label->setFont(font);
int pixelWidth = label->fontMetrics().width(label->text());
label->setFixedWidth(pixelWidth);
return label;
}
QTimer *deadman;
};
Upvotes: 2
Views: 986
Reputation: 62848
deadman->start(10); // 10 ms;
(Note: answer written before question was fixed to have 100 ms interval)
You have 10ms interval here, which is 100Hz, not 10Hz. In your question you say "the system I'm working on runs for days and days", so it sounds like you have an embedded system of some sort? Maybe it has trouble keeping up with 100Hz timer.
Note that updating the label 100 times a second will cause QWidget::update()
to be called that often, and the widget will be painted from event loop very often, even if you do not call repaint()
(which you should not do anyway).
Upvotes: 0
Reputation: 5892
The QTimer can fall behind and queue up many notifications if your thread is busy - this will cause your processing to happen far more than you expect.
To workaround this you should do something like:
void timer_slot()
{
if diff(now - last_time) < timer_interval
return; // Timer has come in too early so don't do anything
last_time = now;
}
If you want to reproduce this just block your thread with a sleep() and you'll notice that the timer slot will be called for as many times as it should have been called while the thread was blocked.
The QTimer docs state that:
"Accuracy and Timer Resolution Timers will never time out earlier than the specified timeout value and they are not guaranteed to time out at the exact value specified. In many situations, they may time out late by a period of time that depends on the accuracy of the system timers. The accuracy of timers depends on the underlying operating system and hardware. Most platforms support a resolution of 1 millisecond, though the accuracy of the timer will not equal this resolution in many real-world situations. If Qt is unable to deliver the requested number of timer clicks, it will silently discard some."
But I've found this not to be true on Windows 7 x64 if the thread is blocked/busy.
Upvotes: 2