1490c3d1d7db1f66c54db25822161fdab141b723
[quicknote] / src / gtk_toolbox.py
1 #!/usr/bin/python
2
3 from __future__ import with_statement
4
5 import sys
6 import traceback
7 import functools
8 import contextlib
9 import warnings
10
11 import gobject
12 import gtk
13
14
15 @contextlib.contextmanager
16 def gtk_lock():
17         gtk.gdk.threads_enter()
18         try:
19                 yield
20         finally:
21                 gtk.gdk.threads_leave()
22
23
24 def find_parent_window(widget):
25         while True:
26                 parent = widget.get_parent()
27                 if isinstance(parent, gtk.Window):
28                         return parent
29                 widget = parent
30
31
32 def make_idler(func):
33         """
34         Decorator that makes a generator-function into a function that will continue execution on next call
35         """
36         a = []
37
38         @functools.wraps(func)
39         def decorated_func(*args, **kwds):
40                 if not a:
41                         a.append(func(*args, **kwds))
42                 try:
43                         a[0].next()
44                         return True
45                 except StopIteration:
46                         del a[:]
47                         return False
48
49         return decorated_func
50
51
52 def asynchronous_gtk_message(original_func):
53         """
54         @note Idea came from http://www.aclevername.com/articles/python-webgui/
55         """
56
57         def execute(allArgs):
58                 args, kwargs = allArgs
59                 with gtk_lock():
60                         original_func(*args, **kwargs)
61                 return False
62
63         @functools.wraps(original_func)
64         def delayed_func(*args, **kwargs):
65                 gobject.idle_add(execute, (args, kwargs))
66
67         return delayed_func
68
69
70 def synchronous_gtk_message(original_func):
71         """
72         @note Idea came from http://www.aclevername.com/articles/python-webgui/
73         """
74
75         @functools.wraps(original_func)
76         def immediate_func(*args, **kwargs):
77                 with gtk_lock():
78                         return original_func(*args, **kwargs)
79
80         return immediate_func
81
82
83 class ErrorDisplay(object):
84
85         def __init__(self, widgetTree):
86                 super(ErrorDisplay, self).__init__()
87                 self.__errorBox = widgetTree.get_widget("errorEventBox")
88                 self.__errorDescription = widgetTree.get_widget("errorDescription")
89                 self.__errorClose = widgetTree.get_widget("errorClose")
90                 self.__parentBox = self.__errorBox.get_parent()
91
92                 self.__errorBox.connect("button_release_event", self._on_close)
93
94                 self.__messages = []
95                 self.__parentBox.remove(self.__errorBox)
96
97         def push_message_with_lock(self, message):
98                 with gtk_lock():
99                         self.push_message(message)
100
101         def push_message(self, message):
102                 if 0 < len(self.__messages):
103                         self.__messages.append(message)
104                 else:
105                         self.__show_message(message)
106
107         def push_exception_with_lock(self, exception = None):
108                 with gtk_lock():
109                         self.push_exception(exception)
110
111         def push_exception(self, exception = None):
112                 if exception is None:
113                         userMessage = str(sys.exc_value)
114                         warningMessage = str(traceback.format_exc())
115                 else:
116                         userMessage = str(exception)
117                         warningMessage = str(exception)
118                 self.push_message(userMessage)
119                 warnings.warn(warningMessage, stacklevel=3)
120
121         def pop_message(self):
122                 if 0 < len(self.__messages):
123                         self.__show_message(self.__messages[0])
124                         del self.__messages[0]
125                 else:
126                         self.__hide_message()
127
128         def _on_close(self, *args):
129                 self.pop_message()
130
131         def __show_message(self, message):
132                 self.__errorDescription.set_text(message)
133                 self.__parentBox.pack_start(self.__errorBox, False, False)
134                 self.__parentBox.reorder_child(self.__errorBox, 1)
135
136         def __hide_message(self):
137                 self.__errorDescription.set_text("")
138                 self.__parentBox.remove(self.__errorBox)
139
140
141 class DummyErrorDisplay(object):
142
143         def __init__(self):
144                 super(DummyErrorDisplay, self).__init__()
145
146                 self.__messages = []
147
148         def push_message_with_lock(self, message):
149                 self.push_message(message)
150
151         def push_message(self, message):
152                 if 0 < len(self.__messages):
153                         self.__messages.append(message)
154                 else:
155                         self.__show_message(message)
156
157         def push_exception(self, exception = None):
158                 if exception is None:
159                         warningMessage = traceback.format_exc()
160                 else:
161                         warningMessage = exception
162                 warnings.warn(warningMessage, stacklevel=3)
163
164         def pop_message(self):
165                 if 0 < len(self.__messages):
166                         self.__show_message(self.__messages[0])
167                         del self.__messages[0]
168
169         def __show_message(self, message):
170                 warnings.warn(message, stacklevel=2)
171
172
173 class MessageBox(gtk.MessageDialog):
174
175         def __init__(self, message):
176                 parent = None
177                 gtk.MessageDialog.__init__(
178                         self,
179                         parent,
180                         gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
181                         gtk.MESSAGE_ERROR,
182                         gtk.BUTTONS_OK,
183                         message,
184                 )
185                 self.set_default_response(gtk.RESPONSE_OK)
186                 self.connect('response', self._handle_clicked)
187
188         def _handle_clicked(self, *args):
189                 self.destroy()
190
191
192 class MessageBox2(gtk.MessageDialog):
193
194         def __init__(self, message):
195                 parent = None
196                 gtk.MessageDialog.__init__(
197                         self,
198                         parent,
199                         gtk.DIALOG_DESTROY_WITH_PARENT,
200                         gtk.MESSAGE_ERROR,
201                         gtk.BUTTONS_OK,
202                         message,
203                 )
204                 self.set_default_response(gtk.RESPONSE_OK)
205                 self.connect('response', self._handle_clicked)
206
207         def _handle_clicked(self, *args):
208                 self.destroy()
209
210
211 class PopupCalendar(object):
212
213         def __init__(self, parent, displayDate, title = ""):
214                 self._displayDate = displayDate
215
216                 self._calendar = gtk.Calendar()
217                 self._calendar.select_month(self._displayDate.month, self._displayDate.year)
218                 self._calendar.select_day(self._displayDate.day)
219                 self._calendar.set_display_options(
220                         gtk.CALENDAR_SHOW_HEADING |
221                         gtk.CALENDAR_SHOW_DAY_NAMES |
222                         gtk.CALENDAR_NO_MONTH_CHANGE |
223                         0
224                 )
225                 self._calendar.connect("day-selected", self._on_day_selected)
226
227                 self._popupWindow = gtk.Window()
228                 self._popupWindow.set_title(title)
229                 self._popupWindow.add(self._calendar)
230                 self._popupWindow.set_transient_for(parent)
231                 self._popupWindow.set_modal(True)
232                 self._popupWindow.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
233                 self._popupWindow.set_skip_pager_hint(True)
234                 self._popupWindow.set_skip_taskbar_hint(True)
235
236         def run(self):
237                 self._popupWindow.show_all()
238
239         def _on_day_selected(self, *args):
240                 try:
241                         self._calendar.select_month(self._displayDate.month, self._displayDate.year)
242                         self._calendar.select_day(self._displayDate.day)
243                 except StandardError, e:
244                         warnings.warn(e.message)
245
246
247 if __name__ == "__main__":
248         if False:
249                 import datetime
250                 cal = PopupCalendar(None, datetime.datetime.now())
251                 cal._popupWindow.connect("destroy", lambda w: gtk.main_quit())
252                 cal.run()
253
254         gtk.main()