Python + Tkinter + OpenGL应用示例

以下是一个完整的Python + Tkinter + OpenGL应用示例,展示了一个交互式3D立方体旋转程序。这个程序结合了Tkinter的GUI控件和OpenGL的3D渲染能力:

运行说明

  1. 安装依赖库

    pip install numpy pyopengl pyopengl-accelerate pyopengltk
  2. 运行程序

    python your_file_name.py

功能特点

  1. 3D渲染

    • 彩色立方体显示

    • 坐标轴可视化

    • 顶点高亮显示

    • 动态光照效果

  2. 交互控制

    • 鼠标拖拽旋转立方体

    • 滚轮缩放视图

    • 控制面板调整参数

    • 自动旋转开关

  3. GUI控制面板

    • 光源强度调节

    • 旋转速度控制

    • 自动旋转开关

    • 重置视图按钮

    • 实时状态显示

技术要点

  1. OpenGL集成

    • 使用pyopengltk库集成OpenGL到Tkinter

    • 现代OpenGL渲染管线

    • 光照模型实现

    • 坐标系统管理

  2. 交互实现

    • 鼠标事件处理

    • 动画循环

    • 参数实时更新

  3. 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可视化应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值