[PYTHON] PyOpenGL GUI selection and separation of drawing and GUI

Before going to Shader or VBO, organize the connection between GUI and PyOpenGL. PyOpenGL can be operated with various GUIs, but if it is a sample, it will stop at glut, so I will organize that area. If it's a slightly complicated app, you want to display a list or tree next to it.

The environment is Windows 10 (64bit) + Anaconda python3.5 (64bit)

glut For some reason, OpenGL starts here. Since the theme is to separate drawing and GUI, the part that only draws OpenGL is as follows.

simple_renderer.py


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

def initialize():
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glDepthFunc(GL_LESS)
    glEnable(GL_DEPTH_TEST)

def resize(Width, Height):
    # viewport
    if Height == 0:
        Height = 1
    glViewport(0, 0, Width, Height)
    # projection
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)

def draw():
    # clear
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    # view
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    # model
    glTranslatef(0.0, 0.0, -6.0)
    glBegin(GL_POLYGON)
    glVertex3f(0.0, 1.0, 0.0)
    glVertex3f(1.0, -1.0, 0.0)
    glVertex3f(-1.0, -1.0, 0.0)
    glEnd()

    glFlush()

Code that calls simple_renderer from glut.

simple_glut.py


from OpenGL.GLUT import *
import sys
import simple_renderer


def reshape_func(w, h):
    simple_renderer.resize(w, h == 0 and 1 or h)

def disp_func():
    simple_renderer.draw()
    glutSwapBuffers()

if __name__=="__main__":
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
    glutInitWindowSize(256, 256)
    glutCreateWindow(b"triangle")
    glutDisplayFunc(disp_func)
    glutIdleFunc(disp_func)
    glutReshapeFunc(reshape_func)

    simple_renderer.initialize()

    glutMainLoop()

simple_glut.png

Rendering (simple_renderer.py) doesn't know about glut, and GUI (simple_gtlut.py) doesn't know about OpenGL. So, I want to share the simple_renderer.py part in each GUI.

glglue library

I extended simple_renderer.py and made a library that can be described as follows.

https://github.com/ousttrue/glglue

controller_sample.py


# coding: utf-8
from OpenGL.GL import *


class Controller(object):
    def __init__(self):
        pass

    def onResize(self, w, h):
        glViewport(0, 0, w, h)

    def onLeftDown(self, x, y):
        print('onLeftDown', x, y)

    def onLeftUp(self, x, y):
        print('onLeftUp', x, y)

    def onMiddleDown(self, x, y):
        print('onMiddleDown', x, y)

    def onMiddleUp(self, x, y):
        print('onMiddleUp', x, y)

    def onRightDown(self, x, y):
        print('onRightDown', x, y)

    def onRightUp(self, x, y):
        print('onRightUp', x, y)

    def onMotion(self, x, y):
        print('onMotion', x, y)

    def onWheel(self, d):
        print('onWheel', d)

    def onKeyDown(self, keycode):
        print('onKeyDown', keycode)

    def onUpdate(self, d):
        print('onUpdate', d)

    def draw(self):
        glClearColor(0.9, 0.5, 0.0, 0.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glBegin(GL_TRIANGLES)
        glVertex(-1.0,-1.0)
        glVertex( 1.0,-1.0)
        glVertex( 0.0, 1.0)
        glEnd()

        glFlush()


if __name__=="__main__":
    controller=Controller()
    import glglue.glut
    glglue.glut.mainloop(controller, width=640, height=480, title=b"sample")    

Call Win32 API directly

You shouldn't just create a window with ctypes. python has the ability to call dlls with ctypes, so do that. It was the only method on Windows that did not require an additional installation, If you use Anaconda, PyQt4 is fine. It is a merit to partition the main loop by yourself.

wgl_sample.py


import controller_sample
import glglue.wgl

if __name__=="__main__":
    controller=controller_sample.Controller()
    glglue.wgl.mainloop(controller, width=640, height=480, title=b"sample")

tkinter A Tcl / TK python wrapper that comes with Python soberly. Unfortunately I need to install an additional dll (TOGL) for the OpenGL widget Therefore, it cannot be used immediately without doing anything. If you're not using Anaconda and want to save storage, you might use it.

Postscript

I was modifying (↓) to replace the 3D view of PMCA with TOGL, but I found out that TOGL is not supported more than I expected.

http://togl.sourceforge.net/

You can download the 32-bit version of TOGL2.0.dll with TCL / TK, which is compatible with TCL / TK up to 3.3. It doesn't work after 3.4. There is no 64-bit version of the dll in the first place.

Furthermore, PyOpenGL's home Had the following cautions. TOGL seems to be awkward to compile.

Note that Togl support is deprecated, it's there for legacy code (once you compile Togl), but you should choose a GUI library that has OpenGL support built-in for any new development. Togl support has been dropped due to the complexity of compilation and the headache of maintenance. There are projects which attempt to provide Togl support, feel free to install those into your Python's Tk installation if you need Togl under Python.

The red icon is the proof of TOGL.

pmca_ss.png

TOGL was a possible option given the installation effort and cross-platform. It is out of support.

Did the following Python3 incompatibility error be left unattended also indicate that TOGL is out of support?

glglue> c:\Anaconda3\python.exe tkinter_sample.py
Traceback (most recent call last):
  File "tkinter_sample.py", line 3, in <module>
    import glglue.togl
  File "D:\dev\_python\glglue\glglue\togl.py", line 4, in <module>
    import OpenGL.Tk
  File "c:\Anaconda3\lib\site-packages\OpenGL\Tk\__init__.py", line 102, in <mod
ule>
    if sys.maxint > 2**32:
AttributeError: module 'sys' has no attribute 'maxint'

Modify sys.maxint to sys.maxsize.

PyQt4 It was included in Anaconda. PyQt5 is good anyway, For the time being, this is my favorite.

pyqt4_sample.py


from PyQt4 import Qt
import controller_sample
import glglue.qgl
class Window(Qt.QWidget):
    def __init__(self, parent=None):
        Qt.QWidget.__init__(self, parent)
        # setup opengl widget
        self.controller=controller_sample.Controller()
        self.glwidget=glglue.qgl.Widget(self, self.controller)
        # packing
        mainLayout = Qt.QHBoxLayout()
        mainLayout.addWidget(self.glwidget)
        self.setLayout(mainLayout)

import sys
app = Qt.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Summary

PyQt4 is good for Anaconda.

In the future, I plan to use VBO and Shader with numpy.

Recommended Posts

PyOpenGL GUI selection and separation of drawing and GUI
Separation of design and data in matplotlib
Separation of Japanese surname and given name with BERT
Selection of measurement data