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

Вход на сайт

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

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

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

Не получается, емаё
огромное спасибо за подробное объяснение про 3д графику на питоне, в интернете очень мало подобной информации
dobryj den, popytalas otkryt prikreplionnyj fail ctoby posmotret kak rabotaet, no mne ego ne pokazyvaet vydajet osibku. Pochemu?
Очень интересно! ии сайт крутой жалко что умирает(
У Вас число превысит максимальное число int. Можно использовать в Вашем случае uint, но лучше все переписать на double.

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

Рейтинг@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 кб