9.12. Календарь

Виджет Calendar это эффективный способ для отображения или получения дат. Он очень прост в создании и работе.

Создание виджета gtk.Calendar также просто как:

  calendar = gtk.Calendar()

По умолчанию календарь будет отображать текущий месяц и год.

Случается, что вам нужно изменить большое количество информации в этом виджете, и следующие методы позволяют делать множественные изменения в виджете Calendar, без мерцания обновлений на экране.

  calendar.freeze()

calendar.thaw()

Они работают также как методы freeze/thaw на любом другом виджете.

Виджет Calendar имеет несколько настроек, которые позволяют изменить внешний вид виджета, управляются они с помощью метода:

  calendar.display_options(flags)

Аргумент flags может быть создан смешиванием следующих пяти опций с помощью операции логического сложения OR (|):

CALENDAR_SHOW_HEADING Эта опция указывает что месяц и год должны быть отображены на календаре.
CALENDAR_SHOW_DAY_NAMES Эта опция указывает что необходимо отображать трёхбуквенные названия дней недели (Пнд, Втр, Ср и т.д.)
CALENDAR_NO_MONTH_CHANGE Эта опция задаёт что пользователь не должен и не может изменять отображаемый месяц. Это может быть полезно когда вам нужно отобразить определённый месяц, так, если нужно отобразить 12 календарей на каждый месяц определённого года.
CALENDAR_SHOW_WEEK_NUMBERS Эта опция указывает что номер каждой недели должен быть показан слева от календаря. (так 1 Янв = 1 неделя, 31 Дек =  52 неделя).
CALENDAR_WEEK_START_MONDAY Эта опция задаёт начало календарной недели с понедельника, а не с воскресенья как выбрано по умолчанию. Это влияет только на порядок в котором дни отображаются слева направо. Заметим, что в PyGTK 2.4 и выше эта опция считается устаревшей.

Следующие методы используются для установки отображаемой даты:

  result = calendar.select_month(month, year)

calendar.select_day(day)

Логическое значение возвращаемое после метода select_month() показывает успешность выбора.

С методом select_day() указывается номер дня в текущем месяце, если это возможно. День 0 снимает любой текущий выбор.

В дополнение к выбору, любое число дней в месяце могут быть "отмечены". Отмеченный день выделяется подсветкой. Следующие методы предоставлены для управления отмеченными днями:

  result = calendar.mark_day(day)

result = calendar.unmark_day(day)

calendar.clear_marks()

mark_day() и unmark_day() возвращают логическое значение, показывающее успешность выполнения. Заметим что метки сохраняются при изменении месяца или года.

Последний метод виджета Calendar используется для получения текущей выбранной даты, месяца и/или года.

  year, month, day = calendar.get_date()

Виджет Calendar может генерировать несколько сигналов означающих выбор даты и её изменение. Названия говорят сами за себя, вот они:

  month_changed

day_selected

day_selected_double_click

prev_month

next_month

prev_year

next_year

Остаётся нам только поместить все это в наш пример calendar.pyРисунок 9.12, “Пример календаря” показывает интерфейс программы:

Рисунок 9.12. Пример календаря

Calendar Example

Исходный код calendar.py:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # example calendar.py
  4. #
  5. # Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias Gronlund
  6. # Copyright (C) 2000 Tony Gale
  7. # Copyright (C) 2001-2004 John Finlay
  8. # Copyright (C) 2011 Sergey Zavgorodniy, Grigoriy Kramarenko
  9. #
  10. # This program is free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation; either version 2 of the License, or
  13. # (at your option) any later version.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with this program; if not, write to the Free Software
  22. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. import pygtk
  25. pygtk.require('2.0')
  26. import gtk, pango
  27. import time
  28.  
  29. class CalendarExample:
  30. DEF_PAD = 10
  31. DEF_PAD_SMALL = 5
  32. TM_YEAR_BASE = 1900
  33.  
  34. calendar_show_header = 0
  35. calendar_show_days = 1
  36. calendar_month_change = 2
  37. calendar_show_week = 3
  38.  
  39. def calendar_date_to_string(self):
  40. year, month, day = self.window.get_date()
  41. mytime = time.mktime((year, month+1, day, 0, 0, 0, 0, 0, -1))
  42. return time.strftime("%x", time.localtime(mytime))
  43.  
  44. def calendar_set_signal_strings(self, sig_str):
  45. prev_sig = self.prev_sig.get()
  46. self.prev2_sig.set_text(prev_sig)
  47.  
  48. prev_sig = self.last_sig.get()
  49. self.prev_sig.set_text(prev_sig)
  50. self.last_sig.set_text(sig_str)
  51.  
  52. def calendar_month_changed(self, widget):
  53. buffer = "month_changed: %s" % self.calendar_date_to_string()
  54. self.calendar_set_signal_strings(buffer)
  55.  
  56. def calendar_day_selected(self, widget):
  57. buffer = "day_selected: %s" % self.calendar_date_to_string()
  58. self.calendar_set_signal_strings(buffer)
  59.  
  60. def calendar_day_selected_double_click(self, widget):
  61. buffer = "day_selected_double_click: %s"
  62. buffer = buffer % self.calendar_date_to_string()
  63. self.calendar_set_signal_strings(buffer)
  64.  
  65. year, month, day = self.window.get_date()
  66.  
  67. if self.marked_date[day-1] == 0:
  68. self.window.mark_day(day)
  69. self.marked_date[day-1] = 1
  70. else:
  71. self.window.unmark_day(day)
  72. self.marked_date[day-1] = 0
  73.  
  74. def calendar_prev_month(self, widget):
  75. buffer = "prev_month: %s" % self.calendar_date_to_string()
  76. self.calendar_set_signal_strings(buffer)
  77.  
  78. def calendar_next_month(self, widget):
  79. buffer = "next_month: %s" % self.calendar_date_to_string()
  80. self.calendar_set_signal_strings(buffer)
  81.  
  82. def calendar_prev_year(self, widget):
  83. buffer = "prev_year: %s" % self.calendar_date_to_string()
  84. self.calendar_set_signal_strings(buffer)
  85.  
  86. def calendar_next_year(self, widget):
  87. buffer = "next_year: %s" % self.calendar_date_to_string()
  88. self.calendar_set_signal_strings(buffer)
  89.  
  90. def calendar_set_flags(self):
  91. options = 0
  92. for i in range(5):
  93. if self.settings[i]:
  94. options = options + (1<<i)
  95. if self.window:
  96. self.window.display_options(options)
  97.  
  98. def calendar_toggle_flag(self, toggle):
  99. j = 0
  100. for i in range(5):
  101. if self.flag_checkboxes[i] == toggle:
  102. j = i
  103.  
  104. self.settings[j] = not self.settings[j]
  105. self.calendar_set_flags()
  106.  
  107. def calendar_font_selection_ok(self, button):
  108. self.font = self.font_dialog.get_font_name()
  109. if self.window:
  110. font_desc = pango.FontDescription(self.font)
  111. if font_desc:
  112. self.window.modify_font(font_desc)
  113.  
  114. def calendar_select_font(self, button):
  115. if not self.font_dialog:
  116. window = gtk.FontSelectionDialog("Диалог выбора шрифта")
  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. lambda wid: wid.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. "Показать заголовок",
  141. "Показать название дня",
  142. "Не изменять месяц",
  143. "Показать номера недель",
  144. ]
  145. self.window = None
  146. self.font = None
  147. self.font_dialog = None
  148. self.flag_checkboxes = 5*[None]
  149. self.settings = 5*[0]
  150. self.marked_date = 31*[0]
  151.  
  152. window = gtk.Window(gtk.WINDOW_TOPLEVEL)
  153. window.set_title("Пример календаря")
  154. window.set_border_width(5)
  155. window.connect("destroy", lambda x: gtk.main_quit())
  156.  
  157. window.set_resizable(False)
  158.  
  159. vbox = gtk.VBox(False, self.DEF_PAD)
  160. window.add(vbox)
  161.  
  162. # Верхняя часть окна, календарь, флаги и выбор шрифта.
  163. hbox = gtk.HBox(False, self.DEF_PAD)
  164. vbox.pack_start(hbox, True, True, self.DEF_PAD)
  165. hbbox = gtk.HButtonBox()
  166. hbox.pack_start(hbbox, False, False, self.DEF_PAD)
  167. hbbox.set_layout(gtk.BUTTONBOX_SPREAD)
  168. hbbox.set_spacing(5)
  169.  
  170. # Календарь
  171. frame = gtk.Frame("Календарь")
  172. hbbox.pack_start(frame, False, True, self.DEF_PAD)
  173. calendar = gtk.Calendar()
  174. self.window = calendar
  175. self.calendar_set_flags()
  176. calendar.mark_day(19)
  177. self.marked_date[19-1] = 1
  178. frame.add(calendar)
  179. calendar.connect("month_changed", self.calendar_month_changed)
  180. calendar.connect("day_selected", self.calendar_day_selected)
  181. calendar.connect("day_selected_double_click",
  182. self.calendar_day_selected_double_click)
  183. calendar.connect("prev_month", self.calendar_prev_month)
  184. calendar.connect("next_month", self.calendar_next_month)
  185. calendar.connect("prev_year", self.calendar_prev_year)
  186. calendar.connect("next_year", self.calendar_next_year)
  187.  
  188. separator = gtk.VSeparator()
  189. hbox.pack_start(separator, False, True, 0)
  190.  
  191. vbox2 = gtk.VBox(False, self.DEF_PAD)
  192. hbox.pack_start(vbox2, False, False, self.DEF_PAD)
  193.  
  194. # Создаём рамку справа, с флагами внутри
  195. frame = gtk.Frame("Флаги")
  196. vbox2.pack_start(frame, True, True, self.DEF_PAD)
  197. vbox3 = gtk.VBox(True, self.DEF_PAD_SMALL)
  198. frame.add(vbox3)
  199.  
  200. for i in range(len(flags)):
  201. toggle = gtk.CheckButton(flags[i])
  202. toggle.connect("toggled", self.calendar_toggle_flag)
  203. vbox3.pack_start(toggle, True, True, 0)
  204. self.flag_checkboxes[i] = toggle
  205.  
  206. # Создаём кнопку выбора шрифта
  207. button = gtk.Button("Шрифт...")
  208. button.connect("clicked", self.calendar_select_font)
  209. vbox2.pack_start(button, False, False, 0)
  210.  
  211. # Создаём часть сигнал-событие.
  212. frame = gtk.Frame("События сигналов")
  213. vbox.pack_start(frame, True, True, self.DEF_PAD)
  214.  
  215. vbox2 = gtk.VBox(True, self.DEF_PAD_SMALL)
  216. frame.add(vbox2)
  217.  
  218. hbox = gtk.HBox (False, 3)
  219. vbox2.pack_start(hbox, False, True, 0)
  220. label = gtk.Label("Сигнал:")
  221. hbox.pack_start(label, False, True, 0)
  222. self.last_sig = gtk.Label("")
  223. hbox.pack_start(self.last_sig, False, True, 0)
  224.  
  225. hbox = gtk.HBox (False, 3)
  226. vbox2.pack_start(hbox, False, True, 0)
  227. label = gtk.Label("Предыдущий сигнал:")
  228. hbox.pack_start(label, False, True, 0)
  229. self.prev_sig = gtk.Label("")
  230. hbox.pack_start(self.prev_sig, False, True, 0)
  231.  
  232. hbox = gtk.HBox (False, 3)
  233. vbox2.pack_start(hbox, False, True, 0)
  234. label = gtk.Label("Второй предыдущий сигнал:")
  235. hbox.pack_start(label, False, True, 0)
  236. self.prev2_sig = gtk.Label("")
  237. hbox.pack_start(self.prev2_sig, False, True, 0)
  238.  
  239. bbox = gtk.HButtonBox ()
  240. vbox.pack_start(bbox, False, False, 0)
  241. bbox.set_layout(gtk.BUTTONBOX_END)
  242.  
  243. button = gtk.Button(gtk.STOCK_CLOSE, gtk.STOCK_CLOSE)
  244. button.connect("clicked", lambda w: gtk.main_quit())
  245. bbox.add(button)
  246. button.set_flags(gtk.CAN_DEFAULT)
  247. button.grab_default()
  248.  
  249. window.show_all()
  250.  
  251. def main():
  252. gtk.main()
  253. return 0
  254.  
  255. if __name__ == "__main__":
  256. CalendarExample()
  257. main()
  258.