嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

为应用程序设计图形化接口,使用Python Tkinter 模块

作者

张嘉钧

难度

普通

材料表

Windows 或Linux 环境计算机

目录

  1. Tkinter 簡介
  2. 第一個 Tkinter程式 : Hello World
  3. 視窗元件總攬
  4. 簡單的元件實作

3-1. 標籤 ( Label )

3-2. 按鈕 ( Button )

3-3. 使用Label顯示圖片

  1. 我的設計方法與Grid 佈局
  2. 進階GUI實作

6-1. 全螢幕視窗

6-2. 按按鈕開啟圖像

6-3. 開啟攝影機進行即時影像擷取

Tkinter 简介

因为近年来Python的使用率大幅提升,根据TIOBE的数据显示2020年全球的程序语言使用率Python稳居第三,而PyPl的套件安装Python的数量则是第一名。为什么提到这件事呢? Tkinter是TK GUI整合到Python中的GUI开发工具包,更白话一点就是Python内建的GUI设计套件,当你使用Python开发项目的时候可以考虑使用Tkinter来制作操作接口。在Python开发GUI的榜上前两位是Tkinter与PyQt,两者的差别:pyqt整合度高、有图形化接口可以使用;Tkinter因为是Python自带的GUI套件,功能简单但效能可能更好。如果要追求高颜质的接口,我比较推荐pyqt,但如果是只简单的接口设计,追求效率就选择Tkinter吧!

1、第一个 Tkinter程序 : Hello World

我们来建构一个简单的窗口并且显示Hello World吧!

步骤一、导入 Tkinter 函式库 ( Python 3 ),如果使用Python 2 则T要大写

import tkinter as tk

步骤二、定义一个窗口 名叫 window

window = tk.Tk()

步骤三、设定标题

window.title('window')

步骤四、设定像素大小

window.geometry('600x800')

步骤五、宣告一个标签

lbl_1 = tk.Label(window, text='Hello World', bg='yellow', fg='#263238', font=('Arial', 12))

步骤六、设定放置的位置 ( 使用 grid 布局 )

lbl_1.grid(column=0, row=0)

步骤七、主窗口循环显示

window.mainloop()

完整程序代码如下:

import tkinter as tk

window = tk.Tk()
window.title('window')
window.geometry('500x100')
lbl_1 = tk.Label(window, text='Hello World', bg='yellow', fg='#263238', font=('Arial', 12))
lbl_1.grid(column=0, row=0)
window.mainloop()

这样就完成一个简单的GUI接口啰!结果如下:

01_hello_world1_a4ccb36e8a00fd7c03660ea3fcf24d88018d3cbf.jpg

二、窗口组件总揽

这边顺便提供一个很实用的教学文件:https://tkdocs.com/tutorial/index.html

类别 介绍
Frame 窗口。
Label 文字卷标。
Button 按钮。
Canvas 可以用来绘图、文字等都可以,像我就会来拿放图片。
Checkbutton 核取按钮。
Entry 文字输入栏。
Listbox 列表选单。
Menu 选单列的下拉式选单。
LabelFrame 文字卷标窗口。
MenuButton 选单的选项。
Message 类似 Label ,可多行。
OptionMenu 下拉式的选项选单。
PaneWindow 类似 Frame ,可包含其他窗口组件。
Radiobutton 单选按钮。
Scale 拉杆。
Scrollbar 滚动条。
Spinbox 微调器
Text 文本框。
Toplevel 新增窗口。

三、简单的组件实作

由于组件的参数或详细用法网络上已经有很多介绍了,所以我这边快速带过。

3-1. 标签 ( Label )

02_label1_5c9c9696bef0306126f1f72330c4dd84c8b58c7f.jpg

我们在宣告标签的时候,需要先宣告一个卷标,再给予位置,如果没有给予位置信息的话将不会被放在窗口上面,这边我们使用grid来告诉窗口卷标要放在该容器中的 (0, 0) 这个位置,此外没有给定窗口宽、高大小的话,窗口会依照Widget大小而自动去调整:

import tkinter as tk

window = tk.Tk()
window.title('window')

def create_label(txt):
    lbl_1 = tk.Label(window, text=txt, bg='yellow', fg='#263238', font=('Arial', 12), width=100, height=2)
    lbl_1.grid(column=0, row=0)

create_label('Hello World !!!')

window.mainloop()

稍微介绍一下组件的参数,大部分的组件在宣告的时候,可以引入的参数都雷同,第一个是你要放在哪个容器当中,我们给予window,代表这个标签会放在window当中,而text代表文字,bg是background背景,fg是foreground前景,可以当作是文字的颜色,font是文字的格式width、height是宽与高,这边要注意的是他们的单位是字符宽度、字符高度,而不是像素值,此外还有很多属性可以用像是边框、影像、对齐样式等等。

3-2. 按钮 ( Button )

03_button1_9e6099abf704c6f7d1ea70653cf7d2081288f683.jpg

上方宣告 label的时候是将 width 跟 height 带入到宣告的时候,也有另外一个做法,就是使用字典的方式宣告属性,bt_1 的 ‘width’ 属性要定义成如何,这边可以注意到 width 是定义字符的宽、height是定义字符的高,我个人觉得tkinter的组件大小很难控制,与分辨率大小没有一定的关系,待会在教怎么样可以平均大小跟自动缩放;此外这边还有提供 activebackground跟activeforeground为按下按钮的背景、前景颜色变化。

import tkinter as tk

window = tk.Tk()
window.title('window')

def create_button(txt):
    bt_1 = tk.Button(window, text=txt, bg='red', fg='white', font=('Arial', 12))
    bt_1['width'] = 50
    bt_1['height'] = 4
    bt_1['activebackground'] = 'red'        # 按钮被按下的背景颜色
    bt_1['activeforeground'] = 'yellow'     # 按钮被按下的文字颜色 ( 前景 )

    bt_1.grid(column=0, row=0)

create_button('Button')

window.mainloop()

3-3. 使用Label显示图片

04_cat1_714af4c35ae5e63433db4e309e9f65e9f32e7032.jpg

这边要注意的是需要将图片转成Tkinter 可以读的格式:

import tkinter as tk
from PIL import Image, ImageTk

window = tk.Tk()
window.title('window')
# 放照片在UI上
def create_label_image():
    img = Image.open('./images/cat_1.jpg')                    # 读取图片
    img = img.resize( (img.width // 10, img.height // 10) )   # 缩小图片
    imgTk =  ImageTk.PhotoImage(img)                        # 转换成Tkinter可以用的图片
    lbl_2 = tk.Label(window, image=imgTk)                   # 宣告卷标并且设定图片
    lbl_2.image = imgTk
    lbl_2.grid(column=0, row=0)                             # 排版位置

create_label_image()
window.mainloop()

四、我的设计方法与Grid 布局

在开始实作复杂的GUI前,我先介绍一下我设计GUI时的做法以及使用Grid编排的方式。首先,在设计一个GUI之前我会先进行空间的区隔。

f1_frame1_746509720ffde81955961195d0a611e8d3c79ee1.png

我会透过frame按照上方的图绘分成四个颜色的区块,灰色是主要窗口,蓝色为显示图片的区块,橘色为显示文字的区块,绿色为按钮的区块。

实作程序代码如下:

import tkinter as tk
from PIL import Image, ImageTk

window = tk.Tk()
window.title('Window')

div_size = 200
img_size = div_size * 2
div1 = tk.Frame(window,  width=img_size , height=img_size , bg='blue')
div2 = tk.Frame(window,  width=div_size , height=div_size , bg='orange')
div3 = tk.Frame(window,  width=div_size , height=div_size , bg='green')

div1.grid(column=0, row=0, rowspan=2)
div2.grid(column=1, row=0)
div3.grid(column=1, row=1)

显示结果如下:

06_frames1_ea35174b05941dbc6e726e92463362357dae470e.jpg

但目前会有缩放的问题,当你放大之后他的大小仍然会保持原样,不会跟着缩放:

07_windows1_bbb17e157e67f6bef54dc8c8682cbf36f11bf7bb.jpg

倘若要能够伸缩需要使用到 columnconfigure、rowconfigure,其中可以用的参数:

minsize

最小的窗口大小( pixel )

pad

上下左右各添加多少 ( pixel )

weight

如果weight=0的话就不会进行缩放的动作,可以想象是权重值 ( 1 除以 weight ),参考下列范例当有两个组件需要定义网格的时候:

div1.columnconfigure(0, weight=1)

div1.columnconfigure(1, weight=5)

这时候Tkinter会将六分之一的空间分配给第0栏的组件,其余六分之五给第二栏的组件。

08_weights_11_2d216b436cc9574677bd48945410d90c97118f30.jpg

如果大小都是填1则是各一半

div1.columnconfigure(0, weight=1)

div1.columnconfigure(1, weight=1)

09_weights_21_e3f9a6633562cfb13587d39ea1290f9ca99bebb1.jpg

接下来针对先前写好的frame添加权重,这边我先写了一个副函式 ( define_layout ) 用来定义grid,引入obj为UI widget、cols该widget中有几栏、row该widget中有几列:

def define_layout(obj, cols=1, rows=1):
    
    def method(trg, col, row):
        
        for c in range(cols):    
            trg.columnconfigure(c, weight=1)
        for r in range(rows):
            trg.rowconfigure(r, weight=1)

    if type(obj)==list:        
        [ method(trg, cols, rows) for trg in obj ]
    else:
        trg = obj
        method(trg, cols, rows)

接着套用到刚刚的框架,稍微修改了一些地方,主要在grid的部分加上了 pad以及 sticky,pad是向外拓展 ( pixel );sticky 是对齐方式,这边用 align_mode 来统一所有的对其方式,而给予字符串 ‘nswe’ 是置中的意思 ( n 上 s 下 w左 e 右):

window = tk.Tk()
window.title('Window')
align_mode = 'nswe'
pad = 5

div_size = 200
img_size = div_size * 2
div1 = tk.Frame(window,  width=img_size , height=img_size , bg='blue')
div2 = tk.Frame(window,  width=div_size , height=div_size , bg='orange')
div3 = tk.Frame(window,  width=div_size , height=div_size , bg='green')

div1.grid(column=0, row=0, padx=pad, pady=pad, rowspan=2, sticky=align_mode)
div2.grid(column=1, row=0, padx=pad, pady=pad, sticky=align_mode)
div3.grid(column=1, row=1, padx=pad, pady=pad, sticky=align_mode)

定义好UI之后在来处理布局问题,先来看第一行要注意的地方是如果下层UI要套用权重上层的也一定要套用,所以如果三个frame要套用的话,最主要的窗口window也需要使用weight分配:

define_layout(window, cols=2, rows=2)
define_layout([div1, div2, div3])

执行下去可以看到进行缩放的时候,我们的三个frame也会跟着拉伸:

10_frames_21_3e2c844ff9df4964c902cf746f172dab428784c8.jpg


11_frames_31_e01ff6ec95b6a99e9f87115b20c6b0dbed6b95c5.jpg

 

接着再把需要的UI项目放进去,完整程序代码如下:

window = tk.Tk()
window.title('Window')
align_mode = 'nswe'
pad = 5

div_size = 200
img_size = div_size * 2
div1 = tk.Frame(window,  width=img_size , height=img_size , bg='blue')
div2 = tk.Frame(window,  width=div_size , height=div_size , bg='orange')
div3 = tk.Frame(window,  width=div_size , height=div_size , bg='green')

window.update()
win_size = min( window.winfo_width(), window.winfo_height())
print(win_size)

div1.grid(column=0, row=0, padx=pad, pady=pad, rowspan=2, sticky=align_mode)
div2.grid(column=1, row=0, padx=pad, pady=pad, sticky=align_mode)
div3.grid(column=1, row=1, padx=pad, pady=pad, sticky=align_mode)

define_layout(window, cols=2, rows=2)
define_layout([div1, div2, div3])

im = Image.open('./images/cat_1.jpg')
imTK = ImageTk.PhotoImage( im.resize( (img_size, img_size) ) )

image_main = tk.Label(div1, image=imTK)
image_main['height'] = img_size
image_main['width'] = img_size

image_main.grid(column=0, row=0, sticky=align_mode)

lbl_title1 = tk.Label(div2, text='Hello', bg='orange', fg='white')
lbl_title2 = tk.Label(div2, text="World", bg='orange', fg='white')

lbl_title1.grid(column=0, row=0, sticky=align_mode)
lbl_title2.grid(column=0, row=1, sticky=align_mode)

bt1 = tk.Button(div3, text='Button 1', bg='green', fg='white')
bt2 = tk.Button(div3, text='Button 2', bg='green', fg='white')
bt3 = tk.Button(div3, text='Button 3', bg='green', fg='white')
bt4 = tk.Button(div3, text='Button 4', bg='green', fg='white')

bt1.grid(column=0, row=0, sticky=align_mode)
bt2.grid(column=0, row=1, sticky=align_mode)
bt3.grid(column=0, row=2, sticky=align_mode)
bt4.grid(column=0, row=3, sticky=align_mode)

bt1['command'] = lambda : get_size(window, image_main, im)

define_layout(window, cols=2, rows=2)
define_layout(div1)
define_layout(div2, rows=2)
define_layout(div3, rows=4)

window.mainloop()

.

最终结果:

12_cat_22_6c2e9219cb0b78a638da5bee1cd25015fea506e0.jpg

13_cat_31_4eae9ebd595a14dd531b7d08dc2d5142f9efd857.jpg

那布局的使用方法,大家在这里应该也练习得差不多了,接下来会提供几个常用的功能,像是全屏幕、按按钮互动、实时影响读取等等。

6、进阶GUI实作

在这里为了参数调用的方便,我会使用Class来完成接下来的实作。

6-1. 全屏幕窗口 ( bind )

我们默认按下F12的时候切换成全屏幕窗口,在按下一次则返回,这边会使用到 bind 的函式,这个是可以将函式绑定到动作上面,像是按下左键、放开左键、按下F12等。首先我们要先能够将窗口全屏幕,在Windows环境下我们将去调整他的attributes成为 ‘-fullscreen,而Linux环境下则调整成 ‘-zoomed’,范例程序如下:

window.attributes('-fullscreen', True)  # For Windows
window.attributes('-zoomed', True)      # For Linux

接着我们要想办法辨识系统环境,在Python中自带一个函式库叫 Platform,可以使用Platform.system()得知现在的环境,在我的程序当中如果系统是Windows的话就回传1不是的话就回传 0 ,进而再给予特定的全屏幕变量。

def toggle_fullScreen(self, event):

    is_windows = lambda : 1 if platform.system() == 'Windows' else 0

    self.isFullScreen = not self.isFullScreen
    self.window.attributes("-fullscreen" if is_windows() else "-zoomed", self.isFullScreen)

接着使用bind将动作连结到toggle_fullScreen函式:

# 切换全屏幕
self.isFullScreen = False
self.window.bind('<F12>', self.toggle_fullScreen)

接着就可以执行看看了,在给完整程序代码之前可以先看一下结果:

14_cat_41_183127f2e3f2edb995906f157defb5811f3dfede.jpg

15_cat_51_12018dcb5761f491055c26eeec00a130bd331d74.jpg

由于全屏幕的关系工具栏也会被取消掉,不过我常做的GUI都会带一个关闭程序的按钮,或者我们也可以透过 bind 将 ESC 按键绑定关闭窗口:

def del_window(self, event):
    self.window.destroy()

self.window.bind('<Escape>', self.del_window)

完整程序代码如下:

self.window = tk.Tk()
self.window.title('Window')

im = Image.open('./images/cat.jpg').resize( (300, 300) )
imTK = ImageTk.PhotoImage( im )
self.lbl_img = tk.Label(self.window, image=imTK)
self.lbl_img.image = imTK
self.lbl_img.grid(column=0, row=0, sticky='nwes')
# 切换全屏幕
self.isFullScreen = False
self.window.bind('<F12>', self.toggle_fullScreen)

self.window.mainloop()

6-2. 按按钮开启图像 ( command )

1643_172151cca71b58b2ac31e0aeafdb5fe47d937208.jpg

整体设计概念很简单,在一开始的时候由frame将按钮及图片显示区块隔开,宣告个别组件。

def define_layout(self, obj, cols=1, rows=1):
    
  def method(trg, col, row):
        [ trg.columnconfigure(c, weight=1)  for c in range(cols) ]  
        [ trg.rowconfigure(r, weight=1)     for r in range(rows) ]
    if type(obj)==list:        
        [ method(trg, cols, rows) for trg in obj ]
    else:
        method(obj, cols, rows)

self.window = tk.Tk()
self.window.title('Window')

self.align_mode = 'nsew'
self.pad = 10

self.div_size, self.img_size = 200, 400
self.div1 = tk.Frame(self.window,  width=self.div_size , height=self.div_size)
self.div2 = tk.Frame(self.window,  width=self.img_size , height=self.img_size) 

self.div1.grid(column=0, row=0, padx=self.pad, pady=self.pad, sticky=self.align_mode)
self.div2.grid(column=0, row=1, padx=self.pad, pady=self.pad, sticky=self.align_mode)

self.bt1 = tk.Button(self.div1, text='Cat')
self.bt1.grid(column=0, row=0, sticky=self.align_mode)
self.bt2 = tk.Button(self.div1, text='Dog')
self.bt2.grid(column=1, row=0, sticky=self.align_mode)
self.bt3 = tk.Button(self.div1, text='Clear')
self.bt3.grid(column=2, row=0, sticky=self.align_mode)
self.bt4 = tk.Button(self.div1, text='Quit')
self.bt4.grid(column=3, row=0, sticky=self.align_mode)

self.define_layout(self.window, cols=1, rows=2)

接着先来显示图片,由于一开始开启程序希望是没有图片的所以给予空白值,但因为是空白值宽高也是0,图片区域的frame会因此变得很小,所以我将该frame的参数grid_propagate() 设为False,这个动作目的是要不要让该frame被子组件的图片大小给影响,也就是维持一开始预设的大小:

self.imTK = ''      # 预设给空白
self.lbl_img = tk.Label(self.div2, image=self.imTK)
self.lbl_img.image = self.imTK
self.lbl_img.grid(column=0, row=0, sticky=self.align_mode)
self.div2.grid_propagate(0)                 # 不会被子组件改变大小

接着就是按钮事件的宣告,按下按钮的时候希望执行什么动作:

def bt1_event(self):
    im = Image.open('./images/cat_1.jpg')
    self.imTK = ImageTk.PhotoImage( im.resize( (self.img_size, self.img_size) ) )
    self.lbl_img.configure(image=self.imTK) # image有时会被清除
    self.lbl_img.image = self.imTK    # 防止图片被垃圾清扫给除掉

def bt2_event(self):
    im = Image.open('./images/dog_1.jpg')
    self.imTK = ImageTk.PhotoImage( im.resize( (self.img_size, self.img_size) ) )
    self.lbl_img.configure(image=self.imTK)  # image有时会被清除
    self.lbl_img.image = self.imTK    # 防止图片被垃圾清扫给除掉

def bt3_event(self):
    self.lbl_img.configure(image='')

# 绑定按钮事件
self.bt1['command'] = self.bt1_event
self.bt2['command'] = self.bt2_event
self.bt3['command'] = self.bt3_event
# self.bt4['command'] = lambda : self.window.destroy()
self.bt4.bind('<Button-1>', self.del_window)

可以注意到绑定方法与刚刚全屏幕窗口的做法不太相同,可以在宣告按钮的时候直接用command绑定事件,也可以透过我这种方式额外宣告,在bt4_event可以看到用bind的方法也是可以的,只是要去找一下对应的动作参数是什么,像在这里如果bt4被左键点击定义为 <’Button-1’>,除此之外我也提供了lambda的写法,可以再参考看看。

6-3. 随窗口大小改变图片大小(静态) – bind 延伸

从上一个案例开始应用到bind之后,我们可以在窗口大小改变的时候 ( Configure ) 或许该窗口的大小或某一个组件的大小,实验程序如下,大家可以玩玩看,注意这个是静态的,只有在点击按钮生成图片的时候才会符合缩放后的大小:

def bt1_event(self):
    im = Image.open('./images/cat_1.jpg')
    self.imTK = ImageTk.PhotoImage( im.resize( (self.w, self.h) ) )
    self.lbl_img.configure(image=self.imTK)
    self.lbl_img.image = self.imTK         

def get_size(self, event, obj=''):

    trg_obj = self.window if obj == '' else obj
    self.w, self.h = trg_obj.winfo_width(), trg_obj.winfo_height()
    print(f'\r{(self.w, self.h)}', end='')

# 每次改变状态,都会获取 某组件 大小
self.window.bind('<Configure>', lambda event, obj=self.div2 :self.get_size(event, obj))
# 按钮的部分
self.bt1['command'] = self.bt1_event

1744_3fa64e81d246329dfe718403f83fba89670cb96f.jpg

6-4. 开启摄影机进行实时影像撷取 (after)

实时影像的部分会带到一个新的使用方法以及一个新的UI组件,第一个组件,由于是实时影像,如果有写过OpenCV实时影像撷取的人应该知道,需要写一个While循环不断撷取帧 ( Frame ),并且透过不断显示获取到的帧来构成一个实时影像画面,而在Tkinter中也是要如此,但这边我们会使用 「after」来模拟While循环;另个要介绍的Widget是对话框,我个人平常不会写但是有时候蛮好玩的,所以也记录下来使用方法。

先来完成简单版本的实时影像撷取,先宣告一个架构出来:

window = tk.Tk()
window.title('Video Stream')

main = tk.Frame(window, bg="white")
main.grid()
video = tk.Label(main)
video.grid()
window.bind('<Escape>', lambda event: window.destroy())

接下来透OpenCV取得摄影机以及进行实时影像撷取,使用after来模拟While不停执行的状况,这边10是指10「毫秒」:

# 宣告摄影机
status, frame = 0, []
cap = cv2.VideoCapture(0)   

def stream():

    # 读取当前的影像
    global status, frame
    status, frame = cap.read()
    # 如果有影像的话
    if status:
        # 将 OpenCV 色改格式 ( BGR ) 转换成 RGB
        im_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
        # 将 OpenCV 图档转换成 PIL
        im_pil = Image.fromarray(im_rgb)
        # 转换成 ImageTK
        imgTK = ImageTk.PhotoImage(image=im_pil)
        # 放入图片
        video.configure(image=imgTK)
        # 防止图片丢失,做二次确认
        video.image = imgTK
    # 10 豪秒 后执行 stream 函式,这里是模拟 While 循环的部分
    window.after(10, stream) 

# 先执行一次
stream()

window.mainloop()

# 释出摄影机内存、关闭所有窗口
cap.release()
cv2.destroyAllWindows()

对话框的部分,加在主程序里就可以了:

def quit(self, event):

    quit_check = tk.messagebox.askokcancel('提示', '是否要退出?')
    if quit_check:
        print('离开程序')
        cv2.destroyAllWindows()
        self.cap.release()
        self.window.destroy()	

self.window.bind('<Escape>', self.quit)

1842_645cd08cf2f6f8a49ea552f5ab3a561e8a255073.jpg

6-5. 图片随着窗口大小而改变 (动态)

有了仿真循环的功能,我们可以来玩玩看图片的动态缩放了!由于如果一直实时改变会相当的消耗资源,所以我这边有设定参数resize_rate,可以决定几秒更新一次,使用的架构是之前写的范例,先来看看结果吧!

1941_28fcc9d26d37b8e91f78fedfda00aba837282509.jpg

首先是每次窗口更动的时候撷取大小:

def get_size(self, event):
    self.w = self.div2.winfo_width()
    self.h = self.div2.winfo_height()

self.window.bind('<ButtonRelease>', lambda event: self.get_size)

接着是Update的部分,这里的逻辑是「当我没有缩放窗口的时候不断获取当前时间,一旦更动窗口大小N秒后进行缩放,进行缩放的时候会抓取最后的窗口大小」

def update(self):
    if self.w == self.div2.winfo_width() and self.h ==self.div2.winfo_height():
        self.reszie_time = time.time()
    
    if self.w != self.div2.winfo_width() or self.h !=self.div2.winfo_height():
        if time.time()-self.reszie_time>= self.resize_rate and self.im is not '':
            self.w, self.h = self.div2.winfo_width(), self.div2.winfo_height()    
            self.imTK = ImageTk.PhotoImage( self.im.resize( (self.w, self.h) ) )
            self.lbl_img.configure(image=self.imTK)
            self.lbl_img.image = self.imTK             
            
    self.window.after(10, self.update)

self.reszie_time, self.resize_rate = 0, 0.5
self.w, self.h = self.div2.winfo_width(), self.div2.winfo_height()
self.update()
self.window.mainloop()

_aa3b106c30ac2d54f9209a8499b8351d58050279.gif

结语

经过一连串小范例的实作,是否了解Tkinter的用法了?当然如果要熟悉的话还是多找几个小专题是做看看,一定会越来越厉害的。一些边缘装置专题分享,或多或少都会结合小的屏幕去做触碰操作或显示,这时候制作GUI就很重要了!接下来的文章会模拟工厂产线问题进行小专题制作,届时也会制作一个小界面供用户使用,更多小技巧会收录哦!

相关文章

Python is TIOBE's Programming Language of 2020!

Tkinter's Grid Geometry Manager

[Tkinter 教程15] event 事件绑定

如何在 Tkinter 中建立全屏窗口

Tkinter 消息提示

CAVEDU Education is devoted into robotics education and maker movement since 2008, and is intensively active in teaching fundamental knowledge and skills. We had published many books for readers in all ages, topics including Deep Learning, edge computing, App Inventor, IoT and robotics. Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw

评论