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