UI
上图 V1

上图 V2
V3





Code
python
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 = root
self.root.title("Design_By_Tim")
self.root.geometry("1200x700")
self.root.configure(bg="#333333")
# 图像加载相关变量
self.img_index = 0
self.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.322
dimensions = [("上边 (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 += 1
except 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 = 7930
try:
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:
break
self.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, 100
scale = min(1000 / max(points), 1) # 自动缩放比例
x2 = x1 + points[0] * scale
y2 = y1 + points[1] * scale
self.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 = True
details = []
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 = False
details.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, 100
scale = min(1000 / max(dimensions.values()), 1) # 自动缩放比例
std_x2 = std_x1 + dimensions['top'] * scale
std_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:
pass
def 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("错误", "请输入有效的公差值!")
return
for 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_dimensions
self.standard_tolerances = standard_tolerances
self.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()
python
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 = root
self.root.title("Design_By_Tim")
self.root.geometry("1200x700")
self.root.configure(bg="#333333")
# 图像加载相关变量
self.img_index = 0
self.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.322
dimensions = [("上边 (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 += 1
except 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 = 7930
try:
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:
break
self.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, 100
scale = min(1000 / max(points), 1) # 自动缩放比例
x2 = x1 + points[0] * scale
y2 = y1 + points[1] * scale
self.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 = True
details = []
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 = False
details.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, 100
scale = min(1000 / max(dimensions.values()), 1) # 自动缩放比例
std_x2 = std_x1 + dimensions['top'] * scale
std_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:
pass
def 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("错误", "请输入有效的公差值!")
return
for 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_dimensions
self.standard_tolerances = standard_tolerances
self.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()