以下是一个完整的Python + Tkinter + OpenGL应用示例,展示了一个交互式3D立方体旋转程序。这个程序结合了Tkinter的GUI控件和OpenGL的3D渲染能力:
运行说明
-
安装依赖库:
pip install numpy pyopengl pyopengl-accelerate pyopengltk
-
运行程序:
python your_file_name.py
功能特点
-
3D渲染:
-
彩色立方体显示
-
坐标轴可视化
-
顶点高亮显示
-
动态光照效果
-
-
交互控制:
-
鼠标拖拽旋转立方体
-
滚轮缩放视图
-
控制面板调整参数
-
自动旋转开关
-
-
GUI控制面板:
-
光源强度调节
-
旋转速度控制
-
自动旋转开关
-
重置视图按钮
-
实时状态显示
-
技术要点
-
OpenGL集成:
-
使用
pyopengltk
库集成OpenGL到Tkinter -
现代OpenGL渲染管线
-
光照模型实现
-
坐标系统管理
-
-
交互实现:
-
鼠标事件处理
-
动画循环
-
参数实时更新
-
-
Tkinter高级功能:
-
使用ttk主题化控件
-
响应式布局设计
-
状态栏信息显示
-
完整代码
import tkinter as tk
from tkinter import ttk
import numpy as np
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from pyopengltk import OpenGLFrame
import math
import time
class OpenGL3DViewer(OpenGLFrame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
# 初始化参数
self.rotation_x = 30
self.rotation_y = 45
self.zoom = 3.0
self.light_intensity = 0.7
self.animation_speed = 1.0
self.animate = True
# 绑定事件
self.bind("<B1-Motion>", self.on_mouse_drag)
self.bind("<MouseWheel>", self.on_mouse_wheel)
# 立方体顶点和颜色
self.vertices = np.array([
[ 1, 1, 1], [-1, 1, 1], [-1, -1, 1], [ 1, -1, 1], # 前
[ 1, 1, -1], [-1, 1, -1], [-1, -1, -1], [ 1, -1, -1], # 后
], dtype=np.float32)
self.colors = np.array([
[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 1.0, 0.0],
[1.0, 0.0, 1.0], [0.0, 1.0, 1.0], [0.5, 0.5, 0.5], [1.0, 1.0, 1.0],
], dtype=np.float32)
# 立方体面(每个面由4个顶点组成)
self.faces = [
(0, 1, 2, 3), # 前
(4, 5, 6, 7), # 后
(0, 4, 7, 3), # 右
(1, 5, 6, 2), # 左
(0, 1, 5, 4), # 上
(3, 2, 6, 7) # 下
]
self.last_time = time.time()
def initgl(self):
"""初始化OpenGL设置"""
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glClearColor(0.1, 0.1, 0.2, 1.0) # 深蓝色背景
# 设置光源
glLightfv(GL_LIGHT0, GL_POSITION, [2.0, 3.0, 4.0, 1.0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1.0])
glLightfv(GL_LIGHT0, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0])
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, self.width / self.height, 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
def redraw(self):
"""渲染场景"""
current_time = time.time()
delta_time = current_time - self.last_time
self.last_time = current_time
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
# 设置相机位置
gluLookAt(0, 0, self.zoom, 0, 0, 0, 0, 1, 0)
# 应用旋转
if self.animate:
self.rotation_y += 30 * delta_time * self.animation_speed
glRotatef(self.rotation_x, 1, 0, 0)
glRotatef(self.rotation_y, 0, 1, 0)
# 更新光源强度
glLightfv(GL_LIGHT0, GL_DIFFUSE, [self.light_intensity] * 3 + [1.0])
# 绘制坐标轴
self.draw_axes()
# 绘制立方体
glBegin(GL_QUADS)
for face in self.faces:
for vertex_idx in face:
glColor3fv(self.colors[vertex_idx])
glVertex3fv(self.vertices[vertex_idx])
glEnd()
# 绘制顶点
glPointSize(8.0)
glBegin(GL_POINTS)
for i, vertex in enumerate(self.vertices):
glColor3fv(self.colors[i])
glVertex3fv(vertex)
glEnd()
def draw_axes(self):
"""绘制坐标轴"""
glLineWidth(2.0)
glBegin(GL_LINES)
# X轴 (红色)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(0, 0, 0)
glVertex3f(2.5, 0, 0)
# Y轴 (绿色)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(0, 0, 0)
glVertex3f(0, 2.5, 0)
# Z轴 (蓝色)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(0, 0, 0)
glVertex3f(0, 0, 2.5)
glEnd()
# 绘制轴标签
self.draw_text(2.6, 0, 0, "X")
self.draw_text(0, 2.6, 0, "Y")
self.draw_text(0, 0, 2.6, "Z")
def draw_text(self, x, y, z, text):
"""在3D空间中绘制文本"""
glColor3f(1, 1, 1)
glRasterPos3f(x, y, z)
for char in text:
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ord(char))
def on_mouse_drag(self, event):
"""鼠标拖拽旋转立方体"""
dx = event.x - self.prev_mouse_x
dy = event.y - self.prev_mouse_y
self.rotation_x += dy * 0.5
self.rotation_y += dx * 0.5
self.rotation_x = max(-90, min(90, self.rotation_x))
self.prev_mouse_x = event.x
self.prev_mouse_y = event.y
self.animate = False
def on_mouse_wheel(self, event):
"""鼠标滚轮缩放"""
self.zoom += event.delta * 0.001
self.zoom = max(1.0, min(10.0, self.zoom))
def mouse_down(self, event):
"""鼠标按下事件"""
self.prev_mouse_x = event.x
self.prev_mouse_y = event.y
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("Python + Tkinter + OpenGL 3D 演示")
self.geometry("900x700")
# 创建主框架
main_frame = ttk.Frame(self)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 创建控制面板
control_frame = ttk.LabelFrame(main_frame, text="控制面板")
control_frame.pack(fill=tk.X, padx=5, pady=5)
# 添加控制组件
ttk.Label(control_frame, text="光源强度:").grid(row=0, column=0, padx=5, pady=5)
self.light_scale = ttk.Scale(control_frame, from_=0.1, to=1.0,
command=self.update_light)
self.light_scale.set(0.7)
self.light_scale.grid(row=0, column=1, padx=5, pady=5, sticky=tk.EW)
ttk.Label(control_frame, text="旋转速度:").grid(row=1, column=0, padx=5, pady=5)
self.speed_scale = ttk.Scale(control_frame, from_=0.0, to=3.0,
command=self.update_speed)
self.speed_scale.set(1.0)
self.speed_scale.grid(row=1, column=1, padx=5, pady=5, sticky=tk.EW)
self.animate_var = tk.BooleanVar(value=True)
self.animate_chk = ttk.Checkbutton(control_frame, text="自动旋转",
variable=self.animate_var,
command=self.toggle_animation)
self.animate_chk.grid(row=2, column=0, columnspan=2, padx=5, pady=5)
ttk.Button(control_frame, text="重置视图", command=self.reset_view).grid(
row=3, column=0, columnspan=2, padx=5, pady=5, sticky=tk.EW)
# 创建OpenGL视图
self.gl_frame = OpenGL3DViewer(main_frame, width=800, height=600)
self.gl_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 状态栏
self.status_var = tk.StringVar()
status_bar = ttk.Label(main_frame, textvariable=self.status_var,
relief=tk.SUNKEN, anchor=tk.W)
status_bar.pack(fill=tk.X, padx=5, pady=5)
self.update_status()
# 动画循环
self.after(10, self.animate)
def animate(self):
"""动画循环"""
if self.gl_frame.animate:
self.gl_frame.redraw()
self.update_status()
self.after(10, self.animate)
def update_status(self):
"""更新状态栏"""
status = f"旋转: X={self.gl_frame.rotation_x:.1f}° Y={self.gl_frame.rotation_y:.1f}° | " \
f"缩放: {self.gl_frame.zoom:.1f} | " \
f"光源: {self.gl_frame.light_intensity*100:.0f}%"
self.status_var.set(status)
def update_light(self, value):
"""更新光源强度"""
self.gl_frame.light_intensity = float(value)
def update_speed(self, value):
"""更新旋转速度"""
self.gl_frame.animation_speed = float(value)
def toggle_animation(self):
"""切换自动旋转状态"""
self.gl_frame.animate = self.animate_var.get()
def reset_view(self):
"""重置视图"""
self.gl_frame.rotation_x = 30
self.gl_frame.rotation_y = 45
self.gl_frame.zoom = 3.0
self.gl_frame.light_intensity = 0.7
self.light_scale.set(0.7)
self.gl_frame.animation_speed = 1.0
self.speed_scale.set(1.0)
self.gl_frame.animate = True
self.animate_var.set(True)
if __name__ == "__main__":
# 初始化GLUT
glutInit()
app = Application()
app.mainloop()
这个程序展示了如何将Tkinter的GUI能力与OpenGL的3D渲染能力完美结合,创建一个功能完整的3D可视化应用。