def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 根據選取的客戶端數量,計算 min-receivers
selected_clients = self.tree.selection()
client_count = len(selected_clients)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', str(client_count), # 根據選取的客戶端數量設定
'--autostart', '5' # 自動啟動,等待 5 秒
])
print(f"檔案 {file_path} 已傳送至 {client_ip},並等待 {client_count} 個接收端")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
上述程式的執行順序如下:
選擇客戶端和檔案:
當調用 select_client_and_file 方法時,程式首先獲取從 self.tree 控制元件中選中的項目(可以是多個客戶端)。
如果有選中的項目,則提取每個選中項目的客戶端 IP。
選擇檔案:
程式接著會彈出檔案對話框,讓使用者選擇要傳送的檔案。
如果使用者選擇了檔案,程式會將檔案清單傳遞給每個選中的客戶端。
準備檔案傳輸清單:
prepare_file_transfer_list 方法被呼叫,接受客戶端 IP 和檔案路徑作為參數。
這個方法會把檔案路徑轉換為字串並列印出來。
隨後,啟動一個新的執行緒,調用 send_file_list 方法,並傳遞客戶端 IP 和檔案路徑。
發送檔案清單:
在 send_file_list 方法中,程式會將檔案路徑清單組合成一個字串,並通過 UDP 傳送給指定的客戶端。
之後,程式會暫停 2 秒鐘,以便客戶端準備接收檔案。
接著,根據選中的客戶端數量計算 min-receivers。
最後,對於每一個檔案,程式會檢查檔案是否存在,並使用 udp-sender.exe 傳送檔案,並且設置自動啟動和接收端的數量。
錯誤處理:
如果在傳送檔案清單的過程中發生任何錯誤,程式會捕獲並打印錯誤訊息。
這個流程中,關鍵的點是使用執行緒來進行非同步檔案傳輸,讓主程式能夠繼續運行而不會被阻塞。
要在第一個檔案傳輸完成後再開始第二個檔案的傳輸,你可以使用回傳值來通知主流程,確保第一個檔案的傳輸成功後再進行下一個檔案的傳輸。這裡有一個簡單的示例來展示如何實現這個流程:
使用 Queue 傳遞狀態:
使用 queue.Queue 將傳輸狀態從執行緒中傳回主執行緒。
修改 send_file_list 方法:
在 send_file_list 方法中,將結果傳回主執行緒。
import queue
import threading
import subprocess
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 傳送檔案清單給每一個選中的客戶端
for client_ip in client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1', # 設置為 1,根據需要可以更改
'--autostart', '5'
], capture_output=True)
# 將回傳值放入 queue
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
# 處理傳輸結果
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
# 如果成功,繼續進行下個檔案的傳輸
print(f"{file_path} 傳輸成功,準備傳送下一個檔案...")
else:
# 處理失敗的情況
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")
要將多個檔案分成多個檔案清單,每一輪執行一個清單,可以修改 select_client_and_file 方法,以便在選擇檔案後將檔案分組,然後在每一輪傳輸時執行一個檔案清單。以下是修改後的示例程式碼:
將檔案分組:可以根據你希望的每個清單的大小來分組檔案。
在傳輸完成後開始下一輪傳輸。
這裡的範例將每五個檔案作為一個清單進行傳輸,你可以根據需要調整這個數量。
import queue
import threading
import subprocess
import os
import time
from tkinter import filedialog
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
self.client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 將檔案分組,每 5 個檔案一組
self.file_lists = [file_paths[i:i + 5] for i in range(0, len(file_paths), 5)]
self.current_file_list_index = 0 # 重置索引
self.send_next_file_list() # 開始傳輸第一個檔案清單
def send_next_file_list(self):
if self.current_file_list_index < len(self.file_lists):
file_paths = self.file_lists[self.current_file_list_index]
for client_ip in self.client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
else:
print("所有檔案已傳送完成。")
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1',
'--autostart', '5'
], capture_output=True)
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
# 傳送完成後更新當前檔案清單索引
self.current_file_list_index += 1
self.send_next_file_list() # 開始下一個檔案清單
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
print(f"{file_path} 傳輸成功。")
else:
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")
接上," # 選擇檔案或資料夾 file_paths = filedialog.askopenfilenames(title="選擇檔案") if file_paths: # 將檔案分組,每 5 個檔案一組 self.file_lists = [file_paths[i:i + 5] for i in range(0, len(file_paths), 5)] self.current_file_list_index = 0 # 重置索引 self.send_next_file_list() # 開始傳輸第一個檔案清單",檔案分組能跟據使用者選擇檔案數量來決定,要如何修正程式?
import queue
import threading
import subprocess
import os
import time
from tkinter import filedialog
from tkinter import simpledialog
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
self.client_ips = [] # 儲存選中的客戶端 IP
self.file_lists = [] # 儲存檔案清單
self.current_file_list_index = 0 # 當前檔案清單索引
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
self.client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 讓使用者輸入每組檔案的大小
group_size = simpledialog.askinteger("檔案分組大小", "請輸入每組檔案的大小:", minvalue=1, maxvalue=len(file_paths))
if group_size is not None: # 如果使用者提供了有效的分組大小
# 將檔案分組
self.file_lists = [file_paths[i:i + group_size] for i in range(0, len(file_paths), group_size)]
self.current_file_list_index = 0 # 重置索引
self.send_next_file_list() # 開始傳輸第一個檔案清單
def send_next_file_list(self):
if self.current_file_list_index < len(self.file_lists):
file_paths = self.file_lists[self.current_file_list_index]
for client_ip in self.client_ips:
self.prepare_file_transfer_list(client_ip, file_paths)
else:
print("所有檔案已傳送完成。")
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1',
'--autostart', '5'
], capture_output=True)
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
# 傳送完成後更新當前檔案清單索引
self.current_file_list_index += 1
self.send_next_file_list() # 開始下一個檔案清單
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
print(f"{file_path} 傳輸成功。")
else:
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")
使用 simpledialog:在選擇檔案後,使用 simpledialog.askinteger 讓使用者輸入每組檔案的大小,並設定最小值和最大值。
分組檔案:根據使用者輸入的 group_size,將檔案進行分組。
有效性檢查:確認使用者輸入的分組大小是有效的(例如大於 0 且不超過選擇的檔案數量)。
這樣的設計使得使用者可以靈活決定檔案的分組方式,並且程序會根據這些設定來執行傳輸。
討論5
import queue
import threading
import subprocess
import os
import time
from tkinter import filedialog
class YourClass:
def __init__(self):
self.transfer_queue = queue.Queue() # 用於存儲傳輸結果
def select_client_and_file(self):
selected_items = self.tree.selection() # 可以選取多個客戶端
if selected_items:
client_ips = [self.tree.item(item, 'values')[0] for item in selected_items] # 獲取所有選中的客戶端 IP
# 選擇檔案或資料夾
file_paths = filedialog.askopenfilenames(title="選擇檔案")
if file_paths:
# 傳送每個檔案的檔案清單給每一個選中的客戶端
for client_ip in client_ips:
for file_path in file_paths:
self.prepare_file_transfer_list(client_ip, [file_path]) # 每個檔案單獨傳送
def prepare_file_transfer_list(self, client_ip, file_paths):
transfer_list = "\n".join(file_paths)
print(f"準備傳輸檔案到 {client_ip}:\n{transfer_list}")
# 傳送檔案清單給 client
threading.Thread(target=self.send_file_list, args=(client_ip, file_paths), daemon=True).start()
def send_file_list(self, client_ip, file_paths):
# 建立檔案傳輸指令
file_list_str = ",".join(file_paths)
try:
self.server_socket.sendto(file_list_str.encode(), (client_ip, 8500))
print(f"檔案傳輸清單已傳送至 {client_ip}")
# 等待2秒後再使用 udp-sender.exe 傳送檔案
time.sleep(2)
# 使用 udp-sender.exe 傳送檔案
for file_path in file_paths:
if os.path.exists(file_path):
result = subprocess.run([
'udp-sender.exe',
'--file', file_path,
'--port', '8500',
'--min-receivers', '1', # 設置為 1,根據需要可以更改
'--autostart', '5'
], capture_output=True)
# 將回傳值放入 queue
self.transfer_queue.put((file_path, result.returncode))
if result.returncode == 0:
print(f"檔案 {file_path} 成功傳送至 {client_ip}")
else:
print(f"檔案 {file_path} 傳送失敗: {result.stderr.decode()}")
except Exception as e:
print(f"傳送檔案清單時發生錯誤: {e}")
def process_transfer_results(self):
# 處理傳輸結果
while not self.transfer_queue.empty():
file_path, return_code = self.transfer_queue.get()
if return_code == 0:
# 如果成功,繼續進行下個檔案的傳輸
print(f"{file_path} 傳輸成功,準備傳送下一個檔案...")
else:
# 處理失敗的情況
print(f"{file_path} 傳輸失敗,檢查檔案或客戶端。")