Tkinter GUI实现点击按钮打印耗时并重置的自动启动计时器
Tkinter GUI计时器功能实现方案
原有代码已知问题
- 缺失
yaml模块导入,调用loadData函数会直接报错 - 实例变量命名冲突,同时定义了
self.start_timer和self._start_timer两个相关变量 __init__方法中存在错误的time()实例化逻辑reset方法判断条件写反,会触发空值计算异常- 缺少程序启动自动初始化计时的逻辑
- 主函数实例化
App时传入了数据集参数,但App类的构造函数没有对应接收逻辑
功能实现说明
已按照要求完成两个核心功能:
- 程序启动时计时器自动启动
- 点击「next set」按钮时,先打印上一轮任务的耗时,再重置计时器开始新一轮计时,原有加载下一组图片的逻辑不受影响
完整修改后的代码
import time import tkinter as tk import csv import yaml # 补充缺失的yaml导入 from pathlib import Path import PIL.Image import PIL.ImageDraw import PIL.ImageTk MAX_HEIGHT = 500 IMAGES_PATH = Path("Images") CSV_LABELS_KEY = "New Labels" CSV_FILE_NAME_KEY = "FolderNum_SeriesNum" CSV_BOUNDING_BOX_KEY = "correct_flip_bbox" counter = 0 timer_id = None class App(tk.Frame): # 修正构造函数,兼容主函数传入的数据集参数 def __init__(self, dataset=None, master=None): super().__init__(master) self.config_paths = ["config 1.yaml", "config 2.yaml", "config 3.yaml"] self.config_index = 0 self.clickStatus = tk.StringVar() self.loadedImages = dict() self.loadedBoxes = dict() self.master.title('Slideshow') # 定义存储计时开始时间的实例变量 self._start_time = None frame = tk.Frame(self) # 调整按钮调用顺序:先处理计时逻辑,再加载下一组图片 tk.Button(frame, text=" Next set ", command=lambda:[self.reset(), self.get_next_image_set()]).pack(side=tk.RIGHT) tk.Button(frame, text=" Exit ", command=self.destroy).pack(side=tk.RIGHT) frame.pack(side=tk.TOP, fill=tk.BOTH) self.canvas = tk.Canvas(self) self.canvas.pack() # 如果有传入数据集就用传入的,没有就自己加载 if dataset: self.image_data = dataset self.currentIndex = 0 self._load_image() else: self._load_dataset() # 程序启动自动开始计时 self._start_time = time.perf_counter() def reset(self): # 已经有开始时间的情况下计算耗时 if self._start_time is not None: elapsed_time = time.perf_counter() - self._start_time # 格式化耗时为hh:mm:ss.ms格式 hours = int(elapsed_time // 3600) minutes = int((elapsed_time % 3600) // 60) seconds = elapsed_time % 60 print(f'Time elapsed (hh:mm:ss.ms) {hours:02d}:{minutes:02d}:{seconds:06.3f}') # 重置计时器,开始新一轮计时 self._start_time = time.perf_counter() def _load_dataset(self): try: config_path = self.config_paths[self.config_index] self.config_index += 1 except IndexError: return image_data = loadData(config_path) self.image_data = image_data self.currentIndex = 0 self._load_image() def _load_image(self): imgName = self.image_data[self.currentIndex]['image_file'] if imgName not in self.loadedImages: self.im = PIL.Image.open(self.image_data[self.currentIndex]['image_file']) ratio = MAX_HEIGHT / self.im.height height, width = int(self.im.height * ratio), int(self.im.width * ratio) self.canvas.config(width=width, height=height) self.im = self.im.resize((width, height)) if self.im.mode == "1": self.img = PIL.ImageTk.BitmapImage(self.im, foreground="white") else: self.img = PIL.ImageTk.PhotoImage(self.im) imgData = self.loadedImages.setdefault(self.image_data[self.currentIndex]['image_file'], dict()) imgData['image'] = self.img imgData['shapes'] = self.image_data[self.currentIndex]['shapes'] self.img = self.loadedImages[self.image_data[self.currentIndex]['image_file']]['image'] self.canvas.create_image(0, 0, anchor=tk.NW, image=self.img) self.show_drag_box() # 补充你原来已有的get_next_image_set方法,保持原有逻辑不变即可 def get_next_image_set(self): # 你的原有加载下一组图片的逻辑 pass # 补充你原来已有的show_drag_box方法 def show_drag_box(self): # 你的原有绘制框的逻辑 pass def loadData(fname): with open(fname, mode='r') as f: return yaml.load(f.read(), Loader=yaml.SafeLoader) if __name__ == "__main__": data = loadData('config 1.yaml') app = App(data) app.pack() app.mainloop()
注意事项
- 代码中预留的
get_next_image_set、show_drag_box方法请替换为你原有业务逻辑即可 - 耗时打印格式已经按照要求转为时分秒毫秒的格式,可根据需要调整打印规则
内容的提问来源于stack exchange,提问作者newtocoding
相关产品推荐
相关产品推荐

