簡介
- 常常需要寫一些指令碼,配置一些引數後就開始執行計算, 並且希望能夠讓他人無障礙使用,會以視覺化方式執行
- 主要使用tkinter庫, 佈局方面, 每一行構造一個Frame然後pack到視窗即可.而每一行的Frame可採用pack或者grid或者place佈局即可. 以此類推可以方面擴充套件多個控制元件
簡單工作流程
- 視窗每個按鈕控制元件會產生事件, 然後一般會回撥函式, 但是它是在主執行緒裡回撥的是阻塞的,不可能我們每次就建立執行緒去非同步執行, 所以用一個訊息佇列來解耦按鈕事件和回撥處理函式, 本來佇列的訊息被消費者消費後直接判斷訊息型別消費即可,但是為了程式碼簡潔交給另一個類處理.
效果
原始碼
import queue
import threading
import time
import traceback
from tkinter import Tk, Button, messagebox, Label, Frame, LEFT, Entry, IntVar, Radiobutton, StringVar, Checkbutton, \
BooleanVar, RIGHT, constants, Scale, HORIZONTAL, Variable, filedialog, Text, font, Scrollbar
from tkinter.font import Font, BOLD
from tkinter.messagebox import showinfo, showerror, showwarning
from tkinter.ttk import Combobox, Widget
from enum import Enum
from import GuiTempldate
class MsgEnum(Enum):
"""
訊息型別列舉
"""
START = 0
STOP = 1
EXIT = 3
def start_demo(gui: GuiTempldate):
""" 自定義計算任務
:param gui: gui元件物件
:return:
"""
count = 20
while gui.Cache.RUNING:
count -= 1
gui.text_btn.insert(constants.END, f"cd {count}\n")
gui.text_btn.see(constants.END)
time.sleep(1)
class GuiTempldate:
class Cache:
RUNING = False
def __init__(self) -> None:
self.msg_center = MsgCenter(self)
self.root = Tk()
self.root.title("xxx工具")
self.root.geometry('500x600+500+200')
self.root.protocol("WM_DELETE_WINDOW", self.close_event)
self.url_var = None
self.url_btn = None
self.mode_var = None
self.name_var = None
self.name_btn = None
self.is_xx_btn = None
self.is_xx_var = None
self.level_var = None
self.scale_btn = None
self.start_btn = None
self.stop_btn = None
self.select_file_var = None
self.text_btn = None
self.initGui()
def initGui(self):
text_str = """版本: 2.0.1
#author burukeyou
#說明:
1) 這是關於
2) 請先開始再停止"""
Label(self.root, text=text_str, justify='left', fg='red').pack(anchor=constants.W)
Label(self.root, text='請求介面').pack(anchor=constants.W)
self.url_var = StringVar(value="http://www.baidu.com")
self.url_btn = Entry(self.root, width=60, textvariable=self.url_var)
self.url_btn.pack(anchor=constants.W)
self.mode_var = IntVar(value=1)
fm01 = Frame(self.root)
fm01.pack(anchor=constants.W)
Label(fm01, text='模式').grid(row=0, column=0, sticky='W')
Radiobutton(fm01, text='a模式', variable=self.mode_var, value=1).grid(row=0, column=1, sticky='E', padx=40)
Radiobutton(fm01, text='b模式', variable=self.mode_var, value=2).grid(row=0, column=2, sticky='E', padx=40)
Radiobutton(fm01, text='c模式', variable=self.mode_var, value=3).grid(row=0, column=3, sticky='E', padx=40)
fm02 = Frame(self.root)
self.name_var = StringVar(value=100)
fm02.pack(anchor=constants.W, fill=constants.X)
Label(fm02, text='名字 ').pack(side=constants.LEFT)
self.name_btn = Entry(fm02, width=20, textvariable=self.name_var)
self.name_btn.pack(side=constants.RIGHT)
self.is_xx_var = BooleanVar()
self.is_xx_btn = Checkbutton(self.root, variable=self.is_xx_var, text='是否xxx', onvalue=True, offvalue=False)
self.is_xx_btn.pack(anchor=constants.W)
fm03 = Frame(self.root)
fm03.pack(anchor=constants.W, fill=constants.X)
self.level_var = StringVar()
Label(fm03, text='統計級別 ').pack(side=constants.LEFT)
com = Combobox(fm03, textvariable=self.level_var)
com.pack(side=constants.RIGHT)
com["value"] = ("級別0", "級別1", "級別2")
com.current(1)
fm04 = Frame(self.root)
fm04.pack(anchor=constants.W, fill=constants.X)
Label(fm04, text='範圍: ').pack(side=constants.LEFT)
self.scale_btn = Scale(fm04, from_=0, to=100, orient=constants.HORIZONTAL, tickinterval=100, length=200)
self.scale_btn.pack(side=constants.RIGHT)
self.scale_btn.set(20)
fm05 = Frame(self.root)
fm05.pack(anchor=constants.W, fill=constants.X)
self.select_file_var = StringVar(value="你沒有選擇任何檔案")
Button(fm05, text="選擇處理的檔案", command=self.click_file_event).pack(side=constants.LEFT)
Label(fm05, textvariable=self.select_file_var).pack(side=constants.RIGHT)
fm22 = Frame(self.root)
fm22.pack(anchor=constants.W, fill=constants.X)
scroll = Scrollbar(fm22)
scroll.pack(side=constants.RIGHT, fill=constants.Y)
ft = Font(family='微軟雅黑', size=18, weight=font.BOLD)
self.text_btn = Text(fm22, height=9, fg="green", font=ft, bg="black", insertbackground="red")
self.text_btn.pack(side=constants.LEFT)
scroll.config(command=self.text_btn.yview)
self.text_btn.config(yscrollcommand=scroll.set)
fm06 = Frame(self.root)
self.start_btn = Button(fm06, text="開始", width=6, height=1, command=self.start_event)
self.start_btn.grid(row=0, column=0, sticky='W', padx=20, pady=20)
self.stop_btn = Button(fm06, text="停止", width=6, height=1, command=self.stop_event)
self.stop_btn.grid(row=0, column=1, sticky='E', padx=20, pady=20)
fm06.pack(side=constants.BOTTOM)
def start_event(self):
self.msg_center.put(MsgEnum.START)
self.Cache.RUNING = True
threading.Thread(target=start_demo, args=(self,)).start()
def stop_event(self):
self.msg_center.put(MsgEnum.STOP)
self.Cache.RUNING = False
def click_file_event(self):
filename = filedialog.askopenfilename()
if filename != '':
self.select_file_var.set(filename)
print(self.select_file_var.get())
def close_event(self):
if self.Cache.RUNING and not messagebox.askokcancel("警告", "任務還在執行中,確定要關閉嗎?"):
return
self.root.destroy()
self.msg_center.put(MsgEnum.EXIT)
def showUI(self):
threading.Thread(target=self.msg_center.mainloop).start()
self.root.mainloop()
class MsgCenter:
"""
訊息佇列
主要處理視窗控制元件訊息
"""
def __init__(self, obj: GuiTempldate) -> None:
self.queue = queue.Queue()
self.obj = obj
def put(self, msg: Enum):
self.queue.put(msg)
def mainloop(self):
while True:
try:
msg = self.queue.get()
print("消費訊息: {}".format(msg))
if msg == MsgEnum.START:
MsgStrategy.start_strategy(self.obj)
elif msg == MsgEnum.STOP:
MsgStrategy.stop_strategy(self.obj)
elif msg == MsgEnum.EXIT:
break
else:
pass
except queue.Empty:
traceback.print_exc()
class MsgStrategy:
@classmethod
def start_strategy(cls, gui: GuiTempldate):
gui.start_btn.config(state=constants.DISABLED)
gui.stop_btn.config(state=constants.NORMAL)
gui.is_xx_btn.config(state=constants.DISABLED)
gui.scale_btn.config(state=constants.DISABLED)
gui.url_btn.config(state=constants.DISABLED)
gui.name_btn.config(state=constants.DISABLED)
val = f"""
介面:[{gui.url_var.get()}]
模式:[{gui.mode_var.get()}]
名字:[{gui.name_var.get()}]
是否xx: [{gui.is_xx_var.get()}]
統計級別:[{gui.level_var.get()}]
範圍: [{gui.scale_btn.get()}]
檔案: [{gui.select_file_var.get()}]
"""
showinfo("", val)
@classmethod
def stop_strategy(cls, gui: GuiTempldate):
gui.start_btn.config(state=constants.NORMAL)
gui.stop_btn.config(state=constants.DISABLED)
gui.is_xx_btn.config(state=constants.NORMAL)
gui.scale_btn.config(state=constants.NORMAL)
gui.url_btn.config(state=constants.NORMAL)
gui.name_btn.config(state=constants.NORMAL)
if __name__ == '__main__':
gui = GuiTempldate()
gui.showUI()
打賞
如果覺得文章有用,你可鼓勵下作者