#!/usr/bin/env python import subprocess as sp import datetime import time import gi gi.require_version("Gtk","3.0") from gi.repository import Gtk calcurse_event_cmd = ["calcurse", "-Q", "--input-datefmt","4", "--output-datefmt","4", "--filter-type", "cal", "--format-recur-apt", '"apt;%S;%E;%m\n"', "--format-apt", '"apt;%S;%E;%m\n"', "--format-event", '"event;%m\n"', "--from"] def get_event_list(date): # Gets the clean event list output from calcurse. date_str = date.isoformat() cmd = calcurse_event_cmd.copy() cmd.append(date_str) calcurse_out = sp.run(cmd,capture_output=True).stdout decoded = calcurse_out.decode().replace("\"","") out = decoded.splitlines()[1:] return out def parse_event_list(event_list): # parses calcurse event list into dicts for future use split_events = [event.split(';') for event in event_list] out = [] for event in split_events: if event[0] == "event": out.append({ 'type':'event', 'start': datetime.time(0), 'description': event[1], }) elif event[0] == "apt": out.append({ 'type':'apt', 'start': parse_time(event[1]), 'end': parse_time(event[2]), 'description': event[3] }) return out def parse_time(time): # Takes a time as returned by calcurse, returns a datetime.time object t = time.split(':') return datetime.time(int(t[0]),int(t[1])) class CalendarWindow(Gtk.ApplicationWindow): def __init__(self): super().__init__() self.header_bar = Gtk.HeaderBar() self.header_bar.set_title("Calendar") self.header_bar.props.show_close_button = True self.set_titlebar(self.header_bar) self.event_list = Gtk.ListBox() self.event_list.fill = True self.calendar_box = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) self.calendar = Gtk.Calendar() self.calendar.fill = True self.calendar.expand = True self.calendar.set_size_request(400,400) self.calendar_box.add(self.calendar) self.calendar_box.add(Gtk.Label(label="PlaceHolder")) self.main_box = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL) # Box containing the list view and cal/buttons self.main_box.add(self.calendar_box) self.main_box.add(self.event_list) self.add(self.main_box) self.connect("destroy",Gtk.main_quit) self.calendar.connect("day_selected",self._on_day_selected) def _on_day_selected(self,calendar): calendar_date = calendar.get_date() date = datetime.date(calendar_date.year,calendar_date.month+1,calendar_date.day) self.header_bar.set_subtitle(date.isoformat()) self.populate_day(date) def populate_day(self,date): for c in self.event_list.get_children(): c.destroy() events = parse_event_list(get_event_list(date)) sorted_events = sorted(events, key=lambda k: k['start']) for event in sorted_events: row = EventRow(event) self.event_list.add(row) self.event_list.show_all() class EventRow(Gtk.ListBoxRow): def __init__(self,event): super().__init__() self.description_label = Gtk.Label(label=event['description']) self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.box.padding = 20 if event['type'] != 'event': timespan = f"{event['start'].strftime('%H:%M')} - {event['end'].strftime('%H:%M')}" self.timespan_label = Gtk.Label(label=timespan) self.box.pack_start(self.timespan_label,0,0,0) self.box.pack_end(self.description_label,0,0,0) else: self.box.pack_start(self.description_label,0,0,0) self.add(self.box) today = datetime.date.today() c = CalendarWindow() c.populate_day(today) c.header_bar.set_subtitle(today.isoformat()) c.show_all() Gtk.main()