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
原文地址:https://www.cnblogs.com/ytwang/p/15111997.html