使用openGL绘制图形显示在pyqt建立的ui界面中

使用openGL绘制图形显示在pyqt建立的ui界面中,一种方法是使用widget对象,将widget提升成自定义成openglwidget显示openGL的图像。

1.安装pyqt、qtdesigner.exe、pyuic.exe工具。
qtdesigner.exe可以像QT的IDE一样,拖拽生成操作界面(所见即所得的界面生成工具)。
pyuic.exe可以把界面的.ui文件转化为py文件。

安装 pyQt5 和 pyQt5-tool, 直接使用 pip 或者 conda 安装就行. 设置好pycharm中的 external tool。参考博客1。
博客1:https://blog.csdn.net/qq_42980303/article/details/87869884

在博客1中提供的路径,安装后可能找不到 designer.exe 文件。
我用的是Anaconda, 是在E:\Anaconda\envs\py_tensorflow_keras\Library\bin路径下找到的。还是找不到的话,参考博客2和博客2.1。
博客2:https://blog.csdn.net/sinat_21427221/article/details/77448857
博客2.1:https://blog.csdn.net/qqwangfan/article/details/114290781

同样的方法安装一下 PYUIC.exe。参考博客1。
我使用的路径是E:\Anaconda\envs\py_tensorflow_keras\python.exe

2.安装openGL。
我使用的版本是py-3.7,直接使用conda或pip安装的openGL使用有问题,无法正常运行。所以使用.whl文件进行安装。
安装文件是 PyOpenGL-3.1.5-cp37-cp37m-win_amd64.whl
还有一个加速包 PyOpenGL_accelerate-3.1.5-cp37-cp37m-win_amd64.whl
下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl

3.使用qtdesigner.exe图形化界面设计。
参考博客3可以进行ui设计。直接使用参考的代码, 可能会出现 AttributeError: ‘MyPyQT_Form’ object has no attribute ‘setCentralWidget’ 的错误。解决方法是继承时使用 QtWidgets.QMainWindow 代替 QtWidgets.QWidget 。参考博客4。
博客3:https://www.cnblogs.com/lsdb/p/9122425.html
博客4:https://blog.csdn.net/wardenjohn/article/details/87628891

4.qt设计的ui中显示openGL图形。
ui中显示openGL图形需要使用widget对象,将widget提升成自定义成openglwidget显示openGL的图像。参考博客5。
博客5:https://www.cxyzjd.com/article/qq_39694792/111719618

5.示例。
设计界面如图:
使用openGL绘制图形显示在pyqt建立的ui界面中_第1张图片
对于按键QPushButton,无法像QT一样直接转到槽,而是需要自行设置。
首先需要添加槽函数。
使用openGL绘制图形显示在pyqt建立的ui界面中_第2张图片

绑定槽函数和信号。
使用openGL绘制图形显示在pyqt建立的ui界面中_第3张图片
设置好发送接收和触发方式,在MainWindows类中定义槽函数即可。

对于显示对象QTextEdit,直接使用即可。

import sys
from PyQt5 import QtWidgets, QtOpenGL
from Ui_design import Ui_MainWindow


# 这里需要用继承的方法. 因为生成的文件可能会因为界面而改变, 直接继承就不用担心代码被覆盖.
class MyPyQT_Form(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyPyQT_Form, self).__init__(parent=parent)
        self.setupUi(self)      # 这个是继承.ui文件的类里面来的.

    #实现pushButton_click()函数,textEdit是我们放上去的文本框的id
    def pushButton_display_click(self):
        self.textEdit_display.setText("你点击了按钮")

    def pushButton_clear_click(self):
        self.textEdit_display.clear()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MyPyQT_Form()
    w.show()
    sys.exit(app.exec_())

参考博客5的方式,将widget对象提升。
右键选择“提升为…”,填写 提升类的对象 和 头文件 即可(任意填写),添加即可。
使用openGL绘制图形显示在pyqt建立的ui界面中_第4张图片

显示效果:
使用openGL绘制图形显示在pyqt建立的ui界面中_第5张图片
在生成头文件opengl_widget.py中,继承QOpenGLWidget。参考博客5(openGL部分代码是直接赋值粘贴过来的)。
此处需要注意,继承的类中有三个虚函数,需要重写。
参考文档:
https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QOpenGLWidget.html

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL

class openGL_Widget(QtWidgets.QOpenGLWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        # 这个三个是虚函数, 需要重写
        # paintGL
        # initializeGL
        # resizeGL

    # 启动时会先调用 initializeGL, 再调用 resizeGL , 最后调用两次 paintGL
    # 出现窗口覆盖等情况时, 会自动调用 paintGL
    # 调用过程参考 https://segmentfault.com/a/1190000002403921
    # 绘图之前的设置
    def initializeGL(self):
        glClearColor(0, 0, 0, 1)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_LIGHT0)
        glEnable(GL_LIGHTING)
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
        glEnable(GL_COLOR_MATERIAL)

	# 绘图函数
    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glBegin(GL_TRIANGLES)
        glColor3f(1.0, 0.0, 0.0)
        glVertex3f(-0.5, -0.5, 0)
        glColor3f(0.0, 1.0, 0.0)
        glVertex3f(0.5, -0.5, 0)
        glColor3f(0.0, 0.0, 1.0)
        glVertex3f(0.0, 0.5, 0)

        glEnd()

    def resizeGL(self, w, h):
        glViewport(0, 0, w, h)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45, w / h, 0.01, 100.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0)

附上ui转换得到的py文件。
这个文件是PyUIC转换得到的,每次修改.ui绘图时都需要重新生成。所以不建议修改此文件,继承使用即可。

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(946, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton_display = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_display.setGeometry(QtCore.QRect(10, 270, 91, 31))
        self.pushButton_display.setObjectName("pushButton_display")
        self.pushButton_clear = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_clear.setGeometry(QtCore.QRect(120, 270, 91, 31))
        self.pushButton_clear.setObjectName("pushButton_clear")
        self.textEdit_display = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit_display.setGeometry(QtCore.QRect(10, 80, 241, 181))
        self.textEdit_display.setObjectName("textEdit_display")
        self.widget = openGL_Widget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(270, 10, 640, 511))
        self.widget.setObjectName("widget")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 946, 23))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.pushButton_display.clicked.connect(MainWindow.pushButton_display_click)
        self.pushButton_clear.clicked.connect(MainWindow.pushButton_clear_click)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_display.setText(_translate("MainWindow", "显示"))
        self.pushButton_clear.setText(_translate("MainWindow", "清除"))
from opengl_widget import openGL_Widget

最终的显示效果:
使用openGL绘制图形显示在pyqt建立的ui界面中_第6张图片
补充一点(代码中也有注释):
在py程序启动时会先调用 initializeGL, 再调用 resizeGL , 最后调用两次 paintGL;出现窗口覆盖等情况时, 会自动调用 paintGL;出现窗口大小变换时会调用resizeGL。
参考博客: https://segmentfault.com/a/1190000002403921

上述方式是被动刷新,是需要对窗口的操作才会调用paintGL进行更新图像。如果需要主动刷新图像,可以使用widget.update()方法,本质上也是调用paintGL进行更新图像。
例如在最开始的代码中,在希望按下按键时刷新图像,可以写为:

    def pushButton_display_click(self):
        self.textEdit_display.setText("你点击了按钮")
        self.widget.update()	# 刷新图像

你可能感兴趣的:(ui,qt,python)