python实现的下载工具

1 import tkinter as tk
2 from tkinter import ttk, filedialog
3 import serial
4 import serial.tools.list_ports
5 import threading
6 import time
7 import os
8 from datetime import datetime
9
10 class TJCDownloadTool(tk.Tk):
11 def __init__(self):
12 super().__init__()
13 self.title("淘晶驰串口屏下载工具")
14 self.geometry("800x600")
15
16 # 串口相关变量
17 self.serial_port = None
18 self.connect_baud = 0
19 self.download_baud = 921600
20 self.connecting = False
21 self.downloading = False
22 self.transferred = 0
23 self.last_update = time.time()
24 self.speed = 0
25
26 # 创建界面组件
27 self.create_widgets()
28 self.scan_serial_ports()
29
30 def create_widgets(self):
31 # 串口选择
32 ttk.Label(self, text="串口选择:").place(x=20, y=20)
33 self.port_combobox = ttk.Combobox(self, width=15)
34 self.port_combobox.place(x=100, y=20)
35
36 # 波特率选择
37 ttk.Label(self, text="下载波特率:").place(x=20, y=60)
38 self.baud_combobox = ttk.Combobox(self, width=15, values=[
39 2400, 4800, 9600, 19200, 38400, 57600,
40 115200, 230400, 256000, 512000, 921600
41 ])
42 self.baud_combobox.set(921600)
43 self.baud_combobox.place(x=100, y=60)
44
45 # 文件选择
46 self.file_btn = ttk.Button(self, text="选择文件", command=self.select_file)
47 self.file_btn.place(x=20, y=100)
48 self.file_path = ttk.Entry(self, width=50)
49 self.file_path.place(x=100, y=100)
50
51 # 下载按钮
52 self.download_btn = ttk.Button(self, text="开始下载", command=self.start_download)
53 self.download_btn.place(x=20, y=140)
54
55 # 下载速度显示
56 ttk.Label(self, text="下载速度:").place(x=20, y=180)
57 self.speed_label = ttk.Label(self, text="0 kB/s")
58 self.speed_label.place(x=100, y=180)
59
60 # 进度条
61 self.progress = ttk.Progressbar(self, orient=tk.HORIZONTAL, length=750, mode='determinate')
62 self.progress.place(x=20, y=220)
63
64 # 日志文本框
65 self.log_text = tk.Text(self, width=95, height=25)
66 self.log_text.place(x=20, y=260)
67
68 def scan_serial_ports(self):
69 ports = sorted(serial.tools.list_ports.comports(), key=lambda p: p.device, reverse=True)
70 self.port_combobox['values'] = [p.device for p in ports]
71 if ports:
72 self.port_combobox.set(ports[0].device)
73
74 def select_file(self):
75 path = filedialog.askopenfilename(filetypes=[("TFT files", "*.tft")])
76 if path:
77 self.file_path.delete(0, tk.END)
78 self.file_path.insert(0, path)
79
80 def log(self, message):
81 self.log_text.insert(tk.END, f"[{datetime.now().strftime('%H:%M:%S')}] {message}\n")
82 self.log_text.see(tk.END)
83
84 def update_speed(self):
85 if self.downloading:
86 now = time.time()
87 elapsed = now - self.last_update
88 if elapsed > 0:
89 self.speed = (self.transferred / elapsed) / 1024
90 self.speed_label.config(text=f"{self.speed:.1f} kB/s")
91 self.last_update = now
92 self.transferred = 0
93 self.after(1000, self.update_speed)
94
95 def start_download(self):
96 if not self.downloading:
97 self.downloading = True
98 self.transferred = 0
99 self.last_update = time.time()
100 self.download_btn.config(text="正在联机...")
101 self.progress['value'] = 0
102 self.after(1000, self.update_speed)
103 threading.Thread(target=self.download_process).start()
104
105 def download_process(self):
106 port = self.port_combobox.get()
107 file_path = self.file_path.get()
108
109 if not port or not file_path:
110 self.log("请先选择串口和文件")
111 self.reset_btn()
112 return
113
114 try:
115 # 尝试联机
116 self.log("开始联机...")
117 connect_bauds = [9600, 115200, 19200, 38400, 57600,
118 230400, 256000, 512000, 921600, 4800, 2400]
119
120 for baud in connect_bauds:
121 self.log(f"尝试 {baud} 波特率...")
122 try:
123 with serial.Serial(port, baud, timeout=0.1) as ser:
124 connect_cmd = bytes.fromhex(
125 "44 52 41 4B 4A 48 53 55 59 44 47 42 4E 43 4A 48 47 4A 4B 53 48 42 44 4E FF FF FF 00 FF FF FF 63 6F 6E 6E 65 63 74 FF FF FF"
126 )
127 ser.write(connect_cmd)
128 time.sleep((1000000 / baud + 30) / 1000)
129 response = ser.read(1024)
130 if b'comok' in response:
131 self.connect_baud = baud
132 self.log(f"联机成功! 波特率: {baud}")
133 self.log(f"设备响应: {response.decode(errors='ignore')}")
134 break
135 except Exception as e:
136 continue
137
138 if not self.connect_baud:
139 self.log("联机失败!")
140 self.reset_btn()
141 return
142
143 # 开始下载
144 self.download_btn.config(text="正在下载...")
145 self.send_download_command(port, file_path)
146
147 except Exception as e:
148 self.log(f"发生错误: {str(e)}")
149 finally:
150 self.reset_btn()
151
152 def send_download_command(self, port, file_path):
153 try:
154 # 获取文件大小
155 file_size = os.path.getsize(file_path)
156 download_baud = int(self.baud_combobox.get())
157 self.progress['maximum'] = file_size
158
159 # 发送whmi-wri指令
160 with serial.Serial(port, self.connect_baud) as ser:
161 cmd = f"whmi-wri {file_size},{download_baud},0".encode()
162 ser.write(cmd + b'\xff\xff\xff')
163 time.sleep(0.35)
164
165 # 切换波特率
166 ser.baudrate = download_baud
167 response = ser.read(1)
168 if response != b'\x05':
169 self.log("设备未响应准备信号")
170 return
171
172 # 发送文件数据
173 self.log("开始传输文件...")
174 with open(file_path, 'rb') as f:
175 while True:
176 chunk = f.read(4096)
177 if not chunk:
178 break
179 ser.write(chunk)
180 self.transferred += len(chunk)
181 self.progress['value'] += len(chunk)
182 # 等待响应
183 while True:
184 resp = ser.read(1)
185 if resp == b'\x05':
186 break
187 self.log("文件传输完成!")
188
189 except Exception as e:
190 self.log(f"下载失败: {str(e)}")
191
192 def reset_btn(self):
193 self.downloading = False
194 self.connect_baud = 0
195 self.download_btn.config(text="开始下载")
196 self.progress['value'] = 0
197 self.speed_label.config(text="0 kB/s")
198
199 if __name__ == "__main__":
200 app = TJCDownloadTool()
201 app.mainloop()
相关链接