• Python的tkinter和pyinstaller打造易用的工具


    Python工具

    打造可执行的工具,方便其他人使用
    

    命令行工具

    命令行工具还是需要了解基本的路径等信息

    图像化界面工具

    tkinter
    界面与逻辑分离
      UI线程和逻辑线程的分离
      TKinter自身刷新GUI是单线程的 
        GUI放在主线程中,而其他代码放在工作线程中,并在它们之间使用线程安全队列进行通信
    主线程轮询是否有回调事件或者数据改变。不论是 Pub/Sub 的方案,还是 Event 的方案,本质上都是这样一种 loop
    解决多线程与 UI 线程交互的问题:  解决办法是利用继承实现界面和业务逻辑的分离
    解决GUI阻塞,而且不在子线程里更新GUI的办法,还是利用python自带的队列Queue,以及Tkinter下面的after方法。
    

    代码

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    # FileName: Openfile
    
    import tkinter as tk
    import tkinter.filedialog
    import tkinter.dialog
    import threading
    import time
    
    
    def fmt_time(time_stamp):
        time_array = time.localtime(time_stamp)
        date_time = time.strftime("%Y-%m-%d %H:%M:%S", time_array)
        return date_time
    
    
    class GUI():
        def __init__(self, root_win):
            # top level的根窗口
            root_win.title("文件处理")
            # 创建主框架frame,采用相对布局pack,有两个大frame,上下
            frame = tk.Frame(root_win)
            frame.pack(side=tk.TOP, padx=100, pady=100)
            # 创建第二层框架frame,放置在主框架frame上。第二层frame有1个放上
            frame_top = tk.Frame(frame)
            frame_top.pack(side=tk.TOP, padx=50, pady=50)
            # side参数,参数可以设置LEFT、RIGHT、TOP 和 BOTTOM 四个方位,默认设置是:side=tkinter.TOP
            # padx, pady 组件外部在x(y)方向上填充的空间大小,默认单位为像素
            # frame_bom-标志执行的命令的位置 ,然后在里面添加一个Button按钮,框架一般是用于在复杂的布局起到将组件分组作用
            frame_bom = tk.LabelFrame(root_win, text="执行", labelanchor="n")
            frame_bom.pack(side=tk.BOTTOM, padx=50, pady=50)
    
            # ###### frame_top
            # 打开文件
            # 创建一个按钮组件,fg=foreground的缩写,就是设置前景色
            self.open_there = tk.Button(frame_top, text="打开文件", fg="blue", command=self.askopenfilename)
            self.open_there.grid(row=0)
            # 设置文本显示框
            self.input_text_show = tk.Text(frame_top, width=30, height=2)
            self.input_text_show.tag_config("tag_1", backgroun="yellow", foreground="red")
            self.input_text_show.grid(row=0, column=1)
    
            # 保存文件
            # # 创建一个按钮组件,fg=foreground的缩写,就是设置前景色
            self.save_there = tk.Button(frame_top, text="保存位置", fg="green", command=self.asksaveasfile)
            self.save_there.grid(row=1)
            # # 设置文本显示框
            self.out_text_show = tk.Text(frame_top, width=30, height=2)
            self.out_text_show.tag_config("tag_2", backgroun="yellow", foreground="red")
            self.out_text_show.grid(row=1, column=1)
    
            # define options for opening or saving a file
            self.file_opt = options = {}
            options['defaultextension'] = '.txt'
            options['filetypes'] = [('all files', '.*'), ('text files', '.txt')]
            options['initialdir'] = 'C:\Users'
            options['initialfile'] = 'myfile.txt'
            options['parent'] = root_win
            options['title'] = u'选择文件'
    
            # ###### frame_bom
            # 第二部分frame
            # # # 创建一个按钮组件,fg=foreground的缩写,就是设置前景色
            self.click_there = tk.Button(frame_bom, text="click", fg="red", width=10, command=self.deal_file_thread)
            self.click_there.pack(side=tk.LEFT, padx=20, pady=20)
    
        def askopenfilename(self):
            # get filename 不定长参数--** 以字典的方式接收参数
            filename = tkinter.filedialog.askopenfilename(**self.file_opt)
            # open file on your own
            if filename is not None:
                self.input_text_show.insert(tk.END, '
    ' + filename + '
    ')
                return self.input_text_show.get(tk.END)
    
        def asksaveasfile(self):
            """Returns an opened file in write mode."""
            file_wrapper = tkinter.filedialog.asksaveasfile(mode='w', **self.file_opt)
            if file_wrapper is not None:
                file_text_out = file_wrapper.name
                return self.out_text_show.insert(tk.END, file_text_out)
    
        def __dealfile(self):
            """Returns an opened file in write mode."""
            file_text_in = self.input_text_show.get('1.0', tk.END)
            input_file = file_text_in.replace('
    ', '')
            file_text_out = self.out_text_show.get('1.0', tk.END)
            out_file = file_text_out.replace('
    ', '')
            with open(input_file, mode='r', encoding='utf-8') as fr, 
                    open(out_file, mode="w", encoding='utf-8') as fw:
                i = 0
                while i < 3:
                    print(fmt_time(time.time()))
                    time.sleep(1)
                    i += 1
                for data in fr:
                    fw.write(data)
            tk.dialog.Dialog(None, {'title': 'File Modified', 'text': '保存完成', 'bitmap': 'warning', 'default': 0,
                                    'strings': ('OK', 'Cancle')})
            print('保存完成')
    
        def deal_file_thread(self):
            T = threading.Thread(target=self.__dealfile, args=())
            T.start()
    
    
    if __name__ == "__main__":
        root_wins = tk.Tk()
        GUI(root_wins)
        root_wins.mainloop()
    

    打包成exe文件

    Pyinstaller
    将 Python 程序打包成一个独立可执行软件包,支持 Windows、Linux 和 Mac OS X
      pip install pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple
    使用:
       -F,-onefile	产生单个的可执行文件
     Pyinstaller.  -F D:PyWindEXEOpenfile.py -i D:PyWindEXEmy.ico  --noconsole
    

    版本V2 Python tk

    #!/usr/bin/env python3
    # -*- coding: UTF-8 -*-
    
    
    import tkinter as tk
    import tkinter.filedialog
    import tkinter.dialog
    import threading
    import time
    import queue
    import sys
    
    
    def fmt_time(time_stamp):
        time_array = time.localtime(time_stamp)
        date_time = time.strftime("%Y-%m-%d %H:%M:%S", time_array)
        return date_time
    
    
    class Redirect2Queue():
    
        def __init__(self, queue_obj):
            self.queue_obj = queue_obj
    
        def write(self, content):
    
            self.queue_obj.put(content)
    
        def flush(self):
            pass
    
    
    class GUI():
        def __init__(self, root_win):
            # top level的根窗口
            self.root_win = root_win
            self.root_win.title("文件处理")
            # 创建主框架frame,采用相对布局pack,有两个大frame,上下
            frame = tk.Frame(self.root_win)
            frame.pack(side=tk.TOP, padx=100, pady=100)
            # 创建第二层框架frame,放置在主框架frame上。第二层frame有1个放上
            frame_top = tk.Frame(frame)
            frame_top.pack(side=tk.TOP, padx=50, pady=50)
            # side参数,参数可以设置LEFT、RIGHT、TOP 和 BOTTOM 四个方位,默认设置是:side=tkinter.TOP
            # padx, pady 组件外部在x(y)方向上填充的空间大小,默认单位为像素
            # frame_bom-标志执行的命令的位置 ,然后在里面添加一个Button按钮,框架一般是用于在复杂的布局起到将组件分组作用
            frame_bom = tk.LabelFrame(self.root_win, text="执行", labelanchor="n")
            frame_bom.pack(side=tk.BOTTOM, padx=50, pady=50)
    
            # ###### frame_top
            # 打开文件
            # 创建一个按钮组件,fg=foreground的缩写,就是设置前景色
            self.open_there = tk.Button(frame_top, text="打开文件", fg="blue", command=self.askopenfilename)
            self.open_there.grid(row=0)
            # 设置文本显示框
            self.input_text_show = tk.Text(frame_top, width=30, height=2)
            self.input_text_show.tag_config("tag_1", backgroun="yellow", foreground="red")
            self.input_text_show.grid(row=0, column=1)
    
            # 保存文件
            # # 创建一个按钮组件,fg=foreground的缩写,就是设置前景色
            self.save_there = tk.Button(frame_top, text="保存位置", fg="green", command=self.asksaveasfile)
            self.save_there.grid(row=1)
            # # 设置文本显示框
            self.out_text_show = tk.Text(frame_top, width=30, height=2)
            self.out_text_show.tag_config("tag_2", backgroun="yellow", foreground="red")
            self.out_text_show.grid(row=1, column=1)
    
            # define options for opening or saving a file
            self.file_opt = options = {}
            options['defaultextension'] = '.txt'
            options['filetypes'] = [('all files', '.*'), ('text files', '.txt')]
            options['initialdir'] = 'C:\Users'
            options['initialfile'] = 'myfile.txt'
            options['parent'] = root_win
            options['title'] = u'选择文件'
    
            # ###### frame_bom
            # 第二部分frame
            # # # 创建一个按钮组件,fg=foreground的缩写,就是设置前景色
            self.click_there = tk.Button(frame_bom, text="click", fg="red", width=10, command=self.deal_file_thread)
            self.click_there.pack(side=tk.LEFT, padx=20, pady=20)
    
            ##用于更新状态的界面
            self.scrollBar = tk.Scrollbar(frame_bom)
            self.scrollBar.pack(side="right", fill="y")
    
            self.text = tk.Text(frame_bom, height=10, width=45, yscrollcommand=self.scrollBar.set)
            self.text.pack(side="bottom", fill=tk.BOTH, padx=10, pady=10)
           ##更新
            # new 一个Quue用于保存输出内容
            self.msg_queue = queue.Queue()
            # 启动after方法
            self.root_win.after(3, self.notify_msg)
            #将stdout映射到re_Text
            sys.stdout = Redirect2Queue(self.msg_queue)
            
            #在notify_msg方法里,从Queue取出元素,输出到 Text
        def notify_msg(self):
            # 判断队列是否为空
            while not self.msg_queue.empty():
                content = self.msg_queue.get()
                self.text.insert(tk.INSERT, content)
                self.text.see(tk.END)
            # after方法再次调用 notify_msg
            self.root_win.after(3, self.notify_msg)
    
        def askopenfilename(self):
            # get filename 不定长参数--** 以字典的方式接收参数
            filename = tkinter.filedialog.askopenfilename(**self.file_opt)
            # open file on your own
            if filename is not None:
                self.input_text_show.insert(tk.END, '
    ' + filename + '
    ')
                return self.input_text_show.get(tk.END)
    
        def asksaveasfile(self):
            """Returns an opened file in write mode."""
            file_wrapper = tkinter.filedialog.asksaveasfile(mode='w', **self.file_opt)
            if file_wrapper is not None:
                file_text_out = file_wrapper.name
                return self.out_text_show.insert(tk.END, file_text_out)
    
        def __dealfile(self):
            """Returns an opened file in write mode."""
            file_text_in = self.input_text_show.get('1.0', tk.END)
            input_file = file_text_in.replace('
    ', '')
            file_text_out = self.out_text_show.get('1.0', tk.END)
            out_file = file_text_out.replace('
    ', '')
            with open(input_file, mode='r', encoding='utf-8') as fr, 
                    open(out_file, mode="w", encoding='utf-8') as fw:
                i = 0
                while i < 3:
                    print(fmt_time(time.time()))
                    time.sleep(0.1)
                    i += 1
                for data in fr:
                    fw.write(data)
            tk.dialog.Dialog(None, {'title': 'File Modified', 'text': '保存完成', 'bitmap': 'warning', 'default': 0,
                                    'strings': ('OK', 'Cancle')})
            print('保存完成')
    
        def deal_file_thread(self):
            T = threading.Thread(target=self.__dealfile, args=())
            T.start()
    
    
    if __name__ == "__main__":
        root_wins = tk.Tk()
        GUI(root_wins)
        root_wins.mainloop()
    

    参考

     Python多处理将子进程的stdout重定向到Tkinter Text https://www.icode9.com/content-3-482676.html
    Python GUI:Tkinter——08 https://blog.csdn.net/weixin_42141390/article/details/106583809
    在tkinter中使用Queue(线程,Python 3)  https://www.pythonheidong.com/blog/article/474937/ee9fad5702114ab469fd/
    Tkinter 吐槽之一:多线程与 UI 交互 https://www.cnblogs.com/libitum/p/14848615.html
    【Python】TKinter在多线程时刷新GUI的一些碎碎念 https://blog.csdn.net/u013700771/article/details/103321783
    Python GUI库TKinter子线程与主线程控件传递消息策略 https://blog.csdn.net/l198738655/article/details/113564328
  • 相关阅读:
    【转】Mybatis常见面试题总结
    【转】深入分析@Transactional的用法
    刷抖音极速版,大家一起来赚钱
    公司喜欢什么样的员工呢?
    边学习新技术边工作的重要性
    聊聊IT行业加班的问题
    软件工程师怎样减轻工作过程中遇到的压力
    如何找兼职工作
    常用的网址导航
    几家主要的配送(跑腿)服务提供商
  • 原文地址:https://www.cnblogs.com/ytwang/p/15111997.html
Copyright © 2020-2023  润新知