GUI系统
Taichi具有内置的GUI系统,可帮助用户可视化结果。
创建一个窗口
ti.``GUI
(title, res, bgcolor = 0x000000)
参数: |
|
---|---|
返回: | (GUI)对象代表窗口 |
创建一个窗口。 如果 res
是标量,则宽度将等于高度。
以下代码创建了一个分辨率为 640x360
的窗口:
gui = ti.GUI('Window Title', (640, 360))
gui.``show
(filename = None)
参数: |
|
---|
在屏幕上显示窗口。
注解
如果指定了 文件名
,则屏幕截图将被保存到该名称指定的文件中。 例如,以下代码会将当前窗口画面保存到 .png
文件中:
for frame in range(10000):
render(img)
gui.set_image(img)
gui.show(f'{frame:06d}.png')
在窗口上作画
gui.``set_image
(img)
参数: |
|
---|
设置要在窗口上显示的图像。
图像像素由 img[i, j]
的值设定,其中 i
表示水平坐标(从左到右), j
表示垂直坐标(从下到上) 。
如果窗口大小是 (x, y)
,则 img
必须是以下之一:
ti.var(shape=(x, y))
,灰度图像ti.var(shape=(x, y, 3))
,其中 3 代表(r, g, b)
通道ti.Vector(3, shape=(x, y))
(参见 向量)np.ndarray(shape=(x, y))
np.ndarray(shape=(x, y, 3))
img
的数据类型必须是以下之一:
uint8
,范围[0, 255]
uint16
,范围[0, 65535]
uint32
,范围[0, 4294967295]
float32
,范围[0, 1]
float64
,范围[0, 1]
注解
当使用 float32
或 float64
作为数据类型时,img
中的每个元素都将被裁剪为 [0, 1]
范围以便展示。
gui.``circle
(pos, color = 0xFFFFFF, radius = 1)
参数: |
|
---|
画一个实心圆。
gui.``circles
(pos, color = 0xFFFFFF, radius = 1)
参数: |
|
---|
画多个实心圆。
注解
如果 color
是一个 numpy 数组,则位于 pos[i]
的圆圈将使用 color[i]
作为颜色,因此 color
的数组长度必须与 pos
相同。
gui.``line
(begin, end, color = 0xFFFFFF, radius = 1)
参数: |
|
---|
画一条线。
gui.``lines
(begin, end, color = 0xFFFFFF, radius = 1)
参数: |
|
---|
画多条线。
gui.``triangle
(a, b, c, color = 0xFFFFFF)
参数: |
|
---|
画一个实心三角形。
gui.``triangles
(a, b, c, color = 0xFFFFFF)
参数: |
|
---|
画一个实心三角形。
gui.``rect
(topleft, bottomright, radius = 1, color = 0xFFFFFF)
参数: |
|
---|
画一个空心的矩形。
gui.``text
(content, pos, font_size = 15, color = 0xFFFFFF)
参数: |
|
---|
在屏幕上画文字。
ti.rgb_to_hex(rgb):
参数: | rgb – (3个浮点数组成的元组) (R, G, B) 浮点数值, 在区间 [0, 1] |
---|---|
返回: | (可选,RGB十六进制或 uint32 的 numpy 数组)转换后的十六进制颜色 |
把 (R, G, B) 的浮点数组转换成单个整数值,例如,
rgb = (0.4, 0.8, 1.0)
hex = ti.rgb_to_hex(rgb) # 0x66ccff
rgb = np.array([[0.4, 0.8, 1.0], [0.0, 0.5, 1.0]])
hex = ti.rgb_to_hex(rgb) # np.array([0x66ccff, 0x007fff])
返回值可以用于其他 GUI 的绘图 API。
事件处理
每个事件都有个 key 和 type。
type
是事件的类型,目前只有三种类型的事件:
ti.GUI.RELEASE # 键盘或鼠标被放开
ti.GUI.PRESS # 键盘或鼠标被按下
ti.GUI.MOTION # 鼠标移动或鼠标滚轮
事件的 ``key`` 是你在键盘或鼠标上按下的按钮,可以是以下之一:
# 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)
元组组成的列表,例如:
# 如果按下或释放ESC:
gui.get_event(ti.GUI.ESCAPE)
# 如果按下任何键:
gui.get_event(ti.GUI.PRESS)
# 如果按ESC或释放SPACE:
gui.get_event((ti.GUI.PRESS, ti.GUI.ESCAPE), (ti.GUI.RELEASE, ti.GUI.SPACE))
gui.``running
参数: | gui – (GUI) |
---|---|
返回: | (bool) 若 ti.GUI.EXIT 事件发生,返回 True ,反之亦然 |
当你点击一个窗口的关闭 (X) 按钮时,就会发生 ti.GUI.EXIT
。所以当窗口关闭的时候,查询 gui.running
会得到 False
。
例如,循环直到按下窗口的关闭按钮:
while gui.running:
render()
gui.set_image(pixels)
gui.show()
你也可以通过手动把 gui.running
设为 False
来关闭窗口:
while gui.running:
if gui.get_event(ti.GUI.ESCAPE):
gui.running = False
render()
gui.set_image(pixels)
gui.show()
gui.``get_event
(a, …)
参数: |
|
---|---|
返回: | (bool) 如果没有待处理的事件,返回 |
尝试从队列中弹出事件,并将其存储在 gui.event
中。
例如:
if gui.get_event():
print('Got event, key =', gui.event.key)
例如,循环直到按下ESC:
gui = ti.GUI('Title', (640, 480))
while not gui.get_event(ti.GUI.ESCAPE):
gui.set_image(img)
gui.show()
gui.``get_events
(a, …)
参数: |
|
---|---|
返回: | (生成器)python生成器,请参见下文 |
基本上与 gui.get_event
相同,只不过它返回一个事件生成器,而不是存储到 gui.event
中:
for e in gui.get_events():
if e.key == ti.GUI.ESCAPE:
exit()
elif e.key == ti.GUI.SPACE:
do_something()
elif e.key in ['a', ti.GUI.LEFT]:
...
gui.``is_pressed
(key, …)
参数: |
|
---|---|
返回: | (bool) 其中一个键处于按下状态,返回 |
警告
必须与 gui.get_event
一起使用,否则将不会更新! 例如:
while True:
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.``get_cursor_pos
()
参数: | gui – (GUI) |
---|---|
返回: | (2元组)窗口中的当前光标位置 |
例如:
mouse_x, mouse_y = gui.get_cursor_pos()
GUI 控件
比起混乱的键位设定,有时使用一些控件比如滑块、按钮,去控制程序变量是十分直观的。Taichi 的 GUI 就提供一系列控件,希望能帮到你直观地控制变量:
gui.``slider
(text, minimum, maximum, step=1)
参数: |
|
---|---|
返回: | (WidgetValue) 一个数值 getter / setter, 见 |
The widget will be display as: {text}: {value:.3f}
, followed with a slider.
gui.``label
(text)
参数: | text – (str) the text to be displayed in the label. |
---|---|
返回: | (WidgetValue) 一个数值 getter / setter, 见 WidgetValue 。 |
The widget will be display as: {text}: {value:.3f}
.
gui.``button
(text, event_name=None)
参数: |
|
---|---|
返回: | (EventKey) the event key for this button, see Event processing. |
class WidgetValue
A getter / setter for widget values.
value
Get / set the current value in the widget where we’re returned from.
例如::
radius = gui.slider('Radius', 1, 50)
while gui.running:
print('The radius now is', radius.value)
...
radius.value += 0.01
...
gui.show()
图片输入/输出
gui.``get_image
()
返回: | a np.ndarray which is the current image shown on the GUI. |
---|
Get the RGBA shown image from the current GUI system which has four channels.
ti.``imwrite
(img, filename)
参数: |
|
---|
Export a np.ndarray
or Taichi tensor (ti.Matrix
, ti.Vector
, or ti.var
) to a specified location filename
.
Same as ti.GUI.show(filename)
, the format of the exported image is determined by the suffix of filename
as well. Now ti.imwrite
supports exporting images to png
, img
and jpg
and we recommend using png
.
Please make sure that the input image has a valid shape. If you want to export a grayscale image, the input shape of tensor should be (height, weight)
or (height, weight, 1)
. For example:
import taichi as ti
ti.init()
shape = (512, 512)
type = ti.u8
pixels = ti.var(dt=type, shape=shape)
@ti.kernel
def draw():
for i, j in pixels:
pixels[i, j] = ti.random() * 255 # integars between [0, 255] for ti.u8
draw()
ti.imwrite(pixels, f"export_u8.png")
Besides, for RGB or RGBA images, ti.imwrite
needs to receive a tensor which has shape (height, width, 3)
and (height, width, 4)
individually.
Generally the value of the pixels on each channel of a png
image is an integar in [0, 255]. For this reason, ti.imwrite
will cast tensors which has different datatypes all into integars between [0, 255]. As a result, ti.imwrite
has the following requirements for different datatypes of input tensors:
- For float-type (
ti.f16
,ti.f32
, etc) input tensors, the value of each pixel should be float between [0.0, 1.0]. Otherwiseti.imwrite
will first clip them into [0.0, 1.0]. Then they are multiplied by 256 and casted to integaters ranging from [0, 255]. - For int-type (
ti.u8
,ti.u16
, etc) input tensors, the value of each pixel can be any valid integer in its own bounds. These integers in this tensor will be scaled to [0, 255] by being divided over the upper bound of its basic type accordingly.
这里是另一个例子:
import taichi as ti
ti.init()
shape = (512, 512)
channels = 3
type = ti.f32
pixels = ti.Matrix(channels, dt=type, shape=shape)
@ti.kernel
def draw():
for i, j in pixels:
for k in ti.static(range(channels)):
pixels[i, j][k] = ti.random() # floats between [0, 1] for ti.f32
draw()
ti.imwrite(pixels, f"export_f32.png")
ti.``imread
(filename, channels=0)
参数: |
|
---|---|
返回: | (np.ndarray) the image read from |
This function loads an image from the target filename and returns it as a np.ndarray(dtype=np.uint8)
.
Each value in this returned tensor is an integer in [0, 255].
ti.``imshow
(img, windname)
参数: |
|
---|
This function will create an instance of ti.GUI
and show the input image on the screen.
It has the same logic as ti.imwrite
for different datatypes.