9.12. Calendar

The Calendar widget is an effective way to display and retrieve monthly date related information. It is a very simple widget to create and work with.

Creating a GtkCalendar widget is a simple as:
 
calendar = GtkCalendar()

The calendar will display the current month and year by default.

There might be times where you need to change a lot of information within this widget and the following methods allow you to make multiple changes to a Calendar widget without the user seeing multiple on-screen updates.
 
calendar.freeze()

calendar.thaw()

They work just like the freeze/thaw methods of every other widget.

The Calendar widget has a few options that allow you to change the way the widget both looks and operates by using the method:
 
calendar.display_options(flags)

The flags argument can be formed by combining either of the following five options using the logical bitwise OR (|) operation:

The following methods are used to set the the currently displayed date:
 
result = calendar.select_month(month, year)

calendar.select_day(day)

The return value from the select_month() method is a boolean value indicating whether the selection was successful.

With the select_day() method the specified day number is selected within the current month, if that is possible. A day value of 0 will deselect any current selection.

In addition to having a day selected, any number of days in the month may be "marked". A marked day is highlighted within the calendar display. The following methods are provided to manipulate marked days:
 
result = calendar.mark_day(day)

result = calendar.unmark_day(day)

calendar.clear_marks()

mark_day() and unmark_day() return a boolean indicating whether the method was successful. Note that marks are persistent across month and year changes.

The final Calendar widget method is used to retrieve the currently selected date, month and/or year.
 
year, month, day = calendar.get_date()

The Calendar widget can generate a number of signals indicating date selection and change. The names of these signals are self explanatory, and are:

That just leaves us with the need to put all of this together into the calendar.py exmple program. Figure 9.11 illustrates the program operation:

Figure 9.11 Calendar Example

The calendar.py source code is:
 
    1   #!/usr/bin/env python
    2   
    3   # example calendar.py
    4   #
    5   # Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias Grönlund
    6   # Copyright (C) 2000 Tony Gale
    7   # Copyright (C) 2001 John Finlay
    8   #
    9   # This program is free software; you can redistribute it and/or modify
   10   # it under the terms of the GNU General Public License as published by
   11   # the Free Software Foundation; either version 2 of the License, or
   12   # (at your option) any later version.
   13   #
   14   # This program is distributed in the hope that it will be useful,
   15   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   16   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17   # GNU General Public License for more details.
   18   #
   19   # You should have received a copy of the GNU General Public License
   20   # along with this program; if not, write to the Free Software
   21   # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   22   
   23   import gtk
   24   import time
   25   
   26   class CalendarExample:
   27       DEF_PAD = 10
   28       DEF_PAD_SMALL = 5
   29       TM_YEAR_BASE = 1900
   30   
   31       calendar_show_header = 0
   32       calendar_show_days = 1
   33       calendar_month_change = 2 
   34       calendar_show_week = 3
   35       calendar_monday_first = 4
   36   
   37       def calendar_date_to_string(self):
   38           year, month, day = self.window.get_date()
   39           mytime = time.mktime((year, month+1, day, 0, 0, 0, 0, 0, 0))
   40           return time.strftime("%x", time.gmtime(mytime))
   41   
   42       def calendar_set_signal_strings(self, sig_str):
   43           prev_sig = self.prev_sig.get()
   44           self.prev2_sig.set_text(prev_sig)
   45   
   46           prev_sig = self.last_sig.get()
   47           self.prev_sig.set_text(prev_sig)
   48           self.last_sig.set_text(sig_str)
   49   
   50       def calendar_month_changed(self, widget):
   51           buffer = "month_changed: %s" % self.calendar_date_to_string()
   52           self.calendar_set_signal_strings(buffer)
   53   
   54       def calendar_day_selected(self, widget):
   55           buffer = "day_selected: %s" % self.calendar_date_to_string()
   56           self.calendar_set_signal_strings(buffer)
   57   
   58       def calendar_day_selected_double_click(self, widget):
   59           buffer = "day_selected_double_click: %s"
   60           buffer = buffer % self.calendar_date_to_string()
   61           self.calendar_set_signal_strings(buffer)
   62   
   63           year, month, day = self.window.get_date()
   64   
   65           if self.marked_date[day-1] == 0:
   66               self.window.mark_day(day)
   67               self.marked_date[day-1] = 1
   68           else:
   69               self.window.unmark_day(day)
   70               self.marked_date[day-1] = 0
   71   
   72       def calendar_prev_month(self, widget):
   73           buffer = "prev_month: %s" % self.calendar_date_to_string()
   74           self.calendar_set_signal_strings(buffer)
   75   
   76       def calendar_next_month(self, widget):
   77           buffer = "next_month: %s" % self.calendar_date_to_string()
   78           self.calendar_set_signal_strings(buffer)
   79   
   80       def calendar_prev_year(self, widget):
   81           buffer = "prev_year: %s" % self.calendar_date_to_string()
   82           self.calendar_set_signal_strings(buffer)
   83   
   84       def calendar_next_year(self, widget):
   85           buffer = "next_year: %s" % self.calendar_date_to_string()
   86           self.calendar_set_signal_strings(buffer)
   87   
   88       def calendar_set_flags(self):
   89           options = 0
   90           for i in range(5):
   91               if self.settings[i]:
   92                   options = options + (1<<i)
   93           if self.window:
   94               self.window.display_options(options)
   95   
   96       def calendar_toggle_flag(self, toggle):
   97           j = 0
   98           for i in range(5):
   99               if self.flag_checkboxes[i] == toggle:
  100                   j = i
  101   
  102           self.settings[j] = not self.settings[j]
  103           self.calendar_set_flags()
  104   
  105       def calendar_font_selection_ok(self, button):
  106           self.font = self.font_dialog.get_font_name()
  107           if self.window:
  108               font = self.font_dialog.get_font()
  109               if font: 
  110                   style = self.window.get_style().copy()
  111                   style.font = font
  112                   self.window.set_style(style)
  113   
  114       def calendar_select_font(self, button):
  115           if not self.font_dialog:
  116               window = gtk.GtkFontSelectionDialog("Font Selection Dialog")
  117               self.font_dialog = window
  118       
  119               window.set_position(gtk.WIN_POS_MOUSE)
  120       
  121               window.connect("destroy", self.font_dialog_destroyed)
  122       
  123               window.ok_button.connect("clicked",
  124                                        self.calendar_font_selection_ok)
  125               window.cancel_button.connect_object("clicked",
  126                                                   self.font_dialog.destroy,
  127                                                   self.font_dialog)
  128           window = self.font_dialog
  129           if not (window.flags() & gtk.VISIBLE):
  130               window.show()
  131           else:
  132               window.destroy()
  133               self.font_dialog = None
  134   
  135       def font_dialog_destroyed(self, data=None):
  136           self.font_dialog = None
  137   
  138       def __init__(self):
  139           flags = [
  140               "Show Heading",
  141               "Show Day Names",
  142               "No Month Change",
  143               "Show Week Numbers",
  144               "Week Start Monday"
  145               ]
  146           self.window = None
  147           self.font = None
  148           self.font_dialog = None
  149           self.flag_checkboxes = 5*[None]
  150           self.settings = 5*[0]
  151           self.marked_date = 31*[0]
  152   
  153           window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
  154           window.set_title("GtkCalendar Example")
  155           window.set_border_width(5)
  156           window.connect("destroy", gtk.mainquit)
  157   
  158           window.set_policy(gtk.FALSE, gtk.FALSE, gtk.TRUE)
  159   
  160           vbox = gtk.GtkVBox(gtk.FALSE, self.DEF_PAD)
  161           window.add(vbox)
  162   
  163           # The top part of the window, Calendar, flags and fontsel.
  164           hbox = gtk.GtkHBox(gtk.FALSE, self.DEF_PAD)
  165           vbox.pack_start(hbox, gtk.TRUE, gtk.TRUE, self.DEF_PAD)
  166           hbbox = gtk.GtkHButtonBox()
  167           hbox.pack_start(hbbox, gtk.FALSE, gtk.FALSE, self.DEF_PAD)
  168           hbbox.set_layout(gtk.BUTTONBOX_SPREAD)
  169           hbbox.set_spacing(5)
  170   
  171           # Calendar widget
  172           frame = gtk.GtkFrame("Calendar")
  173           hbbox.pack_start(frame, gtk.FALSE, gtk.TRUE, self.DEF_PAD)
  174           calendar = gtk.GtkCalendar()
  175           self.window = calendar
  176           self.calendar_set_flags()
  177           calendar.mark_day(19)
  178           self.marked_date[19-1] = 1
  179           frame.add(calendar)
  180           calendar.connect("month_changed", self.calendar_month_changed)
  181           calendar.connect("day_selected", self.calendar_day_selected)
  182           calendar.connect("day_selected_double_click",
  183                            self.calendar_day_selected_double_click)
  184           calendar.connect("prev_month", self.calendar_prev_month)
  185           calendar.connect("next_month", self.calendar_next_month)
  186           calendar.connect("prev_year", self.calendar_prev_year)
  187           calendar.connect("next_year", self.calendar_next_year)
  188   
  189           separator = gtk.GtkVSeparator()
  190           hbox.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)
  191   
  192           vbox2 = gtk.GtkVBox(gtk.FALSE, self.DEF_PAD)
  193           hbox.pack_start(vbox2, gtk.FALSE, gtk.FALSE, self.DEF_PAD)
  194     
  195           # Build the Right frame with the flags in 
  196           frame = gtk.GtkFrame("Flags")
  197           vbox2.pack_start(frame, gtk.TRUE, gtk.TRUE, self.DEF_PAD)
  198           vbox3 = gtk.GtkVBox(gtk.TRUE, self.DEF_PAD_SMALL)
  199           frame.add(vbox3)
  200   
  201           for i in range(5):
  202               toggle = gtk.GtkCheckButton(flags[i])
  203               toggle.connect("toggled", self.calendar_toggle_flag)
  204               vbox3.pack_start(toggle, gtk.TRUE, gtk.TRUE, 0)
  205               self.flag_checkboxes[i] = toggle
  206   
  207           # Build the right font-button 
  208           button = gtk.GtkButton("Font...")
  209           button.connect("clicked", self.calendar_select_font)
  210           vbox2.pack_start(button, gtk.FALSE, gtk.FALSE, 0)
  211   
  212           #  Build the Signal-event part.
  213           frame = gtk.GtkFrame("Signal events")
  214           vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, self.DEF_PAD)
  215   
  216           vbox2 = gtk.GtkVBox(gtk.TRUE, self.DEF_PAD_SMALL)
  217           frame.add(vbox2)
  218     
  219           hbox = gtk.GtkHBox (gtk.FALSE, 3)
  220           vbox2.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)
  221           label = gtk.GtkLabel("Signal:")
  222           hbox.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
  223           self.last_sig = gtk.GtkLabel("")
  224           hbox.pack_start(self.last_sig, gtk.FALSE, gtk.TRUE, 0)
  225   
  226           hbox = gtk.GtkHBox (gtk.FALSE, 3)
  227           vbox2.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)
  228           label = gtk.GtkLabel("Previous signal:")
  229           hbox.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
  230           self.prev_sig = gtk.GtkLabel("")
  231           hbox.pack_start(self.prev_sig, gtk.FALSE, gtk.TRUE, 0)
  232   
  233           hbox = gtk.GtkHBox (gtk.FALSE, 3)
  234           vbox2.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)
  235           label = gtk.GtkLabel("Second previous signal:")
  236           hbox.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
  237           self.prev2_sig = gtk.GtkLabel("")
  238           hbox.pack_start(self.prev2_sig, gtk.FALSE, gtk.TRUE, 0)
  239   
  240           bbox = gtk.GtkHButtonBox ()
  241           vbox.pack_start(bbox, gtk.FALSE, gtk.FALSE, 0)
  242           bbox.set_layout(gtk.BUTTONBOX_END)
  243   
  244           button = gtk.GtkButton("Close")
  245           button.connect("clicked", gtk.mainquit)
  246           bbox.add(button)
  247           button.set_flags(gtk.CAN_DEFAULT)
  248           button.grab_default()
  249   
  250           window.show_all()
  251   
  252   def main():
  253       gtk.mainloop()
  254       return 0
  255   
  256   if __name__ == "__main__":
  257       CalendarExample()
  258       main()