Skip to content

Instantly share code, notes, and snippets.

@thegamecracks
Created February 6, 2025 18:28
Show Gist options
  • Select an option

  • Save thegamecracks/ee37fe60a559a609f6ee666bb1ca4e1c to your computer and use it in GitHub Desktop.

Select an option

Save thegamecracks/ee37fe60a559a609f6ee666bb1ca4e1c to your computer and use it in GitHub Desktop.
import datetime
import textwrap
from tkinter import BooleanVar, Tk
from tkinter.ttk import Checkbutton, Frame, Label, Style
LOREM_IPSUM = "Lorem ipsum odor amet, consectetuer adipiscing elit. Vivamus tincidunt ultricies accumsan feugiat ultrices sagittis tellus? Turpis penatibus convallis; suscipit nisl tincidunt suscipit litora? Ex fusce facilisis ullamcorper aenean iaculis metus pharetra libero. Feugiat fermentum fermentum mauris urna curabitur diam ad. Volutpat parturient arcu nec semper etiam vitae augue convallis dui. Gravida neque lacus donec interdum finibus ullamcorper. Sodales aliquam tempor tempor purus curabitur mauris ridiculus aptent."
class TaskCard(Frame):
MAX_DESCRIPTION_CHARS = 256
MIN_DESCRIPTION_WIDTH = 300
def __init__(
self,
parent,
*,
title: str,
description: str,
date: datetime.date,
done: bool,
) -> None:
super().__init__(parent, borderwidth=1, relief="solid", padding=10)
# Allow our description column to expand horizontally
self.grid_columnconfigure(1, weight=1)
# Use the entire top row for the task title
self.title = Label(self, font="Helvetica 14 bold", text=title)
self.title.grid(row=0, column=0, columnspan=2, sticky="w")
# Use the bottom-left corner for extra info
self.details = TaskDetails(self, date=date, done=done)
self.details.grid(row=1, column=0, padx=(0, 30), sticky="nesw")
# Use the bottom-right corner for description
chars = self.MAX_DESCRIPTION_CHARS
width = self.MIN_DESCRIPTION_WIDTH
description = textwrap.shorten(description, chars, placeholder="...")
self.description = Label(self, text=description, wraplength=width)
self.description.grid(row=1, column=1, sticky="nesw")
# Once this card becomes visible, start wrapping the description text
self.bind("<Map>", lambda event: self._wrap_description())
def _wrap_description(self) -> None:
if not self.winfo_viewable():
return
width = self._get_description_width()
self.description.configure(wraplength=width)
# Normally we'd use <Configure> to check if we've been resized,
# but that event doesn't fire when the window is maximized.
# As a hack, we'll wrap the description every 50ms.
self.after(50, self._wrap_description)
def _get_description_width(self) -> int:
bbox = self.grid_bbox(row=1, column=1) or (0, 0, 0, 0)
return max(self.MIN_DESCRIPTION_WIDTH, bbox[2])
class TaskDetails(Frame):
def __init__(
self,
parent: TaskCard,
*,
date: datetime.date,
done: bool,
) -> None:
super().__init__(parent)
self.parent = parent
self.date = Label(self, text=date.isoformat())
self.date.grid(row=0, column=0, sticky="nw")
self.done_var = BooleanVar(self, done)
self.done = Checkbutton(self, variable=self.done_var, text="Completed")
self.done.grid(row=1, column=0, sticky="nw")
self.done_var.trace_add("write", self._on_done_changed)
def _on_done_changed(self, name1: str, name2: str, op: str) -> None:
# https://www.tcl-lang.org/man/tcl8.6/TclCmd/trace.htm#M14
title = self.parent.title["text"]
done = "done" if self.done_var.get() else "not done"
print(f"Task {title!r} marked as {done}")
def main() -> None:
app = Tk()
app.geometry("680x920") # description wrapping can go nuts if we don't set a size
style = Style(app)
style.configure(".", background="white")
# Put everything inside a themed frame for our background
content = Frame(app, padding=30)
content.pack(expand=True, fill="both")
# Expand cards horizontally, and also expand the top card vertically
content.grid_columnconfigure(0, weight=1)
content.grid_rowconfigure(0, minsize=160, weight=1)
for i in range(5):
card = TaskCard(
content,
title=f"Title of task {i+1}",
date=datetime.date(2025, 2, 5 + i),
description=LOREM_IPSUM,
done=i % 2 == 1,
)
card.grid(pady=(0, 10), sticky="nesw")
app.mainloop()
if __name__ == "__main__":
main()
@thegamecracks
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment