PyQtサンプル

ousttrue2007-11-19

情報収集をしつつPyQtの使用を開始。検索するときはむしろPyQt4とすべきかも。
とりあえずOpenGLの使い方を調べてみた。
pyqtOpenGLサンプルhellogl.pywのコードを減らしながら解読。
QGLWidgetクラスがOpenGLを扱うクラスでこれを継承して、下記のメソッドをオーバーライドする仕組みだとわかった。
特にイベントとコールバックの結びつけなんかは要らなかった。
決まったメソッドをオーバーライドする方式らしい。


QGLWidgetのオーバーライドするメソッド

paintGL()
描画時のコールバック
resizeGL()
ウィンドウリサイズ時のコールバック
initializeGL()
OpenGLの初期設定など

他にマウス関連のメソッドでmousePressEvent、mouseMoveEventなんかがあった。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import math
from PyQt4 import QtCore, QtGui, QtOpenGL
from OpenGL import GL, GLU

class GLWidget(QtOpenGL.QGLWidget):
  def __init__(self, parent=None):
    QtOpenGL.QGLWidget.__init__(self, QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers), parent)
    self.xRot = 0
    self.yRot = 0
    self.zRot = 0
    self.lastPos = QtCore.QPoint()
    self.trolltechGreen = QtGui.QColor.fromCmykF(0.40, 0.0, 1.0, 0.0)
    self.trolltechPurple = QtGui.QColor.fromCmykF(0.39, 0.39, 0.0, 0.0)

  def minimumSizeHint(self):
    return QtCore.QSize(50, 50)

  def sizeHint(self):
    return QtCore.QSize(150, 150)

  def setXRotation(self, angle):
    angle = self.normalizeAngle(angle)
    if angle != self.xRot:
      self.xRot = angle
      self.emit(QtCore.SIGNAL("xRotationChanged(int)"), angle)
      self.updateGL()

  def setYRotation(self, angle):
    angle = self.normalizeAngle(angle)
    if angle != self.yRot:
      self.yRot = angle
      self.emit(QtCore.SIGNAL("yRotationChanged(int)"), angle)
      self.updateGL()

  def setZRotation(self, angle):
    angle = self.normalizeAngle(angle)
    if angle != self.zRot:
      self.zRot = angle
      self.emit(QtCore.SIGNAL("zRotationChanged(int)"), angle)
      self.updateGL()

  def initializeGL(self):
    self.qglClearColor(self.trolltechPurple.dark())
    GL.glClearDepth(1.0)
    self.object = self.makeObject()
    #GL.glShadeModel(GL.GL_FLAT)
    GL.glShadeModel(GL.GL_SMOOTH)
    GL.glDepthFunc(GL.GL_LESS)
    GL.glEnable(GL.GL_DEPTH_TEST)
    GL.glEnable(GL.GL_CULL_FACE)

  def paintGL(self):
    GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
    GL.glLoadIdentity()
    GL.glTranslated(0.0, 0.0, -10.0)
    GL.glRotated(self.xRot / 16.0, 1.0, 0.0, 0.0)
    GL.glRotated(self.yRot / 16.0, 0.0, 1.0, 0.0)
    GL.glRotated(self.zRot / 16.0, 0.0, 0.0, 1.0)
    GL.glEnable(GL.GL_LIGHTING)
    GL.glEnable(GL.GL_LIGHT0)
    GL.glCallList(self.object)

  def resizeGL(self, width, height):
    side = min(width, height)
    GL.glViewport((width - side) / 2, (height - side) / 2, side, side)

    GL.glMatrixMode(GL.GL_PROJECTION)
    GL.glLoadIdentity()
    GL.glOrtho(-0.5, +0.5, +0.5, -0.5, 4.0, 15.0)
    GL.glMatrixMode(GL.GL_MODELVIEW)

  def mousePressEvent(self, event):
    self.lastPos = QtCore.QPoint(event.pos())

  def mouseMoveEvent(self, event):
    dx = event.x() - self.lastPos.x()
    dy = event.y() - self.lastPos.y()

    if event.buttons() & QtCore.Qt.LeftButton:
      self.setXRotation(self.xRot + 8 * dy)
      self.setYRotation(self.yRot + 8 * dx)
    elif event.buttons() & QtCore.Qt.RightButton:
      self.setXRotation(self.xRot + 8 * dy)
      self.setZRotation(self.zRot + 8 * dx)

    self.lastPos = QtCore.QPoint(event.pos())

  def makeObject(self):
    vertices=[
          (-0.1, -0.1, -0.1), (0.1, -0.1, -0.1), (0.1, -0.1, 0.1), (-0.1, -0.1, 0.1),
          (-0.1, 0.1, -0.1), (0.1, 0.1, -0.1), (0.1, 0.1, 0.1), (-0.1, 0.1, 0.1),
        ]
    faces=[
          (0, 1, 2, 3), (7, 6, 5, 4), (1, 0, 4, 5), (2, 1, 5, 6), (3, 2, 6, 7), (0, 3, 7, 4)
        ]
    normals=[(0, -1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 0), (0, 0, -1), (-1, 0, 0)]

    genList = GL.glGenLists(1)
    GL.glNewList(genList, GL.GL_COMPILE)
    GL.glEnable(GL.GL_NORMALIZE)
    GL.glBegin(GL.GL_QUADS)
    self.qglColor(self.trolltechGreen)

    for i in range(len(faces)):
      GL.glNormal3dv(normals[i])
      GL.glVertex(vertices[faces[i][0]])
      GL.glVertex(vertices[faces[i][1]])
      GL.glVertex(vertices[faces[i][2]])
      GL.glVertex(vertices[faces[i][3]])

    GL.glEnd()
    GL.glEndList()
    return genList

  def normalizeAngle(self, angle):
    while angle < 0:
      angle += 360 * 16
    while angle > 360 * 16:
      angle -= 360 * 16
    return angle

class Window(QtGui.QWidget):
  def __init__(self, parent=None):
    QtGui.QWidget.__init__(self, parent)
    self.glWidget = GLWidget()
    mainLayout = QtGui.QHBoxLayout()
    mainLayout.addWidget(self.glWidget)
    self.setLayout(mainLayout)
    self.setWindowTitle(self.tr("Hello GL"))

if __name__ == '__main__':
  app = QtGui.QApplication(sys.argv)
  window = Window()
  window.show()
  sys.exit(app.exec_())