Qt — кроссплатформенный инструментарий разработки ПО на языке программирования C++. Позволяет запускать написанное с его помощью ПО в большинстве современных операционных систем путём простой компиляции программы для каждой ОС без изменения исходного кода. Включает в себя все основные классы, которые могут потребоваться при разработке прикладного программного обеспечения, начиная от элементов графического интерфейса и заканчивая классами для работы с сетью, базами данных и XML. Qt является полностью объектно-ориентированным, легко расширяемым и поддерживающим технику компонентного программирования. -- Wikipedia
Чтобы читателю было проще понять основы программирования графики, используя библиотеку Qt, мы решили рассмотреть отрисовку примитивов на примере реализации аналоговых часов.
Часть 1. Основы работы с QtQt является объектно-ориентированным фреймворком, поэтому чтобы понять весь материал, читатель должен понимать основы ООП.
Для связывания классов в Qt используется механизм сигналов-слотов, который начинающему программисту Qt не всегда удается понять до конца, поэтому в данной статье мы свели использование подобных конструкций на нет.
Любая программа на Qt начинается с объявления класса QApplication a, в конструктор которого передаются те же входные параметры, что и в функцию main в С++.
Класс QWidget является одним из основных классов окон, работать с ним довольно удобно, поэтому он и будет выступать в классе потомка создаваемого нами класса Clock.
Часть 2. АлгоритмЧтобы не утруждать себя реализацией очень эффективного алгоритма, последовательность действий сводится к:
1) Отрисовке циферблата.
2) Вычислению текущего времени и нахождению углов отклонения стрелок.
3) Отрисовка стрелок.
Все эти действия мы будем повторять каждые 1000 миллисекунд, т.е. одну секунду.
Так как основной задачей статьи является показать читателю простоту работы с примитивной графикой в Qt, будет опущено рассмотрение использования классов для вычисления времени.
Часть 3. РеализацияНиже приведен код описания и реализации класса Clock.
// Clock.h #ifndef CLOCK_H #define CLOCK_H #include <QtGui> #include <math.h> class Clock : public QWidget // наследуем наш класс от QWidget'а { Q_OBJECT public: Clock(QWidget *parent = 0); ~Clock(); void paintEvent(QPaintEvent * ); // для рисования нам необходимо переопределить метод paintEvent void drawBase(QPainter &paint); // метод отрисовки циферблата void drawHours(QPainter &paint, double alpha); // метод отрисовки часовой стрелки void drawMinutes(QPainter &paint, double alpha); // ... минутной стрелки void drawSeconds(QPainter &paint, double alpha); // .. секундной стрелки void drawCenter(QPainter &paint); // отрисовка точки в центре void clear(QPainter &paint); // метод очистки экрана void timerEvent(QTimerEvent *); // таймер, чтобы каждую секунду обновлять изображение void initTime(); // метод инициализации времени и углов отклонения стрелок void recountTime(); // метод пересчета вермени QPair<double, double> getPoint(double nowX, double nowY, double alpha); // узнаем смещение стрелок private: int Ox,Oy; // координаты центра int seconds,minutes,hours; // часы, минуты, секунды double upX,upY; // координаты верха double alphaSec,alphaMin,alphaHours; // углы отклонения стрелок bool first; // запускаем ли первый раз }; #endif // CLOCK_H
// Clock.cpp #include "clock.h" #include <QPair> Clock::Clock(QWidget *parent) : QWidget(parent) { resize(300,300); // задаем размер окна Ox=150; Oy=150; // указываем upX=150; upY=5; // константы first=true; initTime(); // инициализируем время startTimer(1000); // запускаем таймер } void Clock::timerEvent(QTimerEvent *) { update(); // каждую секунду обновляем изображение recountTime(); // и пересчитываем время } Clock::~Clock() { } void Clock::initTime() { // в данном методе мы узнаем текущее время и задаем углы отклонения стрелок от указания на 12 часов QTime time=QTime::currentTime(); seconds=time.second(); minutes=time.minute(); hours=time.hour(); alphaSec=6.0*seconds; alphaMin=6.0*minutes; alphaHours=30.0*hours+minutes*0.5+0.025*seconds/3; } void Clock::recountTime() { // пересчет времени и углов seconds++; minutes += (seconds/60); seconds %= 60; hours += (minutes/60); minutes %= 60; alphaSec += 6.0; if(alphaSec == 360.0) alphaSec = 0.0; if(first!=true){ if(seconds%60 == 0) { alphaMin += 6.0; alphaHours += 0.5; } if(alphaMin == 360.0) alphaMin = 0.0; if(alphaHours == 360.0) alphaHours = 0.0; } else first = false; } QPair<double,double> Clock::getPoint(double nowX, double nowY, double alpha) { // используем матрицу поворота для определения координат конца каждой из стрелок после поворота double retX=(nowX-Ox)*cos(alpha*M_PI/180.0)-(nowY-Oy)*sin(alpha*M_PI/180.0)+Ox; double retY=(nowY-Oy)*cos(alpha*M_PI/180.0)+(nowX-Ox)*sin(alpha*M_PI/180.0)+Oy; if(abs(retX)<1e-9) retX=0; if(abs(retY)<1e-9) retY=0; if(retX<0) retX=300+retX; if(retY<0) retY=300+retY; return QPair<double,double>(retX,retY); } void Clock::paintEvent(QPaintEvent *) { QPainter paint(this); // инициализация QPainter'а clear(paint); // очистка экрана drawBase(paint); // отрисовка базы drawHours(paint,alphaHours); // часовой стрелки drawMinutes(paint,alphaMin); // минутной стрелки drawSeconds(paint,alphaSec); // секундной стрелки drawCenter(paint); // центра } void Clock::drawBase(QPainter &paint) { // указываем, что рисуем черным цветом, непрерывной линием толщиной 3 paint.setPen(QPen(Qt::black,3,Qt::SolidLine)); paint.setRenderHint(QPainter::Antialiasing, true); // повышаем качество paint.drawEllipse(5,5,290,290); // рисуем круг paint.setPen(QPen(Qt::black,3,Qt::SolidLine,Qt::RoundCap)); double nowUpX=295,nowUpY=150,nowDownX=280,nowDownY=150; paint.drawLine(nowUpX,nowUpY,nowDownX,nowDownY); // рисуем черточки на 12, 3, 6 и 9 часов nowUpX=150; nowUpY=295; nowDownX=150; nowDownY=280; paint.drawLine(nowUpX,nowUpY,nowDownX,nowDownY); nowUpX=5; nowUpY=150; nowDownX=20; nowDownY=150; paint.drawLine(nowUpX,nowUpY,nowDownX,nowDownY); nowUpX=150; nowUpY=5; nowDownX=150; nowDownY=20; paint.drawLine(nowUpX,nowUpY,nowDownX,nowDownY); // рисуем часовые черточки nowDownY=12; for(int i=1;i<=12;++i){ QPair<double,double> tmp=getPoint(nowUpX,nowUpY,30.0); nowUpX=tmp.first; nowUpY=tmp.second; tmp=getPoint(nowDownX,nowDownY,30.0); nowDownX=tmp.first; nowDownY=tmp.second; if(i==12) continue; paint.drawLine(nowUpX,nowUpY,nowDownX,nowDownY); } // рисуем минутные черточки nowDownY=7; for(int i=1;i<=60;++i){ QPair<double,double> tmp = getPoint(nowUpX,nowUpY,6.0); nowUpX = tmp.first; nowUpY = tmp.second; tmp = getPoint(nowDownX,nowDownY,6.0); nowDownX = tmp.first; nowDownY = tmp.second; paint.drawLine(nowUpX,nowUpY,nowDownX,nowDownY); } } void Clock::drawSeconds(QPainter &paint, double alpha) { // узнаем координаты конца стрелки после отклонения QPair<double,double> tmp=getPoint(upX,upY+15,alpha); paint.setPen(QPen(Qt::darkBlue,1,Qt::SolidLine)); // рисуем синим цветом paint.setRenderHint(QPainter::Antialiasing, true); paint.drawLine(Ox,Oy,tmp.first,tmp.second); tmp=getPoint(upX,100+20,alpha+180.0); paint.drawLine(Ox,Oy,tmp.first,tmp.second); } void Clock::drawMinutes(QPainter &paint, double alpha) { // всё так же, как и в отрисовке секундной стрелки QPair<double,double> tmp=getPoint(upX,upY+15,alpha); paint.setPen(QPen(Qt::cyan,2,Qt::SolidLine)); paint.setRenderHint(QPainter::Antialiasing, true); paint.drawLine(Ox,Oy,tmp.first,tmp.second); } void Clock::drawHours(QPainter &paint, double alpha) { // то же самое, что и в двух методах выше QPair<double,double> tmp = getPoint(upX,upY+50,alpha); paint.setPen(QPen(Qt::blue,2,Qt::SolidLine)); paint.setRenderHint(QPainter::Antialiasing, true); paint.drawLine(Ox,Oy,tmp.first,tmp.second); } void Clock::drawCenter(QPainter &paint) { // рисуем точку в центре paint.setPen(QPen(Qt::black,6,Qt::SolidLine,Qt::RoundCap)); paint.setRenderHint(QPainter::Antialiasing, true); paint.drawPoint(150,150); } void Clock::clear(QPainter &paint) { // просто очищаем наше окно paint.eraseRect(0,0,300,300); }
Как мы видим, за всю отрисовку отвечает функция paintEvent, которая является стандартной для класса QWidget, который мы унаследовали в наш класс.
Вся инициализация занимает всего одну строку -- "QPainter paint(this);", этой строкой мы объявили область на виджете, где мы будем рисовать.
Далее мы очищаем эту область и выбираем тип ручки. Хочется заметить, что в используемом классе QPainter присутствует достаточно много методов для рисования и настройки картинки. Так мы видим, что можно устанавливать разные цвета, тип линии, тип рендеринга, отрисовка линий, эллипсов и многое другое.
Примеры:
1) Отрисовка только базы
2) Отрисовка только стрелочек
3) Конечный результат
Как мы видим, программирование примитивной графики с использованием библиотек Qt не представляет собой что-то очень сложное и непонятное. Любой человек после ознакомления с базовыми классами имеет возможность сразу же начать разрабатывать приложения.
В будущем хотелось бы рассмотреть рисование более сложных моделей, подключение OpenGl и отрисовку 3d объектов в целом.
Прикрепленный файл | Размер |
---|---|
Clock.zip | 2.25 кб |