Python tkinter implementa la plantilla GUI de la herramienta de secuencia de comandos

Introducción

  • A menudo es necesario escribir algunos scripts, configurar algunos parámetros e iniciar cálculos, y esperar que sea accesible para otros, y lo ejecutará de manera visual.
  • Utilice principalmente la biblioteca tkinter, en términos de diseño, puede construir un Marco por línea y empaquetarlo en la ventana. Los marcos en cada línea se pueden diseñar por paquete o cuadrícula o lugar. Por analogía, se pueden extender múltiples controles

Flujo de trabajo simple

  • Cada control de botón de la ventana generará eventos y, por lo general, volverá a llamar a las funciones, pero está bloqueado en el hilo principal para devolver la llamada. Es imposible para nosotros crear hilos para ejecutar de forma asincrónica cada vez, por lo que usamos una cola de mensajes para desacoplar los eventos de los botones. Y la función de procesamiento de devolución de llamada, el consumidor consume el mensaje de la cola original para determinar directamente el consumo del tipo de mensaje, pero en aras de la brevedad del código, se entrega a otra clase para su procesamiento.

Inserte la descripción de la imagen aquí

efecto

Inserte la descripción de la imagen aquí

Código fuente

  • Realice cálculos personalizados en la función start_demo
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:
        # 1、Gui消息队列
        self.msg_center = MsgCenter(self)

        # 2、窗口设置
        self.root = Tk()
        self.root.title("xxx工具")
        # self.root.wm_resizable(False, False) # 设置窗口大小不可拉伸
        self.root.geometry('500x600+500+200')   # 设置窗口: 宽 x 高 + 窗口位置x坐标 + 窗口位置y坐标
        self.root.protocol("WM_DELETE_WINDOW", self.close_event)

        # 3、定义组件, -- 带btn后缀是控件Widget对象, 带var后缀是控件绑定的值Variable对象
        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

        # 4、初始化各个组件和布局
        self.initGui()

    def initGui(self):
        # 1- 标签
        text_str = """版本: 2.0.1
        #author burukeyou
        #说明:
            1)  这是关于
            2) 请先开始再停止"""
        Label(self.root, text=text_str, justify='left', fg='red').pack(anchor=constants.W)

        # 2- 长文本框
        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)

        # 3- 单选框
        self.mode_var = IntVar(value=1)
        fm01 = Frame(self.root)  # , bg='blue'
        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)

        # 4- 文本框
        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)

        # 5- 勾选框
        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)

        # 6-下拉选择
        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)

        # 7- 范围选择组件
        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)

        # 8-  文件选择组件
        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)

        # 9- 多行文本框
        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)

        # text 联动 scroll
        scroll.config(command=self.text_btn.yview)
        self.text_btn.config(yscrollcommand=scroll.set)

        # 10、开始结束按钮
        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

        # todo 开始脚本执行、把当前Gui对象传过去就可以获得各个控件的值
        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()
        # 这个方法会循环阻塞住,监听gui的事件,得放到主线程最后
        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()

Recompensa

Si encuentra útil el artículo, puede animar al autor

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_41347419/article/details/111306949
Recomendado
Clasificación