DatePicker¶
“DatePicker” is a Cocoa sample application that demonstrates how to use
NSDatePicker
control on Mac OS X. NSDatePicker
is a subclass of
NSControl
that provides for visually displaying and editing an
NSDate
instance.
This control uses an NSDatePickerCell
to implement much of the
control’s functionality.
About the Sample¶
DatePicker shows how to manipulate the behaviors of lNSDatePicker
in the
following ways
Changing the picker style.
Changing the picker font size.
Changing the background/text color as well as other appearance aspects.
Turning on and off the various date and time element flags.
Setting the minimum and maximum dates.
Overriding the set date as a delegate.
The sample also shows how to use the various date and time formats using the
NSDateFormatter
class.
Sources¶
MyWindowController.py¶
import Cocoa
import objc
# date/time element popup selections:
kNSHourMinuteDatePickerElementFlag = 0
kNSHourMinuteSecondDatePickerElementFlag = 1
kNSTimeZoneDatePickerElementFlag = 2
kNSYearMonthDatePickerElementFlag = 0
kNSYearMonthDayDatePickerElementFlag = 1
kNSEraDatePickerElementFlag = 2
kSingleDateMode = 0
kRangeDateMode = 1
class MyWindowController(Cocoa.NSWindowController):
datePickerControl = objc.ivar()
shrinkGrowFactor = objc.ivar.int()
outerBox = objc.IBOutlet()
datePickerPlaceHolder = objc.IBOutlet()
dateResult1 = objc.IBOutlet()
dateResult2 = objc.IBOutlet()
dateResult3 = objc.IBOutlet()
dateResult4 = objc.IBOutlet()
dateResult5 = objc.IBOutlet()
# appearance
pickerStylePopup = objc.IBOutlet()
drawsBackgroundCheck = objc.IBOutlet()
bezeledCheck = objc.IBOutlet()
borderedCheck = objc.IBOutlet()
backColorWell = objc.IBOutlet()
textColorWell = objc.IBOutlet()
fontSizePopup = objc.IBOutlet()
# date and time
dateElementChecks = objc.IBOutlet()
timeElementChecks = objc.IBOutlet()
overrideDateCheck = objc.IBOutlet()
overrideDate = objc.IBOutlet()
# date range
datePickerModeRadios = objc.IBOutlet()
secondsRangeEdit = objc.IBOutlet()
secondsRangeEditLabel = objc.IBOutlet()
minDatePicker = objc.IBOutlet()
maxDatePicker = objc.IBOutlet()
setMinDateButton = objc.IBOutlet()
setMaxDateButton = objc.IBOutlet()
clearMinDateButton = objc.IBOutlet()
clearMaxDateButton = objc.IBOutlet()
def awakeFromNib(self):
# based our date formatter on CFDateFormatter: allows more
# configurability and better localization
Cocoa.NSDateFormatter.setDefaultFormatterBehavior_(
Cocoa.NSDateFormatterBehavior10_4
)
self.setupDatePickerControl_(Cocoa.NSClockAndCalendarDatePickerStyle)
# setup the initial Cocoa.NSDatePickerElementFlags since we are using picker style:
# Cocoa.NSClockAndCalendarDatePickerStyle
flags = (
Cocoa.NSYearMonthDatePickerElementFlag
| Cocoa.NSYearMonthDayDatePickerElementFlag
| Cocoa.NSEraDatePickerElementFlag
| Cocoa.NSHourMinuteDatePickerElementFlag
| Cocoa.NSHourMinuteSecondDatePickerElementFlag
| Cocoa.NSTimeZoneDatePickerElementFlag
)
self.datePickerControl.setDatePickerElements_(flags)
self.datePickerModeRadios.cellWithTag_(1).setEnabled_(
False
) # not currently implemented in 10.4.x and earlier
self.minDatePicker.setDateValue_(Cocoa.NSDate.date())
self.maxDatePicker.setDateValue_(Cocoa.NSDate.distantFuture())
self.updateControls() # force update of all UI elements and the picker itself
def applicationShouldTerminateAfterLastWindowClosed_(self, sender):
return True
def setupDatePickerControl_(self, pickerStyle):
# we need to re-create the picker control (due to a resize bug when
# switching between styles)
if (
self.datePickerControl is not None
): # hide and release the previous date picker, if any
self.datePickerControl.setHidden_(True)
self.datePickerControl.setNeedsDisplay_(True)
self.datePickerControl = None
frame = self.datePickerPlaceHolder.frame()
self.shrinkGrowFactor = frame.size.height - 30
# create the date picker control if not created already
if self.datePickerControl is None:
self.datePickerControl = Cocoa.NSDatePicker.alloc().initWithFrame_(frame)
self.datePickerControl.setFrameOrigin_(
Cocoa.NSMakePoint(1, 0)
) # nudge the control placement a little
self.datePickerControl.setDatePickerStyle_(
pickerStyle
) # set our desired picker style
self.datePickerPlaceHolder.addSubview_(
self.datePickerControl
) # embed into our placeholder
self.datePickerControl.setDrawsBackground_(True)
self.datePickerControl.setBezeled_(True)
self.datePickerControl.setBordered_(False)
self.datePickerControl.setEnabled_(True)
self.datePickerControl.setTextColor_(self.textColorWell.color())
self.datePickerControl.setBackgroundColor_(self.backColorWell.color())
# always set the date/time to TODAY
# note that our delete override might block this...
self.datePickerControl.setDateValue_(Cocoa.NSDate.date())
self.datePickerControl.setNeedsDisplay_(True)
self.updateControls() # force update of all UI elements and the picker itself
# synch the picker style popup with the new style change
self.pickerStylePopup.selectItemWithTag_(pickerStyle)
# we want to be the cell's delegate to catch date validation
self.datePickerControl.setDelegate_(self)
# or we can set us as the delegate to its cell like so:
# self.datePickerControl.cell().setDelegate_(self)
# we want to respond to date/time changes
self.datePickerControl.setAction_("datePickerAction:")
def updateDateResult(self):
theDate = self.datePickerControl.dateValue()
if theDate is not None:
formatter = Cocoa.NSDateFormatter.alloc().init()
# some examples:
# formatter.setDateStyle_(Cocoa.NSDateFormatterNoStyle)
# -> <no date displayed>
# formatter.setDateStyle_(Cocoa.NSDateFormatterMediumStyle)
# -> Jan 24, 1984
# formatter.setDateStyle_(Cocoa.NSDateFormatterShortStyle)
# -> 1/24/84
# formatter.setDateStyle_(Cocoa.NSDateFormatterLongStyle)
# -> January 24, 1984
# formatter.setDateStyle_(Cocoa.NSDateFormatterFullStyle)
# -> Tuesday, January 24, 1984
#
# formatter.setTimeStyle_(Cocoa.NSDateFormatterNoStyle)
# -> <no time displayed>
# formatter.setTimeStyle_(Cocoa.NSDateFormatterShortStyle)
# -> 2:44 PM
# formatter.setTimeStyle_(Cocoa.NSDateFormatterMediumStyle)
# -> 2:44:55 PM
# formatter.setTimeStyle_(Cocoa.NSDateFormatterLongStyle)
# -> 2:44:55 PM PDT
# formatter.setTimeStyle_(Cocoa.NSDateFormatterFullStyle)
# -> 2:44:55 PM PDT
#
formatter.setDateStyle_(Cocoa.NSDateFormatterShortStyle)
formatter.setTimeStyle_(Cocoa.NSDateFormatterNoStyle)
formattedDateString = formatter.stringFromDate_(theDate)
self.dateResult1.setStringValue_(formattedDateString)
formatter.setDateStyle_(Cocoa.NSDateFormatterShortStyle)
formatter.setTimeStyle_(Cocoa.NSDateFormatterShortStyle)
formattedDateString = formatter.stringFromDate_(theDate)
self.dateResult2.setStringValue_(formattedDateString)
formatter.setDateStyle_(Cocoa.NSDateFormatterMediumStyle)
formatter.setTimeStyle_(Cocoa.NSDateFormatterShortStyle)
formattedDateString = formatter.stringFromDate_(theDate)
self.dateResult3.setStringValue_(formattedDateString)
formatter.setDateStyle_(Cocoa.NSDateFormatterLongStyle)
formatter.setTimeStyle_(Cocoa.NSDateFormatterShortStyle)
formattedDateString = formatter.stringFromDate_(theDate)
self.dateResult4.setStringValue_(formattedDateString)
formatter.setDateStyle_(Cocoa.NSDateFormatterFullStyle)
formatter.setTimeStyle_(Cocoa.NSDateFormatterFullStyle)
formattedDateString = formatter.stringFromDate_(theDate)
self.dateResult5.setStringValue_(formattedDateString)
def updateControls(self):
self.datePickerControl.setNeedsDisplay_(True) # force it to update
self.updateDatePickerMode()
self.updateDateTimeElementFlags()
self.updateDateResult()
@objc.IBAction
def setPickerStyle_(self, sender):
tag = sender.selectedCell().tag()
windowFrame = self.window().frame()
boxFrame = self.outerBox.frame()
self.datePickerControl.setHidden_(True)
if tag == Cocoa.NSClockAndCalendarDatePickerStyle:
# for this picker style, we need to grow the window to make room
windowFrame.size.height += self.shrinkGrowFactor
windowFrame.origin.y -= self.shrinkGrowFactor
boxFrame.size.height += self.shrinkGrowFactor
self.outerBox.setFrame_(boxFrame)
self.window().setFrame_display_animate_(windowFrame, True, True)
# set our desired picker style
self.datePickerControl.setDatePickerStyle_(
Cocoa.NSClockAndCalendarDatePickerStyle
)
# shows these last
self.dateResult1.setHidden_(False)
self.dateResult2.setHidden_(False)
self.dateResult3.setHidden_(False)
self.dateResult4.setHidden_(False)
else:
currentPickerStyle = self.datePickerControl.datePickerStyle()
# shrink the window only if the current style is "clock and calendar"
if currentPickerStyle == Cocoa.NSClockAndCalendarDatePickerStyle:
# hide these first
self.dateResult1.setHidden_(True)
self.dateResult2.setHidden_(True)
self.dateResult3.setHidden_(True)
self.dateResult4.setHidden_(True)
windowFrame.size.height -= self.shrinkGrowFactor
windowFrame.origin.y += self.shrinkGrowFactor
boxFrame.size.height -= self.shrinkGrowFactor
self.outerBox.setFrame_(boxFrame)
self.window().setFrame_display_animate_(windowFrame, True, True)
self.setupDatePickerControl_(tag) # set our desired picker style
self.datePickerControl.setHidden_(False)
self.updateControls() # force update of all UI elements and the picker itself
@objc.IBAction
def setFontSize_(self, sender):
tag = sender.selectedCell().tag()
if tag == Cocoa.NSMiniControlSize:
self.datePickerControl.cell().setControlSize_(Cocoa.NSMiniControlSize)
self.datePickerControl.cell().setFont_(Cocoa.NSFont.systemFontOfSize_(9.0))
elif tag == Cocoa.NSSmallControlSize:
self.datePickerControl.cell().setControlSize_(Cocoa.NSSmallControlSize)
self.datePickerControl.cell().setFont_(Cocoa.NSFont.systemFontOfSize_(11.0))
elif tag == Cocoa.NSRegularControlSize:
self.datePickerControl.cell().setControlSize_(Cocoa.NSRegularControlSize)
self.datePickerControl.cell().setFont_(Cocoa.NSFont.systemFontOfSize_(13.0))
@objc.IBAction
def datePickerAction_(self, sender):
self.updateDateResult()
@objc.IBAction
def dateOverrideAction_(self, sender):
checked = sender.selectedCell().state()
if checked:
self.datePickerControl.setDelegate_(self)
else:
self.datePickerControl.setDelegate_(None)
self.datePickerControl.setDateValue_(
Cocoa.NSDate.date()
) # force the delete "datePickerCell" to be called
def datePickerCell_validateProposedDateValue_timeInterval_(
self, aDatePickerCell, proposedDateValue, proposedTimeInterval
):
controller = aDatePickerCell.delegate()
if controller is self and aDatePickerCell is self.datePickerControl.cell():
# override the date and time?
if self.overrideDateCheck.cell().state():
# override the date using the user specified date
return (self.overrideDate.dateValue(), proposedTimeInterval)
return (proposedDateValue, proposedTimeInterval)
@objc.IBAction
def setDrawsBackground_(self, sender):
self.datePickerControl.setDrawsBackground_(sender.state())
@objc.IBAction
def setBackgroundColor_(self, sender):
newColor = sender.color()
self.datePickerControl.setBackgroundColor_(newColor)
@objc.IBAction
def setTextColor_(self, sender):
newColor = sender.color()
self.datePickerControl.setTextColor_(newColor)
@objc.IBAction
def setBezeled_(self, sender):
self.datePickerControl.setBezeled_(sender.state())
@objc.IBAction
def setBordered_(self, sender):
self.datePickerControl.setBordered_(sender.state())
@objc.IBAction
def setDateElementFlags_(self, sender):
tag = sender.selectedCell().tag()
flags = self.datePickerControl.datePickerElements()
checked = sender.selectedCell().state()
if tag == kNSYearMonthDatePickerElementFlag:
if checked:
flags |= Cocoa.NSYearMonthDatePickerElementFlag
else:
flags ^= Cocoa.NSYearMonthDatePickerElementFlag
elif tag == kNSYearMonthDayDatePickerElementFlag:
if checked:
flags |= Cocoa.NSYearMonthDayDatePickerElementFlag
else:
flags ^= Cocoa.NSYearMonthDayDatePickerElementFlag
elif tag == kNSEraDatePickerElementFlag:
if checked:
flags |= Cocoa.NSEraDatePickerElementFlag
else:
flags ^= Cocoa.NSEraDatePickerElementFlag
self.datePickerControl.setDatePickerElements_(flags)
self.updateControls() # force update of all UI elements and the picker itself
@objc.IBAction
def setTimeElementFlags_(self, sender):
tag = sender.selectedCell().tag()
flags = self.datePickerControl.datePickerElements()
checked = sender.selectedCell().state()
if tag == kNSHourMinuteDatePickerElementFlag:
if checked:
flags |= Cocoa.NSHourMinuteDatePickerElementFlag
else:
flags ^= Cocoa.NSHourMinuteDatePickerElementFlag
elif tag == kNSHourMinuteSecondDatePickerElementFlag:
if checked:
flags |= Cocoa.NSHourMinuteSecondDatePickerElementFlag
else:
flags ^= Cocoa.NSHourMinuteSecondDatePickerElementFlag
elif tag == kNSTimeZoneDatePickerElementFlag:
if checked:
flags |= Cocoa.NSTimeZoneDatePickerElementFlag
else:
flags ^= Cocoa.NSTimeZoneDatePickerElementFlag
self.datePickerControl.setDatePickerElements_(flags)
self.updateControls() # force update of all UI elements and the picker itself
def updateDateTimeElementFlags(self):
elementFlags = self.datePickerControl.datePickerElements()
# time elements
if (elementFlags & Cocoa.NSHourMinuteDatePickerElementFlag) != 0:
self.timeElementChecks.selectCellWithTag_(0)
if (elementFlags & Cocoa.NSHourMinuteSecondDatePickerElementFlag) != 0:
self.timeElementChecks.selectCellWithTag_(1)
if (elementFlags & Cocoa.NSTimeZoneDatePickerElementFlag) != 0:
self.timeElementChecks.selectCellWithTag_(2)
# date elements
if (elementFlags & Cocoa.NSYearMonthDatePickerElementFlag) != 0:
self.dateElementChecks.selectCellWithTag_(0)
if (elementFlags & Cocoa.NSYearMonthDayDatePickerElementFlag) != 0:
self.dateElementChecks.selectCellWithTag_(1)
if (elementFlags & Cocoa.NSEraDatePickerElementFlag) != 0:
self.dateElementChecks.selectCellWithTag_(2)
@objc.IBAction
def setMinDate_(self, sender):
self.datePickerControl.setMinDate_(self.minDatePicker.dateValue())
self.setMinDateButton.setEnabled_(False)
self.clearMinDateButton.setEnabled_(True)
@objc.IBAction
def setMaxDate_(self, sender):
self.datePickerControl.setMaxDate_(self.maxDatePicker.dateValue())
self.setMaxDateButton.setEnabled_(False)
self.clearMaxDateButton.setEnabled_(True)
@objc.IBAction
def clearMinDate_(self, sender):
self.datePickerControl.setMinDate_(Cocoa.NSDate.distantPast())
self.clearMinDateButton.setEnabled_(False)
self.setMinDateButton.setEnabled_(True)
@objc.IBAction
def clearMaxDate_(self, sender):
self.datePickerControl.setMaxDate_(Cocoa.NSDate.distantFuture())
self.clearMaxDateButton.setEnabled_(False)
self.setMaxDateButton.setEnabled_(True)
@objc.IBAction
def setDatePickerMode_(self, sender):
tag = sender.selectedCell().tag()
if tag == kSingleDateMode:
self.datePickerControl.setDatePickerMode_(Cocoa.NSSingleDateMode)
elif tag == kRangeDateMode:
self.datePickerControl.setDatePickerMode_(Cocoa.NSRangeDateMode)
self.updateControls() # force update of all UI elements and the picker itself
def updateDatePickerMode(self):
mode = self.datePickerControl.datePickerMode()
if mode == Cocoa.NSSingleDateMode:
self.datePickerModeRadios.selectCellWithTag_(0)
# interval value not applicable:
self.secondsRangeEdit.setEnabled_(False)
self.secondsRangeEditLabel.setTextColor_(Cocoa.NSColor.lightGrayColor())
self.datePickerControl.setTimeInterval_(0)
elif mode == Cocoa.NSRangeDateMode:
self.datePickerModeRadios.selectCellWithTag_(1)
# interval value applies:
self.secondsRangeEdit.setEnabled_(True)
self.secondsRangeEditLabel.setTextColor_(Cocoa.NSColor.blackColor())
# set the date range by start date (here we use the current date in the date picker
# control), and time interval (in seconds)
secsStr = self.secondsRangeEdit.stringValue()
numSeconds = int(secsStr)
self.datePickerControl.setTimeInterval_(numSeconds)
def controlTextDidEndEditing_(self, notification):
self.updateDatePickerMode() # force update of the date picker control
main.py¶
import MyWindowController # noqa: F401
from PyObjCTools import AppHelper
AppHelper.runEventLoop()
setup.py¶
"""
Script for building the example.
Usage:
python3 setup.py py2app
"""
from setuptools import setup
setup(
name="DatePicker",
app=["main.py"],
data_files=["English.lproj"],
setup_requires=["py2app", "pyobjc-framework-Cocoa"],
)