1""" 2 SimpleGladeApp.py 3 Module that provides an object oriented abstraction to pygtk and libglade. 4 Copyright (C) 2004 Sandino Flores Moreno 5""" 6 7# This library is free software; you can redistribute it and/or 8# modify it under the terms of the GNU Lesser General Public 9# License as published by the Free Software Foundation; either 10# version 2.1 of the License, or (at your option) any later version. 11# 12# This library is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15# Lesser General Public License for more details. 16# 17# You should have received a copy of the GNU Lesser General Public 18# License along with this library; if not, write to the Free Software 19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 20# USA 21 22import os 23import sys 24import re 25 26import tokenize 27import gtk 28import gtk.glade 29import weakref 30import inspect 31 32__version__ = "1.0" 33__author__ = 'Sandino "tigrux" Flores-Moreno' 34 35def bindtextdomain(app_name, locale_dir=None): 36 """ 37 Bind the domain represented by app_name to the locale directory locale_dir. 38 It has the effect of loading translations, enabling applications for different 39 languages. 40 41 app_name: 42 a domain to look for translations, tipically the name of an application. 43 44 locale_dir: 45 a directory with locales like locale_dir/lang_isocode/LC_MESSAGES/app_name.mo 46 If omitted or None, then the current binding for app_name is used. 47 """ 48 try: 49 import locale 50 import gettext 51 locale.setlocale(locale.LC_ALL, "") 52 gtk.glade.bindtextdomain(app_name, locale_dir) 53 gettext.install(app_name, locale_dir, unicode=1) 54 except (IOError,locale.Error), e: 55 print "Warning", app_name, e 56 __builtins__.__dict__["_"] = lambda x : x 57 58 59class SimpleGladeApp: 60 61 def __init__(self, path, root=None, domain=None, **kwargs): 62 """ 63 Load a glade file specified by glade_filename, using root as 64 root widget and domain as the domain for translations. 65 66 If it receives extra named arguments (argname=value), then they are used 67 as attributes of the instance. 68 69 path: 70 path to a glade filename. 71 If glade_filename cannot be found, then it will be searched in the 72 same directory of the program (sys.argv[0]) 73 74 root: 75 the name of the widget that is the root of the user interface, 76 usually a window or dialog (a top level widget). 77 If None or ommited, the full user interface is loaded. 78 79 domain: 80 A domain to use for loading translations. 81 If None or ommited, no translation is loaded. 82 83 **kwargs: 84 a dictionary representing the named extra arguments. 85 It is useful to set attributes of new instances, for example: 86 glade_app = SimpleGladeApp("ui.glade", foo="some value", bar="another value") 87 sets two attributes (foo and bar) to glade_app. 88 """ 89 if os.path.isfile(path): 90 self.glade_path = path 91 else: 92 glade_dir = os.path.dirname( sys.argv[0] ) 93 self.glade_path = os.path.join(glade_dir, path) 94 for key, value in kwargs.items(): 95 try: 96 setattr(self, key, weakref.proxy(value) ) 97 except TypeError: 98 setattr(self, key, value) 99 self.glade = None 100 self.install_custom_handler(self.custom_handler) 101 self.glade = self.create_glade(self.glade_path, root, domain) 102 if root: 103 self.main_widget = self.get_widget(root) 104 else: 105 self.main_widget = None 106 self.normalize_names() 107 self.add_callbacks(self) 108 self.new() 109 110 def __repr__(self): 111 class_name = self.__class__.__name__ 112 if self.main_widget: 113 root = gtk.Widget.get_name(self.main_widget) 114 repr = '%s(path="%s", root="%s")' % (class_name, self.glade_path, root) 115 else: 116 repr = '%s(path="%s")' % (class_name, self.glade_path) 117 return repr 118 119 def new(self): 120 """ 121 Method called when the user interface is loaded and ready to be used. 122 At this moment, the widgets are loaded and can be refered as self.widget_name 123 """ 124 pass 125 126 def add_callbacks(self, callbacks_proxy): 127 """ 128 It uses the methods of callbacks_proxy as callbacks. 129 The callbacks are specified by using: 130 Properties window -> Signals tab 131 in glade-2 (or any other gui designer like gazpacho). 132 133 Methods of classes inheriting from SimpleGladeApp are used as 134 callbacks automatically. 135 136 callbacks_proxy: 137 an instance with methods as code of callbacks. 138 It means it has methods like on_button1_clicked, on_entry1_activate, etc. 139 """ 140 self.glade.signal_autoconnect(callbacks_proxy) 141 142 def normalize_names(self): 143 """ 144 It is internally used to normalize the name of the widgets. 145 It means a widget named foo:vbox-dialog in glade 146 is refered self.vbox_dialog in the code. 147 148 It also sets a data "prefixes" with the list of 149 prefixes a widget has for each widget. 150 """ 151 for widget in self.get_widgets(): 152 widget_name = gtk.Widget.get_name(widget) 153 prefixes_name_l = widget_name.split(":") 154 prefixes = prefixes_name_l[ : -1] 155 widget_api_name = prefixes_name_l[-1] 156 widget_api_name = "_".join( re.findall(tokenize.Name, widget_api_name) ) 157 gtk.Widget.set_name(widget, widget_api_name) 158 if hasattr(self, widget_api_name): 159 raise AttributeError("instance %s already has an attribute %s" % (self,widget_api_name)) 160 else: 161 setattr(self, widget_api_name, widget) 162 if prefixes: 163 gtk.Widget.set_data(widget, "prefixes", prefixes) 164 165 def add_prefix_actions(self, prefix_actions_proxy): 166 """ 167 By using a gui designer (glade-2, gazpacho, etc) 168 widgets can have a prefix in theirs names 169 like foo:entry1 or foo:label3 170 It means entry1 and label3 has a prefix action named foo. 171 172 Then, prefix_actions_proxy must have a method named prefix_foo which 173 is called everytime a widget with prefix foo is found, using the found widget 174 as argument. 175 176 prefix_actions_proxy: 177 An instance with methods as prefix actions. 178 It means it has methods like prefix_foo, prefix_bar, etc. 179 """ 180 prefix_s = "prefix_" 181 prefix_pos = len(prefix_s) 182 183 is_method = lambda t : callable( t[1] ) 184 is_prefix_action = lambda t : t[0].startswith(prefix_s) 185 drop_prefix = lambda (k,w): (k[prefix_pos:],w) 186 187 members_t = inspect.getmembers(prefix_actions_proxy) 188 methods_t = filter(is_method, members_t) 189 prefix_actions_t = filter(is_prefix_action, methods_t) 190 prefix_actions_d = dict( map(drop_prefix, prefix_actions_t) ) 191 192 for widget in self.get_widgets(): 193 prefixes = gtk.Widget.get_data(widget, "prefixes") 194 if prefixes: 195 for prefix in prefixes: 196 if prefix in prefix_actions_d: 197 prefix_action = prefix_actions_d[prefix] 198 prefix_action(widget) 199 200 def custom_handler(self, 201 glade, function_name, widget_name, 202 str1, str2, int1, int2): 203 """ 204 Generic handler for creating custom widgets, internally used to 205 enable custom widgets (custom widgets of glade). 206 207 The custom widgets have a creation function specified in design time. 208 Those creation functions are always called with str1,str2,int1,int2 as 209 arguments, that are values specified in design time. 210 211 Methods of classes inheriting from SimpleGladeApp are used as 212 creation functions automatically. 213 214 If a custom widget has create_foo as creation function, then the 215 method named create_foo is called with str1,str2,int1,int2 as arguments. 216 """ 217 try: 218 handler = getattr(self, function_name) 219 return handler(str1, str2, int1, int2) 220 except AttributeError: 221 return None 222 223 def gtk_widget_show(self, widget, *args): 224 """ 225 Predefined callback. 226 The widget is showed. 227 Equivalent to widget.show() 228 """ 229 widget.show() 230 231 def gtk_widget_hide(self, widget, *args): 232 """ 233 Predefined callback. 234 The widget is hidden. 235 Equivalent to widget.hide() 236 """ 237 widget.hide() 238 239 def gtk_widget_grab_focus(self, widget, *args): 240 """ 241 Predefined callback. 242 The widget grabs the focus. 243 Equivalent to widget.grab_focus() 244 """ 245 widget.grab_focus() 246 247 def gtk_widget_destroy(self, widget, *args): 248 """ 249 Predefined callback. 250 The widget is destroyed. 251 Equivalent to widget.destroy() 252 """ 253 widget.destroy() 254 255 def gtk_window_activate_default(self, window, *args): 256 """ 257 Predefined callback. 258 The default widget of the window is activated. 259 Equivalent to window.activate_default() 260 """ 261 widget.activate_default() 262 263 def gtk_true(self, *args): 264 """ 265 Predefined callback. 266 Equivalent to return True in a callback. 267 Useful for stopping propagation of signals. 268 """ 269 return True 270 271 def gtk_false(self, *args): 272 """ 273 Predefined callback. 274 Equivalent to return False in a callback. 275 """ 276 return False 277 278 def gtk_main_quit(self, *args): 279 """ 280 Predefined callback. 281 Equivalent to self.quit() 282 """ 283 self.quit() 284 285 def main(self): 286 """ 287 Starts the main loop of processing events. 288 The default implementation calls gtk.main() 289 290 Useful for applications that needs a non gtk main loop. 291 For example, applications based on gstreamer needs to override 292 this method with gst.main() 293 294 Do not directly call this method in your programs. 295 Use the method run() instead. 296 """ 297 gtk.main() 298 299 def quit(self): 300 """ 301 Quit processing events. 302 The default implementation calls gtk.main_quit() 303 304 Useful for applications that needs a non gtk main loop. 305 For example, applications based on gstreamer needs to override 306 this method with gst.main_quit() 307 """ 308 gtk.main_quit() 309 310 def run(self): 311 """ 312 Starts the main loop of processing events checking for Control-C. 313 314 The default implementation checks wheter a Control-C is pressed, 315 then calls on_keyboard_interrupt(). 316 317 Use this method for starting programs. 318 """ 319 try: 320 self.main() 321 except KeyboardInterrupt: 322 self.on_keyboard_interrupt() 323 324 def on_keyboard_interrupt(self): 325 """ 326 This method is called by the default implementation of run() 327 after a program is finished by pressing Control-C. 328 """ 329 pass 330 331 def install_custom_handler(self, custom_handler): 332 gtk.glade.set_custom_handler(custom_handler) 333 334 def create_glade(self, glade_path, root, domain): 335 return gtk.glade.XML(self.glade_path, root, domain) 336 337 def get_widget(self, widget_name): 338 return self.glade.get_widget(widget_name) 339 340 def get_widgets(self): 341 return self.glade.get_widget_prefix("") 342