понеділок, 10 вересня 2007 р.

Використання бібліотеки Qt4 на прикладі простої OpenGL програми

У даній статті я розповім як використовувати OpenGL графіку у програмах основаних на бібліотеці Qt4 ну і власне про принципи роботи з самою бібліотекою. Я не буду ускладнювати задачу 3D графікою, обмежимося лише 2D. Створимо простеньку програмку з меню, одним пунктом у підменю та графічним віджетом. Намалюємо просту сцену з червоним квадратом на синьому фоні, квадрат можна буде рухати стрілками або мищкою та змінювати його розміри.

Для того щоб мати змогу використовувати Qt4 його потрібно спочатку встановити використовуючи менеджер пакетів. Можна також встановити чудову IDE KDevelop, але можна обійтися і найпростішим текстовим редактором.

Отже, перш за все нам потрібно створити віджет який буде відображати графіку. Робиться це не складно, у Qt взагалі дуже просто породжувати нові віджети, для цього потрібно просто створити новий клас як спадкоємець стандартного QGLWidget. Назвемо його наприклад MyGLWidget. По правилу прийнятому у Qt, кожен клас повинен описуватися своєю парою .h та .cpp файлів. Спочатку опишемо сам клас у заголовочному файлі myglwidget.h:

#ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include
#include

class MyGLWidget : public QGLWidget
{
Q_OBJECT
public:
MyGLWidget(QWidget *parent = 0);
~MyGLWidget();
protected:
void resizeGL(int width, int height);
void paintGL();
void keyPressEvent (QKeyEvent *e);
void mousePressEvent(QMouseEvent *event);
private:
int iX, iY, iSize;
};
#endif


Тут особливу увагу потрібо звернути на використання макросу Q_OBJECT, він обробляється макропроцесором Qt і є обов'язковим для опису всіх класів віджетів. Інші методи перекривають стандартні. resizeGL - викликається при зміні розмірів віджету, paintGL - малює сцену, а keyPressEvent та mousePressEvent служать відповідно для перехвачення повідомлень від клавіатури та мишки відповідно. Самі методи реалізуються у файлі myglwidget.cpp (коментарі я додав по ходу програми):


#include "myglwidget.h"

MyGLWidget::MyGLWidget(QWidget *parent)
{
iX=125; iY=125; iSize=50; // задаємо початкові значення положення та розміру квадрату
}

void MyGLWidget::resizeGL(int width, int height)
{
if(height == 0) height = 1;
glViewport(0, 0, width, height);
glLoadIdentity();
if (width <= height) // Змінюємо систему координат таким чином щоб квадрат завжди залишався квадратом
glOrtho (0.0f, 250.0f, 0.0f, 250.0f*height/width, 1.0, -1.0);
else
glOrtho (0.0f, 250.0f*width/height, 0.0f, 250.0f, 1.0, -1.0);
}

void MyGLWidget::paintGL()
{
glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // Задаємо синій колір для фону
glClear(GL_COLOR_BUFFER_BIT); // Очищуємо вікно
glColor3f(1.0f, 0.0f, 0.0f); // Обираємо червоний колір
glRectf(iX-iSize/2, iY+iSize/2, iX+iSize/2, iY-iSize/2); // Малюємо квадрат
glFlush(); // Виводимо зображення на екран
}

void MyGLWidget::keyPressEvent(QKeyEvent *e)
{
switch(e->key()) // Аналізуємо яку клавішу натиснено
{
case Qt::Key_Up: ++iY; break;
case Qt::Key_Down: --iY; break;
case Qt::Key_Left: --iX; break;
case Qt::Key_Right: ++iX; break;
case Qt::Key_Z: ++iSize; break;
case Qt::Key_X: --iSize; break;
default: QWidget::keyPressEvent(e); // Передаємо керування батківському методу
}
updateGL(); // Оновлюємо зображення
}

void MyGLWidget::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) { // Перевіряємо чи натиснуто ліву кнопку мишки
iX=event->x(); // Змінюємо положення квадрату
iY=height()-event->y(); // Зверніть увагу, у системах координат OpenGL та Qt вісь Y направлена у протилежні сторони
updateGL(); // Оновлюємо зображення
}
}

MyGLWidget::~MyGLWidget()
{ } // Деструктор залишаємо пустим


Все, ми створили новий графічний віджет. Тепер, нам потрібно створити клас основного вікна. Для цього потрібно створити спадкоємця для QMainWindow. Нехай новий клас буде називатися qt4gl, тоді файл qt4gl.h буде мати наступний вигляд:

#ifndef QT4GL_H
#define QT4GL_H

#include
#include

class QAction;
class QMenu;
class QTextEdit;
class MyGLWidget;

class qt4gl:public QMainWindow
{
Q_OBJECT

public:
qt4gl();
~qt4gl();

private:
void createActions();
void createMenus();
void readSettings();

MyGLWidget *glCanvas;

QMenu *fileMenu;
QAction *exitAct;
};
#endif


Ну і відповідно реалізація у файлі qt4gl.cpp:


#include
#include "qt4gl.h"
#include "myglwidget.h"

#include

qt4gl::qt4gl()
{
glCanvas = new MyGLWidget; // Створюємо екземпляр віджету MyGLWidget
setCentralWidget(glCanvas); // Робимо його центральним віджетом у вікні
createActions(); // Ініціалізуємо набір екшенів
createMenus(); // Створюємо меню
glCanvas->setFocus(); // передаємо активний фокус нашому графічному віджету
}

void qt4gl::createActions()
{
exitAct = new QAction(tr("E&xit"), this); // Створюємо екшен для реалізації виходу з програми
exitAct->setShortcut(tr("Esc")); // Задаємо коротку клавішу
exitAct->setStatusTip(tr("Exit the application")); // Задаємо підказку
connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); // Задаємо зв'язку сигнал-слот для виконання команди
}

void qt4gl::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File")); // Додаємо підменю у головне меню
fileMenu->addAction(exitAct); // Додаємо пункт меню вказавши відповідний екшен
}

qt4gl::~qt4gl()
{ }


Готово, основне вікно тепер створено. Для не знайомих з філософією Qt може бути незвичним наступний рядок:
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));

Сигналом у поняттях Qt називається подія яка виникає внаслідок тих або інших дій користувача, у даному випадку вибору пункта меню (triggered), але таких стандартних сигналів є досить багато. Слот - це метод який будучи зв'язаним з кокретним сигналом автоматично викликається при настанні тієї чи іншої події. Це основне з положень на якому базується Qt, у спеціальній літературі дане питання описано більш докладно.

Ну і на завершення створимо основний файл програми (main.cpp):


#include
#include "qt4gl.h"

int main(int argc, char *argv[])
{
QApplication app(argc, argv); // Ініціалізуємо програму
qt4gl * mw = new qt4gl(); // Створюємо основне вікно
mw->resize(250,250); // Задаємо розміри центрального віджета
mw->setWindowTitle(QObject::tr("OpenGL + Qt4")); // Вказуємо заголовок вікна
mw->show(); // Виводимо вікно на екран
return app.exec(); // Запускаємо головний цикл програми, return тут використовується для повернення результату виконання програми операційній системі, для *nix вимагається.
}


Ну от. Тепер залишається лише все це зкомпілювати. Для цього створюємо папку qt4gl, копіюємо туди всі наші файли і запускаємо команду:
qmake -project

У папці з програмою з'явиться файл проекта qt4gl.pro. Відкриємо його у будь-якому текстовому редакторі і додамо у його кінець наступний рядок (ця обція необхідна для роботи з OpenGL):
QT += opengl

Після збереження виконуємо команду:
qmake qt4gl.pro

Після завершення виконання даної програми препроцесор Qt створить Makefile під конкретну платформу. Тепер вже можна запускати безпосередньо компіляцію програми:
make

Якщо код набрано без помилок, то по завершенню компіляції у цій же папці з'явиться файл qt4gl. Це і є готовий бінарний файл з програмою. Запустимо його:
./qt4gl

Результатом буде наступне:



Квадрат можна рухати стрілками на клавіатурі або просто клацнувши у будь-якому місці мишкою, клавішами Z та X можна змінювати розмір квадрату. Щоб закрити вікно можна обрати у меню File пункт Exit, або просто натиснути Esc. Зміна розмірів вікна призведе до автоматичного масштабування і квадрату. Тобто все працює так як і було задумано.

На завершення, хочу порекомендувати книжку C++ GUI Programming with Qt 4 By Jasmin Blanchette, Mark Summerfield. Дуже легко читається і у той же час все докладно пояснюється.

Немає коментарів:

Дописати коментар