Coding Headstart¶
A PyObjC Example without documentation
Sources¶
AppController.py¶
import objc
from CalendarStore import CalCalendarStore, CalEvent, CalTask
from Cocoa import NSApp, NSApplication, NSDate, NSLog, NSObject
class AppController(NSObject):
mainWindow = objc.IBOutlet()
taskCreationDialog = objc.IBOutlet()
priorityPopup = objc.IBOutlet()
eventCreationDialog = objc.IBOutlet()
calendarData = objc.IBOutlet()
calItemTitle = objc.ivar()
calItemStartDate = objc.ivar()
calItemEndDate = objc.ivar()
objc.synthesize("calItemTitle", copy=True)
objc.synthesize("calItemStartDate", copy=True)
objc.synthesize("calItemEndDate", copy=True)
@objc.IBAction
def showTaskCreationDialog_(self, sender):
# Set default values for the title, start date and priority
# Cocoa bindings will clear out the related fields in the sheet
self._.calItemTitle = None
self._.calItemStartDate = NSDate.date()
NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.taskCreationDialog,
self.mainWindow,
self,
"didEndSheet:returnCode:contextInfo:",
None,
)
@objc.IBAction
def showEventCreationDialog_(self, sender):
# Set default values for the title and start/end date
# Cocoa bindings will clear out the related fields in the sheet
self._.calItemTitle = None
self._.calItemStartDate = NSDate.date()
self._.calItemEndDate = NSDate.dateWithTimeIntervalSinceNow_(3600)
NSApp.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.eventCreationDialog,
self.mainWindow,
self,
"didEndSheet:returnCode:contextInfo:",
None,
)
# Called when the "Add" button is pressed on the event/task entry sheet
# This starts the sheet dismissal process
@objc.IBAction
def dismissDialog_(self, sender):
NSApp.endSheet_(sender.window())
@objc.selectorFor(
NSApplication.beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_
)
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, contextInfo):
# Find out which calendar was selected for the new event/task
# We do this using the calendarData array controller which is bound to
# the calendar popups in the sheet
selectedCalendar = None
count = len(self.calendarData.selectedObjects())
if count > 0:
selectedCalendarUID = self.calendarData.selectedObjects()[0].uid()
selectedCalendar = CalCalendarStore.defaultCalendarStore().calendarWithUID_(
selectedCalendarUID
)
# Create an event/task based on which sheet was used
if sheet is self.taskCreationDialog:
if self._.calItemTitle is None:
self._.calItemTitle = "My Task"
self.createNewTaskWithCalendar_title_priority_dueDate_(
selectedCalendar,
self._.calItemTitle,
self.priorityPopup.selectedTag(),
self._.calItemStartDate,
)
else:
if self._.calItemTitle is None:
self._.calItemTitle = "My Event"
self.createNewEventWithCalendar_title_startDate_endDate_(
selectedCalendar,
self._.calItemTitle,
self._.calItemStartDate,
self._.calItemEndDate,
)
# Dismiss the sheet
sheet.orderOut_(self)
def createNewEventWithCalendar_title_startDate_endDate_(
self, calendar, title, startDate, endDate
):
# Create a new CalEvent object
newEvent = CalEvent.event()
# Set the calendar, title, start date and end date on the new event
# using the parameters passed to this method
newEvent._.calendar = calendar
newEvent._.title = title
newEvent._.startDate = startDate
newEvent._.endDate = endDate
# Save the new event to the calendar store (CalCalendarStore) and
# return it
res, err = CalCalendarStore.defaultCalendarStore().saveEvent_span_error_(
newEvent, 0, None
)
if res:
return newEvent
NSLog("error:%@", err.localizedDescription())
return None
def createNewTaskWithCalendar_title_priority_dueDate_(
self, calendar, title, priority, dueDate
):
# Create a new CalTask object
newTask = CalTask.task()
# Set the calendar, title, priority and due date on the new task
# using the parameters passed to this method
newTask._.calendar = calendar
newTask._.title = title
newTask._.priority = priority
newTask._.dueDate = dueDate
# Save the new task to the calendar store (CalCalendarStore) and
# return it
res, err = CalCalendarStore.defaultCalendarStore().saveTask_error_(
newTask, None
)
if res:
return newTask
NSLog("error:%@", err.localizedDescription())
return None
CalController.py¶
"""
Bindings and notification support for Calendar data used
by this application. Exposes read-only collections
(calendars, events, tasks) as observable entities.
"""
from CalendarStore import (
CalCalendarsChangedExternallyNotification,
CalCalendarsChangedNotification,
CalCalendarStore,
CalEventsChangedExternallyNotification,
CalEventsChangedNotification,
CalPriorityHigh,
CalPriorityMedium,
CalTasksChangedExternallyNotification,
CalTasksChangedNotification,
)
from Cocoa import NSDate, NSNotificationCenter, NSObject, NSString, NSValueTransformer
highPriority = "High"
normPriority = "Normal"
lowPriority = "Low"
nonePriority = "None"
class CalPriorityToStringTransformer(NSValueTransformer):
"""
The CalPriorityToStringTransformer class allows easy conversion between
CalPriority values (0-9) and human-readable priority strings (High,
Normal, Low, None). This allows us to populate the priority dropdown
using bindings
"""
@classmethod
def transformedValueClass(cls):
return type(NSString)
@classmethod
def allowsReverseTransformation(cls):
return False
def transformedValue_(self, value):
priority = value.unsignedIntValue()
if priority < CalPriorityHigh:
return nonePriority
elif priority < CalPriorityMedium:
return highPriority
elif priority == CalPriorityMedium:
return normPriority
return lowPriority
class CalController(NSObject):
def awakeFromNib(self):
# Register a transformer object for easy generation of
# human-readable priority strings
#
# See CalPriorityToStringTransformer implementation below
prioTransformer = CalPriorityToStringTransformer.alloc().init()
NSValueTransformer.setValueTransformer_forName_(
prioTransformer, "CalPriorityToStringTransformer"
)
# Register for notifications on calendars, events and tasks so we can
# update the GUI to reflect any changes beneath us
NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "calendarsChanged:", CalCalendarsChangedExternallyNotification, None
)
NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "calendarsChanged:", CalCalendarsChangedNotification, None
)
NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "eventsChanged:", CalEventsChangedExternallyNotification, None
)
NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "eventsChanged:", CalEventsChangedNotification, None
)
NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "tasksChanged:", CalTasksChangedExternallyNotification, None
)
NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(
self, "tasksChanged:", CalTasksChangedNotification, None
)
# Set up the read-only calendars/events/tasks arrays from Calendar Store
# as observable keys for Cocoa Bindings
# This in conjunction with the notifications will allow for immediate UI
# updates whenever calendar data changes outside of this app
def calendars(self):
return CalCalendarStore.defaultCalendarStore().calendars()
def events(self):
store = CalCalendarStore.defaultCalendarStore()
# Pull all events starting now from all calendars in the CalendarStore
allEventsPredicate = (
CalCalendarStore.eventPredicateWithStartDate_endDate_calendars_(
NSDate.date(), NSDate.distantFuture(), store.calendars()
)
)
return store.eventsWithPredicate_(allEventsPredicate)
def tasks(self):
store = CalCalendarStore.defaultCalendarStore()
# Pull all uncompleted tasks from all calendars in the CalendarStore
return store.tasksWithPredicate_(
CalCalendarStore.taskPredicateWithUncompletedTasks_(store.calendars())
)
# With the observable keys set up above and the appropriate bindings in IB,
# we can trigger UI updates just by signaling changes to the keys
def calendarsChanged_(self, notification):
self.willChangeValueForKey_("calendars")
self.didChangeValueForKey_("calendars")
def eventsChanged_(self, notification):
self.willChangeValueForKey_("events")
self.didChangeValueForKey_("events")
def tasksChanged_(self, notification):
self.willChangeValueForKey_("tasks")
self.didChangeValueForKey_("tasks")
main.py¶
import AppController # noqa: F401
import CalController # noqa: F401
import objc
from PyObjCTools import AppHelper
objc.setVerbose(True)
AppHelper.runEventLoop()
setup.py¶
"""
Script for building the example.
Usage:
python3 setup.py py2app
"""
from setuptools import setup
setup(
name="PyCalendarStore",
app=["main.py"],
data_files=["English.lproj"],
setup_requires=[
"py2app",
"pyobjc-framework-CalendarStore",
"pyobjc-framework-Cocoa",
],
)