#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2018 Riverbank Computing Limited. ## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ## All rights reserved. ## ## This file is part of the examples of PyQt. ## ## $QT_BEGIN_LICENSE:BSD$ ## You may use this file under the terms of the BSD license as follows: ## ## "Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions are ## met: ## * Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## * Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in ## the documentation and/or other materials provided with the ## distribution. ## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor ## the names of its contributors may be used to endorse or promote ## products derived from this software without specific prior written ## permission. ## ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ## $QT_END_LICENSE$ ## ############################################################################# import sys import math from PyQt5.QtCore import pyqtSignal, QPoint, QSize, Qt from PyQt5.QtGui import QColor, QOpenGLVersionProfile from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QOpenGLWidget, QSlider, QWidget) class Window(QWidget): def __init__(self): super(Window, self).__init__() self.glWidget = GLWidget() self.xSlider = self.createSlider() self.ySlider = self.createSlider() self.zSlider = self.createSlider() self.xSlider.valueChanged.connect(self.glWidget.setXRotation) self.glWidget.xRotationChanged.connect(self.xSlider.setValue) self.ySlider.valueChanged.connect(self.glWidget.setYRotation) self.glWidget.yRotationChanged.connect(self.ySlider.setValue) self.zSlider.valueChanged.connect(self.glWidget.setZRotation) self.glWidget.zRotationChanged.connect(self.zSlider.setValue) mainLayout = QHBoxLayout() mainLayout.addWidget(self.glWidget) mainLayout.addWidget(self.xSlider) mainLayout.addWidget(self.ySlider) mainLayout.addWidget(self.zSlider) self.setLayout(mainLayout) self.xSlider.setValue(15 * 16) self.ySlider.setValue(345 * 16) self.zSlider.setValue(0 * 16) self.setWindowTitle("Hello GL") def createSlider(self): slider = QSlider(Qt.Vertical) slider.setRange(0, 360 * 16) slider.setSingleStep(16) slider.setPageStep(15 * 16) slider.setTickInterval(15 * 16) slider.setTickPosition(QSlider.TicksRight) return slider class GLWidget(QOpenGLWidget): xRotationChanged = pyqtSignal(int) yRotationChanged = pyqtSignal(int) zRotationChanged = pyqtSignal(int) def __init__(self, parent=None): super(GLWidget, self).__init__(parent) self.object = 0 self.xRot = 0 self.yRot = 0 self.zRot = 0 self.lastPos = QPoint() self.trolltechGreen = QColor.fromCmykF(0.40, 0.0, 1.0, 0.0) self.trolltechPurple = QColor.fromCmykF(0.39, 0.39, 0.0, 0.0) def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(400, 400) def setXRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.xRot: self.xRot = angle self.xRotationChanged.emit(angle) self.update() def setYRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.yRot: self.yRot = angle self.yRotationChanged.emit(angle) self.update() def setZRotation(self, angle): angle = self.normalizeAngle(angle) if angle != self.zRot: self.zRot = angle self.zRotationChanged.emit(angle) self.update() def initializeGL(self): version_profile = QOpenGLVersionProfile() version_profile.setVersion(2, 0) self.gl = self.context().versionFunctions(version_profile) self.gl.initializeOpenGLFunctions() self.setClearColor(self.trolltechPurple.darker()) self.object = self.makeObject() self.gl.glShadeModel(self.gl.GL_FLAT) self.gl.glEnable(self.gl.GL_DEPTH_TEST) self.gl.glEnable(self.gl.GL_CULL_FACE) def paintGL(self): self.gl.glClear( self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT) self.gl.glLoadIdentity() self.gl.glTranslated(0.0, 0.0, -10.0) self.gl.glRotated(self.xRot / 16.0, 1.0, 0.0, 0.0) self.gl.glRotated(self.yRot / 16.0, 0.0, 1.0, 0.0) self.gl.glRotated(self.zRot / 16.0, 0.0, 0.0, 1.0) self.gl.glCallList(self.object) def resizeGL(self, width, height): side = min(width, height) if side < 0: return self.gl.glViewport((width - side) // 2, (height - side) // 2, side, side) self.gl.glMatrixMode(self.gl.GL_PROJECTION) self.gl.glLoadIdentity() self.gl.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0) self.gl.glMatrixMode(self.gl.GL_MODELVIEW) def mousePressEvent(self, event): self.lastPos = event.pos() def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.setXRotation(self.xRot + 8 * dy) self.setYRotation(self.yRot + 8 * dx) elif event.buttons() & Qt.RightButton: self.setXRotation(self.xRot + 8 * dy) self.setZRotation(self.zRot + 8 * dx) self.lastPos = event.pos() def makeObject(self): genList = self.gl.glGenLists(1) self.gl.glNewList(genList, self.gl.GL_COMPILE) self.gl.glBegin(self.gl.GL_QUADS) x1 = +0.06 y1 = -0.14 x2 = +0.14 y2 = -0.06 x3 = +0.08 y3 = +0.00 x4 = +0.30 y4 = +0.22 self.quad(x1, y1, x2, y2, y2, x2, y1, x1) self.quad(x3, y3, x4, y4, y4, x4, y3, x3) self.extrude(x1, y1, x2, y2) self.extrude(x2, y2, y2, x2) self.extrude(y2, x2, y1, x1) self.extrude(y1, x1, x1, y1) self.extrude(x3, y3, x4, y4) self.extrude(x4, y4, y4, x4) self.extrude(y4, x4, y3, x3) NumSectors = 200 for i in range(NumSectors): angle1 = (i * 2 * math.pi) / NumSectors x5 = 0.30 * math.sin(angle1) y5 = 0.30 * math.cos(angle1) x6 = 0.20 * math.sin(angle1) y6 = 0.20 * math.cos(angle1) angle2 = ((i + 1) * 2 * math.pi) / NumSectors x7 = 0.20 * math.sin(angle2) y7 = 0.20 * math.cos(angle2) x8 = 0.30 * math.sin(angle2) y8 = 0.30 * math.cos(angle2) self.quad(x5, y5, x6, y6, x7, y7, x8, y8) self.extrude(x6, y6, x7, y7) self.extrude(x8, y8, x5, y5) self.gl.glEnd() self.gl.glEndList() return genList def quad(self, x1, y1, x2, y2, x3, y3, x4, y4): self.setColor(self.trolltechGreen) self.gl.glVertex3d(x1, y1, -0.05) self.gl.glVertex3d(x2, y2, -0.05) self.gl.glVertex3d(x3, y3, -0.05) self.gl.glVertex3d(x4, y4, -0.05) self.gl.glVertex3d(x4, y4, +0.05) self.gl.glVertex3d(x3, y3, +0.05) self.gl.glVertex3d(x2, y2, +0.05) self.gl.glVertex3d(x1, y1, +0.05) def extrude(self, x1, y1, x2, y2): self.setColor(self.trolltechGreen.darker(250 + int(100 * x1))) self.gl.glVertex3d(x1, y1, +0.05) self.gl.glVertex3d(x2, y2, +0.05) self.gl.glVertex3d(x2, y2, -0.05) self.gl.glVertex3d(x1, y1, -0.05) def normalizeAngle(self, angle): while angle < 0: angle += 360 * 16 while angle > 360 * 16: angle -= 360 * 16 return angle def setClearColor(self, c): self.gl.glClearColor(c.redF(), c.greenF(), c.blueF(), c.alphaF()) def setColor(self, c): self.gl.glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()) if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())