clinet端
import socket
import threading
import time
import uuid
import os
import subprocess
import re
# 獲取 MAC 位址
def get_mac_address():
mac = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) for elements in range(0, 2*6, 2)][::-1])
return mac
# 獲取電腦名稱
def get_computer_name():
return socket.gethostname()
# 發送客戶端的報到信息 (IP、MAC、電腦名稱)
def send_report():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
while True:
try:
report_msg = f"{socket.gethostbyname(socket.gethostname())},{get_mac_address()},{get_computer_name()}"
client_socket.sendto(report_msg.encode(), ('<broadcast>', 37020))
except Exception as e:
print(f"發送報到時發生錯誤: {e}")
time.sleep(5)
# 接收檔案清單並處理接收檔案
def receive_file_list():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.bind(("", 8500))
while True:
try:
data, addr = client_socket.recvfrom(1024)
file_list = data.decode().split(",")
print(f"接收到的檔案清單: {file_list}")
# 建立接收檔案的資料夾
os.makedirs("received_files", exist_ok=True)
# 使用 udp-receiver.exe 接收檔案並回報進度
for file_name in file_list:
save_path = os.path.join("received_files", os.path.basename(file_name))
receive_file_with_progress(save_path, addr[0])
except Exception as e:
print(f"接收檔案清單時發生錯誤: {e}")
def receive_file_with_progress(save_path, server_ip):
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 使用 Popen 來執行 udp-receiver.exe 並捕獲輸出
process = subprocess.Popen(
['udp-receiver.exe', '--file', save_path, '--port', '8500'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, creationflags=subprocess.CREATE_NO_WINDOW
)
for line in process.stdout:
# 假設 udp-receiver.exe 的進度輸出包含 "% complete" 等資訊
print(f"接收進度: {line.strip()}") # 顯示在 client 端的控制台上
if re.search(r'(\d+)%', line):
# 如果捕捉到進度,將其回報給 server
client_socket.sendto(line.encode(), (server_ip, 8501))
# 檔案接收完成
if process.wait() == 0:
print("檔案接收完成")
client_socket.sendto("傳輸完成".encode(), (server_ip, 8501))
else:
print("檔案接收失敗")
client_socket.sendto("傳輸失敗".encode(), (server_ip, 8501))
# 啟動客戶端
if __name__ == "__main__":
threading.Thread(target=send_report, daemon=True).start()
threading.Thread(target=receive_file_list, daemon=True).start()
while True:
time.sleep(1)
功能增加-顯示本機伺服端IP
# 取得伺服端的本地 IP
self.server_ip = self.get_local_ip()
# 顯示伺服器 IP 的 Label
self.ip_label = tk.Label(self.root, text=f"伺服器 IP: {self.server_ip}", font=("Arial", 14))
self.ip_label.pack(pady=10)
功能增加-顯示本機伺服端IP,及標題,排在同一列
# 創建一個 Frame 用來放置標題和 IP 顯示
header_frame = tk.Frame(self.root)
header_frame.pack(pady=10)
# 新增標題 "UDP派送檔案"
self.title_label = tk.Label(header_frame, text="UDP派送檔案", font=("Arial", 18), fg="deepskyblue")
self.title_label.pack(side=tk.LEFT) # 將標題放在左側
# 取得伺服端的本地 IP
self.server_ip = self.get_local_ip()
# 顯示伺服器 IP 的 Label
self.ip_label = tk.Label(header_frame, text=f"伺服器 IP: {self.server_ip}", font=("Arial", 14))
self.ip_label.pack(side=tk.LEFT, padx=(10, 0)) # 在標題和 IP 標籤之間加點距離
功能增加-header_frame 只是用來更好地組織界面元素,你的程式將繼續正常運行。
要讓這兩個按鈕在 UI 界面上呈現在同一列上,你可以使用 tk.Frame 將它們包裝起來,然後使用 pack 方法將按鈕放在 Frame 中。這樣就能把按鈕排成一行。以下是示範如何實現
# 創建一個 Frame 用來放置按鈕
button_frame = tk.Frame(self.root)
button_frame.pack(pady=10)
# 建立廣播按鈕
self.broadcast_button = tk.Button(button_frame, text="手動廣播伺服器", command=self.start_broadcast_manually)
self.broadcast_button.pack(side=tk.LEFT, padx=(0, 10)) # 在按鈕之間加點距離
# 建立傳輸檔案按鈕
self.transfer_button = tk.Button(button_frame, text="傳送檔案", command=self.select_client_and_file)
self.transfer_button.pack(side=tk.LEFT) # 將按鈕放在左側
要讓 Treeview 預設顯示 10 列,並在超過後可以滾動,你可以將 Treeview 包裝在一個滾動條中。這樣當行數超過 10 列時,就可以使用滾動條來查看更多的內容。
以下是如何實現這個功能的示範代碼:
# 創建一個 Frame 來放置 Treeview 和滾動條
tree_frame = tk.Frame(self.root)
tree_frame.pack(fill=tk.BOTH, expand=True)
# 創建 Treeview
self.tree = ttk.Treeview(tree_frame, columns=("IP", "MAC", "Name", "Status"), show='headings')
# 設定表格標題與資料置中
self.tree.heading("IP", text="IP 位置", anchor="center")
self.tree.heading("MAC", text="MAC 位置", anchor="center")
self.tree.heading("Name", text="電腦名稱", anchor="center")
self.tree.heading("Status", text="狀態", anchor="center")
# 設定每個欄位的資料置中
self.tree.column("IP", anchor="center")
self.tree.column("MAC", anchor="center")
self.tree.column("Name", anchor="center")
self.tree.column("Status", anchor="center")
# 使用滾動條
self.scrollbar = tk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=self.scrollbar.set)
# 將 Treeview 和滾動條放入 Frame 中
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# 設定 Treeview 的最大顯示行數
self.tree.bind("<Configure>", lambda e: self.adjust_treeview_height())
def adjust_treeview_height(self):
# 計算每一行的高度
row_height = 25 # 根據需要調整行高
max_visible_rows = 10 # 設定最大可見行數
height = min(len(self.tree.get_children()), max_visible_rows) * row_height
self.tree.configure(height=height)
在這段代碼中:
創建了一個 tree_frame,用來包裝 Treeview 和滾動條。
使用 Scrollbar 來創建垂直滾動條,並將其與 Treeview 連接。
使用 pack 方法將 Treeview 和滾動條放入 tree_frame 中,並設置 Treeview 在左側擴展,滾動條在右側填充整個高度。
adjust_treeview_height 函數用於設置 Treeview 的行數和高度,確保在超過 10 行時可以滾動。
這樣就可以實現顯示 10 行的 Treeview,並在需要時使用滾動條來查看更多的行。你可以根據需要調整行高和最大可見行數的設置。
-alert一個文字訊息框
from tkinter import messagebox
# 完成傳輸,更新狀態
self.status_label.config(text="目前傳輸狀態:傳輸完成!")
messagebox.showinfo("傳輸完成", "檔案已完成傳輸!")→使用者看到
-要讓 udp-receiver.exe 以背景方式執行且不顯示終端機畫面,可以使用 subprocess.CREATE_NO_WINDOW 選項。這個選項會讓程序在沒有控制台窗口的情況下運行。以下是一個修改後的範例:
import subprocess
process = subprocess.Popen(
['udp-receiver.exe', '--file', save_path, '--port', '8500'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
creationflags=subprocess.CREATE_NO_WINDOW
)
這樣,udp-receiver.exe 就會在背景執行,且不會顯示任何終端機窗口。如果你需要從這個背景進程中讀取輸出,你可以使用 process.stdout.read() 或者 process.stdout.readline() 來捕獲輸出。例如:
import subprocess
process = subprocess.Popen(
['udp-receiver.exe', '--file', save_path, '--port', '8500'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
creationflags=subprocess.CREATE_NO_WINDOW
)
# 讀取輸出
output, _ = process.communicate()
print(output)
如果你想要選擇一個資料夾並獲取該資料夾內所有的檔案,你可以先使用 filedialog.askdirectory() 來選擇資料夾,然後使用 os 模組來列出該資料夾內的所有檔案。以下是範例程式碼:
python
import os
from tkinter import filedialog
# 選擇資料夾
folder_path = filedialog.askdirectory(title="選擇資料夾")
# 確保使用者選擇了資料夾
if folder_path:
# 獲取該資料夾內所有檔案
file_list = os.listdir(folder_path)
# 列出所有檔案
print("資料夾內的檔案:")
for file_name in file_list:
print(file_name)
else:
print("未選擇任何資料夾。")
這樣,你就能夠選擇一個資料夾,並列出該資料夾內所有的檔案名稱。
如果你想要在 Python 中選擇多個檔案,可以使用 filedialog.askopenfilenames() 函數,這樣就能讓使用者選擇多個檔案。你原本的程式碼已經是正確的,只需確保程式碼的完整性。以下是範例程式碼:
from tkinter import filedialog
# 選擇多個檔案
file_paths = filedialog.askopenfilenames(title="選擇檔案")
# 將選擇的檔案路徑轉換為列表
file_paths = list(file_paths)
# 列出所有選擇的檔案路徑
print("選擇的檔案路徑:")
for path in file_paths:
print(path)
這樣,使用者就可以選擇多個檔案,並且程式會將選擇的檔案路徑列出來。