当前位置: 首页 > news >正文

Python 示例(Tkinter)

目录

  • Windows界面

Windows界面

在这里插入图片描述

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import csv
import datetime
import osclass TableUpperComputer:def __init__(self, root):self.root = rootself.root.title("数据管理上位机")self.root.geometry("1000x600")self.root.minsize(800, 500)# 数据存储(格式:[(ID, 参数名称, 数值, 单位, 状态, 时间戳), ...])self.data = []self.filename = None  # 当前打开的文件路径# 创建界面组件self.create_widgets()# 加载示例数据(首次启动用)self.load_sample_data()def create_widgets(self):# 主框架(统一管理布局)self.main_frame = ttk.Frame(self.root, padding="10")self.main_frame.pack(fill=tk.BOTH, expand=True)# 1. 菜单栏(文件/编辑/帮助)self.create_menu()# 2. 工具栏(常用操作按钮)self.create_toolbar()# 3. 表格区域(带边框和标题)table_frame = ttk.LabelFrame(self.main_frame, text="设备参数表格", padding="10")table_frame.pack(fill=tk.BOTH, expand=True, pady=5)self.create_table(table_frame)# 4. 状态栏(显示操作提示)self.status_var = tk.StringVar()self.status_var.set("就绪 - 已加载示例数据")status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)status_bar.pack(side=tk.BOTTOM, fill=tk.X)def create_menu(self):"""创建菜单栏及快捷键绑定"""menubar = tk.Menu(self.root)# 文件菜单(新建/打开/保存/导出)file_menu = tk.Menu(menubar, tearoff=0)file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")file_menu.add_command(label="另存为", command=self.save_as_file)file_menu.add_separator()file_menu.add_command(label="导出CSV", command=self.export_csv)file_menu.add_command(label="导入CSV", command=self.import_csv)file_menu.add_separator()file_menu.add_command(label="退出", command=self.root.quit)# 编辑菜单(添加/删除/清空)edit_menu = tk.Menu(menubar, tearoff=0)edit_menu.add_command(label="添加行", command=self.add_row, accelerator="Ctrl+I")edit_menu.add_command(label="删除选中行", command=self.delete_selected_row, accelerator="Ctrl+D")edit_menu.add_separator()edit_menu.add_command(label="清空表格", command=self.clear_table)# 帮助菜单(关于/帮助)help_menu = tk.Menu(menubar, tearoff=0)help_menu.add_command(label="关于", command=self.show_about)help_menu.add_command(label="帮助", command=self.show_help)# 组装菜单栏menubar.add_cascade(label="文件", menu=file_menu)menubar.add_cascade(label="编辑", menu=edit_menu)menubar.add_cascade(label="帮助", menu=help_menu)self.root.config(menu=menubar)# 绑定快捷键(兼容Windows/Linux)self.root.bind("<Control-n>", lambda e: self.new_file())self.root.bind("<Control-o>", lambda e: self.open_file())self.root.bind("<Control-s>", lambda e: self.save_file())self.root.bind("<Control-i>", lambda e: self.add_row())self.root.bind("<Control-d>", lambda e: self.delete_selected_row())def create_toolbar(self):"""创建工具栏(快速操作按钮)"""toolbar = ttk.Frame(self.main_frame)toolbar.pack(fill=tk.X, pady=5)# 按钮布局(文件操作)ttk.Button(toolbar, text="新建", command=self.new_file).pack(side=tk.LEFT, padx=2)ttk.Button(toolbar, text="打开", command=self.open_file).pack(side=tk.LEFT, padx=2)ttk.Button(toolbar, text="保存", command=self.save_file).pack(side=tk.LEFT, padx=2)ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)# 按钮布局(数据操作)ttk.Button(toolbar, text="添加行", command=self.add_row).pack(side=tk.LEFT, padx=2)ttk.Button(toolbar, text="删除行", command=self.delete_selected_row).pack(side=tk.LEFT, padx=2)ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)# 按钮布局(CSV导入导出)ttk.Button(toolbar, text="导入CSV", command=self.import_csv).pack(side=tk.LEFT, padx=2)ttk.Button(toolbar, text="导出CSV", command=self.export_csv).pack(side=tk.LEFT, padx=2)def create_table(self, parent):"""创建表格(Treeview组件),支持双击编辑"""# 定义表格列(与数据格式对应)columns = ("id", "name", "value", "unit", "status", "timestamp")self.tree = ttk.Treeview(parent, columns=columns, show="headings")# 设置列标题和对齐方式self.tree.heading("id", text="ID", anchor=tk.CENTER)self.tree.heading("name", text="参数名称", anchor=tk.W)self.tree.heading("value", text="数值", anchor=tk.E)self.tree.heading("unit", text="单位", anchor=tk.CENTER)self.tree.heading("status", text="状态", anchor=tk.CENTER)self.tree.heading("timestamp", text="时间戳", anchor=tk.CENTER)# 设置列宽(适配内容)self.tree.column("id", width=60, anchor=tk.CENTER)self.tree.column("name", width=150, anchor=tk.W)self.tree.column("value", width=100, anchor=tk.E)self.tree.column("unit", width=80, anchor=tk.CENTER)self.tree.column("status", width=100, anchor=tk.CENTER)self.tree.column("timestamp", width=160, anchor=tk.CENTER)# 添加滚动条(支持横竖滚动)scrollbar_y = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.tree.yview)scrollbar_x = ttk.Scrollbar(parent, orient=tk.HORIZONTAL, command=self.tree.xview)self.tree.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)# 布局:表格占满父容器,滚动条贴边self.tree.grid(row=0, column=0, sticky=tk.NSEW)scrollbar_y.grid(row=0, column=1, sticky=tk.NS)scrollbar_x.grid(row=1, column=0, sticky=tk.EW)# 适配窗口大小变化(表格随窗口拉伸)parent.grid_rowconfigure(0, weight=1)parent.grid_columnconfigure(0, weight=1)# 绑定事件:双击编辑单元格、选中行提示self.tree.bind("<Double-1>", self.on_cell_double_click)self.tree.bind("<<TreeviewSelect>>", self.on_select)def on_cell_double_click(self, event):"""双击单元格触发编辑(ID和时间戳不可编辑)"""# 识别点击区域(仅单元格可编辑)region = self.tree.identify_region(event.x, event.y)if region != "cell":return# 获取当前行、列信息row_id = self.tree.identify_row(event.y)column = self.tree.identify_column(event.x)column_index = int(column.replace("#", "")) - 1  # 转为0开始索引# 保护列:ID(0列)和时间戳(5列)不可编辑if column_index in [0, 5]:self.status_var.set("提示:ID和时间戳不可编辑")return# 获取单元格位置和当前值(用于输入框定位)x, y, width, height = self.tree.bbox(row_id, column)current_value = self.tree.item(row_id, "values")[column_index]# 创建临时输入框(覆盖单元格)self.entry = ttk.Entry(self.tree, font=("Arial", 10))self.entry.place(x=x, y=y, width=width, height=height)self.entry.insert(0, current_value)self.entry.focus_set()  # 自动聚焦输入框# 保存编辑上下文(后续用于更新数据)self.current_edit = {"row_id": row_id,"column_index": column_index,"original_value": current_value}# 绑定输入框事件(失焦/回车/ESC)self.entry.bind("<FocusOut>", self.save_edited_value)self.entry.bind("<Return>", self.save_edited_value)self.entry.bind("<Escape>", lambda e: self.entry.destroy())def save_edited_value(self, event=None):"""保存编辑后的单元格值(同步到表格和数据源)"""if not hasattr(self, "current_edit"):return# 获取新值并验证(非空校验)new_value = self.entry.get().strip()if not new_value:messagebox.showwarning("警告", "值不能为空,请重新输入")return# 从上下文获取行和列信息row_id = self.current_edit["row_id"]column_index = self.current_edit["column_index"]# 1. 更新表格显示values = list(self.tree.item(row_id, "values"))values[column_index] = new_valueself.tree.item(row_id, values=values)# 2. 更新数据源(按ID匹配行)for i, item in enumerate(self.data):if str(item[0]) == str(values[0]):  # ID可能是数字/字符串,统一转字符串匹配self.data[i] = tuple(values)break# 清理编辑状态self.entry.destroy()del self.current_editself.status_var.set(f"已更新:ID={values[0]},{values[1]}={new_value}")def on_select(self, event):"""选中行时显示提示信息"""selected_rows = self.tree.selection()if selected_rows:row_data = self.tree.item(selected_rows[0], "values")self.status_var.set(f"选中:ID={row_data[0]},{row_data[1]}({row_data[2]}{row_data[3]})")else:self.status_var.set("就绪 - 未选中任何行")def load_sample_data(self):"""加载示例数据(首次启动或新建文件时用)"""self.clear_table(skip_confirm=True)  # 跳过确认(内部调用时用)# 示例数据(与CSV格式一致)sample_data = [(1, "温度", "25.6", "°C", "正常", "2025-09-08 17:08:26"),(2, "压力", "101.3", "kPa", "正常", "2025-09-08 17:08:26"),(3, "流量", "1.2", "L/min", "正常", "2025-09-08 17:08:26"),(4, "电压", "220.5", "V", "正常", "2025-09-08 17:08:26"),(5, "电流", "1.8", "A", "注意", "2025-09-08 17:08:26")]# 添加到表格和数据源for item in sample_data:self.tree.insert("", tk.END, values=item)self.data = sample_dataself.status_var.set(f"就绪 - 已加载{len(sample_data)}条示例数据")def add_row(self):"""添加新行(自动生成ID和当前时间戳)"""# 生成新ID(取当前最大ID+1,无数据时为1)new_id = 1if self.data:new_id = max(int(item[0]) for item in self.data) + 1# 新行默认数据(时间戳为当前时间)current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")new_row = (new_id, "新参数", "", "", "未知", current_time)# 添加到表格和数据源self.tree.insert("", tk.END, values=new_row)self.data.append(new_row)# 滚动到新行(方便编辑)self.tree.see(self.tree.get_children()[-1])self.status_var.set(f"已添加新行:ID={new_id}(双击编辑参数信息)")def delete_selected_row(self):"""删除选中的行(需确认,防止误操作)"""selected_rows = self.tree.selection()if not selected_rows:messagebox.showwarning("警告", "请先选中要删除的行")return# 确认删除(避免误操作)row_data = self.tree.item(selected_rows[0], "values")confirm = messagebox.askyesno("确认删除", f"是否删除 ID={row_data[0]} 的「{row_data[1]}」行?")if not confirm:return# 1. 从表格删除self.tree.delete(selected_rows[0])# 2. 从数据源删除(按ID匹配)self.data = [item for item in self.data if str(item[0]) != str(row_data[0])]self.status_var.set(f"已删除行:ID={row_data[0]},{row_data[1]}")def clear_table(self, skip_confirm=False):"""清空表格(默认需确认,内部调用可跳过)"""if not self.data:return# 确认清空(外部调用时触发)if not skip_confirm:confirm = messagebox.askyesno("确认清空", "是否清空所有数据?(操作不可撤销)")if not confirm:return# 1. 清空表格显示for item in self.tree.get_children():self.tree.delete(item)# 2. 清空数据源self.data = []self.filename = Noneself.root.title("数据管理上位机")  # 重置窗口标题(清除文件名)self.status_var.set("就绪 - 表格已清空")def new_file(self):"""新建文件(先保存现有数据,再加载示例数据)"""if self.data:confirm = messagebox.askyesno("提示", "当前有未保存的数据,是否先保存?")if confirm:self.save_file()# 清空并加载示例数据self.clear_table(skip_confirm=True)self.load_sample_data()self.filename = Noneself.root.title("数据管理上位机")def open_file(self):"""打开文件(支持.txt和.csv,强制UTF-8编码)"""# 先处理未保存数据if self.data:confirm = messagebox.askyesno("提示", "当前有未保存的数据,是否先保存?")if confirm:self.save_file()# 选择文件(限制格式)filename = filedialog.askopenfilename(title="打开数据文件",defaultextension=".txt",filetypes=[("数据文件", "*.txt;*.csv"), ("所有文件", "*.*")],initialdir=os.getcwd()  # 默认打开当前目录)if not filename:returntry:# 清空现有数据self.clear_table(skip_confirm=True)# 读取文件(强制UTF-8编码,避免中文乱码)with open(filename, 'r', encoding='utf-8', newline='') as f:reader = csv.reader(f)for row in reader:# 验证行格式(需6列数据,ID为数字)if len(row) != 6:continue  # 跳过格式错误的行# 尝试转换ID为整数(确保数据有效性)try:int(row[0])except ValueError:continue  # 跳过ID非数字的行# 添加到表格和数据源self.tree.insert("", tk.END, values=tuple(row))self.data.append(tuple(row))# 更新文件信息和窗口标题self.filename = filenameself.root.title(f"数据管理上位机 - {os.path.basename(filename)}")self.status_var.set(f"已打开文件:{os.path.basename(filename)}({len(self.data)}条数据)")except Exception as e:messagebox.showerror("打开失败", f"文件读取错误:{str(e)}")self.status_var.set("错误 - 文件打开失败")def save_file(self):"""保存文件(默认保存为.txt,强制UTF-8编码)"""if not self.data:messagebox.showwarning("保存警告", "无数据可保存")return# 若未指定文件名,触发"另存为"if not self.filename:self.save_as_file()returntry:# 写入文件(UTF-8编码,避免中文乱码)with open(self.filename, 'w', encoding='utf-8', newline='') as f:writer = csv.writer(f)writer.writerows(self.data)self.status_var.set(f"已保存:{os.path.basename(self.filename)}({len(self.data)}条数据)")except Exception as e:messagebox.showerror("保存失败", f"文件写入错误:{str(e)}")self.status_var.set("错误 - 文件保存失败")def save_as_file(self):"""另存为文件(支持.txt和.csv,强制UTF-8编码)"""if not self.data:messagebox.showwarning("保存警告", "无数据可保存")return# 选择保存路径和格式filename = filedialog.asksaveasfilename(title="另存为",defaultextension=".txt",filetypes=[("文本文件", "*.txt"), ("CSV文件", "*.csv"), ("所有文件", "*.*")],initialdir=os.getcwd())if not filename:returntry:# 写入文件(UTF-8编码)with open(filename, 'w', encoding='utf-8', newline='') as f:writer = csv.writer(f)writer.writerows(self.data)# 更新文件信息和窗口标题self.filename = filenameself.root.title(f"数据管理上位机 - {os.path.basename(filename)}")self.status_var.set(f"已另存为:{os.path.basename(filename)}({len(self.data)}条数据)")except Exception as e:messagebox.showerror("另存失败", f"文件写入错误:{str(e)}")self.status_var.set("错误 - 文件另存失败")def import_csv(self):"""导入CSV文件(仅处理标准CSV,强制UTF-8编码,支持跳过表头)"""# 选择CSV文件filename = filedialog.askopenfilename(title="导入CSV文件",defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],initialdir=os.getcwd())if not filename:returntry:# 询问是否清空现有数据clear_existing = Falseif self.data:clear_existing = messagebox.askyesno("导入提示", "是否清空现有数据后导入?")# 若需清空,先清理表格和数据源if clear_existing:self.clear_table(skip_confirm=True)# 读取CSV文件(UTF-8编码)imported_count = 0with open(filename, 'r', encoding='utf-8', newline='') as f:reader = csv.reader(f)header_skipped = False  # 标记是否跳过表头for row in reader:# 跳过空行if not row:continue# 自动跳过表头(若首行第一列是"ID"或非数字)if not header_skipped:if row[0].strip().lower() == "id" or not row[0].strip().isdigit():header_skipped = Truecontinueheader_skipped = True  # 首行是数据,无需跳过# 验证行格式(需6列数据,ID为数字)if len(row) != 6:continuetry:int(row[0].strip())except ValueError:continue# 处理重复ID(自动重新编号)existing_ids = [int(item[0]) for item in self.data]new_id = int(row[0].strip())while new_id in existing_ids:new_id += 1row[0] = str(new_id)# 添加到表格和数据源self.tree.insert("", tk.END, values=tuple(row))self.data.append(tuple(row))imported_count += 1self.status_var.set(f"导入成功:从{os.path.basename(filename)}导入{imported_count}条数据")except Exception as e:messagebox.showerror("导入失败", f"CSV读取错误:{str(e)}")self.status_var.set("错误 - CSV导入失败")def export_csv(self):"""导出CSV文件(带标准表头,使用utf-8-sig编码解决Excel乱码)"""if not self.data:messagebox.showwarning("导出警告", "无数据可导出")return# 选择导出路径filename = filedialog.asksaveasfilename(title="导出CSV文件",defaultextension=".csv",filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],initialdir=os.getcwd())if not filename:returntry:# 关键修复:使用utf-8-sig编码(带BOM),确保Excel正确识别中文with open(filename, 'w', encoding='utf-8-sig', newline='') as f:writer = csv.writer(f)# 写入标准表头(与表格列对应)writer.writerow(["ID", "参数名称", "数值", "单位", "状态", "时间戳"])# 写入数据writer.writerows(self.data)self.status_var.set(f"导出成功:{os.path.basename(filename)}({len(self.data)}条数据+1个表头)")except Exception as e:messagebox.showerror("导出失败", f"CSV写入错误:{str(e)}")self.status_var.set("错误 - CSV导出失败")def show_about(self):"""显示关于对话框"""messagebox.showinfo("关于数据管理上位机","版本:v1.2(修复Excel中文乱码)\n""功能:表格显示、双击编辑、数据保存/导入/导出\n""编码:导出CSV使用utf-8-sig(兼容Excel中文显示)\n""适配:标准CSV格式(逗号分隔,表头可选)")def show_help(self):"""显示帮助对话框"""messagebox.showinfo("操作帮助","一、基础操作\n""1. 双击表格单元格:编辑数据(ID和时间戳不可编辑)\n""2. 选中行:状态栏显示行信息\n""3. 添加行:自动生成ID和当前时间戳\n""\n二、文件操作\n""1. 打开/保存:支持.txt和.csv格式(UTF-8编码)\n""2. 导入CSV:自动跳过表头,处理重复ID\n""3. 导出CSV:使用utf-8-sig编码,Excel可直接打开无乱码\n""\n三、快捷键\n""Ctrl+N:新建文件  Ctrl+O:打开文件  Ctrl+S:保存文件\n""Ctrl+I:添加行    Ctrl+D:删除选中行")if __name__ == "__main__":# 启动应用root = tk.Tk()app = TableUpperComputer(root)root.mainloop()

文章转载自:

http://mzhY7fNT.mzcrs.cn
http://K5kiI7Gr.mzcrs.cn
http://Q5CT6MwZ.mzcrs.cn
http://Vc7Y4cXp.mzcrs.cn
http://PnTFNc78.mzcrs.cn
http://I6Trw45x.mzcrs.cn
http://LuXz01U3.mzcrs.cn
http://mEtDOdbV.mzcrs.cn
http://wHmOGXOQ.mzcrs.cn
http://rVd8LVsr.mzcrs.cn
http://E97Q7bIO.mzcrs.cn
http://GhIgX6Da.mzcrs.cn
http://YisqOIBQ.mzcrs.cn
http://WkNxVfPH.mzcrs.cn
http://gSZs59S8.mzcrs.cn
http://dPrpqIw2.mzcrs.cn
http://QKNlfstX.mzcrs.cn
http://AWjQXSQX.mzcrs.cn
http://IL2pH49u.mzcrs.cn
http://SJr5Ycu7.mzcrs.cn
http://iuUBOmeG.mzcrs.cn
http://0NBF2vIp.mzcrs.cn
http://Lp1G8o7q.mzcrs.cn
http://S4IFXDzx.mzcrs.cn
http://Yp0P8ZNL.mzcrs.cn
http://2s831VsI.mzcrs.cn
http://TMLC1xE2.mzcrs.cn
http://r2aJNZeC.mzcrs.cn
http://g78AX50E.mzcrs.cn
http://dclLOpO4.mzcrs.cn
http://www.dtcms.com/a/374205.html

相关文章:

  • 学习如何基于ACP-SDK构建多智能体系统
  • Dify 从入门到精通(第 83/100 篇):Dify 的多模态模型性能调优(高级篇)
  • 【docker】镜像制作
  • 前端安全攻防:XSS, CSRF 等防范与检测
  • Unity鱼眼特效
  • MySQL表结构优化:安全删除字段(DROP COLUMN)的完整指南与避坑手册
  • Java全栈技术选型指南
  • Leptos框架深度解析:用Rust构建高性能Web应用的未来
  • 嵌入式学习day45-硬件—汇编
  • Gazebo1: gz命令工具理解与掌握
  • 电路运行的核心-RTC
  • 高并发下的锁选择:乐观锁 vs 悲观锁全面对比
  • 本地部署大模型和知识库实现问答AI
  • python编程:一文掌握pypiserver的详细使用
  • 【人工智能99问】开源项目RAGflow_by_infiniflow介绍(37/99)
  • Qt C++ 复杂界面处理:巧用覆盖层突破复杂界面处理难题​之一
  • 一种高效绘制余晖波形的方法Qt/C++
  • 本地部署的Qwen3,测试不同数量并发请求的吞吐量
  • 【从零开始java学习|第十三篇】字符串究极知识总结
  • Linux内核进程管理子系统有什么第四十六回 —— 进程主结构详解(42)
  • Kafka 与 RocketMQ 核心概念与架构对比
  • 【检索通知】2025年IEEE第二届深度学习与计算机视觉国际会议检索
  • 2025年AC-DC电源模块选购指南与应用方案解析
  • LeetCode 面试经典 150 题:删除有序数组中的重复项 II(最多保留 2 次 + 通用 k 次解法详解)
  • 在OpenHarmony上适配图形显示【2】——调试display hdi的技巧
  • 在 JavaScript 中轻松实现 AES 加密与解密:从原理到实战
  • Mockoon:开源免费的本地Mock服务工具,提升前后端联调效率
  • C/C++圣诞树②
  • segYolo添加界面
  • 初学Transformer核心——注意力机制