Создание графических интерфейсов пользователя с использованием Qt 5.12 (1245345), страница 9
Текст из файла (страница 9)
Вместо этого следует обеспечить перерисовку окна при изменении положения фигур. Такую перерисовку обеспечивают методы QWidget update() и repaint(). Использование update() предпочтительно,так как метод сам определяет целесообразность немедленной перерисовки, приспосабливаясь к скорости изменения рисунка (при слишком большой частоте перерисовки она будет выполняться не каждый раз).При создании движущегося изображения сигнал на перерисовку смещенного изображения целесообразно получать от таймера.
Приложение, использующее средства Qt,может создавать произвольное количество таймеров с разными временными интервалами.Для этого используется функция класса QObject:int startTimer(<Временной интервал в мс>) .Число, возвращаемое функцией – номер таймера. Этот номер необходимо проверить,когда активизируется событие timerEvent(), чтобы быть уверенным, что обрабатывается сигнал от нужного таймера.Для прекращения работы таймера используют функцию того же класса.void killTimer(<Номер таймера>)Пример 2.9. Создание движущихся изображений. Обработка события от таймера,событий визуализации и сокрытия окна, а также события перерисовки окна.Пусть необходимо создать приложение, в окне которого вращаются вокруг своихгеометрических центров линия и квадрат (рисунок 2.11).Рисунок 2.11 – Внешний вид окна приложенияПриложение будет состоять из шести объектов: Окно, Кнопка, Холст, Таймер, Линияи Квадрат, не считая объекта самого приложения (рисунок 2.12).
При этом объект Окнобудет отвечать за создание своих компонентов: Холст (поля рисования) и Кнопки, их визуализацию и уничтожение. Основное назначение Кнопки – инициировать закрытие приложения. Холст будет отвечать за создание и уничтожение Таймера, а также за рисованиепошагово перемещаемых фигур Линия и Квадрат при обработке сигналов Таймера.ЗавершитьОкноНарисоватьНарисоватьХолстПерерисоватьЛинияКнопка"Выход"Запустить"Тик"ТаймерОтключитьПерерисоватьКвадратРисунок 2.12 – Фрагмент объектной декомпозиции приложения (без сообщений создания и уничтоженияобъектов)Запускать таймер будем при визуализации Холста в обработчике событияshowEvent(), а выключать – при его сокрытии (в обработчике hideEvent()). Такимобразом всего обработываем четыре типа событий: showEvent() – включение таймера; timerEvent() – инициация перерисовки Холста; paintEvent() – рисование пошагово перемещающихся фигур; hideEvent() – выключение таймера.Для уточнения взаимодействия объектов построим диаграмму последовательностидействий (рисунок 2.13).ОкноНачатьКнопкаnewХолстТаймерnewЛинияКвадратnewnewshow()paint()show()paint()show()startTimer()paint()draw()draw()draw()draw()draw()draw()deletedeletetimerEvent()update()timerEvent()update()Завершитьclose()hide()deletehide()deletekillTimer()Рисунок 2.13 – Диаграмма последовательности действий приложенияНа рисунке 2.14 показана диаграмма классов приложения.
Классы MyLine и MyRectнаследуем от абстрактного класса Figura. При этом используем сложный полиморфизм,поскольку переопределяемый в иерархии метод рисовая draw() будет вызываться из метода move() базового класса.QWidjetQPushButtonWindowWindow()AreamylinemyrectArea()showEvent()timerEvent()paintEvent()hideEvent()~Area()MyLineFiguradraw()MyRectFigura()move()draw()draw()Рисунок 2.14 – Диаграмма классов приложенияВ соответствии с рекомендациями С++ приложение декомпозируем на 7 файлов, зависимость между которыми представлена на рисунке 2.15.
Шесть из семи файлов образуют три модуля, хранящие описания отдельных классов или групп классов. А седьмойфайл source.cpp содержит основную программу.main.cppЗависитwindows.hРеализуетwindows.cppЗависитarea.hРеализуетarea.cppЗависитfigura.hРеализуетfigura.cppРисунок 2.15 – Диаграмма компоновки приложенияФайл figura.h содержит описания классов Figura, MyLine и MyRect:#ifndef figura_h#define figura_h#include <QPainter>class Figura{protected:int x,y,halflen,dx,dy,r;virtual void draw(QPainter *Painter)=0;public:Figura(int X,int Y,int Halflen):x(X),y(Y),halflen(Halflen){}void move(float Alpha,QPainter *Painter);};class MyLine:public Figura{protected:void draw(QPainter *Painter);public:MyLine(int x,int y,int halflen):Figura(x,y,halflen){}};class MyRect:public Figura{protected:void draw(QPainter *Painter);public:MyRect(int x,int y,int halflen):Figura(x,y,halflen){}};#endifФайл figura.cpp содержит описание методов этих же классов:#include "figura.h"#include <math.h>void Figura::move(float Alpha,QPainter *Painter){dx=halflen*cos(Alpha);dy=halflen*sin(Alpha);draw(Painter);}void MyLine::draw(QPainter *Painter){Painter->drawLine(x+dx,y+dy,x-dx,y-dy);}void MyRect::draw(QPainter *Painter){Painter->drawLine(x+dx,y+dy,x+dy,y-dx);Painter->drawLine(x+dy,y-dx,x-dx,y-dy);Painter->drawLine(x-dx,y-dy,x-dy,y+dx);Painter->drawLine(x-dy,y+dx,x+dx,y+dy);}Файл area.h содержит описание класса Area:#include "figura.h"#include <QWidget>class Area : public QWidget{int myTimer; // идентификатор таймераfloat alpha; // угол поворотаpublic:Area(QWidget *parent = 0);~Area();MyLine *myline;MyRect *myrect;protected://обработчики событийvoid paintEvent(QPaintEvent *event);void timerEvent(QTimerEvent *event);void showEvent(QShowEvent *event);void hideEvent(QHideEvent *event);};#endifФайл area.cpp содержит описание методов класса Area, включая обработчикисобытий:#include "area.h"#include <QTimerEvent>Area::Area(QWidget *parent):QWidget(parent){setFixedSize(QSize(300,200));myline=new MyLine(80,100,50);myrect=new MyRect(220,100,50);alpha=0;}void Area::showEvent(QShowEvent *){myTimer=startTimer(50);// создать таймер}void Area::paintEvent(QPaintEvent *){QPainter painter(this);painter.setPen(Qt::red);myline->move(alpha,&painter);myrect->move(alpha*(-0.5),&painter);}void Area::timerEvent(QTimerEvent *event){if (event->timerId() == myTimer) // если наш таймер{alpha=alpha+0.2;update();// обновить внешний вид}elseQWidget::timerEvent(event);// иначе передать для стандартной// обработки}void Area::hideEvent(QHideEvent *){killTimer(myTimer);// уничтожить таймер}Area::~Area(){delete myline;delete myrect;}Файл window.h содержит описание класса окна:#ifndef window_h#define window_h#include "area.h"#include <QPushButton>class Window : public QWidget{protected:QTextCodec *codec;Area * area;// область отображения рисункаQPushButton * btn;public:Window();};#endifФайл window.cpp содержит описание конструктора класса окна:#include "window.h"#include <QVBoxLayout>#include <QTextCodec>Window::Window(){codec = QTextCodec::codecForName("Windows-1251");this->setWindowTitle(codec->toUnicode("Обработка событий"));area = new Area( this );btn = new QPushButton(codec->toUnicode("Завершить"),this );QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(area);layout->addWidget(btn);connect(btn, SIGNAL(clicked(bool)),this,SLOT(close()));};И, наконец, файл main.cpp содержит основную программу:#include "window.h"#include <QApplication>int main(int argc, char *argv[]){QApplication appl(argc, argv);Window win;win.show();return appl.exec();}Для создания проекта был использован файл .pro следующего вида:TEMPLATE = appTARGET = Ex02_09QT += gui widgetsCONFIG += release# InputSOURCES += Source.cpp Window.cpp Area.cpp Figura.cppHEADERS += Window.h Area.h Figura.hЭтот файла позволяет получать проект Visual Studio при наличии в среде специального плагина или напрямую используется для создания проекта Qt Creator.2.5 Вывод данных на экран в виде таблиц.
Строки, массивы строк и файлыКак уже упоминалось ранее библиотека Qt – профессиональная библиотека, включающая не только классы для создания виджетов. Она также содержит большое количество классов для реализации сложных структур данных, таких как списки и деревья, классы для работы с базами данных, классы для работы с файлами и т. п.Описывать все множество классов Qt в рамках данного пособия нецелесообразно,поскольку с этими описаниями можно ознакомиться, используя Qt Assistant, специальныесайты в Интернете или соответствующую литературу [1].
Вместо этого использование перечисленных в заголовке параграфа классов продемонстрируем на примере создания несложного приложения «Записная книжка».Пример 2.10. Записная книжка. Пусть необходимо разработать приложение, которое обеспечивает хранение в файле фамилии и имена абонентов, а также их телефоны.Приложение также должно осуществлять вывод всех хранимых данных на экран и поисктелефона конкретного абонента по фамилии, по имени, а также по имени и фамилии одновременно.Для разрабатываемого приложения можно предложить несколько вариантов интерфейса. Выбираем один из самых простых, минимально обеспечивающий выполнение требуемых функций.На рисунке 2.16 показаны основные формы интерфейса создаваемого приложения.Рисунок 2.16 - Формы интерфейса приложения Записная книжка:а – главная форма приложения; б – форма добавления данных; в – форма запроса на поискданных; г – форма вывода результатов поиска или всего содержимого файлаДиаграмма состояний интерфейса должна предусмотреть вывод сообщения при отсутствии записей о телефоне конкретного абонента (рисунок 2.17).4Главноеменю515Добавлениезаписей253Вывод всехзаписейПоискзаписей65Выводрезультатов78Сообщение«Нет данных»Условные обозначения:1 – нажатие кнопки Добавить;2 – нажатие кнопки Показать;3 – нажатие кнопки Найти;4 – нажатие кнопки Выход;5 – нажатие кнопки Назад:6 – нажатие кнопки Найти после ввода ключей [Записи найдены];7 – нажатие кнопки Найти после ввода ключей [Записи не найдены];8 - нажатие кнопки OKРисунок 2.17 - Диаграмма состояний интерфейса приложения Записная книжкаПри выполнении объектной декомпозиции учтем, что каждое окно – отдельный объект.