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