2020-04-25 18:48:38 +00:00
|
|
|
import PySimpleGUI as sg
|
|
|
|
import datetime
|
|
|
|
import calendar
|
|
|
|
import itertools
|
|
|
|
|
|
|
|
"""
|
|
|
|
Demo_Date_Chooser
|
|
|
|
|
|
|
|
This is the same code that is now used internally in PySimpleGUI as the 'date chooser'
|
|
|
|
It is shown here in a demo program form to demonstrate to you that PySimpleGUI is being used
|
|
|
|
to implement user features. The underlying GUI framework is no longer being used like it was previously
|
|
|
|
|
|
|
|
Copyright 2020 PySimpleGUI
|
|
|
|
"""
|
|
|
|
|
2020-04-26 03:13:01 +00:00
|
|
|
sg.theme('Dark Red')
|
2020-04-25 18:48:38 +00:00
|
|
|
|
|
|
|
def popup_get_date(start_mon=None, start_day=None, start_year=None, begin_at_sunday_plus=0, no_titlebar=True, title='Choose Date', keep_on_top=True, location=(None, None), close_when_chosen=False, icon=None, locale=None, month_names=None, day_abbreviations=None):
|
|
|
|
"""
|
|
|
|
Display a calendar window, get the user's choice, return as a tuple (mon, day, year)
|
|
|
|
|
|
|
|
:param start_mon: The starting month
|
|
|
|
:type start_mon: int
|
|
|
|
:param start_day: The starting day - optional. Set to None or 0 if no date to be chosen at start
|
|
|
|
:type start_day: int or None
|
|
|
|
:param start_year: The starting year
|
|
|
|
:type start_year: int
|
|
|
|
:param begin_at_sunday_plus: Determines the left-most day in the display. 0=sunday, 1=monday, etc
|
|
|
|
:type begin_at_sunday_plus: int
|
|
|
|
:param icon: Same as Window icon parameter. Can be either a filename or Base64 value. For Windows if filename, it MUST be ICO format. For Linux, must NOT be ICO
|
|
|
|
:type icon: str
|
|
|
|
:param locale: locale used to get the day names
|
|
|
|
:type locale: str
|
|
|
|
:param month_names: optional list of month names to use (should be 12 items)
|
|
|
|
:type month_names: List[str]
|
|
|
|
:param day_abbreviations: optional list of abbreviations to display as the day of week
|
|
|
|
:type day_abbreviations: List[str]
|
|
|
|
:return: Tuple containing (month, day, year) of chosen date or None if was cancelled
|
|
|
|
:rtype: None or (int, int, int)
|
|
|
|
"""
|
|
|
|
|
|
|
|
if month_names is not None and len(month_names) != 12:
|
|
|
|
sg.popup_error('Incorrect month names list specified. Must have 12 entries.', 'Your list:', month_names)
|
|
|
|
|
|
|
|
if day_abbreviations is not None and len(day_abbreviations) != 7:
|
|
|
|
sg.popup_error('Incorrect day abbreviation list. Must have 7 entries.', 'Your list:', day_abbreviations)
|
|
|
|
|
|
|
|
day_font = 'TkFixedFont 9'
|
|
|
|
mon_year_font = 'TkFixedFont 10'
|
|
|
|
arrow_font = 'TkFixedFont 7'
|
|
|
|
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
cur_month, cur_day, cur_year = now.month, now.day, now.year
|
|
|
|
cur_month = start_mon or cur_month
|
|
|
|
if start_mon is not None:
|
|
|
|
cur_day = start_day
|
|
|
|
else:
|
|
|
|
cur_day = cur_day
|
|
|
|
cur_year = start_year or cur_year
|
|
|
|
|
|
|
|
|
|
|
|
def update_days(window, month, year, begin_at_sunday_plus):
|
|
|
|
[window[(week, day)].update('') for day in range(7) for week in range(6)]
|
|
|
|
weeks = calendar.monthcalendar(year, month)
|
|
|
|
month_days = list(itertools.chain.from_iterable([[0 for _ in range(8 - begin_at_sunday_plus)]] + weeks))
|
|
|
|
if month_days[6] == 0:
|
|
|
|
month_days = month_days[7:]
|
|
|
|
if month_days[6] == 0:
|
|
|
|
month_days = month_days[7:]
|
|
|
|
for i, day in enumerate(month_days):
|
|
|
|
offset = i
|
|
|
|
if offset >= 6 * 7:
|
|
|
|
break
|
|
|
|
window[(offset // 7, offset % 7)].update(str(day) if day else '')
|
|
|
|
|
|
|
|
def make_days_layout():
|
|
|
|
days_layout = []
|
|
|
|
for week in range(6):
|
|
|
|
row = []
|
|
|
|
for day in range(7):
|
|
|
|
row.append(sg.T('', size=(4, 1), justification='c', font=day_font, key=(week, day), enable_events=True, pad=(0, 0)))
|
|
|
|
days_layout.append(row)
|
|
|
|
return days_layout
|
|
|
|
|
|
|
|
|
|
|
|
# Create table of month names and week day abbreviations
|
|
|
|
|
|
|
|
if day_abbreviations is None or len(day_abbreviations) != 7:
|
|
|
|
fwday = calendar.SUNDAY
|
|
|
|
try:
|
|
|
|
if locale is not None:
|
|
|
|
_cal = calendar.LocaleTextCalendar(fwday, locale)
|
|
|
|
else:
|
|
|
|
_cal = calendar.TextCalendar(fwday)
|
|
|
|
day_names = _cal.formatweekheader(3).split()
|
|
|
|
except Exception as e:
|
|
|
|
print('Exception building day names from locale', locale, e)
|
|
|
|
day_names = ('Sun', 'Mon', 'Tue', 'Wed', 'Th', 'Fri', 'Sat')
|
|
|
|
else:
|
|
|
|
day_names = day_abbreviations
|
|
|
|
|
|
|
|
mon_names = month_names if month_names is not None and len(month_names) == 12 else [calendar.month_name[i] for i in range(1,13)]
|
|
|
|
days_layout = make_days_layout()
|
|
|
|
|
|
|
|
layout = [[sg.B('◄◄', font=arrow_font, border_width=0, key='-YEAR-DOWN-', pad=((10,2),2)),
|
|
|
|
sg.B('◄', font=arrow_font, border_width=0, key='-MON-DOWN-', pad=(0,2)),
|
|
|
|
sg.Text('{} {}'.format(mon_names[cur_month - 1], cur_year), size=(16, 1), justification='c', font=mon_year_font, key='-MON-YEAR-', pad=(0,2)),
|
|
|
|
sg.B('►', font=arrow_font,border_width=0, key='-MON-UP-', pad=(0,2)),
|
|
|
|
sg.B('►►', font=arrow_font,border_width=0, key='-YEAR-UP-', pad=(2,2))]]
|
|
|
|
layout += [[sg.Col([[sg.T(day_names[i - (7 - begin_at_sunday_plus) % 7], size=(4,1), font=day_font, background_color=sg.theme_text_color(), text_color=sg.theme_background_color(), pad=(0,0)) for i in range(7)]], background_color=sg.theme_text_color(), pad=(0,0))]]
|
|
|
|
layout += days_layout
|
|
|
|
if not close_when_chosen:
|
|
|
|
layout += [[sg.Button('Ok', border_width=0,font='TkFixedFont 8'), sg.Button('Cancel',border_width=0, font='TkFixedFont 8')]]
|
|
|
|
|
|
|
|
window = sg.Window(title, layout, no_titlebar=no_titlebar, grab_anywhere=True, keep_on_top=keep_on_top, font='TkFixedFont 12', use_default_focus=False, location=location, finalize=True, icon=icon)
|
|
|
|
|
|
|
|
update_days(window, cur_month, cur_year, begin_at_sunday_plus)
|
|
|
|
|
|
|
|
prev_choice = chosen_mon_day_year = None
|
|
|
|
|
|
|
|
if cur_day:
|
|
|
|
chosen_mon_day_year = cur_month, cur_day, cur_year
|
|
|
|
for week in range(6):
|
|
|
|
for day in range(7):
|
|
|
|
if window[(week,day)].DisplayText == str(cur_day):
|
|
|
|
window[(week,day)].update(background_color=sg.theme_text_color(), text_color=sg.theme_background_color())
|
|
|
|
prev_choice = (week,day)
|
|
|
|
break
|
|
|
|
|
|
|
|
while True: # Event Loop
|
|
|
|
event, values = window.read()
|
|
|
|
if event in (None, 'Cancel'):
|
|
|
|
chosen_mon_day_year = None
|
|
|
|
break
|
|
|
|
if event == 'Ok':
|
|
|
|
break
|
|
|
|
if event in ('-MON-UP-', '-MON-DOWN-', '-YEAR-UP-','-YEAR-DOWN-'):
|
|
|
|
cur_month += (event == '-MON-UP-')
|
|
|
|
cur_month -= (event == '-MON-DOWN-')
|
|
|
|
cur_year += (event == '-YEAR-UP-')
|
|
|
|
cur_year -= (event == '-YEAR-DOWN-')
|
|
|
|
if cur_month > 12:
|
|
|
|
cur_month = 1
|
|
|
|
cur_year += 1
|
|
|
|
elif cur_month < 1:
|
|
|
|
cur_month = 12
|
|
|
|
cur_year -= 1
|
|
|
|
window['-MON-YEAR-'].update('{} {}'.format(mon_names[cur_month - 1], cur_year))
|
|
|
|
update_days(window, cur_month, cur_year, begin_at_sunday_plus)
|
|
|
|
if prev_choice:
|
|
|
|
window[prev_choice].update(background_color=sg.theme_background_color(), text_color=sg.theme_text_color())
|
|
|
|
elif type(event) is tuple:
|
|
|
|
if window[event].DisplayText != "":
|
|
|
|
chosen_mon_day_year = cur_month, int(window[event].DisplayText), cur_year
|
|
|
|
if prev_choice:
|
|
|
|
window[prev_choice].update(background_color=sg.theme_background_color(), text_color=sg.theme_text_color())
|
|
|
|
window[event].update(background_color=sg.theme_text_color(), text_color=sg.theme_background_color())
|
|
|
|
prev_choice = event
|
|
|
|
if close_when_chosen:
|
|
|
|
break
|
|
|
|
window.close()
|
|
|
|
return chosen_mon_day_year
|
|
|
|
|
|
|
|
|
|
|
|
print(popup_get_date())
|