Reputation: 481
I am trying to piece example code for a PyGame PyOpenGL tutorial into example code for a PyQt5 QOpenGLWidget. The goal of this code is to set up a cube with one corner skewed upward in order to identify the angle of the camera. It works fine in PyGame, but there are several problems with the PyQt5 version:
First, the aspect ratio seems to be off. Second, the window recalls paintGL every time I make it active again. Third, most of the variables are not transferring the same in regards to glTranslatef and glRotatef.
The code I am using for PyGame:
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
verticies = (
(1, -1, -1),
(1, 1, -1),
(-1, 1, -1),
(-1, -1, -1),
(1, -1, 2),
(1, 1, 1),
(-1, -1, 1),
(-1, 1, 1)
)
edges = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4),
(5,7)
)
colors = (
(1,0,0),
(0,1,0),
(0,0,1),
(0,1,0),
(1,1,1),
(0,1,1),
(1,0,0),
(0,1,0),
(0,0,1),
(1,0,0),
(1,1,1),
(0,1,1),
)
surfaces = (
(0,1,2,3),
(3,2,7,6),
(6,7,5,4),
(4,5,1,0),
(1,5,7,2),
(4,0,3,6)
)
def Cube():
glBegin(GL_QUADS)
for surface in surfaces:
x = 0
for vertex in surface:
x+=1
glColor3fv(colors[x])
glVertex3fv(verticies[vertex])
glEnd()
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(verticies[vertex])
glEnd()
def main():
pygame.init()
display = (800,600)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
glTranslatef(0,0, -10) #these two lines set the camera facing at the cube from the position 0, -10, 0.
glRotatef(-90, 2, 0, 0)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
glTranslatef(-0.5,0,0)
if event.key == pygame.K_RIGHT:
glTranslatef(0.5,0,0)
if event.key == pygame.K_UP:
glTranslatef(0,1,0)
if event.key == pygame.K_DOWN:
glTranslatef(0,-1,0)
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
glTranslatef(0,0,1.0)
if event.button == 5:
glTranslatef(0,0,-1.0)
#glRotatef(1, 3, 1, 1) #rotation code that was commented out.
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
Cube()
pygame.display.flip()
pygame.time.wait(10)
main()
The PyQt5 code:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.uic import *
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
class mainWindow(QMainWindow): #Main class.
verticies = (
(1, -1, -1),
(1, 1, -1),
(-1, 1, -1),
(-1, -1, -1),
(1, -1, 2),
(1, 1, 1),
(-1, -1, 1),
(-1, 1, 1)
)
edges = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4),
(5,7)
)
colors = (
(1,0,0),
(0,1,0),
(0,0,1),
(0,1,0),
(1,1,1),
(0,1,1),
(1,0,0),
(0,1,0),
(0,0,1),
(1,0,0),
(1,1,1),
(0,1,1),
)
surfaces = (
(0,1,2,3),
(3,2,7,6),
(6,7,5,4),
(4,5,1,0),
(1,5,7,2),
(4,0,3,6)
)
def __init__(self):
super(mainWindow, self).__init__()
self.width = 700 #Variables used for the setting of the size of everything
self.height = 600
self.setGeometry(0, 0, self.width, self.height) #Set the window size
def setupUI(self):
self.openGLWidget = QOpenGLWidget(self) #Create the GLWidget
self.openGLWidget.setGeometry(0, 0, self.width, self.height) #Size it the same as the window.
self.openGLWidget.initializeGL()
self.openGLWidget.resizeGL(self.width, self.height) #Resize GL's knowledge of the window to match the physical size?
self.openGLWidget.paintGL = self.paintGL #override the default function with my own?
def paintGL(self):
gluPerspective(45, self.width / self.height, 0.1, 50.0) #set perspective?
glTranslatef(0, 0, -2) #I used -10 instead of -2 in the PyGame version.
glRotatef(-90, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) #Straight from the PyGame version, with 'self' inserted occasionally
glBegin(GL_QUADS) #tell GL to draw surfaces
for surface in self.surfaces:
x = 0
for vertex in surface:
x+=1
glColor3fv(self.colors[x])
glVertex3fv(self.verticies[vertex])
glEnd() #tell GL to stop drawing surfaces
glBegin(GL_LINES) #tell GL to draw lines
for edge in self.edges:
for vertex in edge:
glVertex3fv(self.verticies[vertex])
glEnd() #tell GL to stop drawing lines.
app = QApplication([])
window = mainWindow()
window.setupUI()
window.show()
sys.exit(app.exec_())
The result:
When I switch to another window, then switch back to the Qt window, the scene updates and paintGL is called again. Also, the cube appears to be squashed and the camera acts differently. What can I do to fix these?
Python 3.8 Windows 10
Upvotes: 1
Views: 437
Reputation: 210878
The OpenGL matrix operations (like gluPerspective
, glTranslate
, glRotate
, ...) do not just set a matrix. The operations define a new matrix and multiply the current matrix by the new matrix. The causes that the matrix continuously and progressively changes, every time when paintGL
is called.
The issue can be solved with ease, by loading the identity matrix by glLoadIdentity
at the begin of paintGL
::
class mainWindow(QMainWindow):
# [...]
def paintGL(self):
glLoadIdentity()
gluPerspective(45, self.width / self.height, 0.1, 50.0) #set perspective?
glTranslatef(0, 0, -10) #I used -10 instead of -2 in the PyGame version.
glRotatef(-90, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
But Legacy OpenGL provides different matrices (see glMatrixMode
).
It is recommend to pout the projection matrix to the current GL_PROJECTION
matrix and the model view matrix to the current GL_MODELVIEW
matrix.
Update the viewport rectangle (glViewport
) and the projection matrix in the resize event callback (resizeGL
). Set the model view matrix in paintGL
:
class mainWindow(QMainWindow):
# [...]
def setupUI(self):
# [...]
self.openGLWidget.paintGL = self.paintGL
self.openGLWidget.resizeGL = self.resizeGL
def resizeGL(self, width, height):
self.width, self.height = width, height
# update viewport
glViewport(0, 0, self.width, self.height)
# set projection matrix
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, self.width / self.height, 0.1, 50.0) #set perspective?
def paintGL(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -10) #I used -10 instead of -2 in the PyGame version.
glRotatef(-90, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) #Straight from the PyGame version, with 'self' inserted occasionally
glBegin(GL_QUADS) #tell GL to draw surfaces
for surface in self.surfaces:
x = 0
for vertex in surface:
x+=1
glColor3fv(self.colors[x])
glVertex3fv(self.verticies[vertex])
glEnd() #tell GL to stop drawing surfaces
glBegin(GL_LINES) #tell GL to draw lines
for edge in self.edges:
for vertex in edge:
glVertex3fv(self.verticies[vertex])
glEnd() #tell GL to stop drawing lines.
Upvotes: 1