1#!/usr/bin/env python
2
3"""
4helloglwidget.py
5
6A simple OpenGL custom widget for Qt Designer.
7
8Copyright (C) 2007 David Boddie <david@boddie.org.uk>
9Copyright (C) 2005-2006 Trolltech ASA. All rights reserved.
10
11This program is free software; you can redistribute it and/or modify
12it under the terms of the GNU General Public License as published by
13the Free Software Foundation; either version 2 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24"""
25
26import math
27
28from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QPoint, QSize, Qt
29from PyQt5.QtGui import QColor
30from PyQt5.QtWidgets import QApplication, QOpenGLWidget
31
32
33class HelloGLWidget(QOpenGLWidget):
34    """HelloGLWidget(QOpenGLWidget)
35
36    Provides a custom widget to display an OpenGL-rendered Qt logo.
37    Various properties and slots are defined so that the user can rotate
38    the logo, and signals are defined to enable other components to
39    react to changes to its orientation.
40    """
41
42    # We define three signals that are used to indicate changes to the
43    # rotation of the logo.
44    xRotationChanged = pyqtSignal(int)
45    yRotationChanged = pyqtSignal(int)
46    zRotationChanged = pyqtSignal(int)
47
48    def __init__(self, parent=None):
49        super(HelloGLWidget, self).__init__(parent)
50
51        self.object = 0
52        self.xRot = 0
53        self.yRot = 0
54        self.zRot = 0
55
56        self.lastPos = QPoint()
57
58        self.trolltechGreen = QColor.fromCmykF(0.40, 0.0, 1.0, 0.0)
59        self.trolltechPurple = QColor.fromCmykF(0.39, 0.39, 0.0, 0.0)
60
61        self.setWindowTitle("Hello GL")
62
63    # The rotation of the logo about the x-axis can be controlled using the
64    # xRotation property, defined using the following getter and setter
65    # methods.
66
67    def getXRotation(self):
68        return self.xRot
69
70    # The setXRotation() setter method is also a slot.
71    @pyqtSlot(int)
72    def setXRotation(self, angle):
73        angle = self.normalizeAngle(angle)
74        if angle != self.xRot:
75            self.xRot = angle
76            self.xRotationChanged.emit(angle)
77            self.update()
78
79    xRotation = pyqtProperty(int, getXRotation, setXRotation)
80
81    # The rotation of the logo about the y-axis can be controlled using the
82    # yRotation property, defined using the following getter and setter
83    # methods.
84
85    def getYRotation(self):
86        return self.yRot
87
88    # The setYRotation() setter method is also a slot.
89    @pyqtSlot(int)
90    def setYRotation(self, angle):
91        angle = self.normalizeAngle(angle)
92        if angle != self.yRot:
93            self.yRot = angle
94            self.yRotationChanged.emit(angle)
95            self.update()
96
97    yRotation = pyqtProperty(int, getYRotation, setYRotation)
98
99    # The rotation of the logo about the z-axis can be controlled using the
100    # zRotation property, defined using the following getter and setter
101    # methods.
102
103    def getZRotation(self):
104        return self.zRot
105
106    # The setZRotation() setter method is also a slot.
107    @pyqtSlot(int)
108    def setZRotation(self, angle):
109        angle = self.normalizeAngle(angle)
110        if angle != self.zRot:
111            self.zRot = angle
112            self.zRotationChanged.emit(angle)
113            self.update()
114
115    zRotation = pyqtProperty(int, getZRotation, setZRotation)
116
117    def minimumSizeHint(self):
118        return QSize(50, 50)
119
120    def sizeHint(self):
121        return QSize(200, 200)
122
123    def initializeGL(self):
124        self.gl = self.context().versionFunctions()
125        self.gl.initializeOpenGLFunctions()
126
127        self.setClearColor(self.trolltechPurple.darker())
128        self.object = self.makeObject()
129        self.gl.glShadeModel(self.gl.GL_SMOOTH)
130        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
131        self.gl.glEnable(self.gl.GL_CULL_FACE)
132
133    def paintGL(self):
134        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)
135        self.gl.glLoadIdentity()
136        self.gl.glTranslated(0.0, 0.0, -10.0)
137        self.gl.glRotated(self.xRot / 16.0, 1.0, 0.0, 0.0)
138        self.gl.glRotated(self.yRot / 16.0, 0.0, 1.0, 0.0)
139        self.gl.glRotated(self.zRot / 16.0, 0.0, 0.0, 1.0)
140        self.gl.glCallList(self.object)
141
142    def resizeGL(self, width, height):
143        side = min(width, height)
144        self.gl.glViewport((width - side) / 2, (height - side) / 2, side, side)
145
146        self.gl.glMatrixMode(self.gl.GL_PROJECTION)
147        self.gl.glLoadIdentity()
148        self.gl.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)
149        self.gl.glMatrixMode(self.gl.GL_MODELVIEW)
150
151    def mousePressEvent(self, event):
152        self.lastPos = QPoint(event.pos())
153
154    def mouseMoveEvent(self, event):
155        dx = event.x() - self.lastPos.x()
156        dy = event.y() - self.lastPos.y()
157
158        if event.buttons() & Qt.LeftButton:
159            self.setXRotation(self.xRot + 8 * dy)
160            self.setYRotation(self.yRot + 8 * dx)
161        elif event.buttons() & Qt.RightButton:
162            self.setXRotation(self.xRot + 8 * dy)
163            self.setZRotation(self.zRot + 8 * dx)
164
165        self.lastPos = QPoint(event.pos())
166
167    def makeObject(self):
168        genList = self.gl.glGenLists(1)
169        self.gl.glNewList(genList, self.gl.GL_COMPILE)
170
171        self.gl.glBegin(self.gl.GL_QUADS)
172
173        x1 = +0.06
174        y1 = -0.14
175        x2 = +0.14
176        y2 = -0.06
177        x3 = +0.08
178        y3 = +0.00
179        x4 = +0.30
180        y4 = +0.22
181
182        self.quad(x1, y1, x2, y2, y2, x2, y1, x1)
183        self.quad(x3, y3, x4, y4, y4, x4, y3, x3)
184
185        self.extrude(x1, y1, x2, y2)
186        self.extrude(x2, y2, y2, x2)
187        self.extrude(y2, x2, y1, x1)
188        self.extrude(y1, x1, x1, y1)
189        self.extrude(x3, y3, x4, y4)
190        self.extrude(x4, y4, y4, x4)
191        self.extrude(y4, x4, y3, x3)
192
193        Pi = 3.14159265358979323846
194        NumSectors = 200
195
196        for i in range(NumSectors):
197            angle1 = (i * 2 * Pi) / NumSectors
198            x5 = 0.30 * math.sin(angle1)
199            y5 = 0.30 * math.cos(angle1)
200            x6 = 0.20 * math.sin(angle1)
201            y6 = 0.20 * math.cos(angle1)
202
203            angle2 = ((i + 1) * 2 * Pi) / NumSectors
204            x7 = 0.20 * math.sin(angle2)
205            y7 = 0.20 * math.cos(angle2)
206            x8 = 0.30 * math.sin(angle2)
207            y8 = 0.30 * math.cos(angle2)
208
209            self.quad(x5, y5, x6, y6, x7, y7, x8, y8)
210
211            self.extrude(x6, y6, x7, y7)
212            self.extrude(x8, y8, x5, y5)
213
214        self.gl.glEnd()
215        self.gl.glEndList()
216
217        return genList
218
219    def quad(self, x1, y1, x2, y2, x3, y3, x4, y4):
220        self.setColor(self.trolltechGreen)
221
222        self.gl.glVertex3d(x1, y1, -0.05)
223        self.gl.glVertex3d(x2, y2, -0.05)
224        self.gl.glVertex3d(x3, y3, -0.05)
225        self.gl.glVertex3d(x4, y4, -0.05)
226
227        self.gl.glVertex3d(x4, y4, +0.05)
228        self.gl.glVertex3d(x3, y3, +0.05)
229        self.gl.glVertex3d(x2, y2, +0.05)
230        self.gl.glVertex3d(x1, y1, +0.05)
231
232    def extrude(self, x1, y1, x2, y2):
233        self.setColor(self.trolltechGreen.darker(250 + int(100 * x1)))
234
235        self.gl.glVertex3d(x1, y1, +0.05)
236        self.gl.glVertex3d(x2, y2, +0.05)
237        self.gl.glVertex3d(x2, y2, -0.05)
238        self.gl.glVertex3d(x1, y1, -0.05)
239
240    def normalizeAngle(self, angle):
241        while angle < 0:
242            angle += 360 * 16
243        while angle > 360 * 16:
244            angle -= 360 * 16
245        return angle
246
247    def setClearColor(self, c):
248        self.gl.glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF())
249
250    def setColor(self, c):
251        self.gl.glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF())
252
253
254if __name__ == "__main__":
255
256    import sys
257
258    app = QApplication(sys.argv)
259    widget = HelloGLWidget()
260    widget.show()
261    sys.exit(app.exec_())
262