UI
上圖 V1
上圖 V2
?
V3
Code
import tkinter as tk
from tkinter import messagebox, scrolledtext
import socket
import threading
from datetime import datetime
import os
import logging
from PIL import Image, ImageTk
import subprocess# 定義文件夾路徑
folder_path = r'c:\Log123'# 創建日志文件夾
if not os.path.exists(folder_path):os.makedirs(folder_path)# 設置日志記錄
logging.basicConfig(filename=os.path.join(folder_path, 'log.log'), level=logging.INFO,format='%(asctime)s:%(levelname)s:%(message)s')class IndustrialApp:def __init__(self, root):self.root = rootself.root.title("Design_By_Tim")self.root.geometry("1200x700")self.root.configure(bg="#333333")# 圖像加載相關變量self.img_index = 0self.current_img = None# 創建三列布局self.create_image_column() # 左側圖像列self.create_control_column() # 中間控制列self.create_status_column() # 右側狀態列# 初始化網絡連接self.client_socket = None self.standard_dimensions = {}self.standard_tolerances = {}self.update_standard_rectangle() # 從輸入框初始化標準數據# 啟動圖像更新self.img_update()def create_image_column(self):"""創建左側圖像列"""image_frame = tk.Frame(self.root, bg="#222222", width=400)image_frame.pack(side=tk.LEFT, fill=tk.BOTH, padx=(10,5), pady=10)image_frame.pack_propagate(False) # 固定寬度# 圖像顯示標簽self.img_label = tk.Label(image_frame, bg="#222222")self.img_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)# 視覺打開按鈕vision_button = tk.Button(image_frame, text="打開視覺系統", command=self.open_vision_system,font=("黑體", 18, "bold"), fg="#FFFFFF", bg="#006699",relief=tk.RAISED, borderwidth=3)vision_button.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)def create_control_column(self):"""創建中間控制列"""control_frame = tk.Frame(self.root, bg="#333333")control_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=10)# 工業風啟動按鈕self.start_button = tk.Button(control_frame, text="開始測量", command=self.send_command,font=("黑體", 16, "bold"), fg="#FFFFFF", bg="#006699",relief=tk.RAISED, borderwidth=3, width=8)self.start_button.pack(side=tk.TOP, fill=tk.X, anchor=tk.NW, padx=10, pady=10)# 繪圖畫布self.canvas = tk.Canvas(control_frame, bg="#444444", highlightthickness=0)self.canvas.pack(fill=tk.BOTH, expand=True)# 新增: 醒目的判斷結果展示區域self.result_frame = tk.Frame(control_frame, bg="#333333", height=80)self.result_frame.pack(fill=tk.X, pady=(10, 0))# 初始狀態為"等待測量"self.result_label = tk.Label(self.result_frame, text="等待測量...", font=("黑體", 24, "bold"), bg="#333333", fg="#FFFFFF")self.result_label.pack(expand=True, fill=tk.BOTH)# 詳細結果標簽self.detail_result_label = tk.Label(self.result_frame, text="", font=("黑體", 12), bg="#333333", fg="#FFFFFF")self.detail_result_label.pack(fill=tk.X, pady=(0, 5))def create_status_column(self):"""創建右側狀態列"""right_frame = tk.Frame(self.root, bg="#333333", width=300)right_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(5,10), pady=10)right_frame.pack_propagate(False) # 固定寬度# 標準矩形設置區settings_frame = tk.LabelFrame(right_frame, text="標準設置", font=("黑體", 12),bg="#333333", fg="#FFFFFF")settings_frame.pack(pady=10, fill=tk.X)# 尺寸輸入框 315.021,131.784,315.085,132.322dimensions = [("上邊 (mm):", "top", 315.021), ("右邊 (mm):", "right", 131.784),("下邊 (mm):", "bottom", 315.085), ("左邊 (mm):", "left", 132.322)]self.entries = {}for i, (label, name, default_value) in enumerate(dimensions):tk.Label(settings_frame, text=label, bg="#333333", fg="#FFFFFF").grid(row=i, column=0, padx=5, pady=5)entry = tk.Entry(settings_frame, width=10)entry.grid(row=i, column=1, padx=5, pady=5)entry.insert(0, str(default_value))self.entries[name] = entry# 公差輸入框tolerances = [("上邊公差 (mm):", "top_tol", 0.1), ("右邊公差 (mm):", "right_tol", 0.1),("下邊公差 (mm):", "bottom_tol", 0.1), ("左邊公差 (mm):", "left_tol", 0.1)]self.tolerance_entries = {}for i, (label, name, default_value) in enumerate(tolerances):tk.Label(settings_frame, text=label, bg="#333333", fg="#FFFFFF").grid(row=i, column=2, padx=5, pady=5)entry = tk.Entry(settings_frame, width=10)entry.grid(row=i, column=3, padx=5, pady=5)entry.insert(0, str(default_value))self.tolerance_entries[name] = entry# 更新按鈕update_button = tk.Button(settings_frame, text="更新標準數據", command=self.update_standard_rectangle,font=("黑體", 12), fg="#FFFFFF", bg="#555555")update_button.grid(row=len(dimensions), column=0, columnspan=4, pady=10)# 日志區域log_frame = tk.LabelFrame(right_frame, text="操作日志", font=("黑體", 12), bg="#333333", fg="#FFFFFF")log_frame.pack(fill=tk.X, pady=(10, 5))self.log_text = scrolledtext.ScrolledText(log_frame, width=55, height=12,bg="#444444", fg="#FFFFFF", font=("Consolas", 10))self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)self.log_text.insert(tk.END, "操作日志:\n")# 實時狀態區域(分為兩行,每行顯示兩組)status_frame = tk.LabelFrame(right_frame, text="實時狀態", font=("黑體", 10), bg="#333333", fg="#FFFFFF")status_frame.pack(fill=tk.X, pady=(5, 10))self.status_labels = {"top": {"name": tk.Label(status_frame, text="上邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},"right": {"name": tk.Label(status_frame, text="右邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},"bottom": {"name": tk.Label(status_frame, text="下邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},"left": {"name": tk.Label(status_frame, text="左邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},}# 實時狀態布局:兩行,每行顯示兩組row1_keys = ["top"]row2_keys = ["right"]row3_keys = ["bottom"]row4_keys = ["left"]for i, key in enumerate(row1_keys):self.status_labels[key]["name"].grid(row=0, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=0, column=i*2+1, sticky="w", padx=(5,10), pady=2)for i, key in enumerate(row2_keys):self.status_labels[key]["name"].grid(row=1, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=1, column=i*2+1, sticky="w", padx=(5,10), pady=2)for i, key in enumerate(row3_keys):self.status_labels[key]["name"].grid(row=2, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=2, column=i*2+1, sticky="w", padx=(5,10), pady=2)for i, key in enumerate(row4_keys):self.status_labels[key]["name"].grid(row=3, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=3, column=i*2+1, sticky="w", padx=(5,10), pady=2)def img_update(self):"""實時圖像更新邏輯"""img_dir = r"C:\Log\Picture\POL"try:if os.path.exists(img_dir):files = sorted([f for f in os.listdir(img_dir) if f.lower().endswith(('.png','.jpg','.bmp'))])if files:# 帶緩存的圖像加載path = os.path.join(img_dir, files[self.img_index % len(files)])with Image.open(path) as img:img = img.resize((280, 420), Image.Resampling.LANCZOS) # 調整圖像大小以適應列寬self.current_img = ImageTk.PhotoImage(img)self.img_label.config(image=self.current_img)self.img_index += 1except Exception as e:logging.error(f"圖像加載異常: {str(e)}")finally:self.root.after(1000, self.img_update) # 定時刷新def open_vision_system(self):"""打開視覺系統""" try:#vision_path = r"E:\Tim_Study\POL_Case\POLV1\Public_Release\POLV1.exe"vision_path = r"E:\Tim_Study\POL_Case\Vision\Public_Release\Vision.exe"if os.path.exists(vision_path):subprocess.Popen(vision_path)self.log_message("視覺系統已啟動")else:messagebox.showerror("錯誤", f"未找到視覺系統程序: {vision_path}")self.log_message(f"視覺系統程序未找到: {vision_path}")except Exception as e:logging.error(f"啟動視覺系統錯誤: {str(e)}")messagebox.showerror("錯誤", f"啟動視覺系統失敗: {e}")self.log_message(f"啟動視覺系統錯誤: {e}")def log_message(self, message):timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")log_entry = f"{timestamp} - {message}\n"self.log_text.insert(tk.END, log_entry)self.log_text.see(tk.END)logging.info(message)def send_command(self):server_ip = "127.0.0.1"port = 7930try:self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.client_socket.connect((server_ip, port))self.client_socket.sendall(b"V1_Point_Draw")threading.Thread(target=self.receive_data).start()self.log_message("命令發送成功: V1_Point_Draw")# 更新結果展示區域狀態self.result_label.config(text="測量中...", fg="#FFFF00") # 黃色表示測量中self.detail_result_label.config(text="")except Exception as e:logging.error(f"連接錯誤 {e}")messagebox.showerror("錯誤", f"連接失敗: {e}\n\n\n請先確認視覺系統")self.log_message(f"連接錯誤: {e}")# 更新結果展示區域狀態self.result_label.config(text="連接失敗", fg="#FF0000") # 紅色表示錯誤self.detail_result_label.config(text=str(e))def receive_data(self):try:while True:data = self.client_socket.recv(1024).decode('utf-8')if not data:breakself.update_ui(data)except Exception as e:logging.error(f"數據接收錯誤 {e}")messagebox.showerror("錯誤", f"數據接收失敗: {e}")self.log_message(f"數據接收錯誤: {e}")# 更新結果展示區域狀態self.result_label.config(text="接收錯誤", fg="#FF0000")self.detail_result_label.config(text=str(e))finally:self.client_socket.close()self.log_message("連接已關閉")def update_ui(self, data):self.draw_rectangle_with_dimensions(data)self.log_message(f"接收數據: {data}")# 更新結果展示區域self.update_result_display(data)def draw_rectangle_with_dimensions(self, data):try:self.canvas.delete("all")points = list(map(float, data.split(',')))if len(points) != 4:raise ValueError("數據格式錯誤,需要4個參數")# 繪制標準矩形self.draw_standard_rectangle()# 繪制實時矩形x1, y1 = 80, 100scale = min(1000 / max(points), 1) # 自動縮放比例x2 = x1 + points[0] * scaley2 = y1 + points[1] * scaleself.canvas.create_rectangle(x1, y1, x2, y2, outline="#FF0000", width=2)# 實時矩形尺寸標注self.create_dimension_text((x1 + x2)/2, y1-30, f"{points[0]:.3f} mm", "#FF0000")self.create_dimension_text((x1 + x2)/2, y2+30, f"{points[2]:.3f} mm", "#FF0000")self.create_dimension_text(x1-30, (y1 + y2)/2, f"{points[3]:.3f} mm", "#FF0000", 90)self.create_dimension_text(x2+30, (y1 + y2)/2, f"{points[1]:.3f} mm", "#FF0000", 90)# 實時矩形中心顯示"當前測量數據"self.canvas.create_text((x1 + x2)/2, (y1 + y2)/2, text=" ",fill="#FFFFFF", font=("Arial", 12), angle=90)# 更新實時狀態self.update_status(points)except Exception as e:logging.error(f"繪圖錯誤 {e}")messagebox.showerror("錯誤", f"繪圖失敗: {e}")self.log_message(f"繪圖錯誤: {e}")# 更新結果展示區域狀態self.result_label.config(text="繪圖錯誤", fg="#FF0000")self.detail_result_label.config(text=str(e))def update_result_display(self, data):"""優化后的結果判斷邏輯"""try:points = list(map(float, data.split(',')))if len(points) != 4:raise ValueError("需要4個測量參數")all_ok = Truedetails = []status_colors = {}for key, value in zip(["top", "right", "bottom", "left"], points):std = self.standard_dimensions[key]tol = self.standard_tolerances[f"{key}_tol"]diff = abs(value - std)if diff > tol:all_ok = Falsedetails.append(f"{key} 超差 {diff:.3f}mm")status_colors[key] = "#FF0000"else:details.append(f"{key} 合格 ±{diff:.3f}mm")status_colors[key] = "#00FF00"# 更新實時狀態顯示self.status_labels[key]["status"].config(text=f"{value:.3f}mm (標準{std:.3f}±{tol:.3f})",fg=status_colors[key])# 更新總體結果顯示if all_ok:self.result_label.config(text="測量合格", fg="#00FF00")self.detail_result_label.config(text="所有尺寸均在公差范圍內")else:self.result_label.config(text="測量不合格", fg="#FF0000")self.detail_result_label.config(text=" | ".join(details))except Exception as e:self.result_label.config(text="數據解析錯誤", fg="#FF0000")self.detail_result_label.config(text=str(e))logging.error(f"結果判斷錯誤: {str(e)}")def draw_standard_rectangle(self):try:# 獲取標準尺寸dimensions = {k: float(v.get()) for k, v in self.entries.items() if v.get()}if len(dimensions) != 4:return# 更新標準尺寸self.standard_dimensions = dimensions# 標準矩形參數std_x1, std_y1 = 80, 100scale = min(1000 / max(dimensions.values()), 1) # 自動縮放比例std_x2 = std_x1 + dimensions['top'] * scalestd_y2 = std_y1 + dimensions['right'] * scale# 繪制標準矩形self.canvas.create_rectangle(std_x1, std_y1, std_x2, std_y2, outline="#00FF00", width=2)self.canvas.create_text((std_x1 + std_x2)/2, (std_y1 + std_y2)/2,text="白色標準值\n\n紅色測量值", fill="#FFFFFF", font=("黑體", 14, "bold"))# 標準尺寸標注self.create_dimension_text((std_x1 + std_x2)/2, std_y1-10, f"{dimensions['top']:.3f} mm", "#FFFFFF")self.create_dimension_text((std_x1 + std_x2)/2, std_y2+10, f"{dimensions['bottom']:.3f} mm", "#FFFFFF")self.create_dimension_text(std_x1-10, (std_y1 + std_y2)/2, f"{dimensions['left']:.3f} mm", "#FFFFFF", 90)self.create_dimension_text(std_x2+10, (std_y1 + std_y2)/2, f"{dimensions['right']:.3f} mm", "#FFFFFF", 90)except ValueError:passdef create_dimension_text(self, x, y, text, color, angle=0):return self.canvas.create_text(x, y, text=text, fill=color,font=("Arial", 10), angle=angle, anchor=tk.CENTER)def update_status(self, real_time_data):# 獲取公差值try:tolerances = {k: float(v.get()) for k, v in self.tolerance_entries.items() if v.get()}except ValueError:messagebox.showerror("錯誤", "請輸入有效的公差值!")returnfor key, value in zip(["top", "right", "bottom", "left"], real_time_data):standard_value = self.standard_dimensions[key]tolerance = tolerances[f"{key}_tol"] # 動態獲取對應邊的公差diff = abs(value - standard_value)if diff <= tolerance:status = "OK"color = "#00FF00"else:status = f"NG ({diff - tolerance:.3f} mm)"color = "#FF0000"# 更新狀態標簽self.status_labels[key]["status"].config(text=status, fg=color)def update_standard_rectangle(self):"""更新標準矩形尺寸和公差"""try:# 驗證并獲取標準尺寸standard_dimensions = {}required_keys = ["top", "right", "bottom", "left"]for key in required_keys:value = self.entries[key].get()if not value:raise ValueError(f"請填寫{key}尺寸")standard_dimensions[key] = float(value)# 驗證并獲取公差值standard_tolerances = {}required_tols = ["top_tol", "right_tol", "bottom_tol", "left_tol"]for key in required_tols:value = self.tolerance_entries[key].get()if not value:raise ValueError(f"請填寫{key}公差")standard_tolerances[key] = float(value)# 更新標準數據self.standard_dimensions = standard_dimensionsself.standard_tolerances = standard_tolerancesself.draw_standard_rectangle()self.log_message("標準數據更新成功")except ValueError as e:messagebox.showerror("輸入錯誤", str(e))logging.error(f"標準數據更新失敗: {str(e)}")if __name__ == "__main__":root = tk.Tk()app = IndustrialApp(root)root.mainloop()
import tkinter as tk
from tkinter import messagebox, scrolledtext
import socket
import threading
from datetime import datetime
import os
import logging
from PIL import Image, ImageTk
import subprocess# 定義文件夾路徑
folder_path = r'c:\Log123'# 創建日志文件夾
if not os.path.exists(folder_path):os.makedirs(folder_path)# 設置日志記錄
logging.basicConfig(filename=os.path.join(folder_path, 'log.log'), level=logging.INFO,format='%(asctime)s:%(levelname)s:%(message)s')class IndustrialApp:def __init__(self, root):self.root = rootself.root.title("Design_By_Tim")self.root.geometry("1200x700")self.root.configure(bg="#333333")# 圖像加載相關變量self.img_index = 0self.current_img = None# 創建三列布局self.create_image_column() # 左側圖像列self.create_control_column() # 中間控制列self.create_status_column() # 右側狀態列# 初始化網絡連接self.client_socket = None self.standard_dimensions = {}self.standard_tolerances = {}self.update_standard_rectangle() # 從輸入框初始化標準數據# 啟動圖像更新self.img_update()def create_image_column(self):"""創建左側圖像列"""image_frame = tk.Frame(self.root, bg="#222222", width=400)image_frame.pack(side=tk.LEFT, fill=tk.BOTH, padx=(10,5), pady=10)image_frame.pack_propagate(False) # 固定寬度# 圖像顯示標簽self.img_label = tk.Label(image_frame, bg="#222222")self.img_label.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)# 視覺打開按鈕vision_button = tk.Button(image_frame, text="打開視覺系統", command=self.open_vision_system,font=("黑體", 18, "bold"), fg="#FFFFFF", bg="#006699",relief=tk.RAISED, borderwidth=3)vision_button.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)def create_control_column(self):"""創建中間控制列"""control_frame = tk.Frame(self.root, bg="#333333")control_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=10)# 工業風啟動按鈕self.start_button = tk.Button(control_frame, text="開始測量", command=self.send_command,font=("黑體", 16, "bold"), fg="#FFFFFF", bg="#006699",relief=tk.RAISED, borderwidth=3, width=8)self.start_button.pack(side=tk.TOP, fill=tk.X, anchor=tk.NW, padx=10, pady=10)# 繪圖畫布self.canvas = tk.Canvas(control_frame, bg="#444444", highlightthickness=0)self.canvas.pack(fill=tk.BOTH, expand=True)# 新增: 醒目的判斷結果展示區域self.result_frame = tk.Frame(control_frame, bg="#333333", height=80)self.result_frame.pack(fill=tk.X, pady=(10, 0))# 初始狀態為"等待測量"self.result_label = tk.Label(self.result_frame, text="等待測量...", font=("黑體", 24, "bold"), bg="#333333", fg="#FFFFFF")self.result_label.pack(expand=True, fill=tk.BOTH)# 詳細結果標簽self.detail_result_label = tk.Label(self.result_frame, text="", font=("黑體", 12), bg="#333333", fg="#FFFFFF")self.detail_result_label.pack(fill=tk.X, pady=(0, 5))def create_status_column(self):"""創建右側狀態列"""right_frame = tk.Frame(self.root, bg="#333333", width=300)right_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(5,10), pady=10)right_frame.pack_propagate(False) # 固定寬度# 標準矩形設置區settings_frame = tk.LabelFrame(right_frame, text="標準設置", font=("黑體", 12),bg="#333333", fg="#FFFFFF")settings_frame.pack(pady=10, fill=tk.X, padx=(6, 0))# 尺寸輸入框 315.021,131.784,315.085,132.322dimensions = [("上邊 (mm):", "top", 315.021), ("右邊 (mm):", "right", 131.784),("下邊 (mm):", "bottom", 315.085), ("左邊 (mm):", "left", 132.322)]self.entries = {}for i, (label, name, default_value) in enumerate(dimensions):tk.Label(settings_frame, text=label, bg="#333333", fg="#FFFFFF").grid(row=i, column=0, padx=5, pady=5)entry = tk.Entry(settings_frame, width=10)entry.grid(row=i, column=1, padx=5, pady=5)entry.insert(0, str(default_value))self.entries[name] = entry# 公差輸入框tolerances = [("上邊公差 (mm):", "top_tol", 0.1), ("右邊公差 (mm):", "right_tol", 0.1),("下邊公差 (mm):", "bottom_tol", 0.1), ("左邊公差 (mm):", "left_tol", 0.1)]self.tolerance_entries = {}for i, (label, name, default_value) in enumerate(tolerances):tk.Label(settings_frame, text=label, bg="#333333", fg="#FFFFFF").grid(row=i, column=2, padx=5, pady=5)entry = tk.Entry(settings_frame, width=10)entry.grid(row=i, column=3, padx=5, pady=5)entry.insert(0, str(default_value))self.tolerance_entries[name] = entry# 更新按鈕update_button = tk.Button(settings_frame, text="更新標準數據", command=self.update_standard_rectangle,font=("黑體", 12), fg="#FFFFFF", bg="#555555")update_button.grid(row=len(dimensions), column=0, columnspan=4, pady=10)# 日志區域log_frame = tk.LabelFrame(right_frame, text="操作日志", font=("黑體", 12), bg="#333333", fg="#FFFFFF")log_frame.pack(fill=tk.X, pady=(10, 5), padx=(6, 0))self.log_text = scrolledtext.ScrolledText(log_frame, width=55, height=12,bg="#444444", fg="#FFFFFF", font=("Consolas", 10))self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)self.log_text.insert(tk.END, "操作日志:\n")# 實時狀態區域(分為兩行,每行顯示兩組)status_frame = tk.LabelFrame(right_frame, text="實時狀態", font=("黑體", 10), bg="#333333", fg="#FFFFFF")status_frame.pack(fill=tk.X, pady=(5, 10), padx=(6, 0))self.status_labels = {"top": {"name": tk.Label(status_frame, text="上邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},"right": {"name": tk.Label(status_frame, text="右邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},"bottom": {"name": tk.Label(status_frame, text="下邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},"left": {"name": tk.Label(status_frame, text="左邊:", bg="#333333", fg="#FFFFFF"),"status": tk.Label(status_frame, text="OK", bg="#333333", fg="#00FF00")},}# 實時狀態布局:兩行,每行顯示兩組row1_keys = ["top"]row2_keys = ["right"]row3_keys = ["bottom"]row4_keys = ["left"]for i, key in enumerate(row1_keys):self.status_labels[key]["name"].grid(row=0, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=0, column=i*2+1, sticky="w", padx=(5,10), pady=2)for i, key in enumerate(row2_keys):self.status_labels[key]["name"].grid(row=1, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=1, column=i*2+1, sticky="w", padx=(5,10), pady=2)for i, key in enumerate(row3_keys):self.status_labels[key]["name"].grid(row=2, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=2, column=i*2+1, sticky="w", padx=(5,10), pady=2)for i, key in enumerate(row4_keys):self.status_labels[key]["name"].grid(row=3, column=i*2, sticky="w", padx=(10,5), pady=2)self.status_labels[key]["status"].grid(row=3, column=i*2+1, sticky="w", padx=(5,10), pady=2)def img_update(self):"""實時圖像更新邏輯"""img_dir = r"C:\Log\Picture\POL"try:if os.path.exists(img_dir):files = sorted([f for f in os.listdir(img_dir) if f.lower().endswith(('.png','.jpg','.bmp'))])if files:# 帶緩存的圖像加載path = os.path.join(img_dir, files[self.img_index % len(files)])with Image.open(path) as img:img = img.resize((280, 420), Image.Resampling.LANCZOS) # 調整圖像大小以適應列寬self.current_img = ImageTk.PhotoImage(img)self.img_label.config(image=self.current_img)self.img_index += 1except Exception as e:logging.error(f"圖像加載異常: {str(e)}")finally:self.root.after(1000, self.img_update) # 定時刷新def open_vision_system(self):"""打開視覺系統""" try:#vision_path = r"E:\Tim_Study\POL_Case\POLV1\Public_Release\POLV1.exe"vision_path = r"E:\Tim_Study\POL_Case\Vision\Public_Release\Vision.exe"if os.path.exists(vision_path):subprocess.Popen(vision_path)self.log_message("視覺系統已啟動")else:messagebox.showerror("錯誤", f"未找到視覺系統程序: {vision_path}")self.log_message(f"視覺系統程序未找到: {vision_path}")except Exception as e:logging.error(f"啟動視覺系統錯誤: {str(e)}")messagebox.showerror("錯誤", f"啟動視覺系統失敗: {e}")self.log_message(f"啟動視覺系統錯誤: {e}")def log_message(self, message):timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")log_entry = f"{timestamp} - {message}\n"self.log_text.insert(tk.END, log_entry)self.log_text.see(tk.END)logging.info(message)def send_command(self):server_ip = "127.0.0.1"port = 7930try:self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.client_socket.connect((server_ip, port))self.client_socket.sendall(b"V1_Point_Draw")threading.Thread(target=self.receive_data).start()self.log_message("命令發送成功: V1_Point_Draw")# 更新結果展示區域狀態self.result_label.config(text="測量中...", fg="#FFFF00") # 黃色表示測量中self.detail_result_label.config(text="")except Exception as e:logging.error(f"連接錯誤 {e}")messagebox.showerror("錯誤", f"連接失敗: {e}\n\n\n請先確認視覺系統")self.log_message(f"連接錯誤: {e}")# 更新結果展示區域狀態self.result_label.config(text="連接失敗", fg="#FF0000") # 紅色表示錯誤self.detail_result_label.config(text=str(e))def receive_data(self):try:while True:data = self.client_socket.recv(1024).decode('utf-8')if not data:breakself.update_ui(data)except Exception as e:logging.error(f"數據接收錯誤 {e}")messagebox.showerror("錯誤", f"數據接收失敗: {e}")self.log_message(f"數據接收錯誤: {e}")# 更新結果展示區域狀態self.result_label.config(text="接收錯誤", fg="#FF0000")self.detail_result_label.config(text=str(e))finally:self.client_socket.close()self.log_message("連接已關閉")def update_ui(self, data):self.draw_rectangle_with_dimensions(data)self.log_message(f"接收數據: {data}")# 更新結果展示區域self.update_result_display(data)def draw_rectangle_with_dimensions(self, data):try:self.canvas.delete("all")points = list(map(float, data.split(',')))if len(points) != 4:raise ValueError("數據格式錯誤,需要4個參數")# 繪制標準矩形self.draw_standard_rectangle()# 繪制實時矩形x1, y1 = 80, 100scale = min(1000 / max(points), 1) # 自動縮放比例x2 = x1 + points[0] * scaley2 = y1 + points[1] * scaleself.canvas.create_rectangle(x1, y1, x2, y2, outline="#FF0000", width=2)# 實時矩形尺寸標注self.create_dimension_text((x1 + x2)/2, y1-30, f"{points[0]:.3f} mm", "#FF0000")self.create_dimension_text((x1 + x2)/2, y2+30, f"{points[2]:.3f} mm", "#FF0000")self.create_dimension_text(x1-30, (y1 + y2)/2, f"{points[3]:.3f} mm", "#FF0000", 90)self.create_dimension_text(x2+30, (y1 + y2)/2, f"{points[1]:.3f} mm", "#FF0000", 90)# 實時矩形中心顯示"當前測量數據"self.canvas.create_text((x1 + x2)/2, (y1 + y2)/2, text=" ",fill="#FFFFFF", font=("Arial", 12), angle=90)# 更新實時狀態self.update_status(points)except Exception as e:logging.error(f"繪圖錯誤 {e}")messagebox.showerror("錯誤", f"繪圖失敗: {e}")self.log_message(f"繪圖錯誤: {e}")# 更新結果展示區域狀態self.result_label.config(text="繪圖錯誤", fg="#FF0000")self.detail_result_label.config(text=str(e))def update_result_display(self, data):"""優化后的結果判斷邏輯"""try:points = list(map(float, data.split(',')))if len(points) != 4:raise ValueError("需要4個測量參數")all_ok = Truedetails = []status_colors = {}for key, value in zip(["top", "right", "bottom", "left"], points):std = self.standard_dimensions[key]tol = self.standard_tolerances[f"{key}_tol"]diff = abs(value - std)if diff > tol:all_ok = Falsedetails.append(f"{key} 超差 {diff:.3f}mm")status_colors[key] = "#FF0000"else:details.append(f"{key} 合格 ±{diff:.3f}mm")status_colors[key] = "#00FF00"# 更新實時狀態顯示self.status_labels[key]["status"].config(text=f"{value:.3f}mm (標準{std:.3f}±{tol:.3f})",fg=status_colors[key])# 更新總體結果顯示if all_ok:self.result_label.config(text="測量合格", fg="#00FF00")self.detail_result_label.config(text="所有尺寸均在公差范圍內")else:self.result_label.config(text="測量不合格", fg="#FF0000")self.detail_result_label.config(text=" | ".join(details))except Exception as e:self.result_label.config(text="數據解析錯誤", fg="#FF0000")self.detail_result_label.config(text=str(e))logging.error(f"結果判斷錯誤: {str(e)}")def draw_standard_rectangle(self):try:# 獲取標準尺寸dimensions = {k: float(v.get()) for k, v in self.entries.items() if v.get()}if len(dimensions) != 4:return# 更新標準尺寸self.standard_dimensions = dimensions# 標準矩形參數std_x1, std_y1 = 80, 100scale = min(1000 / max(dimensions.values()), 1) # 自動縮放比例std_x2 = std_x1 + dimensions['top'] * scalestd_y2 = std_y1 + dimensions['right'] * scale# 繪制標準矩形self.canvas.create_rectangle(std_x1, std_y1, std_x2, std_y2, outline="#00FF00", width=2)self.canvas.create_text((std_x1 + std_x2)/2, (std_y1 + std_y2)/2,text="白色標準值\n\n紅色測量值", fill="#FFFFFF", font=("黑體", 14, "bold"))# 標準尺寸標注self.create_dimension_text((std_x1 + std_x2)/2, std_y1-10, f"{dimensions['top']:.3f} mm", "#FFFFFF")self.create_dimension_text((std_x1 + std_x2)/2, std_y2+10, f"{dimensions['bottom']:.3f} mm", "#FFFFFF")self.create_dimension_text(std_x1-10, (std_y1 + std_y2)/2, f"{dimensions['left']:.3f} mm", "#FFFFFF", 90)self.create_dimension_text(std_x2+10, (std_y1 + std_y2)/2, f"{dimensions['right']:.3f} mm", "#FFFFFF", 90)except ValueError:passdef create_dimension_text(self, x, y, text, color, angle=0):return self.canvas.create_text(x, y, text=text, fill=color,font=("Arial", 10), angle=angle, anchor=tk.CENTER)def update_status(self, real_time_data):# 獲取公差值try:tolerances = {k: float(v.get()) for k, v in self.tolerance_entries.items() if v.get()}except ValueError:messagebox.showerror("錯誤", "請輸入有效的公差值!")returnfor key, value in zip(["top", "right", "bottom", "left"], real_time_data):standard_value = self.standard_dimensions[key]tolerance = tolerances[f"{key}_tol"] # 動態獲取對應邊的公差diff = abs(value - standard_value)if diff <= tolerance:status = "OK"color = "#00FF00"else:status = f"NG ({diff - tolerance:.3f} mm)"color = "#FF0000"# 更新狀態標簽self.status_labels[key]["status"].config(text=status, fg=color)def update_standard_rectangle(self):"""更新標準矩形尺寸和公差"""try:# 驗證并獲取標準尺寸standard_dimensions = {}required_keys = ["top", "right", "bottom", "left"]for key in required_keys:value = self.entries[key].get()if not value:raise ValueError(f"請填寫{key}尺寸")standard_dimensions[key] = float(value)# 驗證并獲取公差值standard_tolerances = {}required_tols = ["top_tol", "right_tol", "bottom_tol", "left_tol"]for key in required_tols:value = self.tolerance_entries[key].get()if not value:raise ValueError(f"請填寫{key}公差")standard_tolerances[key] = float(value)# 更新標準數據self.standard_dimensions = standard_dimensionsself.standard_tolerances = standard_tolerancesself.draw_standard_rectangle()self.log_message("標準數據更新成功")except ValueError as e:messagebox.showerror("輸入錯誤", str(e))logging.error(f"標準數據更新失敗: {str(e)}")if __name__ == "__main__":root = tk.Tk()app = IndustrialApp(root)root.mainloop()