GUI系统

Taichi具有内置的GUI系统,可帮助用户可视化结果。

创建一个窗口

ti.GUI(title, res, bgcolor = 0x000000)
参数:
  • title – (字符串)窗口标题
  • res – (标量或元组)分辨率/窗口大小
  • bgcolor – (可选,RGB十六进制)窗口的背景颜色
返回:

(GUI)对象代表窗口

创建一个窗口。 如果 res 是标量,则宽度将等于高度。

以下代码创建了一个分辨率为 640x360 的窗口:

gui = ti.GUI('Window Title', (640, 360))
gui.show(filename = None)
参数:
  • gui – (GUI)窗口对象
  • filename – (可选,字符串)请参阅以下注释

在屏幕上显示窗口。

注解

如果指定了 文件名 ,则屏幕截图将被保存到该名称指定的文件中。 例如,以下将窗口的框架保存到 .png 文件中:

for frame in range(10000):
    render(img)
    gui.set_image(img)
    gui.show(f'{frame:06d}.png')

在窗口上作画

gui.set_image(img)
参数:
  • gui – (GUI)窗口对象
  • img – (numpy 数组或 Taichi 张量)包含图像的张量,请参见下面的注释

设置要在窗口上显示的图像。

图像像素由 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]

注解

When using float32 or float64 as the data type, img entries will be clipped into range [0, 1] for display.

gui.circle(pos, color = 0xFFFFFF, radius = 1)
参数:
  • gui – (GUI)窗口对象
  • pos – (2元组)圆的位置
  • color – (可选,RGB十六进制)颜色填充圆圈
  • radius – (可选,标量)圆的半径

画一个实心圆。

gui.circles(pos, color = 0xFFFFFF, radius = 1)
参数:
  • gui – (GUI)窗口对象
  • pos – (numpy 数组)一系列圆的位置
  • color – (可选,RGB十六进制或 uint32 的 numpy 数组)颜色以填充圆圈
  • radius – (可选,float32的标量或np.array)圆的半径(半径)

画多个实心圆。

注解

如果 color 是一个 numpy 数组,则位于 pos[i] 的圆圈将使用 color[i] 作为颜色,因此 color 的数组长度必须与 pos 相同。

gui.line(begin, end, color = 0xFFFFFF, radius = 1)
参数:
  • gui – (GUI)窗口对象
  • begin – (2元组)直线的第一个端点位置
  • end – (2元组)直线的第二个端点位置
  • color – (可选,RGB十六进制)线条颜色
  • radius – (可选,标量)线的宽度

画一条线。

gui.lines(begin, end, color = 0xFFFFFF, radius = 1)
参数:
  • gui – (GUI)窗口对象
  • begin – (np.array) the positions of the first end point of lines
  • end – (np.array) the positions of the second end point of lines
  • color – (optional, RGB hex or np.array of uint32) the color(s) of lines
  • radius – (optional, scalar or np.array of float32) the width(s) of the lines

Draw lines.

gui.triangle(a, b, c, color = 0xFFFFFF)
参数:
  • gui – (GUI)窗口对象
  • a – (2元组)三角形的第一个端点位置
  • b – (2元组)三角形的第二个端点位置
  • c – (2元组)三角形的第三个端点位置
  • color – (可选,RGB十六进制)填充三角形的颜色

画一个实心三角形。

gui.triangles(a, b, c, color = 0xFFFFFF)
参数:
  • gui – (GUI)窗口对象
  • a – (np.array) the positions of the first end point of triangles
  • b – (np.array) the positions of the second end point of triangles
  • c – (np.array) the positions of the third end point of triangles
  • color – (optional, RGB hex or np.array of uint32) the color(s) to fill the triangles

Draw solid triangles.

gui.rect(topleft, bottomright, radius = 1, color = 0xFFFFFF)
参数:
  • gui – (GUI)窗口对象
  • topleft – (2元组)矩形的左上角位置
  • bottomright – (2元组)矩形的右下角位置
  • color – (可选,RGB十六进制)描边线的颜色
  • radius – (可选,标量)描边线的宽度

画一个空心的矩形。

gui.text(content, pos, font_size = 15, color = 0xFFFFFF)
参数:
  • gui – (GUI)窗口对象
  • content – (str) the text to draw
  • pos – (tuple of 2) the top-left point position of the fonts / texts
  • font_size – (optional, scalar) the size of font (in height)
  • color – (optional, RGB hex) the foreground color of text

Draw a line of text on screen.

ti.rgb_to_hex(rgb):
参数:rgb – (tuple of 3 floats) The (R, G, B) float values, in range [0, 1]
返回:(RGB hex or np.array of uint32) The converted hex value

Convert a (R, G, B) tuple of floats into a single integer value. E.g.,

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])

The return values can be used in GUI drawing APIs.

事件处理

Every event have a key and type.

type 是事件的类型,目前只有三种类型的事件:

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

Event key is the key that you pressed on keyboard or mouse, can be one of:

# 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

事件过滤器 是一个由 keytype(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) True if ti.GUI.EXIT event occurred, vice versa

ti.GUI.EXIT occurs when you click on the close (X) button of a window. So gui.running will obtain False when the GUI is being closed.

For example, loop until the close button is clicked:

while gui.running:
    render()
    gui.set_image(pixels)
    gui.show()

You can also close the window by manually setting gui.running to 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, ...)
参数:
  • gui – (GUI)
  • a – (可选,事件过滤器)过滤掉匹配的事件
返回:

(bool) 如果没有待处理的事件,返回 False,反之亦然

尝试从队列中弹出事件,并将其存储在 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, ...)
参数:
  • gui – (GUI)
  • 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, ...)
参数:
  • gui – (GUI)
  • key – (事件的 key) 您要检测的键
返回:

(bool) 其中一个键处于按下状态,返回 True,反之亦然

警告

必须与 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 Widgets

Sometimes it’s more intuitive to use widgets like slider, button to control program variables instead of chaotic keyboard bindings. Taichi GUI provides a set of widgets that hopefully could make variable control more intuitive:

gui.slider(text, minimum, maximum, step=1)
参数:
  • text – (str) the text to be displayed above this slider.
  • minumum – (float) the minimum value of the slider value.
  • maxumum – (float) the maximum value of the slider value.
  • step – (optional, float) the step between two separate value.
返回:

(WidgetValue) a value getter / setter, see WidgetValue.

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) a value getter / setter, see WidgetValue.

The widget will be display as: {text}: {value:.3f}.

gui.button(text, event_name=None)
参数:
  • text – (str) the text to be displayed in the button.
  • event_name – (optional, str) customize the event name.
返回:

(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.

For example:

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)
参数:
  • img – (Matrix or Expr) the image you want to export
  • filename – (string) the location you want to save to

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]. Otherwise ti.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.

Here is another example:

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)
参数:
  • filename – (string) the filename of the image to load
  • channels – (optional int) the number of channels in your specified image. The default value 0 means the channels of the returned image is adaptive to the image file
返回:

(np.ndarray) the image read from filename

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)
参数:
  • img – (Matrix or Expr) the image to show in the GUI
  • windname – (string) the name of the GUI window

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.