python实现的下载工具

../../_images/tjcwiki_tft_download_python.jpg

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

相关链接

HMI下载协议详解/OTA升级

c#实现的下载工具

串口屏实现的下载工具