Ticker: timer event

In the chapter seven of C++ GUI Programming with Qt 4, Second Edition is described a custom widget, named Ticker, used for showing how to use timer events.

I have used it as central widget in my simple test application, where originally I put a QLabel and, well, it works fine.

Its idea is that it contains a text, and the id of a timer. The timer is started and stopped when the ticker is showed and hidden. Any time a Timer event, for the current ticker timer, reach the ticker itself the text is scrolled on the left.

To use the ticker as central widget in my application I used this piece of code:
Ticker* pTicker = new Ticker(this);
pTicker->setText(hello);
QMainWindow::setCentralWidget(pTicker);
And this is the Ticker class definition:
#ifndef TICKER_H
#define TICKER_H

#include <QWidget>

class Ticker : public QWidget
{
Q_OBJECT
public:
  explicit Ticker(QWidget* parent = 0);

  void setText(const QString& text);
  QString text() const { return text_; }

protected:
  void paintEvent(QPaintEvent*);
  void timerEvent(QTimerEvent* event);
  void showEvent(QShowEvent*);
  void hideEvent(QHideEvent*);

private:
  QString text_;
  int offset_;
  int timerId_;
};

#endif // TICKER_H

Notice that we have reimplemented four event handlers: show and hide will be used to start and stop the timer; timer for doing the actual job any ticked time; paint to redesign the widget any time is required.

Here is the implementation code for the class:
#include <QtGui>
#include "Ticker.h"

Ticker::Ticker(QWidget *parent) : QWidget(parent),
offset_(0), timerId_(0) // 1
{}

void Ticker::setText(const QString& text) // 2
{
  text_ = text;
  update();
  updateGeometry();
}

void Ticker::paintEvent(QPaintEvent*) // 3
{
  QPainter painter(this);

  int textWidth = fontMetrics().width(text_);
  if (textWidth < 1)
    return;
  int x = -offset_;
  while (x < width())
  {
    painter.drawText(x, 0, textWidth, height(), Qt::AlignLeft | Qt::AlignVCenter, text_);
    x += textWidth;
  }
}

void Ticker::showEvent(QShowEvent *) // 4
{
  timerId_ = startTimer(30);
}

void Ticker::timerEvent(QTimerEvent *event) // 5
{
  if(event->timerId() == timerId_)
  {
    ++offset_;
    if(offset_ >= fontMetrics().width(text_))
      offset_ = 0;
    update();
  }
  else
    QWidget::timerEvent(event);
}

void Ticker::hideEvent(QHideEvent*) // 6
{
  killTimer(timerId_);
  timerId_ = 0;
}

1. the offset is going to change to draw the text any time in a different position, giving the impression of a scrolling text; the timer id is set initially to zero because this means "no timer", that is exactely what we want.
2. when the user sets a text in the ticker we call update(), that is going to generate a repaint event, and updateGeometry(), to ensure the text is displayed as correctly as possible.
3. the handler for Paint event uses a QPainter object to put in the widget as many copy of the Ticker's text as required to fill its width. Notice that anytime it starts too much on the left (-offset_) giving the idea that the text is scrolling.
4. when the Ticker widget becomes visible, the timer is started, with a period of 30 milliseconds.
5. since there could be many active timer, before doing anything in the timeEvent() we should ensure that the timer id for the current event is the same of the stored timer for the Ticker. If this is the case, we increase the current offset - but when it reaches the size of the text, we reset it to zero - and then call update() to notify that we require the widget to be repainted. This will generate a Paint event, and so the scrolling of the text using the new offset.
6. when the Ticker is not visible we kill the timer. Notice that the timer functionality are part of the QObject interface.

I suggest you reading "C++ GUI Programming with Qt 4, Second Edition" by Jasmin Blanchette and Mark Summerfield for more details on the matter.

No comments:

Post a Comment