PythontkinterはスクリプトツールGUIテンプレートを実装します

前書き

  • 多くの場合、いくつかのスクリプトを作成し、いくつかのパラメーターを構成して計算を開始し、他のユーザーがアクセスできるようにし、視覚的に実行する必要があります。
  • 主にtkinterライブラリを使用します。レイアウトに関しては、行ごとにフレームを作成してウィンドウにパックできます。各行のフレームは、パック、グリッド、または場所ごとにレイアウトできます。類推すると、複数のコントロールを拡張できます。

シンプルなワークフロー

  • ウィンドウの各ボタンコントロールはイベントを生成し、通常は関数をコールバックしますが、メインスレッドでブロックされてコールバックします。毎回非同期で実行するスレッドを作成することは不可能であるため、メッセージキューを使用してボタンイベントを分離します。また、コールバック処理関数である元のキューのメッセージは、メッセージタイプの消費を直接決定するためにコンシューマーによって消費されますが、コードを簡潔にするために、処理のために別のクラスに渡されます。

ここに写真の説明を挿入

効果

ここに写真の説明を挿入

ソースコード

  • 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()

褒賞

記事が役に立ったら、著者を励ますことができます

ここに写真の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_41347419/article/details/111306949