Уроки, алгоритмы, программы, примеры

Вход на сайт

Материалы по разделам

Построения
на плоскости (2D)
Графика
в пространстве (3D)
Вычислительная
геометрия
Физическое
моделирование
Фрактальная
графика

Новые комментарии

torrvic, возможно, Вам нужно добавить -lGLU
Извините за тупой вопрос. У меня при сборке Вашего примера выходит ошибка: "undefined reference to gluLookAt". Не могу найти в какой библиотеке находится эта функция. У меня задано: -lGL -lglut ... Искал в /usr/lib таким образом: nm lib*so* | grep...
Здравствуйте. Спасибо за проект. У меня вопрос, по какой причине определение принадлежности точки многоугольнику работает некорректно, если координаты из больших чисел состоят, например: int[] vertex = new int[] {...
Сейчас проверила нашла причину не запускания // Создание контекста воспроизведения OpenGL и привязка его к панели на форме OpenGLControl1:=TOpenGLControl.Create(Self); with OpenGLControl1 do begin Name:='OpenGLControl1'; //вот тут...
Ну..кажется что то пошло не так http://pp.usera...

Счетчики и рейтинг

Рейтинг@Mail.ru
Среда программирования: 
Qt
Введение

Qt — кроссплатформенный инструментарий разработки ПО на языке программирования C++. Позволяет запускать написанное с его помощью ПО в большинстве современных операционных систем путём простой компиляции программы для каждой ОС без изменения исходного кода. Включает в себя все основные классы, которые могут потребоваться при разработке прикладного программного обеспечения, начиная от элементов графического интерфейса и заканчивая классами для работы с сетью, базами данных и XML. Qt является полностью объектно-ориентированным, легко расширяемым и поддерживающим технику компонентного программирования. -- Wikipedia

Чтобы читателю было проще понять основы программирования графики, используя библиотеку Qt, мы решили рассмотреть отрисовку примитивов на примере реализации аналоговых часов.

Часть 1. Основы работы с Qt

Qt является объектно-ориентированным фреймворком, поэтому чтобы понять весь материал, читатель должен понимать основы ООП.
Для связывания классов в 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) Конечный результат

Часть 4. Заключение

Как мы видим, программирование примитивной графики с использованием библиотек Qt не представляет собой что-то очень сложное и непонятное. Любой человек после ознакомления с базовыми классами имеет возможность сразу же начать разрабатывать приложения.

В будущем хотелось бы рассмотреть рисование более сложных моделей, подключение OpenGl и отрисовку 3d объектов в целом.

Прикрепленный файлРазмер
Clock.zip2.25 кб