1. 引言
Tkinter
(Tk接口)是Python标准库中的一个GUI(图形用户界面)模块,它提供了创建和操作GUI应用程序所需的工具和组件。Tkinter的名字来自于Tk GUI工具包的接口,它是一个由Tcl语言编写的开源GUI工具包。
使用Tkinter,你可以创建窗口、对话框、按钮、标签、文本框、列表框、菜单、画布、滚动条、文本编辑器等多种GUI组件,并在这些组件上添加事件响应函数,实现与用户交互的功能。
tkinter上手相对简单,但其布局方式往往难以把握,新生可能不易达到想要的效果。
本工具是为减少GUI编写的复杂度而编写的父类, 使用时继承即可。
接口比较死板,且只实现了一些简单的功能,使用网格布局方式,经过简单设计后可以实现相对美观的布局。
如下是使用本工具实现的几个示例:
2. 工具源码
import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk
'简易GUI工具'
__author__ = 'littleHuang'
'''
为减少GUI编写的复杂度而编写的父类, 使用时继承即可
接口比较死板,且只实现了一些简单的功能,使用网格布局方式
'''
class EasyGui:
def __init__(self):
self.fatherWindow = tk.Tk()
# 字体设置
self.font = ('Arial', 15)
# 封装一下,子类不需要重新import
self.tk = tk
def setWindowName(self, name=''):
'''
:param name: 窗口名
:return:
'''
self.fatherWindow.title(name)
def setLabel(self, text='', width=1, row=0, column=0, columnspan=1):
'''
:param text: 标签内容
:param width: 标签宽度
:param row: 放置行
:param column: 放置列
:param columnspan: 合并列
:param sticky: 调整位置
:return:
'''
tk.Label(self.fatherWindow, text=text, width=width, font=self.font).grid(row=row, column=column,
columnspan=columnspan, )
def setButton(self, text='', command=None, width=1, row=0, column=0, columnspan=1):
'''
:param text:
:param width:
:param command: 按下按钮后执行的函数
:param row:
:param column:
:param columnspan:
:param sticky:
:return: 按键对象
'''
b = tk.Button(self.fatherWindow, text=text, width=width, command=command, font=self.font)
b.grid(row=row, column=column, columnspan=columnspan)
return b
def setEntry(self, width=1, row=0, column=0, columnspan=1,**args):
'''
:param width:
:param row:
:param column:
:param columnspan:
:param sticky:
:return: 输入框对象
'''
e = tk.Entry(self.fatherWindow, width=width, font=self.font,**args)
e.grid(row=row, column=column, columnspan=columnspan)
return e
def setText(self, width=1, height=1, row=0, column=0, columnspan=1):
'''
:param width:
:param height: 文本框高度
:param row:
:param column:
:param sticky:
:param columnspan:
:return: 文本框对象
'''
t = tk.Text(self.fatherWindow, width=width, height=height, font=self.font)
t.grid(row=row, column=column, columnspan=columnspan)
return t
def setRadiobutton(self, text='', value='', var=None, row=0, column=0, columnspan=1):
'''
:param text:
:param value: 可变字符串点击后的设定值
:param var: 可变字符串
:param row:
:param column:
:param sticky:
:param columnspan:
:return:
'''
tk.Radiobutton(self.fatherWindow, text=text, variable=var, value=value, font=self.font).grid(row=row,
column=column,
columnspan=columnspan)
def set_windows(self, tittle):
'''
:param tittle: 窗口标题
:return: 新创建的窗口
'''
# 创建一个Toplevel窗口
window = tk.Toplevel(self.fatherWindow)
# 设置窗口的标题
window.title(tittle)
return window
def set_img(self, path):
'''
打开一个图片并转化为tk可以显示的格式
:param path: 图片路径
:return: 图像对象
'''
# 打开一张图片
# photo = tk.PhotoImage(path)
image = Image.open(path)
# 将图片转换为Tkinter可以显示的格式
photo = ImageTk.PhotoImage(image)
return photo, image
def getPath(self, entry):
'''
获取文件夹路径
:param entry: 要插入的标签
:return:
'''
entry.delete(0, 'end')
entry.insert(0, filedialog.askdirectory())
def getFilePath(self, entry):
'''
获取文件路径
:param entry: 要插入的标签
:return:
'''
entry.delete(0, 'end')
entry.insert(0, filedialog.askopenfilename())
def run(self):
self.fatherWindow.mainloop()
3. 使用步骤
3.1 前端设计
首先应该评估界面大小,每一个组件的行宽和列高,可以借助Excel等工具先进行简单的绘图。
3.2 前端编写
首先继承此组件,并填写窗口名,设置字体
from easy_gui import EasyGui
class ShowGui(EasyGui):
def __init__(self):
super().__init__()
self.setWindowName('演示程序')
self.font= ("微软雅黑", 15)
之后依次放置各个组件,以label为例,主要参数有:
def setLabel(self, text='', width=1, row=0, column=0, columnspan=1):
'''
:param text: 标签内容
:param width: 标签宽度
:param row: 放置行
:param column: 放置列
:param columnspan: 合并列
:param sticky: 调整位置
:return:
'''
width
标签宽度。最小宽度与内容有关,一般中文字符长度为2,英文和数字为1。小于最小宽度将导致内容不全。
row
放置行,即此标签放置在第几行,从上往下,起始位置为0
column
放置列,即此标签放置在第几列,从左向右,起始位置为0
columnspan
占用列,即此标签占用几列,这个根据我们事先的绘图填写
# 脚本路径四个汉字,最小宽度为8,因此我们宽度设置为8,起始位置在0行0列,根据表格,需要占用两列
self.setLabel("脚本路径", 8, 0, 0, 2)
# 根据表格,输入框宽度是前面标签的3倍,因此设置为24,同样在第0行,前面标签占了两列,因此在第列开始,根据表格占用6列
self.sh_path = self.setEntry(24, 0, 2, 6)
# button的第二个参数是需要绑定的方法,这里我们暂时设置为None,后续补充
self.start_button = self.setButton('开始测试', None, 8, 0, 8, 2)
self.node_name("节点名称", 8, 1, 0, 2)
self.sh_path = self.setEntry(16, 1, 2, 4)
# 这里虽然是两个汉字,最小宽度为4,但为了整齐,参考表格,我们仍然设置为8
self.start_button = self.setButton('攻击', None, 8, 1, 6, 2)
self.recovery_button = self.setButton('恢复', None, 8, 1, 8, 2)
self.function1_button = self.setButton('功能1', None,8,2,0,2)
self.function2_button = self.setButton('功能2', None,8,2,2,2)
self.function3_button = self.setButton('功能3', None,8,2,4,2)
self.function4_button = self.setButton('功能4', None,8,2,6,2)
self.function5_button = self.setButton('功能5', None,8,2,8,2)
# 消息框三个字占用整行,用于分割
self.setLabel("消息框", 40,3,0,10)
# 消息框第二个参数为高度,我们根据需要调整
self.messageText = self.setText(40, 10, 4, 0, 10)
初步效果如图
if __name__ == '__main__':
sg = ShowGui()
sg.run()
3.3 完善功能
为几个按钮编写功能
def start_test(self):
path = self.sh_path.get()
if not os.path.exists(path):
self.messageText.insert('end', '脚本路径不存在\n')
return
pro = Process(target=os.system, args=('bash '+path,))
pro.start()
self.messageText.insert('end', '开始执行测试脚本\n')
def attack_node(self):
node = self.node_name.get()
os.system('attack_node '+node)
self.messageText.insert('end', f'已击毁节点{node}\n')
def recovery_node(self):
node = self.node_name.get()
os.system('recovery_node '+node)
self.messageText.insert('end', f'已恢复节点{node}\n')
def show_img(self, path):
if not os.path.exists(path):
self.messageText.insert('end', '图像路径不存在\n')
return
img_name = path.split('/')[-1]
# 创建一个Toplevel窗口
window = self.set_windows(img_name)
photo, image = self.set_img(path)
label = self.tk.Label(window, image=photo)
label.pack()
image.show()
将功能绑定到按钮上
self.start_button = self.setButton('攻击', self.attack_node, 8, 1, 6, 2)
self.recovery_button = self.setButton(
'恢复', self.recovery_node, 8, 1, 8, 2)
self.function1_button = self.setButton('功能1', functools.partial(
self.show_img, '1.jpg'), 8, 2, 0, 2)
self.function2_button = self.setButton('功能2', functools.partial(
self.show_img, '2.jpg'), 8, 2, 2, 2)
self.function3_button = self.setButton('功能3', functools.partial(
self.show_img, '3.jpg'), 8, 2, 4, 2)
self.function4_button = self.setButton('功能4', functools.partial(
self.show_img, '4.jpg'), 8, 2, 6, 2)
self.function5_button = self.setButton('功能5', functools.partial(
self.show_img, '5.jpg'), 8, 2, 8, 2)
3.4 最终效果
整体代码
from easy_gui import EasyGui
from multiprocessing import Process
import os
import functools
class ShowGui(EasyGui):
def __init__(self):
super().__init__()
self.setWindowName('演示程序')
self.font = ("微软雅黑", 15)
# 四个参数依次是 组件宽度 放置行 放置列 列宽(占用几列)
self.setLabel("脚本路径", 8, 0, 0, 2)
self.sh_path = self.setEntry(24, 0, 2, 6)
self.start_button = self.setButton('开始测试', self.start_test, 8, 0, 8, 2)
self.setLabel("节点名称", 8, 1, 0, 2)
self.node_name = self.setEntry(16, 1, 2, 4)
self.start_button = self.setButton('攻击', self.attack_node, 8, 1, 6, 2)
self.recovery_button = self.setButton(
'恢复', self.recovery_node, 8, 1, 8, 2)
self.function1_button = self.setButton('功能1', functools.partial(
self.show_img, '1.jpg'), 8, 2, 0, 2)
self.function2_button = self.setButton('功能2', functools.partial(
self.show_img, '2.jpg'), 8, 2, 2, 2)
self.function3_button = self.setButton('功能3', functools.partial(
self.show_img, '3.jpg'), 8, 2, 4, 2)
self.function4_button = self.setButton('功能4', functools.partial(
self.show_img, '4.jpg'), 8, 2, 6, 2)
self.function5_button = self.setButton('功能5', functools.partial(
self.show_img, '5.jpg'), 8, 2, 8, 2)
self.setLabel("消息框", 40, 3, 0, 10)
self.messageText = self.setText(40, 10, 4, 0, 10)
# 设置初始值
self.sh_path.insert(0, '/home/xxx/xxx.sh')
self.node_name.insert(0, 'node1')
def start_test(self):
path = self.sh_path.get()
if not os.path.exists(path):
self.messageText.insert('end', '脚本路径不存在\n')
return
pro = Process(target=os.system, args=('bash '+path,))
pro.start()
self.messageText.insert('end', '开始执行测试脚本\n')
def attack_node(self):
node = self.node_name.get()
os.system('attack_node '+node)
self.messageText.insert('end', f'已击毁节点{node}\n')
def recovery_node(self):
node = self.node_name.get()
os.system('recovery_node '+node)
self.messageText.insert('end', f'已恢复节点{node}\n')
def show_img(self, path):
if not os.path.exists(path):
self.messageText.insert('end', '图像路径不存在\n')
return
img_name = path.split('/')[-1]
# 创建一个Toplevel窗口
window = self.set_windows(img_name)
photo, image = self.set_img(path)
label = self.tk.Label(window, image=photo)
label.pack()
image.show()
def run(self):
super().run()
if __name__ == '__main__':
sg = ShowGui()
sg.run()
4. 后记
本工具仅从部分使用场景出发,对部分tkinter库的操作进行了简化,很多功能并不完善,欢迎各位再次基础上进一步完善。