主窗口+子窗口 实现复杂GUI模式
# -*- coding: utf-8 -*- import tkinter as tk from tkinter import filedialog, messagebox, simpledialog from PIL import Image, ImageTk import math from tkinter import scrolledtext import json # 对话页面 说明如何设置robotic griper # 需要 from tkinter import scrolledtext class ManualDialog: def __init__(self, parent, title="吸盘夹具",txt=('line1','line2')): self.parent = parent self.top = tk.Toplevel(parent) self.top.title(title) self.top.geometry("300x300") # 增加了窗口的宽度和高度 #显示设备或打印介质的“DPI”(Dots Per Inch,每英寸点数)或“PPI”(Pixels Per Inch,每英寸像素数) # 创建用于输出多行文本的 Text 小部件 # 使用 scrolledtext.ScrolledText 提供内置的滚动条 root = self.top self.text_output = scrolledtext.ScrolledText(root, wrap=tk.WORD, width=40, height=15) self.text_output.pack(pady=10) def add_text_output(text): # 在输出区域的末尾插入文本 self.text_output.insert(tk.END, text + "\n") # 添加换行符以分隔不同的输出 # 添加一些示例文本输出 # add_text_output("这是第一行输出文本。") # add_text_output("这是第二行输出文本,它可能更长一些,以展示文本换行效果。") # add_text_output("这是第三行输出文本。") for each in txt: add_text_output(each) # 你可以选择将 state 设置为 DISABLED 以防止用户编辑 self.text_output.config(state=tk.DISABLED) # 对话窗口 输入绝对点坐标 class Point2DInputDialog: def __init__(self, parent, title="Enter absolute 2D point"): self.parent = parent self.top = tk.Toplevel(parent) self.top.title(title) self.top.geometry("300x300") # 增加了窗口的宽度和高度 #显示设备或打印介质的“DPI”(Dots Per Inch,每英寸点数)或“PPI”(Pixels Per Inch,每英寸像素数) self.label1 = tk.Label(self.top, text="Enter X Pixels:") self.label1.pack(pady=10) self.entry1 = tk.Entry(self.top) self.entry1.pack(pady=5) self.label2 = tk.Label(self.top, text="Enter Y Pixels:") self.label2.pack(pady=5) self.entry2 = tk.Entry(self.top) self.entry2.pack(pady=10) self.label3 = tk.Label(self.top, text="Enter Radius Pixels:") self.label3.pack(pady=5) self.entry3 = tk.Entry(self.top) self.entry3.pack(pady=10) # 使用 fill=X 和 expand=True 来让按钮水平扩展并填充剩余空间 self.done_button = tk.Button(self.top, text="Done", command=self.done) self.done_button.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.X, expand=True) self.clear_button = tk.Button(self.top, text="Clear", command=self.clear) self.clear_button.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.X, expand=True) self.cancel_button = tk.Button(self.top, text="Cancel", command=self.cancel) self.cancel_button.pack(side=tk.RIGHT, padx=10, pady=10, fill=tk.X, expand=True) self.numbers = [None, None] def done(self): num1_str = self.entry1.get().strip() num2_str = self.entry2.get().strip() num3_str = self.entry3.get().strip() num1 = None num2 = None num3 = None try: num1 = float(num1_str) except ValueError: messagebox.showerror("Invalid Input", f"Invalid number for the first entry: {num1_str}") try: num2 = float(num2_str) except ValueError: messagebox.showerror("Invalid Input", f"Invalid number for the second entry: {num2_str}") try: num3 = float(num3_str) except ValueError: messagebox.showerror("Invalid Input", f"Invalid number for the second entry: {num2_str}") if num1 is not None and num2 is not None and num3 is not None: self.numbers = [num1, num2, num3] # 对话框关闭后重新显示主窗口 # root.withdraw() # 隐藏主窗口 #self.parent.deiconify() self.top.destroy() def clear(self): self.numbers = [None, None] self.entry1.delete(0,tk.END) self.entry2.delete(0,tk.END) def cancel(self): self.numbers = [None, None] self.top.destroy() def get_numbers(self): return self.numbers # 对话窗口 输入相对点坐标 class AngleDistanceInputDialog: def __init__(self, parent, title="Enter relative 2D point"): self.parent = parent self.top = tk.Toplevel(parent) self.top.title(title) self.top.geometry("300x200") # 增加了窗口的宽度和高度 #显示设备或打印介质的“DPI”(Dots Per Inch,每英寸点数)或“PPI”(Pixels Per Inch,每英寸像素数) self.label1 = tk.Label(self.top, text="Enter Angle Degrees:") self.label1.pack(pady=10) self.entry1 = tk.Entry(self.top) self.entry1.pack(pady=5) self.label2 = tk.Label(self.top, text="Enter Distance Pixels:") self.label2.pack(pady=5) self.entry2 = tk.Entry(self.top) self.entry2.pack(pady=10) # 使用 fill=X 和 expand=True 来让按钮水平扩展并填充剩余空间 self.done_button = tk.Button(self.top, text="Done", command=self.done) self.done_button.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.X, expand=True) self.clear_button = tk.Button(self.top, text="Clear", command=self.clear) self.clear_button.pack(side=tk.LEFT, padx=10, pady=10, fill=tk.X, expand=True) self.cancel_button = tk.Button(self.top, text="Cancel", command=self.cancel) self.cancel_button.pack(side=tk.RIGHT, padx=10, pady=10, fill=tk.X, expand=True) self.numbers = [None, None] def done(self): num1_str = self.entry1.get().strip() num2_str = self.entry2.get().strip() num1 = None num2 = None try: num1 = float(num1_str) except ValueError: messagebox.showerror("Invalid Input", f"Invalid number for the first entry: {num1_str}") try: num2 = float(num2_str) except ValueError: messagebox.showerror("Invalid Input", f"Invalid number for the second entry: {num2_str}") if num1 is not None and num2 is not None: self.numbers = [num1, num2] # 对话框关闭后重新显示主窗口 # root.withdraw() # 隐藏主窗口 #self.parent.deiconify() self.top.destroy() def clear(self): self.numbers = [None, None] self.entry1.delete(0,tk.END) self.entry2.delete(0,tk.END) def cancel(self): self.numbers = [None, None] self.top.destroy() def get_numbers(self): return self.numbers # 用来机械爪抓取方式 class GripperDialog(tk.Toplevel): def __init__(self, parent, title="Robotic Gripper Dialog"): super().__init__(parent) self.parent = parent # 设置对话框的标题 self.title(title) # 设置对话框的大小(可选) self.geometry("640x500") # 绑定关闭事件(可选,确保对话框可以通过窗口管理器关闭按钮正确关闭) self.protocol("WM_DELETE_WINDOW", self.on_close) # 设定爪方式值 self.json_dict = dict() # 属性 self.image_path = None # 路径 self.image = None # 图片 self.tk_image = None self.start_x = None self.start_y = None self.angle = None # 与x轴夹角 self.distance = None # 绘图板控件 放图片 self.canvas = tk.Canvas(self, cursor="cross") self.canvas.pack(fill=tk.BOTH, expand=True) self.canvas.bind("<Motion>", self.show_pixel_info)#绑定鼠标移动事件,显示像素信息 # 菜单容器 self.menu = tk.Menu(self) #一级菜单容器 self.config(menu=self.menu)# 放入Toplevel容器中 self.file_menu = tk.Menu(self.menu, tearoff=0) #二级菜单容器 # 吸盘:Suction Cup # 两爪夹具:Two-Jaw Clamp(或者 Two-Finger Clamp,具体取决于上下文和使用的领域) # 三抓夹具:Three-Jaw Chuck(在机床或工具制造中常用)或者 Three-Point Clamp(在更一般的夹持应用中) self.suction_cup_menu = tk.Menu(self.menu, tearoff=0) #二级菜单容器 self.two_jaw_menu = tk.Menu(self.menu, tearoff=0) #二级菜单容器 self.three_jaw_menu = tk.Menu(self.menu, tearoff=0) #二级菜单容器 #一级菜单 self.menu.add_cascade(label="打开图片", menu=self.file_menu) #一级菜单 self.menu.add_cascade(label="吸盘夹具", menu=self.suction_cup_menu) #一级菜单 self.menu.add_cascade(label="两爪夹具", menu=self.two_jaw_menu) #一级菜单 self.menu.add_cascade(label="三爪夹具", menu=self.three_jaw_menu) #二级菜单 file self.file_menu.add_command(label="读入图片", command=self.open_image) self.file_menu.add_separator() self.file_menu.add_command(label="Exit", command=self.on_close) #二级菜单 吸盘夹具 self.suction_cup_menu.add_command(label="设置吸盘夹持中心", command=self.suction_cup_center) self.suction_cup_menu.add_separator() self.suction_cup_menu.add_command(label="设置零件夹持方向", command=self.suction_cup_direction) self.suction_cup_menu.add_separator() self.suction_cup_menu.add_command(label="设置吸盘夹具说明", command=self.suction_cup_manual) # 二级菜单 两爪夹具 self.two_jaw_menu.add_command(label="设置夹持中心", command=self.two_jaw_center) self.two_jaw_menu.add_separator() self.two_jaw_menu.add_command(label="设置第一个夹持点", command=self.two_jaw_1point) self.two_jaw_menu.add_separator() self.two_jaw_menu.add_command(label="设置第二个夹持点", command=self.two_jaw_2point) self.two_jaw_menu.add_separator() self.two_jaw_menu.add_command(label="设置两爪夹具说明", command=self.two_jaw_manual) # 二级菜单 三爪夹具 self.three_jaw_menu.add_command(label="设置夹持中心", command=self.three_jaw_center) self.three_jaw_menu.add_separator() self.three_jaw_menu.add_command(label="设置第一个夹持点", command=self.three_jaw_1point) self.three_jaw_menu.add_separator() self.three_jaw_menu.add_command(label="设置第二个夹持点", command=self.three_jaw_2point) self.three_jaw_menu.add_separator() self.three_jaw_menu.add_command(label="设置第三个夹持点", command=self.three_jaw_3point) self.three_jaw_menu.add_separator() self.three_jaw_menu.add_command(label="设置三爪夹具说明", command=self.three_jaw_manual) # 提示标签 底部 self.label_var = tk.StringVar() self.label = tk.Label(self, textvariable=self.label_var, anchor="w", bg="black", fg="white") self.label.pack(side=tk.BOTTOM,fill=tk.X) def on_ok(self): # 对话框关闭后重新显示主窗口 # root.withdraw() # 隐藏主窗口 self.parent.deiconify() # 关闭对话框 self.destroy() def on_close(self): # 如果没有其他操作需要执行,可以直接调用destroy方法关闭对话框 # 这里只是为了演示如何绑定关闭事件,所以直接调用on_ok方法 # 在实际应用中,你可能需要在这里添加额外的清理代码或确认对话框 self.destroy() self.parent.deiconify() # 重新显示主窗口 # 将字典转为字符串并输出 def gripper_dict_label(self): # 将字典转换为 JSON 字符串 #json_string = json.dumps(self.json_dict, indent=4) # indent 参数用于美化输出,使其更易读 json_string = json.dumps(self.json_dict, indent=None, separators=(', ', ':')) self.label_var.set(json_string[1:-1]) # 根据圆心点,半径绘制圆 def draw_circle(self,center_x = 0,center_y = 0, radius = 0): # 绘制圆形(椭圆) self.canvas.create_oval( center_x - radius, # 左上角x坐标 center_y - radius, # 左上角y坐标 center_x + radius, # 右下角x坐标 center_y + radius, # 右下角y坐标 tags="circle", outline="blue", # 边框颜色 width=2 # 边框宽度 ) print("绘制夹持中心圆") # 根据点,绘制直线 def draw_line(self): if self.start_x is not None and self.start_y is not None: if self.angle is not None and self.distance is not None: # 将30度转换为弧度 angle_in_degrees = self.angle angle_in_radians = math.radians(angle_in_degrees) # 计算余弦值 cosine_value = math.cos(angle_in_radians) sine_value = math.sin(angle_in_radians) end_x = self.distance * cosine_value +self.start_x end_y = self.distance * sine_value +self.start_y self.canvas.create_line(self.start_x, self.start_y, end_x, end_y, tags="line", width=2, capstyle=tk.ROUND, smooth=tk.TRUE,fill="blue") self.angle = None # 与x轴夹角 self.distance = None print("绘制夹持点和中心连线") # 鼠标移动时,显示像素信息 def show_pixel_info(self, event): if self.tk_image: x, y = event.x, event.y try: pixel = self.image.getpixel((x, y)) self.label_var.set(f"Pixel at ({x}, {y}):RGB {pixel}") except IndexError: self.label_var.set(f"Pixel out of bounds at ({x}, {y})") # 打开图像文件 #二级菜单 file def open_image(self): self.image_path = filedialog.askopenfilename(filetypes=[("PNG files", "*.png")]) if self.image_path: self.image = Image.open(self.image_path) self.tk_image = ImageTk.PhotoImage(self.image) self.canvas.config(width=self.image.width, height=self.image.height) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_image) #二级菜单 吸盘夹具 def suction_cup_center(self): # 对话窗口 输入绝对点坐标 dialog = Point2DInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None or input_numbers[2] is None: print("Input numbers is None") return # 设置为直线段的起点 self.start_x,self.start_y, radius = input_numbers # 保存 机械爪设置值 self.json_dict.clear() self.json_dict["gripper_type"] = "suction_cup" self.json_dict["gripper_center"] = input_numbers self.gripper_dict_label() # 打印机器爪设置值 # 绘制 夹持中心圆 self.draw_circle( center_x=self.start_x, center_y=self.start_y, radius=radius) #二级菜单 吸盘夹具 def suction_cup_direction(self): # 对话窗口 输入相对点坐标 dialog = AngleDistanceInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict["gripper_firstPoint"] = input_numbers # 设置为直线段的相对起点的终点 self.angle, self.distance = input_numbers# 与x轴夹角 self.gripper_dict_label() # 打印机器爪设置值 # 根据点,绘制直线 self.draw_line() # 二级菜单 吸盘夹具 def suction_cup_manual(self): txt=("1)打开零件图片,图片上有零件几何中心和外接矩形的尺寸,单位是像素。", "2)设置夹持中心圆,圆心一般是零件的几何中心,单位是像素,给定半径绘制圆。", "3)设置夹持方向,通过设置一个相对夹持中心点的点的距离和角度表示方向,角度以X轴为准。", "4)观察零件图片,检查是否设置成功。") # 对话窗口 输入绝对点坐标 dialog = ManualDialog(self, title="吸盘夹具", txt=txt) self.wait_window(dialog.top) # 等待对话框关闭 # 二级菜单 两爪夹具 def two_jaw_center(self): # 对话窗口 输入绝对点坐标 dialog = Point2DInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None or input_numbers[2] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict.clear() self.json_dict["gripper_type"] = "two_jaw" self.json_dict["gripper_center"] = input_numbers # 设置为直线段的起点 self.start_x, self.start_y, radius = input_numbers self.gripper_dict_label() # 打印机器爪设置值 # 绘制 夹持中心圆 self.draw_circle(center_x=self.start_x, center_y=self.start_y, radius=radius) #二级菜单 两爪夹具 def two_jaw_1point(self): # 对话窗口 输入相对点坐标 dialog = AngleDistanceInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict["gripper_firstPoint"] = input_numbers # 设置为直线段的相对起点的终点 self.angle, self.distance = input_numbers# 与x轴夹角 self.gripper_dict_label() # 打印机器爪设置值 # 根据点,绘制直线 self.draw_line() #二级菜单 两爪夹具 def two_jaw_2point(self): # 对话窗口 输入相对点坐标 dialog = AngleDistanceInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict["gripper_secondPoint"] = input_numbers # 设置为直线段的相对起点的终点 self.angle, self.distance = input_numbers# 与x轴夹角 self.gripper_dict_label() # 打印机器爪设置值 # 根据点,绘制直线 self.draw_line() # 二级菜单 两爪夹具 def two_jaw_manual(self): txt=("1)打开零件图片,图片上有零件几何中心和外接矩形的尺寸,单位是像素。", "2)设置夹持中心圆,圆心一般是零件的几何中心,单位是像素,给定半径绘制圆。", "3)设置第1夹持点,即设置第1夹持点相对夹持中心点的距离和角度。角度以X轴为准。", "4)设置第2夹持点,即设置第2夹持点相对夹持中心点的距离和角度。角度以X轴为准。", "5)观察零件图片,检查是否设置成功。") # 对话窗口 输入绝对点坐标 dialog = ManualDialog(self, title="两爪夹具", txt=txt) self.wait_window(dialog.top) # 等待对话框关闭 # 二级菜单 三爪夹具 def three_jaw_center(self): # 对话窗口 输入绝对点坐标 dialog = Point2DInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None or input_numbers[2] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict.clear() self.json_dict["gripper_type"] = "three_jaw" self.json_dict["gripper_center"] = input_numbers # 设置为直线段的起点 self.start_x, self.start_y, radius = input_numbers self.gripper_dict_label() # 打印机器爪设置值 # 绘制 夹持中心圆 self.draw_circle(center_x=self.start_x, center_y=self.start_y, radius=radius) #二级菜单 三爪夹具 def three_jaw_1point(self): # 对话窗口 输入相对点坐标 dialog = AngleDistanceInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict["gripper_firstPoint"] = input_numbers # 设置为直线段的相对起点的终点 self.angle, self.distance = input_numbers# 与x轴夹角 self.gripper_dict_label() # 打印机器爪设置值 # 根据点,绘制直线 self.draw_line() #二级菜单 三爪夹具 def three_jaw_2point(self): # 对话窗口 输入相对点坐标 dialog = AngleDistanceInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict["gripper_secondPoint"] = input_numbers # 设置为直线段的相对起点的终点 self.angle, self.distance = input_numbers# 与x轴夹角 self.gripper_dict_label() # 打印机器爪设置值 # 根据点,绘制直线 self.draw_line() #二级菜单 三爪夹具 def three_jaw_3point(self): # 对话窗口 输入相对点坐标 dialog = AngleDistanceInputDialog(self) self.wait_window(dialog.top) # 等待对话框关闭 input_numbers = dialog.get_numbers() if input_numbers[0] is None or input_numbers[1] is None: print("Input numbers is None") return # 保存 机械爪设置值 self.json_dict["gripper_thirdPoint"] = input_numbers # 设置为直线段的相对起点的终点 self.angle, self.distance = input_numbers# 与x轴夹角 self.gripper_dict_label() # 打印机器爪设置值 # 根据点,绘制直线 self.draw_line() # 二级菜单 三爪夹具 def three_jaw_manual(self): txt=("1)打开零件图片,图片上有零件几何中心和外接矩形的尺寸,单位是像素。", "2)设置夹持中心圆,圆心一般是零件的几何中心,单位是像素,给定半径绘制圆。", "3)设置第1夹持点,即设置第1夹持点相对夹持中心点的距离和角度。角度以X轴为准。", "4)设置第2夹持点,即设置第2夹持点相对夹持中心点的距离和角度。角度以X轴为准。", "5)设置第3夹持点,即设置第3夹持点相对夹持中心点的距离和角度。角度以X轴为准。", "6)观察零件图片,检查是否设置成功。") # 对话窗口 输入绝对点坐标 dialog = ManualDialog(self, title="三爪夹具", txt=txt) self.wait_window(dialog.top) # 等待对话框关闭 # 因为使用了菜单,容器只能是 Toplevel 容器 或 主窗口 # 因为使用了菜单,容器只能是 Toplevel 容器 或 主窗口 class MainWindow(tk.Tk): def __init__(self): super().__init__() self.child_window = None self.title("主窗口") self.geometry("300x200") self.button = tk.Button(self, text="打开子窗口", command=self.open_child_window) self.button.pack(pady=20) self.withdraw() # 隐藏主窗口,直到点击按钮 self.after(100, self.deiconify) # 延时100毫秒后显示主窗口 def open_child_window(self): self.withdraw() # 隐藏主窗口 self.child_window = GripperDialog(self) if __name__ == "__main__": app = MainWindow() app.mainloop()
合集:
一个有点复杂的项目-MVC
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)