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