![]() |
Главная · Все классы · Основные классы · Классы по группам · Модули · Функции | ![]() |
[Предыдущий: Урок 7] [Учебное пособие] [Следующий: Урок 9]
Файлы:
В этом примере мы познакомимся с первым пользовательским виджетом, который может самостоятельно отрисовывать себя. Также добавим полезный клавиатурный интерфейс (с помощью двух строчек кода).
Этот файл практически совпадает с файлом lcdrange.h из предыдущего урока. Мы добавили один слот: setRange().
void setRange(int minValue, int maxValue);
Теперь добавим возможность установки диапазона значений LCDRange. До сих пор он принимал значения между 0 и 99.
Внесем в конструктор изменение (обсудим его позже).
void LCDRange::setRange(int minValue, int maxValue) { if (minValue < 0 || maxValue > 99 || minValue > maxValue) { qWarning("LCDRange::setRange(%d, %d)\n" "\tRange must be 0..99\n" "\tand minValue must not be greater than maxValue", minValue, maxValue); return; } slider->setRange(minValue, maxValue); }
Слот setRange() устанавливает диапазон значений ползунка в LCDRange. Поскольку мы установили постоянное отображение двух цифр в QLCDNumber, нам нужно ограничить возможный диапазон значений minVal и maxVal чтобы избежать переполнения QLCDNumber. (Мы готовы принять значения вплоть до -9, но не можем их выбрать.) Если аргументы недопустимы, мы используем функцию Qt qWarning(), чтобы выдать предупреждение пользователю и немедленно выйти. qWarning() - функция аналогичная printf, по умолчанию отправляет свой вывод в stderr. Если вы хотите, можете установить собственную функцию-обработчик используя qInstallMsgHandler().
CannonField - новый пользовательский виджет, который знает как выводить себя на экран.
class CannonField : public QWidget { Q_OBJECT public: CannonField(QWidget *parent = 0);
CannonField унаследован от QWidget. Мы используем тот же стиль, что и для LCDRange.
int angle() const { return currentAngle; } public slots: void setAngle(int angle); signals: void angleChanged(int newAngle);
На данный момент, CannonField содержит только значение угла возвышения, которое мы предоставляем интерфейсу используя тот же прием, что и для значения в LCDRange.
protected: void paintEvent(QPaintEvent *event);
Это второй из многочисленных обработчиков событий в QWidget, с которым мы познакомились. Данная виртуальная функция вызывается Qt всякий раз, когда виджету нужно обновиться (т.е., нарисовать поверхность виджета).
CannonField::CannonField(QWidget *parent) : QWidget(parent) {
Вновь мы используем тот же прием, что и для LCDRange в предыдущем уроке.
currentAngle = 45; setPalette(QPalette(QColor(250, 250, 200))); setAutoFillBackground(true); }
Конструктор инициализирует значение угла возвышения равным 45 градусам и устанавливает пользовательскую палитру для данного виджета.
Эта палитра использует индикаторный (indicated) цвет в качестве фона и подбирает другие цвета соответственно. (Для данного виджета фактически будут использоваться только цвет фона и цвет текста.) Затем мы вызываем setAutoFillBackground(true) чтобы сообщить Qt о необходимости автоматически залить фон.
Цвет QColor указывается в виде трех чисел цветовой модели RGB (красный-зеленый-синий), каждое число может принять значение между 0 (темнее) и 255 (светлее). Вместо задания значения цветовой модели RGB можно использовать предопределенный цвет, например Qt::yellow.
void CannonField::setAngle(int angle) { if (angle < 5) angle = 5; if (angle > 70) angle = 70; if (currentAngle == angle) return; currentAngle = angle; update(); emit angleChanged(currentAngle); }
Данная функция устанавливает значение угла возвышения. Мы выбрали допустимый диапазон значений от 5 до 70 градусов и соответственно корректируем переданное значение угла. Мы решили не выдавать предупреждение, если новое значение угла будет вне диапазона.
Если новое значение угла равно старому, то мы немедленно возвращаемся. Это важно только для испускания сигнала angleChanged(), когда угол возвышения действительно изменится.
Тогда мы установим новое значение угла возвышения и перерисуем наш виджет. Функция QWidget::update() очистит виджет (обычно его заливают цветом фона) и отправляет событие рисования виджету. Это приводит к вызову функции события рисования виджета.
В заключение, испускается сигнал angleChanged() чтобы сообщить всем остальным виджетам о том, что угол возвышения изменился. Ключевое слово emit является уникальным для Qt и имеет нестандартный для C++ синтаксис. Фактически это макрос.
void CannonField::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
painter.drawText(200, 200,
tr("Angle = ") + QString::number(currentAngle));
}
Это наша первая попытка написать обработчик события рисования. Аргумент event содержит информацию о событии рисования, например, информацию об области виджета, которая должна быть обновлена. На некоторое время мы будем ленивыми и просто что-нибудь нарисуем.
Наш код отображает в заданном месте виджета значение угла возвышения. Чтобы этого добиться мы создали рисовальщика QPainter, действующего на виджете CannonField, и используем его для рисования строкового представления значения угла возвышения currentAngle. Позднее мы вернемся к QPainter; с его помощью можно сделать великое множество вещей.
#include "cannonfield.h"
Подключаем определение нашего нового класса.
class MyWidget : public QWidget { public: MyWidget(QWidget *parent = 0); };
Класс MyWidget будет включать в себя один объект LCDRange и один объект CannonField.
LCDRange *angle = new LCDRange;
В конструкторе мы создаем и устанавливаем виджет LCDRange.
angle->setRange(5, 70);
Мы установили допустимыми для LCDRange углы от 5 до 70 градусов.
CannonField *cannonField = new CannonField;
Мы создали наш виджет CannonField.
connect(angle, SIGNAL(valueChanged(int)), cannonField, SLOT(setAngle(int))); connect(cannonField, SIGNAL(angleChanged(int)), angle, SLOT(setValue(int)));
Здесь мы соединили сигнал valueChanged() виджета LCDRange со слотом setAngle() виджета CannonField. Таким образом значение угла возвышения CannonField будет обновляться всякий раз, когда пользователь работает с LCDRange. Мы также сделали обратное соединение, так что изменение угла возвышения в CannonField автоматически обновит значение LCDRange. В нашем примере мы никогда не меняли угол возвышения в CannonField непосредственно; но сделав последний вызов connect() мы гарантируем, что никакие будущие изменения не разрушат синхронизацию между этими двумя значениями.
Это иллюстрирует мощь компонентного программирования и правильной инкапсуляции.
Обращаем внимание на то, как важно испускать сигнал angleChanged() только тогда, когда угол возвышения действительно изменит значение. Если и LCDRange, и CannonField пренебрегут такой проверкой, то программа войдет в бесконечный цикл после первого же изменения одного из значений.
QGridLayout *gridLayout = new QGridLayout;
До настоящего времени мы использовали QVBoxLayout для управления геометрией. Однако теперь мы хотим получить больший контроль над размещением, и потому переключимся на более мощный класс QGridLayout. QGridLayout не является виджетом; это отдельный класс, который может управлять любым дочерним виджетом.
Нам не нужно указывать какие-либо размеры в конструкторе QGridLayout. QGridLayout определит количество строк и столбцов на основе заполненных нами ячеек сетки.
![]() | ![]() |
На вышеприведенной диаграмме показана компоновка, который мы пытаемся добиться. Слева схематично показана компоновка; справа - снимок экрана реальной программы.
gridLayout->addWidget(quit, 0, 0);
Мы добавили кнопку Quit в верхнюю левую ячейку сетки, т.е., ячейку с координатами (0, 0).
gridLayout->addWidget(angle, 1, 0);
В ячейку (1, 0) мы поместили ЖК-индикатор LCDRange значения угла возвышения.
gridLayout->addWidget(cannonField, 1, 1, 2, 1);
Объекту CannonField разрешили занять ячейки (1, 1) и (2, 1).
gridLayout->setColumnStretch(1, 10);
Сообщаем QGridLayout о том, что правый столбец (столбец 1) является растягиваемым с коэффициентом растяжения равным 10. Поскольку левый столбец не является растягиваемым (по умолчанию его коэффициент растяжения равен 0), QGridLayout будет стараться сохранять размеры виджетов слева неизменными и будет изменять размеры только CannonField, когда будут изменяются размеры MyWidget.
В данном примере любое значение коэффициента растяжения больше 0 для столбца 1 будет иметь тот же эффект. В более сложных компоновках вы можете использовать коэффициенты растяжения чтобы сообщить о том, что отдельные столбцы или строки будут растягивать вдвое быстрее, чем другие в соответствии с заданными коэффициентами растяжения.
angle->setValue(60);
Устанавливаем начальное значение угла возвышения. Обратите внимание на то, что это инициирует соединение от LCDRange к CannonField.
angle->setFocus();
Нашим последним действием является установка на angle фокуса ввода с клавиатуры таким образом, чтобы ввод шел по умолчанию на виджет LCDRange.
LCDRange не содержит никаких функций keyPressEvent(), поэтому это не выглядит сильно полезным. Однако, в конструктор просто добавится новая строчка:
setFocusProxy(slider);
LCDRange установит ползунок в качестве своего прокси фокуса (focus proxy). Это означает, что когда что-либо (программа или пользователь) захотят передать LCDRange фокус ввода с клавиатуры, ползунок примет его на себя. QSlider имеет подходящий клавиатурный интерфейс, поэтому с помощью только одной строчки кода мы передаем его LCDRange.
Теперь клавиатура на кое-что годится: Клавиши стрелок, Home, End, PageUp и PageDown делают что-то осмысленное.
Когда работают с ползунком, CannonField отображает новое значение угла. Пока размеры не будут изменены вручную CannonField получает все доступное пространство.
Попробуйте изменить размеры окна. Что произойдет, если вы сделаете его слишком узким или слишком приплюснутым?
Если вы зададите левому столбцу положительный коэффициент растяжения, что произойдет когда вы измените размер окна?
Отключите вызов QWidget::setFocus(). Какое поведение вы предпочтете?
Попробуйте заменить надпись "Quit" на "&Quit". Как изменится внешний вид кнопки? (Изменится ли он или нет - зависит от платформы.) Что случится, если вы нажмете Alt+Q во время работы программы?
Отцентрируйте текст в виджете CannonField.
[Предыдущий: Урок 7] [Учебное пособие] [Следующий: Урок 9]
Copyright © 2008 Trolltech | Торговые марки | Qt 4.3.5 |