Выбрать главу

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() для фактического вывода рисунка на экран.