Repository navigation
Scheduler
A calendar of appointments: day, week, work week, month and agenda views, where people create, move and resize items by mouse, touch or keyboard. The app (its BFF) stays the source of truth: changes apply at once, the app saves them, and puts an item back if the server says no.
Added in #67. The design and decisions are in Spec: Scheduler.
import datetime as dt
from wapyt import ScheduleItem, SchedulerConfig
sched = self.add_scheduler("main", SchedulerConfig(view="week", items=[
ScheduleItem("standup", "Stand-up", dt.datetime(2026, 10, 8, 9, 30), dt.datetime(2026, 10, 8, 9, 45), color="aqua"),
ScheduleItem("offsite", "Offsite", dt.date(2026, 10, 12), dt.date(2026, 10, 14), all_day=True, color="green"),
]))
sched.on_range_change(lambda r: sched.set_items(load_items(r["start"], r["end"])))
sched.on_create(lambda p: open_new_item_form(p["start"], p["end"], p["all_day"]))
sched.on_open(lambda p: open_item_form(p["item"]))
def moved(p):
if not save_item_times(p["id"], p["start"], p["end"]):
sched.revert(p["id"]) # back where it was
sched.set_item_error(p["id"], "That room is booked")
sched.on_move(moved)
sched.on_resize(moved)| View | Shows |
|---|---|
day |
One day as a column of time slots, with an all-day row on top |
week |
Seven days (from week_start, Monday by default) |
work_week |
Monday to Friday |
month |
Six weeks of days; a busy day shows "+3 more", which lists the day |
agenda |
The next agenda_days (14) as a list grouped by day |
The toolbar has Today, previous and next, the range as a title, and the view switch. A mini month beside the views jumps to any date; it hides when the scheduler is narrower than 640px. Clicking a day number opens that day.
ScheduleItem(id, title, start, end, all_day=False, color=None, editable=True, description=None, series_id=None, data=None).
-
Times are naive local times: plain
datetimes with no time zone (or ISO strings), shown as given. Convert to the viewer's local time before handing items over. All-day items usedates, and theirendis the day after the last day (a one-day item on the 12th ends on the 13th). -
color:blue,orange,aqua,yellow,magenta,green,violet,red(each with a light and a dark version), or a hex colour. - Overlapping items sit side by side. An item that runs past midnight continues on the next day.
-
editable=Falselocks an item: no moving, resizing or deleting.SchedulerConfig(read_only=True)locks everything. - Recurring items arrive already expanded, one item per occurrence;
series_idgroups them. The scheduler doesn't compute recurrence rules.
| Action | Mouse / touch | Keyboard | Event |
|---|---|---|---|
| Create | Drag across empty slots, or double-click one (a day, in month or the all-day row: an all-day item) | Arrow to a slot, Enter | on_create {start, end, all_day} |
| Move | Drag the item to another time or day | Arrows, then Enter | on_move {id, start, end, all_day, old, item} |
| Resize | Drag its bottom edge | Shift+↑/↓, then Enter |
on_resize (same payload) |
| Open | Click | Enter | on_open {id, item} |
| Delete | (the app's own button or menu) | Delete | on_delete {id, item} |
- Times snap to
slot_minutes(30), and a label shows the times while dragging. Escape cancels a drag; dragging near an edge scrolls. -
Creating only reports the chosen slot. Add the item with
add_itemonce it exists (after the person fills in a form, say). -
Moves and resizes apply straight away and report the new and
oldtimes. If the server refuses,revert(id)puts the item back, andset_item_error(id, message)marks it (cleared by its next good change). -
Delete only asks: call
remove_item(id)once it's gone. - With the keyboard, arrows show the item at its new place (dashed) until Enter confirms or Escape cancels. The time grid and the month grid are one Tab stop each; items are in the Tab order. Moves are read out to screen readers.
on_range_change fires with {start, end, view} whenever the visible range
changes, so the app can load just those items (get_range() for the first
load). set_items, add_item, update_item(id, title=..., start=..., ...),
remove_item, get_items and get_item keep the view current without
reloading it.
-
export_ics(filename="schedule.ics")downloads the loaded items as an iCalendar file any calendar app imports. Times are written as local ("floating") times, like the scheduler shows them.wapyt.scheduler.to_ics(items)builds the same text in Python, for a server-side export. -
print()prints just the scheduler: the current view without its buttons, the whole day (not only the scrolled part), in black on white.
SchedulerConfig |
|
|---|---|
view, views
|
The starting view, and which views the switch offers |
date |
The date to show (today by default) |
week_start |
0 Sunday to 6 Saturday (1, Monday) |
day_start, day_end, scroll_to
|
Hours shown (0–24) and the hour it opens at (8) |
slot_minutes, slot_height
|
Grid step (5, 10, 15, 20, 30 or 60) and its height in pixels (22) |
time_format, locale
|
"12" or "24"; a language tag for day and month names |
now_indicator |
A line at the current time (on) |
min_item_minutes |
Shortest an item can be resized to (one slot) |
mini_calendar, agenda_days, height, title
|
The mini month (on), the agenda's length (14), a fixed height, the accessible name |
A resource timeline (rows per person or room), drag-to-create across several days in month, and time zones per item. See the spec.
wapyt is MIT-licensed · Repository · pyTincture · Issues
Getting started
Widgets
- Layout and MainWindow
- Tree
- DataTable
- Form
- ModalWindow
- Window
- TabWidget
- Toolbar
- Sidebar
- Chat
- Terminal
- CardPanel
- ResourceBoard
- File Transfer
- Message
- ContextMenu
- MenuBar
- Listbox
- Kanban
- ProgressBar
- Pagination
- Popup and tooltips
- Chart
- Scheduler
- MediaPlayer
Guides
Planning
Contributing