GUI 系统
Taichi 有一个内置的 GUI 系统,用于对 Taichi fields 或 NumPy ndarray 等数据容器内的数据行进视觉模拟 Taichi GUI 同时对基本几何体的绘制提供了简单的支持。
创建并显示窗口
The following code creates a 640x360
window with a "Hello World!" title:
gui = ti.GUI('Hello World!', (640, 360))
Displays it by calling gui.show()
:
while gui.running:
gui.show()
note
请在 while
循环内调用 gui.show()
。 否则,这个窗口将闪烁一次后消失。
关闭窗口
您可以在 while
循环内通过设置 gui.running=False
关闭GUI:
gui = ti.GUI('Window Title', (640, 360))
some_events_happend = lambda: random.random() < 0.8
while gui.running:
if some_events_happend():
gui.running = False
gui.show()
坐标系统
每个窗口都建立在坐标系统上:坐标原点位于左下角, +x
方向向右延伸, +y
方向向上延伸。
显示一个 field 或 ndarray
请调用 gui.set_image()
显示Taichi field 或 NumPy ndarray。 该方法接受这两种类型作为输入。
gui = ti.GUI('Set Image', (640, 480))
image = ti.Vector.field(3, ti.f32, shape=(640, 480))
while gui.running:
gui.set_image(image)
gui.show()
因为Taichi field 是一个 全局的 数据容器, 如果向量 field image
在while
循环之间被更新过,GUI 窗口将会刷新以显示最新图像。
IMPORTANT
请确保输入的形状与GUI 窗口的分辨率相匹配。
Zero-copying frame buffer
在 gui.et_image()
方法调用的每个循环内, GUI 系统都会将图像数据转换为可显示的格式,并将结果复制到窗口缓冲区。 当窗口大小较大时,这会造成巨大的超负荷,使得很难实现高 FPS (每秒帧率)。
如果您只需要调用 set_image()
方法而不使用任何绘图命令, 您可以启用 fast_gui
模式以提高性能。 这种模式允许 Taichi GUI 直接将图像数据写入帧缓冲器而不需要额外复制,大幅增加了 FPS。
gui = ti.GUI('Fast GUI', res=(400, 400), fast_gui=True)
要使这种模式能够正常运行,请确保传入 gui.set_image()
的数据格式与显示器兼容。 换言之,如果它是Taichi field,请确保它是以下之一:
- a vector field
ti.field(3, dtype, shape)
compatible with RGB format. - 向量 field
ti.field(4, dtype, shape)
,兼容 RGBA 格式。
注意 dtype
必须是 ti.f32
, ti.f64
, 或 ti.u8
的其中之一。
在窗口上绘画
Taichi's GUI system supports drawing simple geometries, such as lines, circles, triangles, rectangles, arrows, and texts.
Single geometry
In Taichi, drawing basic geometric shapes on the GUI is very intuitive. In most cases, all we need to do is specify information such as the position and size of the geometry and call the corresponding APIs.
Line
You can draw a single line on a GUI canvas by specifying its begin and end points:
import numpy as np
gui = ti.GUI('Single Line', res=(400, 400))
begin = [0.1, 0.1]
end = [0.9, 0.9]
while gui.running:
gui.line(begin, end, radius=1, color=0x068587)
gui.show()
note
Coordinates such as begin
and end
for single geometry can be Python lists, Numpy arrays or ti.Vector
, as long as it's subscriptable and its dimension is (2, ).
Circle
You can draw a single circle on a GUI canvas by specifying its center poistion and its radius:
import numpy as np
gui = ti.GUI('Single Circle', res=(400, 400))
center = [0.5, 0.5]
while gui.running:
gui.circle(pos=center, radius=30, color=0xED553B)
gui.show()
Triangle
You can draw a single triangle on a GUI canvas by specifying its three end points:
import numpy as np
gui = ti.GUI('Single Triangle', res=(400, 400))
p1 = [0.5, 0.5]
p2 = [0.6, 0.5]
p3 = [0.5, 0.6]
while gui.running:
gui.triangle(a=p1, b=p2, c=p3, color=0xEEEEF0)
gui.show()
Rectangle
You can draw a single rectangle on a GUI canvas by specifying its topleft and bottomright points:
import numpy as np
gui = ti.GUI('Single Rectangle', res=(400, 400))
p1 = [0.3, 0.4]
p2 = [0.7, 0.6]
while gui.running:
gui.rect(topleft=p1, bottomright=p2, color=0xFFFFFF)
gui.show()
Arrow
You can draw a single arrow on a GUI canvas by specifying its start point and direction:
import numpy as np
gui = ti.GUI('Single Arrow', res=(400, 400))
begin = [0.3, 0.3]
increment = [0.5, 0.5]
while gui.running:
gui.arrow(orig=begin, direction=increment, color=0xFFFFFF)
gui.show()
Text
You can draw a single line of text on a GUI canvas by specifying its position and contents:
gui = ti.GUI('Text', res=(400, 400))
position = [0.3, 0.5]
while gui.running:
gui.text(content='Hello Taichi', pos=position, font_size=34, color=0xFFFFFF)
gui.show()
Multiple geometries
It's also possible to draw multiple geometries at once by providing a collection of their positions to the GUI. 每个绘图方法的 pos
参数都接受 Taichi field 或 NumPy 数组。 不是 Python 原始数据类型。 field 或数组的每个元素都是一对浮点数,从 0.0
到 1.0
代表几何形状的相对位置。 例如:
(0.0, 0.0)
: 窗口左下角。(1.0, 1.0)
: 窗口右上角。
Lines
下面的代码绘制五个蓝色线段,其宽度为2, 其中的 x
和 y
分别代表五个线段的起点和终点。
import numpy as np
X = np.random.random((5, 2))
Y = np.random.random((5, 2))
gui = ti.GUI("lines", res=(400, 400))
while gui.running:
gui.lines(begin=X, end=Y, radius=2, color=0x068587)
gui.show()
Circles
下面的代码绘制了 50 个半径为 5
的圆圈,共有三种不同的颜色由 一个大小与 pos
相同的整数数组 indices
随机分配, 。
import numpy as np
pos = np.random.random((50, 2))
# Create an array of 50 integer elements whose values are randomly 0, 1, 2
# 0 corresponds to 0x068587
# 1 corresponds to 0xED553B
# 2 corresponds to 0xEEEEF0
indices = np.random.randint(0, 2, size=(50,))
gui = ti.GUI("circles", res=(400, 400))
while gui.running:
gui.circles(pos, radius=5, palette=[0x068587, 0xED553B, 0xEEEEF0], palette_indices=indices)
gui.show()
Triangles
以下代码绘制了两个橙色三角形橙色,其中 x
, y
, 和 z
分别代表这三个三角形的三个顶点。
import numpy as np
X = np.random.random((2, 2))
Y = np.random.random((2, 2))
Z = np.random.random((2, 2))
gui = ti.GUI("triangles", res=(400, 400))
while gui.running:
gui.triangles(a=X, b=Y, c=Z, color=0xED553B)
gui.show()
Arrows
The following code generates 100 random sized arrows, with begins
and direction
represents their begin points and incrementals:
import numpy as np
begins = np.random.random((100, 2))
directions = np.random.uniform(low=-0.05, high=0.05, size=(100, 2))
gui = ti.GUI('arrows', res=(400, 400))
while gui.running:
gui.arrows(orig=begins, direction=directions, radius=1)
gui.show()
Notice that we used low
and high
in the call to np.random.uniform()
to limit the range of generated random numbers.
事件处理
Taichi的图形界面系统也提供了一套方法用于鼠标和键盘的控制。 输入事件分为三类:
ti.GUI.RELEASE # key up or mouse button up
ti.GUI.PRESS # key down or mouse button down
ti.GUI.MOTION # mouse motion or mouse wheel
事件键指的是您从键盘或鼠标中按下的键。 可以是以下其中之一
# for ti.GUI.PRESS and ti.GUI.RELEASE event:
ti.GUI.ESCAPE # Esc
ti.GUI.SHIFT # Shift
ti.GUI.LEFT # Left Arrow
'a' # we use lowercase for alphabet
'b'
...
ti.GUI.LMB # Left Mouse Button
ti.GUI.RMB # Right Mouse Button
# for ti.GUI.MOTION event:
ti.GUI.MOVE # Mouse Moved
ti.GUI.WHEEL # Mouse Wheel Scrolling
事件过滤器 可以是 key、 type 或 (type, key) 元组。 例如:
# if ESC pressed or released:
gui.get_event(ti.GUI.ESCAPE)
# if any key is pressed:
gui.get_event(ti.GUI.PRESS)
# if ESC is pressed or SPACE is released:
gui.get_event((ti.GUI.PRESS, ti.GUI.ESCAPE), (ti.GUI.RELEASE, ti.GUI.SPACE))
gui.get_event()
将一个事件从队列中抛出并保存到 gui.event
。 例如:
if gui.get_event():
print('Got event, key =', gui.event.key)
下面的代码定义了 当
循环持续到 ESC 键被按下
while gui.running:
if gui.get_event(ti.GUI.ESCAPE):
break
gui.show()
gui.is_pressed()
检测到按键。 如下代码片段所示,您必须将它与 gui.get_event()
一起调用。 否则,按键将不会被更新。
例如:
while gui.running:
gui.get_event() # must be called before is_pressed
if gui.is_pressed('a', ti.GUI.LEFT):
print('Go left!')
elif gui.is_pressed('d', ti.GUI.RIGHT):
print('Go right!')
gui.show()
warning
Call gui.get_event()
before calling gui.is_pressed()
. Otherwise, gui.is_pressed()
does not take effect.
获取光标位置
gui.get_cursor_pos()
返回光标在当前窗口的位置。 返回值是范围内 [0.0, 1.0]
的一对浮点数。 例如:
mouse_x, mouse_y = gui.get_cursor_pos()
GUI 窗口部件
Taichi 的 GUI 系统也提供了包括 slider()
, label()
, 和 buton()
在内的窗口部件, 方便您自定义您的控制界面。 请看以下代码片段:
import taichi as ti
gui = ti.GUI('GUI widgets')
radius = gui.slider('Radius', 1, 50, step=1)
xcoor = gui.label('X-coordinate')
okay = gui.button('OK')
xcoor.value = 0.5
radius.value = 10
while gui.running:
for e in gui.get_events(gui.PRESS):
if e.key == gui.ESCAPE:
gui.running = False
elif e.key == 'a':
xcoor.value -= 0.05
elif e.key == 'd':
xcoor.value += 0.05
elif e.key == 's':
radius.value -= 1
elif e.key == 'w':
radius.value += 1
elif e.key == okay:
print('OK clicked')
gui.circle((xcoor.value, 0.5), radius=radius.value)
gui.show()