1
2############################################################################
3##
4## Copyright (C) 2013 Riverbank Computing Limited.
5## Copyright (C) 2016 The Qt Company Ltd.
6## Contact: http://www.qt.io/licensing/
7##
8## This file is part of the Qt for Python examples of the Qt Toolkit.
9##
10## $QT_BEGIN_LICENSE:BSD$
11## You may use this file under the terms of the BSD license as follows:
12##
13## "Redistribution and use in source and binary forms, with or without
14## modification, are permitted provided that the following conditions are
15## met:
16##   * Redistributions of source code must retain the above copyright
17##     notice, this list of conditions and the following disclaimer.
18##   * Redistributions in binary form must reproduce the above copyright
19##     notice, this list of conditions and the following disclaimer in
20##     the documentation and/or other materials provided with the
21##     distribution.
22##   * Neither the name of The Qt Company Ltd nor the names of its
23##     contributors may be used to endorse or promote products derived
24##     from this software without specific prior written permission.
25##
26##
27## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
38##
39## $QT_END_LICENSE$
40##
41############################################################################
42
43"""PySide2 port of the opengl/legacy/hellogl example from Qt v5.x"""
44
45import sys
46import math
47from PySide2 import QtCore, QtGui, QtWidgets, QtOpenGL
48
49try:
50    from OpenGL import GL
51except ImportError:
52    app = QtWidgets.QApplication(sys.argv)
53    messageBox = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, "OpenGL hellogl",
54                                       "PyOpenGL must be installed to run this example.",
55                                       QtWidgets.QMessageBox.Close)
56    messageBox.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate")
57    messageBox.exec_()
58    sys.exit(1)
59
60
61class Window(QtWidgets.QWidget):
62    def __init__(self, parent=None):
63        QtWidgets.QWidget.__init__(self, parent)
64
65        self.glWidget = GLWidget()
66
67        self.xSlider = self.createSlider(QtCore.SIGNAL("xRotationChanged(int)"),
68                                         self.glWidget.setXRotation)
69        self.ySlider = self.createSlider(QtCore.SIGNAL("yRotationChanged(int)"),
70                                         self.glWidget.setYRotation)
71        self.zSlider = self.createSlider(QtCore.SIGNAL("zRotationChanged(int)"),
72                                         self.glWidget.setZRotation)
73
74        mainLayout = QtWidgets.QHBoxLayout()
75        mainLayout.addWidget(self.glWidget)
76        mainLayout.addWidget(self.xSlider)
77        mainLayout.addWidget(self.ySlider)
78        mainLayout.addWidget(self.zSlider)
79        self.setLayout(mainLayout)
80
81        self.xSlider.setValue(170 * 16)
82        self.ySlider.setValue(160 * 16)
83        self.zSlider.setValue(90 * 16)
84
85        self.setWindowTitle(self.tr("Hello GL"))
86
87    def createSlider(self, changedSignal, setterSlot):
88        slider = QtWidgets.QSlider(QtCore.Qt.Vertical)
89
90        slider.setRange(0, 360 * 16)
91        slider.setSingleStep(16)
92        slider.setPageStep(15 * 16)
93        slider.setTickInterval(15 * 16)
94        slider.setTickPosition(QtWidgets.QSlider.TicksRight)
95
96        self.glWidget.connect(slider, QtCore.SIGNAL("valueChanged(int)"), setterSlot)
97        self.connect(self.glWidget, changedSignal, slider, QtCore.SLOT("setValue(int)"))
98
99        return slider
100
101
102class GLWidget(QtOpenGL.QGLWidget):
103    xRotationChanged = QtCore.Signal(int)
104    yRotationChanged = QtCore.Signal(int)
105    zRotationChanged = QtCore.Signal(int)
106
107    def __init__(self, parent=None):
108        QtOpenGL.QGLWidget.__init__(self, parent)
109
110        self.object = 0
111        self.xRot = 0
112        self.yRot = 0
113        self.zRot = 0
114
115        self.lastPos = QtCore.QPoint()
116
117        self.trolltechGreen = QtGui.QColor.fromCmykF(0.40, 0.0, 1.0, 0.0)
118        self.trolltechPurple = QtGui.QColor.fromCmykF(0.39, 0.39, 0.0, 0.0)
119
120    def xRotation(self):
121        return self.xRot
122
123    def yRotation(self):
124        return self.yRot
125
126    def zRotation(self):
127        return self.zRot
128
129    def minimumSizeHint(self):
130        return QtCore.QSize(50, 50)
131
132    def sizeHint(self):
133        return QtCore.QSize(400, 400)
134
135    def setXRotation(self, angle):
136        angle = self.normalizeAngle(angle)
137        if angle != self.xRot:
138            self.xRot = angle
139            self.emit(QtCore.SIGNAL("xRotationChanged(int)"), angle)
140            self.updateGL()
141
142    def setYRotation(self, angle):
143        angle = self.normalizeAngle(angle)
144        if angle != self.yRot:
145            self.yRot = angle
146            self.emit(QtCore.SIGNAL("yRotationChanged(int)"), angle)
147            self.updateGL()
148
149    def setZRotation(self, angle):
150        angle = self.normalizeAngle(angle)
151        if angle != self.zRot:
152            self.zRot = angle
153            self.emit(QtCore.SIGNAL("zRotationChanged(int)"), angle)
154            self.updateGL()
155
156    def initializeGL(self):
157        self.qglClearColor(self.trolltechPurple.darker())
158        self.object = self.makeObject()
159        GL.glShadeModel(GL.GL_FLAT)
160        GL.glEnable(GL.GL_DEPTH_TEST)
161        GL.glEnable(GL.GL_CULL_FACE)
162
163    def paintGL(self):
164        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
165        GL.glLoadIdentity()
166        GL.glTranslated(0.0, 0.0, -10.0)
167        GL.glRotated(self.xRot / 16.0, 1.0, 0.0, 0.0)
168        GL.glRotated(self.yRot / 16.0, 0.0, 1.0, 0.0)
169        GL.glRotated(self.zRot / 16.0, 0.0, 0.0, 1.0)
170        GL.glCallList(self.object)
171
172    def resizeGL(self, width, height):
173        side = min(width, height)
174        GL.glViewport(int((width - side) / 2),int((height - side) / 2), side, side)
175
176        GL.glMatrixMode(GL.GL_PROJECTION)
177        GL.glLoadIdentity()
178        GL.glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0)
179        GL.glMatrixMode(GL.GL_MODELVIEW)
180
181    def mousePressEvent(self, event):
182        self.lastPos = QtCore.QPoint(event.pos())
183
184    def mouseMoveEvent(self, event):
185        dx = event.x() - self.lastPos.x()
186        dy = event.y() - self.lastPos.y()
187
188        if event.buttons() & QtCore.Qt.LeftButton:
189            self.setXRotation(self.xRot + 8 * dy)
190            self.setYRotation(self.yRot + 8 * dx)
191        elif event.buttons() & QtCore.Qt.RightButton:
192            self.setXRotation(self.xRot + 8 * dy)
193            self.setZRotation(self.zRot + 8 * dx)
194
195        self.lastPos = QtCore.QPoint(event.pos())
196
197    def makeObject(self):
198        genList = GL.glGenLists(1)
199        GL.glNewList(genList, GL.GL_COMPILE)
200
201        GL.glBegin(GL.GL_QUADS)
202
203        x1 = +0.06
204        y1 = -0.14
205        x2 = +0.14
206        y2 = -0.06
207        x3 = +0.08
208        y3 = +0.00
209        x4 = +0.30
210        y4 = +0.22
211
212        self.quad(x1, y1, x2, y2, y2, x2, y1, x1)
213        self.quad(x3, y3, x4, y4, y4, x4, y3, x3)
214
215        self.extrude(x1, y1, x2, y2)
216        self.extrude(x2, y2, y2, x2)
217        self.extrude(y2, x2, y1, x1)
218        self.extrude(y1, x1, x1, y1)
219        self.extrude(x3, y3, x4, y4)
220        self.extrude(x4, y4, y4, x4)
221        self.extrude(y4, x4, y3, x3)
222
223        Pi = 3.14159265358979323846
224        NumSectors = 200
225
226        for i in range(NumSectors):
227            angle1 = (i * 2 * Pi) / NumSectors
228            x5 = 0.30 * math.sin(angle1)
229            y5 = 0.30 * math.cos(angle1)
230            x6 = 0.20 * math.sin(angle1)
231            y6 = 0.20 * math.cos(angle1)
232
233            angle2 = ((i + 1) * 2 * Pi) / NumSectors
234            x7 = 0.20 * math.sin(angle2)
235            y7 = 0.20 * math.cos(angle2)
236            x8 = 0.30 * math.sin(angle2)
237            y8 = 0.30 * math.cos(angle2)
238
239            self.quad(x5, y5, x6, y6, x7, y7, x8, y8)
240
241            self.extrude(x6, y6, x7, y7)
242            self.extrude(x8, y8, x5, y5)
243
244        GL.glEnd()
245        GL.glEndList()
246
247        return genList
248
249    def quad(self, x1, y1, x2, y2, x3, y3, x4, y4):
250        self.qglColor(self.trolltechGreen)
251
252        GL.glVertex3d(x1, y1, +0.05)
253        GL.glVertex3d(x2, y2, +0.05)
254        GL.glVertex3d(x3, y3, +0.05)
255        GL.glVertex3d(x4, y4, +0.05)
256
257        GL.glVertex3d(x4, y4, -0.05)
258        GL.glVertex3d(x3, y3, -0.05)
259        GL.glVertex3d(x2, y2, -0.05)
260        GL.glVertex3d(x1, y1, -0.05)
261
262    def extrude(self, x1, y1, x2, y2):
263        self.qglColor(self.trolltechGreen.darker(250 + int(100 * x1)))
264
265        GL.glVertex3d(x1, y1, -0.05)
266        GL.glVertex3d(x2, y2, -0.05)
267        GL.glVertex3d(x2, y2, +0.05)
268        GL.glVertex3d(x1, y1, +0.05)
269
270    def normalizeAngle(self, angle):
271        while angle < 0:
272            angle += 360 * 16
273        while angle > 360 * 16:
274            angle -= 360 * 16
275        return angle
276
277    def freeResources(self):
278        self.makeCurrent()
279        GL.glDeleteLists(self.object, 1)
280
281if __name__ == '__main__':
282    app = QtWidgets.QApplication(sys.argv)
283    window = Window()
284    window.show()
285    res = app.exec_()
286    window.glWidget.freeResources()
287    sys.exit(res)
288