Жасмин Бланшет - QT 4: программирование GUI на С++
Среди переменных—членов находится pixmap, которая имеет тип QPixmap. Эта переменная содержит копию всего виджета, идентичную его изображению на экране. График всегда сначала строится вне экрана на пиксельной карте, и затем пиксельная карта помещается на виджет.
45 class PlotSettings
46 {
47 public:
48 PlotSettings();
49 void scroll(int dx, int dy);
50 void adjust();
51 double spanX() const { return maxX - minX; }
52 double spanY() const { return maxY - minY; }
53 double minX;
54 double maxX;
55 int numXTicks;
56 double minY;
57 double maxY;
58 int numYTicks;
59 private:
60 static void adjustAxis(double &min, double &max, int &numTicks);
61 };
62 #endif
Класс PlotSettings задает диапазон значений по осям x и y и количество отметок на этих осях. На рис. 5.8 показано соответствие между объектом PlotSettings и виджетом Plotter.
По условному соглашению значение в numXTicks и numYTicks задается на единицу меньше; если numXTicks равно 5, Plotter будет на самом деле выводить 6 отметок по оси x. Это упростит расчеты в будущем.
Рис. 5.8. Переменные—члены настроек графика PlotSettings.
Теперь давайте рассмотрим файл реализации:
001 #include <QtGui>
002 #include <cmath>
003 #include "plotter.h"
Мы включаем необходимые заголовочные файлы и импортируем все символы пространства имен std в глобальное пространство имен. Это позволяет нам получать доступ к функциям, объявленным в <cmath>, без указания префикса std:: (например, floor() вместо std::floor()).
004 Plotter::Plotter(QWidget *parent)
005 : QWidget(parent)
006 {
007 setBackgroundRole(QPalette::Dark);
008 setAutoFillBackground(true);
009 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
010 setFocusPolicy(Qt::StrongFocus);
011 rubberBandIsShown = false;
012 zoomInButton = new QToolButton(this);
013 zoomInButton->setIcon(QIcon(":/images/zoomin.png"));
014 zoomInButton->adjustSize();
015 connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));
016 zoomOutButton = new QToolButton(this);
017 zoomOutButton->setIcon(QIcon(":/images/zoomout.png"));
018 zoomOutButton->adjustSize();
019 connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
020 setPlotSettings(PlotSettings());
021 }
Вызов setBackgroundRole() указывает QWidget на необходимость использования для цвета стирания виджета «темного» компонента палитры вместо компонента «window» (окно). Этим мы определяем цвет, который будет использоваться в Qt по умолчанию для заполнения любых вновь появившихся пикселей при увеличении размеров виджета прежде, чем paintEvent() получит возможность рисования нового пикселя. Для включения этого механизма необходимо также вызвать setAutoFillBackground(true). (По умолчанию дочерние виджеты наследуют фон своего родительского виджета.)
Вызов setSizePolicy() устанавливает политику размера виджета по обоим направлениям на значение QSizePolicy::Expanding. Это подсказывает любому менеджеру компоновки, который ответственен за виджет, что он прежде всего склонен к росту, но может также сжиматься. Такая настройка параметров типична для виджетов, которые занимают много места на экране. По умолчанию в обоих направлениях устанавливается политика QSizePolicy::Preferred, означающая, что для виджета предпочтительно устанавливать размер на основе его идеального размера, но он может сжиматься до своего минимального идеального размера или расширяться в любых пределах при необходимости.
Вызов setFocusPolicy(Qt::StrongFocus) заставляет виджет получать фокус при нажатии клавиши табуляции Tab. Когда Plotter получает фокус, он будет реагировать на события нажития клавиш. Виджет Plotter понимает несколько клавиш: «+» для увеличения изображения, «—» для уменьшения изображения и клавиш стрелок для прокрутки вверх, вниз, влево и вправо.
Рис. 5.9. Скроллинг виджета Plotter.
Также в конструкторе мы создаем две кнопки QToolButtons, каждая из которых имеет пиктограмму. Эти кнопки дают возможность пользователю увеличивать и уменьшать масштаб изображения. Пиктограммы кнопок хранятся в файле ресурсов, поэтому любое приложение, использующее виджет Plotter, должно иметь следующую строку в файле .pro:
RESOURCES = plotter.qrc
Этот файл ресурсов похож на файл, который мы использовали для приложения Электронная таблица:
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/zoomin.png</file>
<file>images/zoomout.png</file>
</qresource>
</RCC>
Вызовы функции adjustSize() устанавливают для кнопок их идеальные размеры. Кнопки не размещаются в менеджере компоновки; вместо этого мы задаем их положение вручную при обработке события изменения размеров виджета Plotter. Поскольку мы не пользуемся никакими менеджерами компоновки, необходимо явно задавать родительский виджет кнопок, передавая this конструктору QPushButton.
Вызов в конце функции setPlotSettings() завершает инициализацию.
022 void Plotter::setPlotSettings(const PlotSettings &settings)
023 {
024 zoomStack.clear();
025 zoomStack.append(settings);
026 curZoom = 0;
027 zoomInButton->hide();
028 zoomOutButton->hide();
029 refreshPixmap();
030 }
Функция setPlotSettings() устанавливает настройки PlotSettings для отображения графика. Ее вызывает конструктор Plotter, и она может также вызываться пользователями класса. Построитель кривых начинает работу с принятого по умолчанию масштаба изображения. Каждый раз, когда пользователь увеличивает изображение, создается новый экземпляр PlotSettings, который затем помещается в стек масштабов изображения. Этот стек масштабов изображений представлен двумя переменными—членами:
• zoomStack содержит настройки для различных масштабов изображения в объекте QVector<PlotSettings>;
• curZoom содержит индекс текущего элемента PlotSettings стека zoomStack.
После вызова функции setPlotSettings() в стеке масштабов изображений будет находиться только один элемент, а кнопки Zoom In и Zoom Out будут скрыты. Эти кнопки не будут видны на экране до тех пор, пока мы не вызовем для них функцию show() в слотах zoomIn() и zoomOut(). (Обычно для показа всех дочерних виджетов достаточно вызвать функцию show() для виджета верхнего уровня. Но когда мы явным образом вызываем для дочернего виджета функцию hide(), этот виджет будет скрыт до вызова для него функции show().)
Вызов функции refreshPixmap() необходим для обновления изображения на экране. Обычно мы вызываем функцию update(), но здесь мы поступаем немного по-другому, потому что хотим иметь пиксельную карту QPixmap постоянно в обновленном состоянии. После регенерации пиксельной карты функция refreshPixmap() вызывает update() для помещения пиксельной карты на виджет.
031 void Plotter::zoomOut()
032 {
033 if (curZoom > 0) {
034 --curZoom;
035 zoomOutButton->setEnabled(curZoom > 0);
036 zoomInButton->setEnabled(true);
037 zoomInButton->show();
038 refreshPixmap();
039 }
040 }
Слот zoomOut() уменьшает масштаб диаграммы, если она отображена крупным планом. Он уменьшает на единицу текущий масштаб изображения и включает или выключает кнопку ZoomOut, в зависимости от возможности дальнейшего уменьшения диаграммы. Кнопка Zoom In включается и отображается на экране, а изображение диаграммы обновляется посредством вызова функции refreshPixmap().
041 void Plotter::zoomIn()
042 {
043 zoomInButton->setEnabled(curZoom< zoomStack.count() - 1);
044 if (curZoom < zoomStack.count() - 1) {
045 ++curZoom;
046 zoomOutButton->setEnabled(true);
047 zoomOutButton->show();
048 refreshPixmap();
049 }
050 }
Если пользователь сначала увеличил изображение, а затем вновь его уменьшил, настройки PlotSettings для следующего масштаба изображения уже будут в стеке масштабов изображения, и мы можем увеличить его. (В противном случае можно все же увеличить изображение при помощи резиновой ленты.)
Слот увеличивает на единицу значение curZoom для перехода на один уровень вглубь стека масштабов изображения, включает или выключает кнопку Zoom In взависимости от возможности дальнейшего увеличения изображения и включает и показывает кнопку Zoom Out. И вновь мы вызываем refreshPixmap() для использования построителем графиков настроек самого последнего масштаба изображения.