编程环境:
Qt5.8.0 + Windows10(64bit) + freeglut
(Qt下载版本)
前言(可略过……):
最近计算机图形学作业要求编写带下拉菜单栏menu的OpenGL程序,推荐用Qt,但也可以用MFC或者其他GUI库。但是据说Qt比 其他的使用起来更简单,所以下载了Qt。
因为之前都是在VS2015下利用freeglut进行编程,转换到Qt平台下当然也希望能用freeglut(OpenGL1.0)编程,虽然Qt已经支持OpenGL3.0。但是网上貌似找不到Qt + freeglut的教程。另外,网上大部分教程、代码都是Qt4版本(在Qt5中,引入了QOpenGL*系列类,以取代Qt4时代的QGL*系列类),两个版本差别还是挺大的,用Qt4时代的QGL*系列类会报各种错。有些QOpenGL*系列类的教程也介绍得很不全面。简言之,就是,网上的教程参差不齐(我也花了两天时间才把下面这最简单的程序框架实现)。
总之,下面实现的这份代码可以直接作为任何Qt下openGL程序的标准框架来使用。
程序基本功能:
显示简单图形 + 能检测键盘、鼠标输入 + 下拉菜单选择不同功能
实现方法:
1、Qt下新建Qt Widgets Application项目,选择QMainWindow作为基类。创建好项目后,系统已经帮我们生成了mainwindow.h、mainwindow.cpp、main.cpp三份代码文件以及界面文件mainwindow.ui(用于设计)。在做菜单栏之前,前三份代码基本上都是不需要做什么修改的。
2、项目右键->添加新文件-> C++ -> C++ Class ->输入class name :如openglwindow。之后就会生成openglwindow.h、openglwindow.cpp,即我们用于编程的主要文件。
openglwindow.h :
#ifndef OPENGLWINDOW_H
#define OPENGLWINDOW_H
#include <QOpenGLWidget>
#include <QKeyEvent>
#include <QMouseEvent>
class openglwindow : public QOpenGLWidget {
Q_OBJECT
public:
openglwindow(QWidget *parent = 0);
~openglwindow();
//用于菜单栏回调,改变方块颜色
void changeColorGreen();
void changeColorYellow();
protected:
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void keyPressEvent(QKeyEvent * e); //需要先在.ui里面设置Widget的focusPolicy
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
void drawQuads(int r, int g, int b);
GLfloat eyeX, eyeY, eyeZ; //摄像机位置
GLfloat rotateAngle; //正方体旋转角度
int redChannel;
};
#endif // OPENGLWINDOW_H
openglwindow.cpp :
#include "openglwindow.h"
#include "iostream"
#include <gl/freeglut.h>
using namespace std;
float int2float(int intensity) {
return (float)intensity / 255.0f;
}
openglwindow::openglwindow(QWidget *parent)
:QOpenGLWidget(parent)
{
rotateAngle = 0;
eyeX = eyeY = eyeZ = 0.5f;
redChannel = 0;
}
openglwindow::~openglwindow() {
}
void openglwindow::drawQuads(int r, int g, int b) {
const GLfloat x1 = -0.05f, x2 = 0.05f;
const GLfloat y1 = -0.05f, y2 = 0.05f;
const GLfloat point[4][2] = { { x1,y1 },{ x1,y2 },{ x2,y2 },{ x2,y1 } };
glColor3f(int2float(r), int2float(g), int2float(b));
glBegin(GL_QUADS);
for (int i = 0; i < 4; i++) {
glVertex2fv(point[i]);
}
glEnd();
}
void openglwindow::initializeGL() {
glClearColor( 0.0, 0.0, 0.0, 0.0 );
glEnable( GL_DEPTH_TEST );
}
void openglwindow::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, 0, 1, 0);
//front
glPushMatrix();
glTranslatef(-0.4f, 0, 0);
drawQuads(redChannel, 255, 0);
glPopMatrix();
rotateAngle += 1.0f;
// cout << "rotateAngle " << rotateAngle << endl;
glRotatef(rotateAngle, 0, 1, 0);
//front
glPushMatrix();
glTranslatef(0, 0, 0.05f);
drawQuads(0, 255, 0);
glPopMatrix();
//behind
glPushMatrix();
glTranslatef(0, 0, -0.05f);
drawQuads(255, 255, 0);
glPopMatrix();
//left
glPushMatrix();
glTranslatef(-0.05f, 0, 0);
glRotatef(90, 0, 1, 0);
drawQuads(0, 255, 255);
glPopMatrix();
//right
glPushMatrix();
glTranslatef(0.05f, 0, 0);
glRotatef(270, 0, 1, 0);
drawQuads(255, 0, 0);
glPopMatrix();
//top
glPushMatrix();
glTranslatef(0, 0.05f, 0);
glRotatef(90, 1, 0, 0);
drawQuads(0, 0, 255);
glPopMatrix();
//bottom
glPushMatrix();
glTranslatef(0, -0.05f, 0);
glRotatef(270, 1, 0, 0);
drawQuads(255, 0, 255);
glPopMatrix();
//repaint
update();
}
void openglwindow::resizeGL(int width, int height) {
if ( height == 0 )
height = 1;
glViewport( 0, 0, (GLint)width, (GLint)height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
void openglwindow::keyPressEvent(QKeyEvent * e) {
switch (e->key()) {
case Qt::Key_W:
cout << "w" << endl;
break;
case Qt::Key_A:
cout << "a" << endl;
break;
case Qt::Key_S:
cout << "s" << endl;
break;
case Qt::Key_D:
cout << "d" << endl;
break;
default:
break;
}
}
void openglwindow::mousePressEvent(QMouseEvent *event) {
if (event->buttons() == Qt::LeftButton) {
cout << "LeftButton mousePos (" << event->pos().x() << ", " << event->pos().y() << ")" << endl;
}
else if (event->buttons() & Qt::RightButton) {
cout << "RightButton mousePos (" << event->pos().x() << ", " << event->pos().y() << ")" << endl;
}
}
void openglwindow::mouseMoveEvent(QMouseEvent *event) {
}
void openglwindow::mouseReleaseEvent(QMouseEvent *event) {
}
void openglwindow::changeColorGreen() {
cout << "###### change Color Green!" << endl;
redChannel = 0;
}
void openglwindow::changeColorYellow() {
cout << "###### change Color Yellow!" << endl;
redChannel = 255;
}
相信有过freeglut编程的同学,认真看下代码也能大概搞清怎么写的了。
(1)简单图形显示:主要是initializeGL(); resizeGL(int width, int height); paintGL(); 三个函数。应该不用多说了,逻辑跟普通的OpenGL程序类似,但有一点是paintGL()里面调用了update()方法,为重新绘制。由于代码是让正方体绕y轴自动旋转,如果不加update()会静止不动,update()为重新绘制场景(旋转角时刻改变)。
(2)检测键盘、鼠标输入:即keyPressEvent(QKeyEvent * e) 、mousePressEvent(QMouseEvent *event) 那4个方法(注意include的头文件)。这几个方法也不难理解,逻辑也跟普通的OpenGL程序类似。
(3)下拉菜单选择不同功能:这个在下面说了ui界面之后再回头说。
3、有了这两份代码,还不能直接跑!需要在mainwindow.ui给程序设置一个显示窗口:
双击mainwindow.ui -> 左边的空间栏找到Widget,拖到设计界面,设置大小。
此时我们发现右边栏,centralWidget下多了一个子widget:
在widget上右键,提升为/提升的窗口部件 -> 输入类名称openglwindow -> 添加、提升:
看到widget那一行改成openglwindow 名字,即为提升成功。
此时,运行程序。应该就能看到图形了!
4、不过,此时是不是发现鼠标、键盘没响应?
由于在Qt下,widget只有获取焦点focus才能响应鼠标、键盘事件。我们回到.ui文件,点击上面的widget,看右下方属性栏,发现focusPolicy一栏为NoFocus,从这就可以知道原因了。点击,改为ClickFocus,保存,运行。就能检测到两种事件了:
5、最后为下拉菜单:
(1)先还是在界面文件内,可以看到中间设计界面上方,有个“在这里输入”按钮,就可以编辑菜单啦:
(2)做成上面这样的时候,可以看到右边栏,已经在menuBar下生产子menu了:
6、问题来了,怎么能让点击的菜单按钮跟代码方法连接起来呢?
从上面的openglwindow.h、openglwindow.cpp我们可以看到两个方法void changeColorGreen(); void changeColorYellow(); 也就是改变颜色,这个好写。还记得我上面说了,做菜单栏之前,mainwindow.h、mainwindow.cpp、main.cpp三份代码基本上不需要做什么修改。但是若要加菜单栏,就需要在这几个文件做文章了。(其实main.cpp也没做什么改动)
main.cpp :
#include "mainwindow.h"
#include "openglwindow.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
a.setApplicationName("My First openGL Widget");
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h :
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void changeColorGreen();
void changeColorYellow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp :
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->actionGreen_2, SIGNAL(triggered()), this, SLOT(changeColorGreen()));
connect(ui->actionYellow_2, SIGNAL(triggered()), this, SLOT(changeColorYellow()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::changeColorGreen() {
ui->widget->changeColorGreen();
}
void MainWindow::changeColorYellow() {
ui->widget->changeColorYellow();
}
改动的地方就是在.h文件了加了private slots两个方法,在.cpp文件里实现两个方法,并且用connect方法做连接(具体用法, 请鼠标靠近函数名按F1查看)。我们可以看到actionGreen_2、actionYellow_2、widget三个都是在ui文件里 几个对象的名称 :
好了,连接做好了,运行点击试试看~~~
7、最后放上整个项目文件结构:
但是值得一提的是,点击上面的“项目”,选择文件系统,会发现有个ui_mainwindow.h文件(mainwindow.cpp会include),可以点开研究下,对整个窗口、菜单之间串联连接会有更深理解的。
好了,关于Qt的OpenGL程序就介绍到这了。上面的框架可以直接使用哦~~~