04 public:
05 OvenTimer(QWidget *parent = 0);
06 void setDuration(int secs);
07 int duration() const;
08 void draw(QPainter *painter);
09 signals:
10 void timeout();
11 protected:
12 void paintEvent(QPaintEvent *event);
13 void mousePressEvent(QMouseEvent *event);
14 private:
15 QDateTime finishTime;
16 QTimer *updateTimer;
17 QTimer *finishTimer;
18 }
Класс OvenTimer наследует QWidget и переопределяет две виртуальные функции: paintEvent() и mousePressEvent().
const double DegreesPerMinute = 7.0;
const double DegreesPerSecond = DegreesPerMinute / 60;
const int MaxMinutes = 45;
const int MaxSeconds = MaxMinutes * 60;
const int UpdateInterval = 1;
Мы начнем с нескольких констант, управляющих внешним видом и режимом работы таймера духовки.
01 OvenTimer::OvenTimer(QWidget *parent)
02 : QWidget(parent)
03 {
04 finishTime = QDateTime::currentDateTime();
05 updateTimer = new QTimer(this);
06 connect(updateTimer, SIGNAL(timeouf()),
07 this, SLOT(update()));
08 finishTimer = new QTimer(this);
09 finishTimer->setSingleShot(true);
10 connect(finishTimer, SIGNAL(timeout()),
11 this, SIGNAL(timeout()));
12 connect(finishTimer, SIGNAL(timeout()),
13 updateTimer, SLOT(stop()));
14 }
В конструкторе мы создаем два объекта QTimer: updateTimer используется для обновления внешнего вида виджета через каждую секунду, a finishTimer генерирует сигнал виджета timeout() при достижении отметки 0. Объект finishTimer должен генерировать только один сигнал тайм-аута, поэтому мы вызываем setSingleShot(true); по умолчанию таймеры запускаются повторно, пока они не будут остановлены или не будут уничтожены. Последний вызов connect() является оптимизационным и обеспечивает прекращение обновления виджета каждую секунду, когда таймер неактивен.
01 void OvenTimer::setDuration(int secs)
02 {
03 if (secs > MaxSeconds) {
04 secs = MaxSeconds;
05 } else if (secs <= 0) {
06 secs = 0;
07 }
08 finishTime = QDateTime::currentDateTime().addSecs(secs);
09 if (secs > 0) {
10 updateTimer->start(UpdateInterval * 1000);
11 finishTimer->start(secs * 1000);
12 } else {
13 updateTimer->stop();
14 finishTimer->stop();
15 }
16 update();
17 }
Функция setDuration() выставляет таймер духовки, задавая требуемое количество секунд. Время окончания мы рассчитываем путем добавления продолжительности его работы к текущему времени, полученному функцией QDateTime::currentDateTime(), и сохраняем его в закрытой переменной finishTime. B конце мы вызываем update() для перерисовки виджета с новой продолжительностью работы.
Переменная finishTime имеет тип QDateTime. Поскольку она содержит дату и время, мы избегаем ошибки из-за смены суток, когда текущее время оказывается до полуночи, а время окончания — после полуночи.
01 int OvenTimer::duration() const
02 {
03 int secs = QDateTime::currentDateTime().
04 secsTo(finishTime);
05 if (secs < 0)
06 secs = 0;
07 return secs;
08 }
Функция duration() возвращает количество секунд, оставшееся до завершения работы таймера. Если таймер неактивен, мы возвращаем 0.
01 void OvenTimer::mousePressEvent(QMouseEvent *event)
02 {
03 QPointF point = event->pos() - rect().center();
04 double theta = atan2(-point.x(), -point.y()) * 180 / 3.14159265359;
05 setDuration(duration() + int(theta / DegreesPerSecond));
06 update();
07 }
Если пользователь щелкает по этому виджету, мы находим ближайшую метку, используя тонкую, но эффективную математическую формулу, а результат идет на установку новой продолжительности таймера. Затем мы генерируем событие перерисовки. Метка, по которой щелкнул пользователь, теперь будет располагаться сверху поворотного диска и будет поворачиваться против часовой стрелки до тех пор, пока не будет достигнуто значение 0.
01 void OvenTimer::paintEvent(QPaintEvent * /* event */)
02 {
03 QPainter painter(this);
04 painter.setRenderHint(QPainter::Antialiasing, true);
05 int side = qMin(width(), height());
06 painter.setViewport((width() - side) / 2, (height() - side) / 2,
07 side, side);
08 painter.setWindow(-50, -50, 100, 100);
09 draw(&painter);
10 }
B paintEvent() мы устанавливаем область отображения на максимальный квадрат, который можно разместить внутри виджета, и мы устанавливаем окно на прямоугольник (—50, —50, 100, 100), то есть на прямоугольник с размерами 100 × 100, который покрывает пространство от точки (—50, —50) до точки (+50, +50). Шаблонная функция qMin возвращает наименьшее из двух значений аргументов. Затем мы вызываем функцию draw() для фактического вывода рисунка на экран.