使用 tkintertools 模块显示简单的 3D 效果

简介: 如何使用 Python 快速又简单地显示 3D 效果呢?使用 tkintertools 模块,轻松地做到这一点!(本文适用于 tkintertools-v2.6.6)

tkintertools 是基于 Python 的内置模块 tkinter 二次开发而成的,具体信息见:https://github.com/Xiaokang2022/tkintertools

本文适用于 tkintertools-v2.6.6


创建基本的 3D 对象

基本的 3D 对象包括点(Point)、线(Line)和面(Side),除此之外,还有可以用于数学计算的基类 _Point、_Line 和 _Side,这些基类是无法显示出来的,也不需要承载容器,只能用于数学计算。

这些对象的载体一共有两种,分别是 Canvas_3D 和 Space,它们的区别在于,Space 已经内置好了平移、旋转和缩放等基本功能的调用,使用者调用 3D 对象后不需要做任何的配置和设定,直接就可以通过鼠标的拖动来完成对 3D 对象的平移、旋转和缩放,一般用于观察某些特定的 3D 对象。而 Canvas_3D 没有做任何的配置,用户可以自定义它的绑定事件和功能。实际上,Canvas_3D 就是 Space 的父类。

下面的展示我们都将采用 Space 作为载体来完成。

Note

Canvas_3D 与 Space 和原来的 Canvas 控件在位置布局上不太一样!它们的 (0, 0) 坐标在画布中央!

我们通过类 Point 来显示一个点

效果图:

3d_point.png

源代码:

importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间forx, rinzip([-100, 0, 100], ['00', '77', 'FF']):
fory, ginzip([-100, 0, 100], ['00', '77', 'FF']):
forz, binzip([-100, 0, 100], ['00', '77', 'FF']):
t3d.Point(space, [x, y, z], fill='#'+r+g+b, size=5)  # 创建点space.space_sort()  # 给它们的空间位置排序以正确显示root.mainloop()  # 消息事件循环

线

我们通过类 Line 来显示一条有限长直(折)线

效果图:3d_line.png

源代码:

importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间forx, rinzip([-100, 0, 100], ['00', '77', 'FF']):
fory, ginzip([-100, 0, 100], ['00', '77', 'FF']):
forz, binzip([-100, 0, 100], ['00', '77', 'FF']):
t3d.Line(space, [0]*3, [x, y, z], fill='#'+r+g+b, width=3)  # 创建线space.space_sort()  # 给它们的空间位置排序以正确显示root.mainloop()  # 消息事件循环

我们通过类 Side 来显示一个有限面积直边平面

效果图:3d_side.png

源代码:

importitertools# 组合数支持importmath# 数学支持importstatistics# 计算平均数importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间m=200*math.sqrt(50-10*math.sqrt(5))/10n=200*math.sqrt(50+10*math.sqrt(5))/10points= []  # 顶点列表dis_side=200* (3*math.sqrt(3)+math.sqrt(15))/12/ \
    ((math.sqrt(10+2*math.sqrt(5)))/4)  # 面到中心的距离count, color_lst=0, ['00', '77', 'FF']  # 颜色可能值color= ['#%s%s%s'% (r, g, b)  # 颜色列表forrincolor_lstforgincolor_lstforbincolor_lst]
foriinm, -m:
forjinn, -n:
points.append([0, j, i])
points.append([i, 0, j])
points.append([j, i, 0])
forpinitertools.combinations(points, 3):  # 所有的顶点组合dis=math.hypot(*[statistics.mean(c[i] forcinp) foriinrange(3)])
ifmath.isclose(dis, dis_side):
t3d.Side(space, *p, fill=color[count])  # 创建面count+=1space.space_sort()  # 给它们的空间位置排序以正确显示root.mainloop()  # 消息事件循环

创建复杂的 3D 对象

复杂的 3D 对象是指非基本的对象,如长方体(Cuboid)、四面体(Tetrahedron)等复杂的几何体,当然,你也可以直接用基本的 3D 对象将它们模仿出来,但这并不是封装好的,它们并非一个整体,直接使用 Geometry 及其子类是更加推荐的选择。

长方体

我们可以直接使用 Cuboid 来创建一个长方体

效果图:3d_cuboid.png

源代码:

importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间forain-100, 0, 100:
forbin-100, 0, 100:
forcin-100, 0, 100:
t3d.Cuboid(space, a-50, b-50, c-50, 100, 100, 100,  # 创建长(正)方体color_up='white', color_down='yellow', color_left='red',
color_right='orange', color_front='blue', color_back='green')
space.space_sort()  # 给它们的空间位置排序以正确显示root.mainloop()  # 消息事件循环

四面体

同上,我们也可以用 Tetrahedron 直接创建四面体

效果图:3d_tetrahedron.png

源代码:

importmath# 数学支持importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间# 创建两个(正)四面体t3d.Tetrahedron(space, [-100, 0, 0+10], [50, 50*math.sqrt(3), 0+10],
                [50, -50*math.sqrt(3), 0+10], [0, 0, 100*math.sqrt(2)+10],
colors=['red', 'yellow', 'blue', 'green'])
t3d.Tetrahedron(space, [-100, 0, 0-10], [50, 50*math.sqrt(3), 0-10],
                [50, -50*math.sqrt(3), 0-10], [0, 0, -100*math.sqrt(2)-10],
colors=['red', 'yellow', 'blue', 'green'])
space.space_sort()  # 给它们的空间位置排序以正确显示root.mainloop()  # 消息事件循环

任意凸面几何体

除了上面提到的两种,实际我们可以通过 Geometry 创建任意的(凸面)多面体。但这里要注意的是,凹面几何体也是可以创建的,不过在调用它的某些 API 时可能会造成显示错误(如 scale 方法),因此需慎用凹面几何体。

本质上,Geometry 的背后都是 Side,它只不过是将其进行了一个组合和封装,因此我们就是通过多个 Side 类创建它的,此外,我们在创建了它之后,还可以用它的 append 方法向其继续添加 Side。

自定义 3D 画布

tkintertools 的 3D 画布不是只有一个,它实际的继承关系是下面这样的:

tk.Canvas -> tkt.Canvas -> t3d.Canvas_3D -> t3d.Space

内置的 Space 类

内置的 Space 类已经帮我们绑定好了相关的操作方法,如平移(translate)、旋转(rotate)和缩放(scale)等,一般用于查看 3D 对象的外观。

但这里要说明的一点是,由于 tkinter 的事件绑定是一个代码覆盖的过程,也就是说,原来对同一事件绑定的代码将失效,故 Space 类中暂时无法使用其他的 tkintertools 控件,因为其部分功能会失效。

底层父类 Canvas_3D

Canvas_3D 属于是什么都没有绑定的一个容器控件了。它存在的目的就是让使用者可以自定义绑定的事件及绑定的函数,而不是强制使用某些按键来触发某些事件(如鼠标左键拖动旋转 3D 对象)。这里也要说明一点的就是,由于 Canvas_3D 没有绑定任何额外的事件,因此 tkintertools 的控件在上面是可以完全正常使用的。

绑定事件

每个 3D 对象都有三个基本的功能,分别为平移(translate)、旋转(rotate)和缩放(scale),使用者可以根据自身需要,自定义绑定事件并调用相关的方法。比如说,我们现在想做这样一个绑定:按下等号键和减号键可以使每个 3D 对象自身的大小进行缩放,那么就可以按下面的步骤做。

分析:使 3D 对象自身大小进行缩放需要调用方法 scale,且缩放中心就是每个 3D 对象自身的几何中心,查阅 文档 得知,参数 center 值为 None,也就是默认值的时候,它会按自身的几何中心进行缩放。这里可以使用 Canvas_3D 作为容器控件,也可以使用 Space,这并不会影响到这个功能的实现。

效果图:

3d_bind.png

源代码:

importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间forain-100, 0, 100:
forbin-100, 0, 100:
forcin-100, 0, 100:
t3d.Cuboid(space, a-50, b-50, c-50, 100, 100, 100,  # 创建正方体color_up='white', color_down='yellow', color_left='red',
color_right='orange', color_front='blue', color_back='green')
space.space_sort()  # 空间位置排序,以正确显示defscale(event):
""" 缩放事件 """k=1.05ifevent.keysym=='equal'else0.95ifevent.keysym=='minus'else1# 缩放比率forgeoinspace.geos():  # 遍历所有的几何体(不包括基本 3D 对象)geo.scale(k, k, k)  # 缩放geo.update()  # 更新改对象的实际画面space.space_sort()  # 空间前后位置排序root.bind('<Key-equal>', scale)  # 绑定等号按键root.bind('<Key-minus>', scale)  # 绑定减号按键root.mainloop()  # 消息事件循环

简单的 3D 动画

动画嘛,很简单,原理和上面的类似,只不过将手动改变状态改成了自动改变状态,如让它自动旋转。下面给一个简单的示例。

效果图:3d_animation.gif

源代码:

importmath# 数学支持importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间# 创建两个(正)四面体t1=t3d.Tetrahedron(
space, [-100, 0, 0+10], [50, 50*math.sqrt(3), 0+10],
    [50, -50*math.sqrt(3), 0+10], [0, 0, 100*math.sqrt(2)+10],
colors=['red', 'yellow', 'blue', 'green'])
t2=t3d.Tetrahedron(
space, [-100, 0, 0-10], [50, 50*math.sqrt(3), 0-10],
    [50, -50*math.sqrt(3), 0-10], [0, 0, -100*math.sqrt(2)-10],
colors=['red', 'yellow', 'blue', 'green'])
defspin():
""" 自动旋转 """t1.rotate(dz=0.01)
t2.rotate(dz=0.01)
deffloating(value):
""" 上下浮动 """t1.translate(dz=math.sin(value))
t2.translate(dz=math.sin(value))
defanimation(value=0):
""" 形成动画 """spin()
floating(value)
space.space_sort()  # 给它们的空间位置排序以正确显示t1.update()
t2.update()
space.after(10, animation, value+math.pi/60)
animation()
root.mainloop()  # 消息事件循环

如果你看到了这里,想必你已经明白如何创建一个 3D 对象了,那不妨试试把下面的这个 3D 对象(苯环)画出来?

效果图:

3d_test.png

源代码:

importmath# 数学支持importtkintertoolsastkt# 引入基础模块fromtkintertoolsimporttools_3dast3d# 引入 3d 子模块root=tkt.Tk('3D', 1280, 720)  # 创建窗口space=t3d.Space(root, 1280, 720, 0, 0)  # 创建空间last_point= [0, 100*math.cos(-math.pi/3), 100*math.sin(-math.pi/3)]
color_lst= ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
color_lst+=color_lstforiinrange(6):
rad=i*math.pi/3next_point= [0, 100*math.cos(rad), 100*math.sin(rad)]
point_h2= [0, 150*math.cos(rad), 150*math.sin(rad)]
t3d.Line(space, last_point, next_point, width=3, fill=color_lst[i])
t3d.Line(space, next_point, point_h2, width=3, fill=color_lst[i+1])
t3d.Point(space, last_point, size=20, fill=color_lst[i+2])
t3d.Point(space, point_h2, size=10, fill=color_lst[i+3])
last_point=next_pointspace.space_sort()  # 给它们的空间位置排序以正确显示root.mainloop()  # 消息事件循环
目录
相关文章
|
存储 数据采集 数据可视化
Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算
Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算
17517 1
Open3d系列 | 1. Open3d实现点云数据读写、点云配准、点云法向量计算
|
容器
Pyside6-第十三篇-布局(最后一章废话-理论篇)
Pyside6-第十三篇-布局(最后一章废话-理论篇)
1400 0
|
机器学习/深度学习 算法 数据可视化
复杂网络-常用绘图软件和库
复杂网络-常用绘图软件和库
853 0
复杂网络-常用绘图软件和库
|
网络协议 Linux
Centos IP、DNS设置
1、CentOS 修改DNS 修改对应网卡的DNS的配置文件# vi /etc/resolv.conf 修改以下内容 nameserver 8.8.8.8 #google域名服务器nameserver 8.8.4.4 #google域名服务器2、CentOS 修改网关 修改对应网卡的网关的配置文件[root@centos]# vi /etc/sysconfig/network 修改以下内容NETWORKING=yes(表示系统是否使用网络,一般设置为yes。
3533 0
|
7月前
|
存储 监控 Java
如何对迁移到Docker容器中的应用进行性能优化?
如何对迁移到Docker容器中的应用进行性能优化?
486 59
|
机器学习/深度学习 人工智能 自然语言处理
三行代码实现实时语音转文本,支持自动断句和语音唤醒,用 RealtimeSTT 轻松创建高效语音 AI 助手
RealtimeSTT 是一款开源的实时语音转文本库,支持低延迟应用,具备语音活动检测、唤醒词激活等功能,适用于语音助手、实时字幕等场景。
2822 18
三行代码实现实时语音转文本,支持自动断句和语音唤醒,用 RealtimeSTT 轻松创建高效语音 AI 助手
|
Python
turtle库的几个案例进阶,代码可直接运行(python经典编程案例)
该文章展示了使用Python的turtle库进行绘图的进阶案例,包括绘制彩色圆形和复杂图案的代码示例。
4691 8
turtle库的几个案例进阶,代码可直接运行(python经典编程案例)
|
数据可视化 数据挖掘 BI
三万字长文详解神级绘图框架 plotly
三万字长文详解神级绘图框架 plotly
2789 14
|
设计模式 负载均衡 API
深入理解微服务架构中的服务发现与注册机制
在微服务架构的海洋中,服务发现和注册机制如同星辰导航,指引着服务的互联。本文将揭开服务发现与注册的神秘面纱,探讨它们如何在动态变化的云环境中确保服务间的高效通信。我们将从基本概念出发,逐步深入到实现原理,最后探讨如何在实际项目中应用这些机制以提升系统的可扩展性、可靠性和敏捷性。
|
数据可视化 Python
Python中的等值线平滑处理技术
Python中的等值线平滑处理技术
550 2