如何用Tkinter和Matplotlib制作类Excel可滚动伸缩时间轴?
为Tkinter小程序实现带可伸缩滚动条的时间轴
我想用Tkinter做一个带可滚动功能的时间轴,目前已经能用Matplotlib动态生成并更新图表,但想要实现类似Excel时间轴的效果——能传入起止日期数据进行滚动。我试过ttkwidgets里的TimeLine组件,它自带滚动条和放大按钮,但没法同时实现伸缩功能,我的代码如下:
import tkinter as tk from ttkwidgets import TimeLine window = tk.Tk() timeline = TimeLine( window, categories={str(key): {"text": "Category {}".format(key)} for key in range(0, 5)}, height=100, extend=True ) menu = tk.Menu(window, tearoff=False) menu.add_command(label="Some Action", command=lambda: print("Command Executed")) timeline.draw_timeline() timeline.grid() window.after(5000, lambda: print(timeline.time)) window.mainloop()
请问能不能给这个组件添加可伸缩滚动条?
可以通过扩展ttkwidgets.TimeLine的功能,或结合Tkinter原生组件实现同时支持滚动与伸缩的效果,以下是两种可行方案:
方案1:扩展ttkwidgets.TimeLine,绑定滚动与伸缩逻辑
ttkwidgets.TimeLine的extend=True参数已开启时间范围扩展能力,我们只需添加滚动条并绑定起止日期控制逻辑,就能实现需求:
import tkinter as tk from ttkwidgets import TimeLine from tkinter import ttk import datetime def update_timeline_range(start_date, end_date): # 将日期转换为时间戳适配TimeLine组件 start_ts = start_date.timestamp() end_ts = end_date.timestamp() # 设置时间轴起止范围并刷新 timeline.set_time_range(start_ts, end_ts) timeline.draw_timeline() window = tk.Tk() window.title("可伸缩滚动时间轴") # 初始化TimeLine组件 timeline = TimeLine( window, categories={str(key): {"text": "Category {}".format(key)} for key in range(0, 5)}, height=200, extend=True ) # 添加水平滚动条并绑定 scrollbar = ttk.Scrollbar(window, orient="horizontal", command=timeline.xview) timeline.configure(xscrollcommand=scrollbar.set) # 添加起止日期控制栏 control_frame = ttk.Frame(window) control_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) ttk.Label(control_frame, text="起始日期:").grid(row=0, column=0, padx=2) start_entry = ttk.Entry(control_frame) start_entry.insert(0, "2024-01-01") start_entry.grid(row=0, column=1, padx=2) ttk.Label(control_frame, text="结束日期:").grid(row=0, column=2, padx=2) end_entry = ttk.Entry(control_frame) end_entry.insert(0, "2024-01-10") end_entry.grid(row=0, column=3, padx=2) def apply_date_range(): try: start = datetime.datetime.strptime(start_entry.get(), "%Y-%m-%d") end = datetime.datetime.strptime(end_entry.get(), "%Y-%m-%d") update_timeline_range(start, end) except ValueError: print("日期格式错误,请输入YYYY-MM-DD") ttk.Button(control_frame, text="应用", command=apply_date_range).grid(row=0, column=4, padx=5) # 布局配置 timeline.grid(row=0, column=0, sticky="nsew") scrollbar.grid(row=2, column=0, sticky="ew") window.grid_rowconfigure(0, weight=1) window.grid_columnconfigure(0, weight=1) window.mainloop()
方案说明
- 保留TimeLine原生的滚轮缩放、放大按钮功能,新增水平滚动条支持拖动滚动
- 通过日期输入框可手动设置起止时间,实现精确的时间范围伸缩
- 利用
set_time_range方法直接控制时间轴显示范围,配合draw_timeline刷新界面
方案2:基于Canvas自定义时间轴(灵活性更高)
如果ttkwidgets组件限制较多,可直接用Tkinter Canvas实现完全自定义的时间轴,适配Excel样式:
import tkinter as tk from tkinter import ttk import datetime class ScrollableTimeline(tk.Canvas): def __init__(self, parent, categories, **kwargs): super().__init__(parent, **kwargs) self.categories = categories self.cat_height = 40 self.start_date = datetime.datetime(2024, 1, 1) self.end_date = datetime.datetime(2024, 1, 10) self.pixels_per_day = 50 # 每天对应像素宽度 # 绑定滚动条 self.scrollbar = ttk.Scrollbar(parent, orient="horizontal", command=self.xview) self.configure(xscrollcommand=self.scrollbar.set) # 绑定滚轮缩放事件 self.bind("<MouseWheel>", self.on_zoom) self.bind("<Button-4>", self.on_zoom) self.bind("<Button-5>", self.on_zoom) self.draw_timeline() def draw_timeline(self): self.delete("all") # 绘制分类标签 for idx, cat in enumerate(self.categories.values()): y_pos = idx * self.cat_height + 20 self.create_text(10, y_pos, text=cat["text"], anchor="w") # 绘制时间刻度与轴线 total_days = (self.end_date - self.start_date).days total_width = total_days * self.pixels_per_day self.configure(scrollregion=(0, 0, total_width, len(self.categories)*self.cat_height)) for day in range(total_days + 1): current_date = self.start_date + datetime.timedelta(days=day) x_pos = day * self.pixels_per_day self.create_line(x_pos, 0, x_pos, len(self.categories)*self.cat_height, fill="#ccc") self.create_text(x_pos, 5, text=current_date.strftime("%m-%d"), anchor="n") def on_zoom(self, event): # 滚轮向上放大,向下缩小,限制最小缩放比例 delta = event.delta if hasattr(event, "delta") else -1 if event.num == 5 else 1 if delta > 0: self.pixels_per_day = max(10, self.pixels_per_day - 5) else: self.pixels_per_day += 5 self.draw_timeline() def set_date_range(self, start, end): self.start_date = start self.end_date = end self.draw_timeline() window = tk.Tk() window.title("自定义可伸缩滚动时间轴") # 初始化自定义时间轴 categories = {str(key): {"text": "Category {}".format(key)} for key in range(0, 5)} timeline = ScrollableTimeline(window, categories, height=200, bg="white") # 添加日期控制栏 control_frame = ttk.Frame(window) control_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5) ttk.Label(control_frame, text="起始日期:").grid(row=0, column=0, padx=2) start_entry = ttk.Entry(control_frame) start_entry.insert(0, "2024-01-01") start_entry.grid(row=0, column=1, padx=2) ttk.Label(control_frame, text="结束日期:").grid(row=0, column=2, padx=2) end_entry = ttk.Entry(control_frame) end_entry.insert(0, "2024-01-10") end_entry.grid(row=0, column=3, padx=2) def apply_dates(): try: start = datetime.datetime.strptime(start_entry.get(), "%Y-%m-%d") end = datetime.datetime.strptime(end_entry.get(), "%Y-%m-%d") timeline.set_date_range(start, end) except ValueError: print("日期格式错误,请输入YYYY-MM-DD") ttk.Button(control_frame, text="应用", command=apply_dates).grid(row=0, column=4, padx=5) # 布局配置 timeline.grid(row=0, column=0, sticky="nsew") timeline.scrollbar.grid(row=2, column=0, sticky="ew") window.grid_rowconfigure(0, weight=1) window.grid_columnconfigure(0, weight=1) window.mainloop()
方案说明
- 完全基于Canvas实现,可自由定制时间轴样式、刻度密度、交互逻辑
- 支持鼠标滚轮缩放、滚动条拖动,以及手动设置起止日期
- 可轻松扩展添加任务块、标记点等功能,更贴近Excel时间轴的使用体验
内容的提问来源于stack exchange,提问作者Simon Joubert
相关产品推荐
相关产品推荐

