Хорошо, вот что я получил.Он не идеален, но он должен быть в состоянии показать, как можно построить динамическую временную шкалу на основе отправленных дат.
В этом примере будут создаваться метки на холсте, разнесенном на основе количества дней между ними.
Вы сможете нажимать на ярлыки, чтобы получить заметки, которые были сохранены за день, на который вы нажали.
Я предоставил полосу прокрутки для случаев, когда у вас много дат и вы их не видитевсе на экране.
Вы также заметите, что не можете отправить один и тот же день дважды.Хотя вы можете добавить функцию, которая позволит вам обновлять заметки на эту дату.
Для этого вам потребуется 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()
Новые результаты:
