我正在尝试在tkinter中创建一个时间线以绘制日期时间对象。日期可能相隔数年,但不会太多(最多20个)。我希望该行可以缩放,因此第一个日期在该行的开头,最后一个日期在该行的末尾,并在中间标记日期。
我不需要它做任何花哨的事情,但我需要显示时间间隔之间的距离,而不仅仅是显示标签的有序网格。
虽然matplotlib为此提供了一个很好的解决方案,但我无法使用它,因为它会爆炸文件大小(我将其打包),并且在项目中没有其他用途。我还查看了ttkwidgets timeline,但这是针对时间而不是日期的,并且似乎与我给定的时间不一致。
因此,我认为我需要使用Canvas小部件,并绘制一条可以动态缩放的线。然后,我需要绘制标记,并考虑到缩放任何提示都会有用。
答案 0 :(得分:1)
好的,这就是我到目前为止所得到的。它不是很完美,但应该能够说明如何根据提交的日期建立动态时间表。
此示例将根据间隔多少天在画布上创建标签。
您将能够单击标签以获取与单击当天保存的笔记。
当您有多个日期而无法在屏幕上全部看到它们时,我提供了一个滚动条。
您还将注意到,您不能两次提交同一天。尽管您可能希望添加一个功能,以使您可以更新该日期的注释。
为此,您将需要pip
安装tkcalendar
,除非您希望构建自己的日期选择器。但这是很多工作,没有充分的理由。
import tkinter as tk
from tkcalendar import Calendar
class Timeline(tk.Tk):
def __init__(self):
super().__init__()
self.rowconfigure(3, weight=1)
self.columnconfigure(0, weight=1)
self.timeline_list = []
self.timeline_canvas = tk.Canvas(self)
self.note_textbox = tk.Text(self, height=3)
self.text_label = tk.Label(self, text='Notes on date: ')
self.date_button = tk.Button(self, text='Submit new date', command=self.date_selector)
self.img = tk.PhotoImage(file='1x1.png')
self.date_button.grid(row=0, column=0)
self.text_label.grid(row=1, column=0)
self.note_textbox.grid(row=2, column=0, padx=5, pady=5, sticky="nsew")
self.timeline_canvas.grid(row=3, column=0, sticky='ew')
bar = tk.Scrollbar(self, orient='horizontal')
bar.config(command=self.timeline_canvas.xview)
bar.grid(row=4, column=0, sticky='ew')
def on_click(self, event, data="No data!"):
"""You could build a popup menu here that
is activated on mouse-over or on-click
I just used print to test the field"""
print(data)
def append_canvas(self):
list_len = len(self.timeline_list)
if list_len > 1:
first_date = self.timeline_list[0][0]
last_date = self.timeline_list[-1][0]
line_length = last_date - first_date
self.timeline_list.sort()
right_side = 50
self.timeline_canvas.delete('all')
list_of_dates = []
for i in range(list_len):
if i == 0:
list_of_dates.append([self.timeline_list[i], 0])
elif i == list_len-1:
list_of_dates.append([self.timeline_list[i], line_length.days])
else:
list_of_dates.append(
[self.timeline_list[i], (self.timeline_list[i][0] - self.timeline_list[0][0]).days])
for ndex, date_item in enumerate(list_of_dates):
lbl = tk.Label(self.timeline_canvas, text=date_item[0][0], background='gray')
self.timeline_canvas.create_window((right_side, 25), window=lbl)
if ndex < len(list_of_dates)-1:
right_side += (65 + list_of_dates[ndex+1][1])
lbl.bind("<Button-1>", lambda event, d=date_item[0][1].strip(): self.on_click(event, d))
def date_selector(self):
def work_selection():
selected_date = cal.selection_get()
selected_notes = self.note_textbox.get(1.0, 'end')
match_found = False
for each_list in self.timeline_list:
if selected_date == each_list[0]:
match_found = True
break
if match_found is False:
self.timeline_list.append([selected_date, selected_notes])
self.append_canvas()
top.destroy()
top = tk.Toplevel(self)
cal = Calendar(top, selectmode='day')
cal.pack(fill="both", expand=True)
tk.Button(top, text="ok", width=10, command=work_selection).pack()
if __name__ == "__main__":
Timeline().mainloop()
结果:
如果您有任何疑问,请告诉我。 如果有时间,我会在以后做更多的工作。
更新:
我修改了代码,使其包含在500像素长的行上。 我做了一些测试,以确保一切都正常。
如果发现有问题,我可能会稍后对其进行修改,但现在我认为这应该可以满足您列出的所有需求。
我确实注意到一个可能的问题。如果第一个日期和最后一个日期之间有较大的差距,那么一组日期中的日期实际上会彼此接近。我将尝试找到更好的解决方案,但这是我今天得到的。
更新:
我还添加了与鼠标滚轮配合使用的缩放选项。现在,放大时您将不得不使用滚动条在画布上移动,但是它应该适用于在视觉上接近且重叠的日期。
import tkinter as tk
from tkcalendar import Calendar
class Timeline(tk.Tk):
def __init__(self):
super().__init__()
self.rowconfigure(3, weight=1)
self.columnconfigure(0, weight=1)
self.timeline_list = []
self.line_size = 500
self.timeline_canvas = tk.Canvas(self)
self.note_textbox = tk.Text(self, height=3)
self.text_label = tk.Label(self, text='Notes on date: ')
self.date_button = tk.Button(self, text='Submit new date', command=self.date_selector)
self.img = tk.PhotoImage(file='1x1.png')
self.date_button.grid(row=0, column=0)
self.text_label.grid(row=1, column=0)
self.note_textbox.grid(row=2, column=0, padx=5, pady=5, sticky="nsew")
self.timeline_canvas.grid(row=3, column=0, sticky='ew')
bar = tk.Scrollbar(self, orient='horizontal')
bar.config(command=self.timeline_canvas.xview)
bar.grid(row=4, column=0, sticky='ew')
self.timeline_canvas.bind_all("<MouseWheel>", self.zoom_in_out)
def zoom_in_out(self, event):
if event.delta < 0:
self.line_size -= 100
else:
self.line_size += 100
self.append_canvas()
def on_click(self, event=None, date=None, data=None):
"""You could build a popup menu here that
is activated on mouse-over or on-click
I just used print to test the field"""
print(date, data)
def append_canvas(self):
list_len = len(self.timeline_list)
if list_len > 1:
self.timeline_list.sort()
first_date = self.timeline_list[0][0]
last_date = self.timeline_list[-1][0]
line_length = last_date - first_date
self.timeline_canvas.delete('all')
list_of_dates = []
for i in range(list_len):
if i == 0:
list_of_dates.append([self.timeline_list[i], 0])
elif i == list_len-1:
list_of_dates.append([self.timeline_list[i], line_length.days])
else:
list_of_dates.append(
[self.timeline_list[i], (self.timeline_list[i][0] - self.timeline_list[0][0]).days])
self.timeline_canvas.create_line(50, 50, 550, 50, fill="red", dash=(4, 4))
for ndex, date_item in enumerate(list_of_dates):
if ndex == 0:
lbl = tk.Label(self.timeline_canvas, text=ndex + 1, background='gray')
self.timeline_canvas.create_window((50, 50), window=lbl)
elif ndex == len(list_of_dates) - 1:
lbl = tk.Label(self.timeline_canvas, text=ndex + 1, background='gray')
self.timeline_canvas.create_window((self.line_size + 50, 50), window=lbl)
else:
x = (list_of_dates[ndex][1] / list_of_dates[-1][1]) * self.line_size
lbl = tk.Label(self.timeline_canvas, text=ndex + 1, background='gray')
self.timeline_canvas.create_window((x + 50, 50), window=lbl)
lbl.bind("<Button-1>", lambda event, d=date_item[0][0], t=date_item[0][1].strip(): self.on_click(event, d, t))
def date_selector(self):
def work_selection():
selected_date = cal.selection_get()
selected_notes = self.note_textbox.get(1.0, 'end')
match_found = False
for each_list in self.timeline_list:
if selected_date == each_list[0]:
match_found = True
break
if match_found is False:
self.timeline_list.append([selected_date, selected_notes])
self.append_canvas()
top.destroy()
top = tk.Toplevel(self)
cal = Calendar(top, selectmode='day')
cal.pack(fill="both", expand=True)
tk.Button(top, text="ok", width=10, command=work_selection).pack()
if __name__ == "__main__":
Timeline().mainloop()
新结果: