Module eagle
[hide private]
[frames] | no frames]

Source Code for Module eagle

   1  #!/usr/bin/env python2 
   2  # -*- coding: utf-8 -*- 
   3   
   4  # Copyright (C) 2005 by Gustavo Sverzut Barbieri 
   5  # 
   6  # This program is free software; you can redistribute it and/or 
   7  # modify it under the terms of the GNU Lesser General Public License 
   8  # as published by the Free Software Foundation; either version 2 
   9  # of the License, or (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, 
  12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  14  # GNU General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU Lesser General Public License 
  17  # along with this program; if not, write to the Free Software 
  18  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  19   
  20  # Changed: $LastChangedBy: gustavo $ at $LastChangedDate: 2006-04-24 14:33:33 -0300 (Seg, 24 Abr 2006) $ 
  21   
  22  __author__ = "Gustavo Sverzut Barbieri" 
  23  __author_email__ = "barbieri@gmail.com" 
  24  __license__ = "LGPL" 
  25  __url__ = "http://www.gustavobarbieri.com.br/eagle/" 
  26  __version__ = "0.2" 
  27  __revision__ = "$Rev: 20 $" 
  28  __description__ = """\ 
  29  Eagle is an abstraction layer atop Graphical Toolkits focused on 
  30  making simple applications easy to build while powerful in features. 
  31  """ 
  32  __long_description__ = """\ 
  33  Eagle is an abstraction layer atop Graphical Toolkits focused on 
  34  making simple applications easy to build while powerful in features. 
  35   
  36  With Eagle you have many facilities to build application that needs 
  37  just some buttons, user input and a canvas to draw. 
  38   
  39  Canvas is really simple, what makes Eagle a great solution to 
  40  Computer Graphics and Image Processing software, the primary focus 
  41  of this library. 
  42   
  43  User input widgets are persistent, you just need to mark them 
  44  "persistent" or put them in the preferences area. 
  45   
  46  Eagle is not meant to be another Graphical Toolkit, you already 
  47  have a bunch of them, like Qt, Gtk, wxWidgets (to name just a few). 
  48  It's focused on applications that have few windows, with buttons, 
  49  simple user input and canvas. Widgets are laid out automatically 
  50  in 5 areas: left, right, top, bottom and center. 
  51   
  52  It provides useful widgets like: Color selector, Font selector, 
  53  Quit button, Preferences button and bialog, About dialog and Help 
  54  dialog. 
  55  """ 
  56  __doc__ = __long_description__ 
  57   
  58  __all__ = [ 
  59      "run", "quit", "get_value", "set_value", 
  60      "get_app_by_id", "get_widget_by_id", 
  61      "show", "hide", "set_active", "set_inactive", "close", 
  62      "App", 
  63      "Entry", "Password", 
  64      "Spin", "IntSpin", "UIntSpin", 
  65      "CheckBox", 
  66      "Progress", 
  67      "Color", "Font", 
  68      "Button", "AboutButton", "CloseButton", "QuitButton", "HelpButton", 
  69      "OpenFileButton", "SelectFolderButton", "SaveFileButton", 
  70      "PreferencesButton", 
  71      "Selection", 
  72      "Group", "Table", 
  73      "HSeparator", "VSeparator", 
  74      "Label", 
  75      "Canvas", "Image", 
  76      "information", "info", "error", "err", "warning", "warn", 
  77      "yesno", "confirm", 
  78      "AboutDialog", "HelpDialog", "FileChooser", 
  79      ] 
  80   
  81  import os 
  82  import sys 
  83  import gc 
  84  import cPickle as pickle 
  85   
  86  try: 
  87      import pygtk 
  88      pygtk.require( "2.0" ) 
  89      import gtk 
  90      import pango 
  91      import gobject 
  92  except ImportError, e: 
  93      sys.stderr.writelines( 
  94          ( "Missing module: ", str( e ), "\n", 
  95            "This module is part of pygtk (http://pygtk.org).\n", 
  96            ) ) 
  97      sys.exit( -1 ) 
  98   
  99  try: 
 100      import hildon 
 101  except ImportError, e: 
 102      sys.stderr.writelines( 
 103          ( "Missing module: ", str( e ), "\n", 
 104            "This module is part of pymaemo (http://pymaemo.sf.net).\n", 
 105            "HINT: Are you trying to use this on your desktop? If so use" 
 106            "eagle-gtk instead!\n" 
 107            ) ) 
 108      sys.exit( -1 ) 
 109   
 110  required_gtk = ( 2, 6, 0 ) 
 111  m = gtk.check_version( *required_gtk ) 
 112  if m: 
 113      sys.stderr.writelines( 
 114          ( "Error checking GTK version: %s\n" 
 115            "This system requires pygtk >= %s, you have %s installed.\n" ) 
 116          % ( m, 
 117              ".".join( [ str( v ) for v in required_gtk ] ), 
 118              ".".join( [ str( v ) for v in gtk.pygtk_version ] ) 
 119              ) ) 
 120      sys.exit( -1 ) 
 121   
 122  gtk.gdk.threads_init() # enable threads 
 123   
 124  _apps = {} 
 125   
 126   
127 -def _gen_ro_property( name, doc="" ):
128 """Generates a Read-Only property. 129 130 The generated property can be assigned only one value, if this value 131 is not None, it cannot be changed. 132 """ 133 naming = "__ro_%s__" % ( name, )
134 - def get( self ):
135 try: 136 return getattr( self, naming ) 137 except AttributeError: 138 return None
139 # get()
140 - def set( self, value ):
141 try: 142 v = getattr( self, naming ) 143 except AttributeError: 144 v = None 145 if v is None: 146 setattr( self, naming, value ) 147 else: 148 raise Exception( "Read Only property '%s'." % ( name, ) )
149 # set() 150 return property( get, set, None, doc )
151 # _gen_ro_property() 152 153
154 -def _callback_tuple( callback ):
155 if not isinstance( callback, ( tuple, list ) ): 156 if callback is None: 157 return tuple() 158 elif callable( callback ): 159 return ( callback, ) 160 else: 161 raise TypeError( "Callback '%s' is not callable!" % ( callback, ) ) 162 else: 163 for c in callback: 164 if not callable( c ): 165 raise TypeError( "Callback '%s' is not callable!" % ( c, ) ) 166 return callback
167 # _callback_tuple() 168 169
170 -def _str_tuple( string ):
171 if not isinstance( string, ( tuple, list ) ): 172 if string is None: 173 return tuple() 174 else: 175 return ( str( string ), ) 176 else: 177 return tuple( [ str( s ) for s in string ] )
178 # _str_tuple() 179 180
181 -def _obj_tuple( obj ):
182 if not isinstance( obj, ( tuple, list ) ): 183 if obj is None: 184 return tuple() 185 else: 186 return ( obj, ) 187 else: 188 return tuple( obj )
189 # _obj_tuple() 190 191
192 -def _set_icon_list( gtkwidget, stock_id ):
193 style = gtkwidget.get_style() 194 iconset = style.lookup_icon_set( stock_id ) 195 if iconset: 196 icons = [] 197 for s in iconset.get_sizes(): 198 i = iconset.render_icon( style, 199 gtk.TEXT_DIR_NONE, 200 gtk.STATE_NORMAL, 201 s, 202 gtkwidget, 203 None 204 ) 205 icons.append( i ) 206 gtkwidget.set_icon_list( *icons )
207 # _set_icon_list() 208 209
210 -class _Table( gtk.Table ):
211 """Internal widget to arrange components in tabular form. 212 213 @warning: never use it directly in Eagle applications! 214 """ 215 216 padding = 3 217 id = _gen_ro_property( "id" ) 218 children = _gen_ro_property( "children" ) 219 horizontal = _gen_ro_property( "horizontal" ) 220 221
222 - def __init__( self, id, children, horizontal=False ):
223 self.id = id 224 self.horizontal = horizontal 225 self.children = _obj_tuple( children ) 226 227 gtk.Table.__init__( self ) 228 229 self.set_name( id ) 230 self.set_homogeneous( False ) 231 self.__setup_gui__()
232 # __init__() 233 234
235 - def __setup_gui__( self ):
236 """Lay out components in a horizontal or vertical table.""" 237 if not self.children: 238 return 239 240 n = len( self.children ) 241 242 if self.horizontal: 243 self.resize( 2, n ) 244 else: 245 self.resize( n, 2 ) 246 247 for idx, c in enumerate( self.children ): 248 w = c.__get_widgets__() 249 xrm, yrm = c.__get_resize_mode__() 250 251 if len( w ) == 1: 252 if self.horizontal: 253 row0 = 0 254 row1 = 2 255 col0 = idx 256 col1 = idx + 1 257 else: 258 row0 = idx 259 row1 = idx + 1 260 col0 = 0 261 col1 = 2 262 263 self.attach( w[ 0 ], col0, col1, row0, row1, 264 xoptions=xrm, 265 yoptions=yrm, 266 xpadding=self.padding, 267 ypadding=self.padding ) 268 269 elif len( w ) == 2: 270 if isinstance( xrm, int ): 271 xrm = ( xrm, xrm ) 272 if isinstance( yrm, int ): 273 yrm = ( yrm, yrm ) 274 275 if self.horizontal: 276 row0 = 0 277 row1 = 1 278 row2 = 1 279 row3 = 2 280 col0 = idx 281 col1 = idx + 1 282 col2 = idx 283 col3 = idx + 1 284 else: 285 row0 = idx 286 row1 = idx + 1 287 row2 = idx 288 row3 = idx + 1 289 col0 = 0 290 col1 = 1 291 col2 = 1 292 col3 = 2 293 self.attach( w[ 0 ], col0, col1, row0, row1, 294 xoptions=xrm[ 0 ], 295 yoptions=yrm[ 0 ], 296 xpadding=self.padding, 297 ypadding=self.padding ) 298 self.attach( w[ 1 ], col2, col3, row2, row3, 299 xoptions=xrm[ 1 ], 300 yoptions=yrm[ 1 ], 301 xpadding=self.padding, 302 ypadding=self.padding )
303 # __setup_gui__() 304 305
306 - def __get_widgets__( self ):
307 return self.children
308 # __get_widgets__() 309 # _Table 310 311
312 -class _Panel( gtk.ScrolledWindow ):
313 """Internal widget to arrange components. 314 315 @warning: never use it directly in Eagle applications! 316 """ 317 318 spacing = 5 319 app = _gen_ro_property( "app" ) 320 id = _gen_ro_property( "id" ) 321 children = _gen_ro_property( "children" ) 322 323 _horizontal = None 324 _hscrollbar_policy = None 325 _vscrollbar_policy = None 326
327 - def __init__( self, app, id, children ):
328 self.app = app 329 self.id = id 330 self.children = _obj_tuple( children ) 331 332 gtk.ScrolledWindow.__init__( self ) 333 self.set_name( id ) 334 self.__setup_gui__() 335 self.__add_widgets_to_app__()
336 # __init__() 337 338
339 - def __setup_gui__( self ):
340 self.set_shadow_type( gtk.SHADOW_NONE ) 341 self.set_policy( hscrollbar_policy=self._hscrollbar_policy, 342 vscrollbar_policy=self._vscrollbar_policy ) 343 self._tab = _Table( self.id, self.children, self._horizontal ) 344 self.add_with_viewport( self._tab ) 345 self.get_child().set_shadow_type( gtk.SHADOW_NONE )
346 # __setup_gui__() 347 348
349 - def __add_widgets_to_app__( self ):
350 for w in self.children: 351 self.app.__add_widget__( w )
352 # __add_widgets_to_app__() 353 354
355 - def __get_widgets__( self ):
356 return self._tab.__get_widgets__()
357 # __get_widgets__() 358 # _Panel 359 360
361 -class _VPanel( _Panel ):
362 """Internal widget to arrange components vertically. 363 364 @warning: never use it directly in Eagle applications! 365 """ 366 367 _horizontal = False 368 _hscrollbar_policy = gtk.POLICY_NEVER 369 _vscrollbar_policy = gtk.POLICY_AUTOMATIC
370 # _VPanel 371 372
373 -class _HPanel( _Panel ):
374 """Internal widget to arrange components horizontally. 375 376 @warning: never use it directly in Eagle applications! 377 """ 378 379 _horizontal = True 380 _hscrollbar_policy = gtk.POLICY_AUTOMATIC 381 _vscrollbar_policy = gtk.POLICY_NEVER
382 # _HPanel 383 384
385 -class _EGObject( object ):
386 """The basic Eagle Object. 387 388 All eagle objects provides an attribute "id". 389 390 @warning: never use it directly in Eagle applications! 391 """ 392 393 id = _gen_ro_property( "id" ) 394
395 - def __init__( self, id ):
396 self.id = id
397 # __init__() 398 399
400 - def __str__( self ):
401 return "%s( id=%r )" % ( self.__class__.__name__, self.id )
402 # __str__() 403 __repr__ = __str__
404 # _EGObject 405 406
407 -class AutoGenId( object ):
408 """Mix-In to auto-generate ids. 409 410 @warning: never use it directly in Eagle applications! 411 """ 412 last_id_num = 0 413
414 - def __get_id__( classobj ):
415 n = "%s-%d" % ( classobj.__name__, classobj.last_id_num ) 416 classobj.last_id_num += 1 417 return n
418 # __get_id__() 419 __get_id__ = classmethod( __get_id__ )
420 # AutoGenId 421 422
423 -class Image( _EGObject, AutoGenId ):
424 """ 425 An image that can be loaded from files or binary data and saved to files. 426 """ 427
428 - def __init__( self, **kargs ):
429 """Image constructor. 430 431 Images can be constructed in 2 ways using keyword arguments: 432 - from files, in this case you give it B{filename} keyword: 433 434 >>> Image( filename='myfile.png' ) 435 436 - from raw data, in this case you need to provide at least 437 B{data}, B{width} and B{height} as arguments. Optional 438 arguments are I{depth}, I{has_alpha} and I{row_stride}. 439 See L{load_data()} for more information: 440 441 >>> Image( data=data, width=200, height=200, depth=32, has_alpha=False ) 442 443 @see: L{load_data()} 444 @see: L{load_file()} 445 """ 446 _EGObject.__init__( self, self.__get_id__() ) 447 448 self._img = None 449 450 if "filename" in kargs: 451 self.load_file( kargs[ "filename" ] ) 452 elif "data" in kargs and "width" in kargs and "height" in kargs: 453 k = { "data": kargs[ "data" ], 454 "width": kargs[ "width" ], 455 "height": kargs[ "height" ], 456 } 457 if "depth" in kargs: 458 k[ "depth" ] = kargs[ "depth" ] 459 if "has_alpha" in kargs: 460 k[ "has_alpha" ] = kargs[ "has_alpha" ] 461 if "rowstride" in kargs: 462 k[ "rowstride" ] = kargs[ "rowstride" ] 463 self.load_data( **k ) 464 elif "__int_image__" in kargs: 465 if isinstance( kargs[ "__int_image__" ], gtk.gdk.Pixbuf ): 466 self._img = kargs[ "__int_image__" ] 467 else: 468 raise ValueError( "Wrong internal image given!" ) 469 elif len( kargs ) > 0: 470 params = [ "%s=%r" % kv for kv in kargs.iteritems() ] 471 raise ValueError( "Unknow parameters: %s" % params )
472 # __init__() 473 474
475 - def __get_gtk_pixbuf__( self ):
476 return self._img
477 # __get_gtk_pixbuf__() 478 479
480 - def __del__( self ):
481 gc.collect()
482 # __del__() 483 484
485 - def save( self, filename, format=None, **options ):
486 """Save image to a file. 487 488 If format is not specified, it will be guessed from filename. 489 490 Format may be an extension or a mime type, see 491 L{get_writable_formats()}. 492 493 @see: L{get_writable_formats()}. 494 @raise Exception: if errors happened during write 495 @raise ValueError: if format is unsupported 496 """ 497 if isinstance( filename, ( tuple, list ) ): 498 filename = os.path.join( *filename ) 499 500 if format is None: 501 format = filename.split( os.path.extsep )[ -1 ] 502 503 format = format.lower() 504 t = None 505 for f in self.get_writable_formats(): 506 if format == f[ "name" ] or \ 507 format in f[ "extensions" ] or \ 508 format in f[ "mime_types" ]: 509 t = f[ "name" ] 510 break 511 if t: 512 try: 513 self._img.save( filename, t, options ) 514 except gobject.GError, e: 515 raise Exception( e ) 516 else: 517 raise ValueError( "Unsupported file format: \"%s\"" % format )
518 # save() 519 520
521 - def get_formats( self ):
522 """Get supported image format information. 523 524 @return: list of dicts with keys: 525 - B{name}: format name 526 - B{description}: format description 527 - B{extensions}: extensions that match format 528 - B{mime_types}: mime types that match format 529 - B{is_writable}: if it is possible to write in this format, otherwise 530 it's just readable 531 """ 532 return gtk.gdk.pixbuf_get_formats()
533 # get_formats() 534 535
536 - def get_writable_formats( self ):
537 """Get formats that support saving/writing. 538 539 @see: L{get_formats()} 540 """ 541 k = [] 542 for f in self.get_formats(): 543 if f[ "is_writable" ]: 544 k.append( f ) 545 return k
546 # get_writable_formats() 547 548
549 - def load_file( self, filename ):
550 """Load image from file given its filename. 551 552 filename may be a string or a tuple/list with path elements, 553 this helps your program to stay portable across different platforms. 554 555 >>> i = Image() 556 >>> i.load_file( 'img.png' ) 557 >>> i.load_file( ( 'test', 'img.png' ) ) 558 """ 559 if isinstance( filename, ( tuple, list ) ): 560 filename = os.path.join( *filename ) 561 562 try: 563 self._img = gtk.gdk.pixbuf_new_from_file( filename ) 564 except gobject.GError, e: 565 raise Exception( e )
566 # load_file() 567 568
569 - def load_data( self, data, width, height, 570 depth=24, has_alpha=None, rowstride=None ):
571 """Load image from raw data. 572 573 If no value is provided as B{has_alpha}, then it's set to C{False} 574 if B{depth} is less or equal 24 or set to C{True} if depth is 32. 575 576 If no value is provided as B{rowstride}, then it's set to 577 M{width * depth / bits_per_sample}. 578 579 >>> i = Image() 580 >>> i.load_data( my_data1, 800, 600, depth=32, has_alpha=False ) 581 >>> i.load_data( my_data2, 400, 300, depth=24 ) 582 """ 583 colorspace = gtk.gdk.COLORSPACE_RGB 584 bits_per_sample = 8 585 586 if has_alpha is None: 587 if depth <= 24: 588 has_alpha=False 589 else: 590 has_alpha=True 591 592 if rowstride is None: 593 rowstride = width * depth / bits_per_sample 594 595 if len( data ) < height * rowstride: 596 raise ValueError( ( "data must be at least " 597 "width * height * rowstride long." 598 "Values are: data size=%d, required=%d" ) % 599 ( len( data ), height * rowstride ) ) 600 601 if isinstance( data, list ): 602 # Convert to allowed types, from fastest to slower 603 try: 604 import Numeric 605 data = Numeric.array( data, typecode=Numeric.Character ) 606 except ImportError: 607 try: 608 import array 609 data = array.array( 'c', data ) 610 except: 611 data = tuple( data ) 612 613 self._img = gtk.gdk.pixbuf_new_from_data( data, colorspace, 614 has_alpha, bits_per_sample, 615 width, height, rowstride )
616 # load_data() 617 618
619 - def get_data( self ):
620 """Return raw data and information about this image. 621 622 @return: a tuple of: 623 - width 624 - height 625 - depth 626 - has alpha? 627 - rowstride 628 - raw pixel data 629 """ 630 return ( self.get_width(), self.get_height(), self.get_depth(), 631 self.has_alpha(), self.get_rowstride(), 632 self._img.get_pixels() )
633 # get_data() 634
635 - def get_width( self ):
636 return self._img.get_width()
637 # get_width() 638 639
640 - def get_height( self ):
641 return self._img.get_height()
642 # get_height() 643 644
645 - def get_size( self ):
646 """Return a tuple ( width, heigt )""" 647 return ( self.get_width(), self.get_height() )
648 # get_size() 649 650
651 - def get_rowstride( self ):
652 """Row stride is the allocated size of a row. 653 654 Generally, rowstride is the number of elements in a row multiplied 655 by the size of each element (bits per pixel). 656 657 But there are cases that there is more space left, a padding, to 658 align it to some boundary, so you may get different value for 659 row stride. 660 """ 661 return self._img.get_rowstride()
662 # get_rowstride() 663 664
665 - def get_n_channels( self ):
666 """Number of channels.""" 667 return self._img.get_n_channels()
668 # get_n_channels() 669 670
671 - def get_bits_per_pixel( self ):
672 """Bits per pixel""" 673 return self.get_n_channels() * self._img.get_bits_per_sample()
674 # get_bits_per_pixel() 675 get_depth = get_bits_per_pixel 676 677
678 - def has_alpha( self ):
679 """If it has an alpha channel""" 680 return self._img.get_has_alpha()
681 # has_alpha() 682 # Image 683 684 685
686 -class _EGWidget( _EGObject ):
687 """The base of every Graphical Component in Eagle. 688 689 @warning: never use it directly in Eagle applications! 690 """ 691 app = _gen_ro_property( "app" ) 692
693 - def __init__( self, id, app=None ):
694 _EGObject.__init__( self, id ) 695 if app is not None: 696 self.app = app 697 self._widgets = tuple()
698 # __init__() 699 700
701 - def __get_resize_mode__( self ):
702 "Return a tuple with ( horizontal, vertical ) resize mode" 703 return ( gtk.FILL, gtk.FILL )
704 # __get_resize_mode__() 705 706
707 - def __get_widgets__( self ):
708 """Return a list of B{internal} widgets this Eagle widget contains. 709 710 @warning: never use it directly in Eagle applications! 711 """ 712 return self._widgets
713 # __get_widgets__() 714 715
716 - def set_active( self, active=True ):
717 """Set the widget as active. 718 719 An active widget have their actions enabled, while an inactive 720 (active=False) will be grayed and actions disabled. 721 """ 722 for w in self.__get_widgets__(): 723 w.set_sensitive( active )
724 # set_active() 725 726
727 - def set_inactive( self ):
728 """Same as L{set_active}( False )""" 729 self.set_active( False )
730 # set_inactive() 731 732
733 - def show( self ):
734 """Make widget visible.""" 735 for w in self.__get_widgets__(): 736 w.show()
737 # show() 738 739
740 - def hide( self ):
741 """Make widget invisible.""" 742 for w in self.__get_widgets__(): 743 w.hide()
744 # hide() 745 # _EGWidget 746 747
748 -class _EGDataWidget( _EGWidget ):
749 """The base of every Eagle widget that holds data. 750 751 Widgets that holds data implement the interface with L{get_value}() and 752 L{set_value}(). 753 754 It can be made persistent with L{persistent}=True 755 """ 756 persistent = False 757
758 - def __init__( self, id, persistent, app=None ):
759 _EGObject.__init__( self, id ) 760 if app is not None: 761 self.app = app 762 self.persistent = persistent 763 self._widgets = tuple()
764 # __init__() 765 766
767 - def get_value( self ):
768 """Get data from this widget.""" 769 raise NotImplementedError
770 # get_value() 771
772 - def set_value( self, value ):
773 """Set data to this widget.""" 774 raise NotImplementedError
775 # set_value() 776 # _EGDataWidget 777 778
779 -class AboutDialog( _EGWidget, AutoGenId ):
780 """A window that displays information about the application. 781 782 @attention: avoid using this directly, use L{AboutButton} instead. 783 """ 784 border = 12 785 spacing = 6 786 width = 600 787 height = 400 788 margin = 6 789
790 - def __init__( self, app, 791 title, author=None, description=None, help=None, 792 version=None, license=None, copyright=None ):
793 _EGWidget.__init__( self, self.__get_id__(), app ) 794 self.title = str( title ) 795 self.author = _str_tuple( author ) 796 self.description = _str_tuple( description ) 797 self.help = _str_tuple( help ) 798 self.version = _str_tuple( version ) 799 self.license = _str_tuple( license ) 800 self.copyright = _str_tuple( copyright ) 801 802 self.__setup_gui__()
803 # __init__() 804 805
806 - def __del__( self ):
807 self._diag.destroy()
808 # __del__() 809 810
811 - def __setup_gui__( self ):
812 win = self.app.__get_window__() 813 btns = ( gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE ) 814 flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT 815 self._diag = gtk.Dialog( title=( "About: %s" % self.title ), 816 parent=win, 817 flags=flags, buttons=btns ) 818 819 self._diag.set_border_width( self.border ) 820 self._diag.set_default_size( self.width, self.height ) 821 self._diag.set_has_separator( False ) 822 self._diag.vbox.set_spacing( self.spacing ) 823 824 _set_icon_list( self._diag, gtk.STOCK_ABOUT ) 825 826 self._sw = gtk.ScrolledWindow() 827 self._diag.vbox.pack_start( self._sw, expand=True, fill=True ) 828 829 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 830 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 831 self._sw.set_shadow_type( gtk.SHADOW_IN ) 832 833 self._text = gtk.TextView() 834 self._sw.add( self._text ) 835 self._text.set_editable( False ) 836 self._text.set_cursor_visible( False ) 837 self._text.set_wrap_mode( gtk.WRAP_WORD ) 838 self._text.set_left_margin( self.margin ) 839 self._text.set_right_margin( self.margin ) 840 841 self.__setup_text__()
842 # __setup_gui__() 843 844
845 - def __setup_text__( self ):
846 buf = self._text.get_buffer() 847 big = buf.create_tag( "big", scale=pango.SCALE_XX_LARGE ) 848 bold = buf.create_tag( "bold", weight=pango.WEIGHT_BOLD ) 849 italic = buf.create_tag( "italic", style=pango.STYLE_ITALIC ) 850 i = buf.get_start_iter() 851 ins = buf.insert_with_tags 852 853 ins( i, self.title, big, bold ) 854 855 if self.version: 856 ins( i, "\n" ) 857 ins( i, ".".join( self.version ), italic ) 858 859 ins( i, "\n\n" ) 860 861 if self.description: 862 for l in self.description: 863 ins( i, l ) 864 ins( i, "\n" ) 865 ins( i, "\n\n" ) 866 867 if self.license: 868 ins( i, "License: ", bold ) 869 ins( i, ", ".join( self.license ) ) 870 ins( i, "\n" ) 871 872 if self.author: 873 if len( self.author ) == 1: 874 ins( i, "Author:\n", bold ) 875 else: 876 ins( i, "Authors:\n", bold ) 877 for a in self.author: 878 ins( i, "\t" ) 879 ins( i, a ) 880 ins( i, "\n" ) 881 ins( i, "\n\n" ) 882 883 if self.help: 884 ins( i, "Help:\n", bold ) 885 for l in self.help: 886 ins( i, l ) 887 ins( i, "\n" ) 888 ins( i, "\n\n" ) 889 890 if self.copyright: 891 ins( i, "Copyright:\n", bold ) 892 for l in self.copyright: 893 ins( i, l ) 894 ins( i, "\n" ) 895 ins( i, "\n\n" )
896 # __setup_text__() 897 898
899 - def run( self ):
900 self._diag.show_all() 901 self._diag.run() 902 self._diag.hide()
903 # run() 904 # AboutDialog 905 906
907 -class HelpDialog( _EGWidget, AutoGenId ):
908 """A window that displays application help. 909 910 @attention: avoid using this directly, use L{HelpButton} instead. 911 """ 912 border = 12 913 spacing = 6 914 width = 600 915 height = 400 916 margin = 6 917
918 - def __init__( self, app, title, help=None ):
919 _EGWidget.__init__( self, self.__get_id__(), app ) 920 self.title = title 921 self.help = _str_tuple( help ) 922 self.__setup_gui__()
923 # __init__() 924 925
926 - def __del__( self ):
927 self._diag.destroy()
928 # __del__() 929 930
931 - def __setup_gui__( self ):
932 win = self.app.__get_window__() 933 btns = ( gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE ) 934 flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT 935 self._diag = gtk.Dialog( title=( "Help: %s" % self.title ), 936 parent=win, 937 flags=flags, buttons=btns ) 938 self._diag.set_border_width( self.border ) 939 self._diag.set_default_size( self.width, self.height ) 940 self._diag.set_has_separator( False ) 941 self._diag.vbox.set_spacing( self.spacing ) 942 _set_icon_list( self._diag, gtk.STOCK_HELP ) 943 944 self._sw = gtk.ScrolledWindow() 945 self._diag.get_child().pack_start( self._sw, expand=True, fill=True ) 946 947 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 948 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 949 self._sw.set_shadow_type( gtk.SHADOW_IN ) 950 951 self._text = gtk.TextView() 952 self._sw.add( self._text ) 953 self._text.set_editable( False ) 954 self._text.set_cursor_visible( False ) 955 self._text.set_wrap_mode( gtk.WRAP_WORD ) 956 self._text.set_left_margin( self.margin ) 957 self._text.set_right_margin( self.margin ) 958 959 self.__setup_text__()
960 # __setup_gui__() 961 962
963 - def __setup_text__( self ):
964 buf = self._text.get_buffer() 965 big = buf.create_tag( "big", scale=pango.SCALE_XX_LARGE ) 966 bold = buf.create_tag( "bold", weight=pango.WEIGHT_BOLD ) 967 italic = buf.create_tag( "italic", style=pango.STYLE_ITALIC ) 968 i = buf.get_start_iter() 969 ins = buf.insert_with_tags 970 971 ins( i, self.title, big, bold ) 972 ins( i, "\n\n" ) 973 974 ins( i, "Help:\n", bold ) 975 for l in self.help: 976 ins( i, l ) 977 ins( i, "\n" )
978 # __setup_text__() 979 980
981 - def run( self ):
982 self._diag.show_all() 983 self._diag.run() 984 self._diag.hide()
985 # run() 986 # HelpDialog 987 988
989 -class FileChooser( _EGWidget, AutoGenId ):
990 """A dialog to choose a file. 991 992 @attention: avoid using this directly, use L{App.file_chooser}, 993 L{OpenFileButton}, L{SaveFileButton} or L{SelectFolderButton} instead. 994 """ 995 ACTION_OPEN = 0 996 ACTION_SAVE = 1 997 ACTION_SELECT_FOLDER = 2 998 ACTION_CREATE_FOLDER = 3 999
1000 - def __init__( self, app, action, filename=None, 1001 title=None, filter=None, multiple=False ):
1002 """Dialog to choose files. 1003 1004 filter may be a single pattern (ie: '*.png'), mime type 1005 (ie: 'text/html') or a list of patterns or mime types or 1006 a list of lists, each sub list with a filter name and mime type/ 1007 patterns accepted. Examples: 1008 [ [ 'Images', '*.ppm', 'image/jpeg', 'image/png' ], 1009 [ 'Text', '*.text', 'text/plain' ], 1010 ] 1011 """ 1012 _EGWidget.__init__( self, self.__get_id__(), app ) 1013 self.action = action 1014 self.filter = filter 1015 self.multiple = multiple or False 1016 self.filename = filename 1017 self.title = title or self.__gen_title__() 1018 1019 if self.multiple: 1020 warn( "Maemo doesn't support multiple file selection!" ) 1021 1022 self.__setup_gui__()
1023 # __init__() 1024 1025
1026 - def __gen_title__( self ):
1027 t = { self.ACTION_OPEN: "Open: %s", 1028 self.ACTION_SAVE: "Save: %s", 1029 self.ACTION_SELECT_FOLDER: "Open Folder: %s", 1030 self.ACTION_CREATE_FOLDER: "Create Folder: %s", 1031 } 1032 title = t.get( self.action, t[ self.ACTION_OPEN ] ) 1033 return title % self.app.title
1034 # __gen_title__() 1035 1036
1037 - def __del__( self ):
1038 self._diag.destroy()
1039 # __del__() 1040 1041
1042 - def __setup_gui__( self ):
1043 win = self.app.__get_window__() 1044 a = { self.ACTION_OPEN: gtk.FILE_CHOOSER_ACTION_OPEN, 1045 self.ACTION_SAVE: gtk.FILE_CHOOSER_ACTION_SAVE, 1046 self.ACTION_SELECT_FOLDER: gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, 1047 self.ACTION_CREATE_FOLDER: gtk.FILE_CHOOSER_ACTION_CREATE_FOLDER, 1048 }.get( self.action, gtk.FILE_CHOOSER_ACTION_OPEN ) 1049 1050 self._diag = hildon.FileChooserDialog( parent=win, 1051 action=a ) 1052 _set_icon_list( self._diag, gtk.STOCK_OPEN ) 1053 if self.filter: 1054 if isinstance( self.filter, ( tuple, list ) ): 1055 for f in self.filter: 1056 filter = gtk.FileFilter() 1057 if isinstance( f, ( tuple, list ) ): 1058 filter.set_name( f[ 0 ] ) 1059 for e in f[ 1 : ]: 1060 if '/' in e: 1061 filter.add_mime_type( e ) 1062 else: 1063 filter.add_pattern( e ) 1064 elif isinstance( f, ( str, unicode ) ): 1065 filter.set_name( f ) 1066 if '/' in f: 1067 filter.add_mime_type( f ) 1068 else: 1069 filter.add_pattern( f ) 1070 else: 1071 raise ValueError( "invalid filter!" ) 1072 self._diag.add_filter( filter ) 1073 1074 elif isinstance( self.filter, ( str, unicode ) ): 1075 filter = gtk.FileFilter() 1076 filter.set_name( self.filter ) 1077 if '/' in self.filter: 1078 filter.add_mime_type( self.filter ) 1079 else: 1080 filter.add_pattern( self.filter ) 1081 self._diag.set_filter( filter ) 1082 else: 1083 raise ValueError( "invalid filter!" ) 1084 if self.filename: 1085 self._diag.set_filename( self.filename )
1086 # __setup_gui__() 1087 1088
1089 - def run( self ):
1090 self._diag.show_all() 1091 r = self._diag.run() 1092 self._diag.hide() 1093 if r == gtk.RESPONSE_OK: 1094 return self._diag.get_filename() 1095 else: 1096 return None
1097 # run() 1098 # FileChooser 1099 1100
1101 -class PreferencesDialog( _EGWidget, AutoGenId ):
1102 """A dialog to present user with preferences. 1103 1104 Preferences is another L{App} area, just like C{left}, C{right}, C{center}, 1105 C{top} or C{bottom}, but will be displayed in a separate window. 1106 1107 @attention: avoid using this directly, use L{PreferencesButton} instead. 1108 """
1109 - def __init__( self, app, children ):
1110 _EGWidget.__init__( self, self.__get_id__(), app ) 1111 self.children = _obj_tuple( children ) 1112 self.__setup_gui__() 1113 self.__add_widgets_to_app__()
1114 # __init__() 1115 1116
1117 - def __del__( self ):
1118 self._diag.destroy()
1119 # __del__() 1120 1121
1122 - def __setup_gui__( self ):
1123 win = self.app.__get_window__() 1124 btns = ( gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE ) 1125 flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT 1126 self._diag = gtk.Dialog( title=( "Preferences: %s" % self.app.title ), 1127 parent=win, 1128 flags=flags, buttons=btns ) 1129 self._diag.set_default_size( 400, 300 ) 1130 _set_icon_list( self._diag, gtk.STOCK_PREFERENCES ) 1131 1132 self._sw = gtk.ScrolledWindow() 1133 self._diag.get_child().pack_start( self._sw, expand=True, fill=True ) 1134 1135 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 1136 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 1137 1138 self._tab = _Table( self.id, self.children ) 1139 self._sw.add_with_viewport( self._tab ) 1140 self._sw.get_child().set_shadow_type( gtk.SHADOW_NONE ) 1141 self._sw.set_shadow_type( gtk.SHADOW_NONE )
1142 # __setup_gui__() 1143 1144
1145 - def __add_widgets_to_app__( self ):
1146 for w in self.children: 1147 if isinstance( w, _EGDataWidget ): 1148 w.persistent = True 1149 self.app.__add_widget__( w )
1150 # __add_widgets_to_app__() 1151 1152
1153 - def run( self ):
1154 self._diag.show_all() 1155 self._diag.run() 1156 self._diag.hide()
1157 # run() 1158 # PreferencesDialog 1159 1160
1161 -class DebugDialog( _EGObject, AutoGenId ):
1162 """Dialog to show uncaught exceptions. 1163 1164 This dialog shows information about uncaught exceptions and also save 1165 the traceback to a file. 1166 """ 1167 # Most of DebugDialog code came from Gazpacho code! Thanks! 1168 border = 12 1169 spacing = 6 1170 width = 600 1171 height = 400 1172 margin = 6 1173
1174 - def __init__( self ):
1175 _EGObject.__init__( self, self.__get_id__() ) 1176 self.__setup_gui__()
1177 # __init__() 1178 1179
1180 - def __setup_gui__( self ):
1181 b = ( gtk.STOCK_QUIT, gtk.RESPONSE_CLOSE ) 1182 self._diag = gtk.Dialog( "Application Crashed!", 1183 parent=None, 1184 flags=gtk.DIALOG_MODAL, 1185 buttons=b ) 1186 self._diag.set_border_width( self.border ) 1187 self._diag.set_default_size( self.width, self.height ) 1188 self._diag.set_has_separator( False ) 1189 self._diag.vbox.set_spacing( self.spacing ) 1190 1191 self._hbox1 = gtk.HBox() 1192 1193 self._label1 = gtk.Label( "<b>Exception type:</b>" ) 1194 self._label1.set_use_markup( True ) 1195 self._hbox1.pack_start( self._label1, False, False, self.spacing ) 1196 self._label1.show() 1197 1198 self._exctype = gtk.Label() 1199 self._hbox1.pack_start( self._exctype, False, True ) 1200 self._exctype.show() 1201 1202 self._diag.vbox.pack_start( self._hbox1, False, False ) 1203 self._hbox1.show() 1204 1205 self._hbox2 = gtk.HBox() 1206 1207 self._label2 = gtk.Label( "<b>This info was saved to:</b>" ) 1208 self._label2.set_use_markup( True ) 1209 self._hbox2.pack_start( self._label2, False, False, self.spacing ) 1210 self._label2.show() 1211 1212 self._save_name = gtk.Label() 1213 self._hbox2.pack_start( self._save_name, False, True ) 1214 self._save_name.show() 1215 1216 self._diag.vbox.pack_start( self._hbox2, False, False ) 1217 self._hbox2.show() 1218 1219 self._sw = gtk.ScrolledWindow() 1220 self._sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC ) 1221 self._sw.set_shadow_type( gtk.SHADOW_IN ) 1222 self._text = gtk.TextView() 1223 self._text.set_editable( False ) 1224 self._text.set_cursor_visible( False ) 1225 self._text.set_wrap_mode( gtk.WRAP_WORD ) 1226 self._text.set_left_margin( self.margin ) 1227 self._text.set_right_margin( self.margin ) 1228 1229 self._sw.add( self._text ) 1230 self._text.show() 1231 self._diag.vbox.pack_start( self._sw, expand=True, fill=True ) 1232 self._sw.show() 1233 self.__setup_text__()
1234 # __setup_gui__() 1235 1236
1237 - def __setup_text__( self ):
1238 self._buf = self._text.get_buffer() 1239 self._buf.create_tag( "label", weight=pango.WEIGHT_BOLD ) 1240 self._buf.create_tag( "code", foreground="gray25", 1241 family="monospace" ) 1242 self._buf.create_tag( "exc", foreground="#880000", 1243 weight=pango.WEIGHT_BOLD )
1244 # __setup_text__() 1245 1246
1247 - def show_exception( self, exctype, value, tb ):
1248 import traceback 1249 self._exctype.set_text( str( exctype ) ) 1250 self.print_tb( tb ) 1251 1252 lines = traceback.format_exception_only( exctype, value ) 1253 msg = lines[ 0 ] 1254 result = msg.split( ' ', 1 ) 1255 if len( result ) == 1: 1256 msg = result[ 0 ] 1257 arguments = "" 1258 else: 1259 msg, arguments = result 1260 1261 self._insert_text( "\n" ) 1262 self._insert_text( msg, "exc" ) 1263 self._insert_text( " " ) 1264 self._insert_text( arguments )
1265 # show_exception() 1266 1267
1268 - def save_exception( self, exctype, value, tb ):
1269 import traceback 1270 import time 1271 progname = os.path.split( sys.argv[ 0 ] )[ -1 ] 1272 filename = "%s-%s-%s.tb" % ( progname, 1273 os.getuid(), 1274 int( time.time() ) ) 1275 filename = os.path.join( os.path.sep, "tmp", filename ) 1276 f = open( filename, "wb" ) 1277 try: 1278 os.chmod( filename, 0600 ) 1279 except: 1280 pass 1281 1282 for e in traceback.format_exception( exctype, value, tb ): 1283 f.write( e ) 1284 f.close() 1285 self._save_name.set_text( filename ) 1286 sys.stderr.write( "Traceback saved to '%s'.\n" % filename )
1287 # save_exception() 1288 1289
1290 - def print_tb( self, tb, limit=None ):
1291 import linecache 1292 1293 if limit is None: 1294 if hasattr( sys, "tracebacklimit" ): 1295 limit = sys.tracebacklimit 1296 n = 0 1297 while tb is not None and ( limit is None or n < limit ): 1298 f = tb.tb_frame 1299 lineno = tb.tb_lineno 1300 co = f.f_code 1301 filename = co.co_filename 1302 name = co.co_name 1303 self._print_file( filename, lineno, name ) 1304 line = linecache.getline( filename, lineno ) 1305 if line: 1306 self._insert_text( " " + line.strip() + "\n\n", "code" ) 1307 tb = tb.tb_next 1308 n = n+1
1309 # print_tb() 1310 1311
1312 - def _insert_text( self, text, *tags ):
1313 end_iter = self._buf.get_end_iter() 1314 self._buf.insert_with_tags_by_name( end_iter, text, *tags )
1315 # _insert_text() 1316 1317
1318 - def _print_file( self, filename, lineno, name ):
1319 if filename.startswith( os.getcwd() ): 1320 filename = filename.replace( self._pwd, '' )[ 1 : ] 1321 1322 self._insert_text( "File: ", "label" ) 1323 self._insert_text( filename ) 1324 self._insert_text( "\n" ) 1325 self._insert_text( "Line: ", "label" ) 1326 self._insert_text( str( lineno ) ) 1327 self._insert_text( "\n" ) 1328 self._insert_text( "Function: ", "label" ) 1329 self._insert_text( name ) 1330 self._insert_text( "\n" )
1331 # _print_file() 1332
1333 - def _start_debugger( self ):
1334 import pdb 1335 pdb.pm()
1336 # _start_debugger() 1337 1338 1339
1340 - def run( self, error=None ):
1341 r = self._diag.run() 1342 if r == gtk.RESPONSE_CLOSE or gtk.RESPONSE_DELETE_EVENT: 1343 raise SystemExit( error )
1344 # run() 1345 1346
1347 - def except_hook( exctype, value, tb ):
1348 if exctype is not KeyboardInterrupt: 1349 d = DebugDialog() 1350 d.save_exception( exctype, value, tb ) 1351 d.show_exception( exctype, value, tb ) 1352 d.run() 1353 d.destroy() 1354 1355 raise SystemExit
1356 # except_hook() 1357 except_hook = staticmethod( except_hook )
1358 # DebugDialog 1359 sys.excepthook = DebugDialog.except_hook 1360 1361
1362 -class _EGWidLabelEntry( _EGDataWidget ):
1363 """Widget that holds a label and an associated Entry. 1364 1365 @note: _EGWidLabelEntry must B{NOT} be used directly! You should use 1366 a widget that specialize this instead. 1367 1368 @attention: B{Widget Developers:} You must setup an instance attribute 1369 C{_entry} before using it, since this will be set as mnemonic for this 1370 label and also returned in L{__get_widgets__}(). 1371 """ 1372 label = _gen_ro_property( "label" ) 1373
1374 - def __init__( self, id, persistent, label="" ):
1375 _EGDataWidget.__init__( self, id, persistent ) 1376 self.label = label or id 1377 self.__setup_gui__()
1378 # __init__() 1379 1380
1381 - def __setup_gui__( self ):
1382 self._label = gtk.Label( self.label ) 1383 self._label.set_justify( gtk.JUSTIFY_RIGHT ) 1384 self._label.set_alignment( xalign=1.0, yalign=0.5 ) 1385 self._label.set_mnemonic_widget( self._entry ) 1386 self._widgets = ( self._label, self._entry )
1387 # __setup_gui__() 1388 1389
1390 - def get_value( self ):
1391 return self._entry.get_value()
1392 # get_value() 1393 1394
1395 - def set_value( self, value ):
1396 self._entry.set_value( value )
1397 # set_value() 1398 1399
1400 - def __str__( self ):
1401 return "%s( id=%r, label=%r, value=%r )" % \ 1402 ( self.__class__.__name__, self.id, self.label, 1403 self.get_value() )
1404 # __str__() 1405 __repr__ = __str__
1406 # _EGWidLabelEntry 1407 1408
1409 -class App( _EGObject, AutoGenId ):
1410 """An application window. 1411 1412 This is the base of Eagle programs, since it will hold every graphical 1413 component. 1414 1415 An App window is split in 5 areas: 1416 - left 1417 - right 1418 - center 1419 - top 1420 - bottom 1421 the first 3 have a vertical layout, the other have horizontal layout. 1422 Every area has its own scroll bars that are shown automatically when 1423 need. 1424 1425 Also provided is an extra area, that is shown in another window. This is 1426 the preferences area. It have a vertical layout and components that 1427 hold data are made persistent automatically. You should use 1428 L{PreferencesButton} to show this area. 1429 1430 Extra information like author, description, help, version, license and 1431 copyright are used in specialized dialogs. You may show these dialogs 1432 with L{AboutButton} and L{HelpButton}. 1433 1434 Widgets can be reach with L{get_widget_by_id}, example: 1435 >>> app = App( "My App", left=Entry( id="entry" ) ) 1436 >>> app.get_widget_by_id( "entry" ) 1437 Entry( id='entry', label='entry', value='' ) 1438 1439 You may also reach widgets using dict-like syntax, but with the 1440 special case for widgets that hold data, these will be provided 1441 using their L{set_data<_EGDataWidget.set_data>} and 1442 L{get_data<_EGDataWidget.get_data>}, it make things easier, but 1443 B{be careful to don't misuse it!}. Example: 1444 1445 >>> app= App( "My App", left=Entry( id="entry" ), 1446 ... right=Canvas( "canvas", 300, 300 ) ) 1447 >>> app[ "entry" ] 1448 '' 1449 >>> app[ "entry" ] = "text" 1450 >>> app[ "entry" ] 1451 'text' 1452 >>> app[ "canvas" ] 1453 Canvas( id='canvas', width=300, height=300, label='' ) 1454 >>> app[ "canvas" ].draw_text( "hello" ) 1455 >>> app[ "entry" ].get_value() # will fail, since it's a data widget 1456 1457 """ 1458 border_width = 10 1459 spacing = 3 1460 1461 title = _gen_ro_property( "title" ) 1462 left = _gen_ro_property( "left" ) 1463 right = _gen_ro_property( "right" ) 1464 top = _gen_ro_property( "top" ) 1465 bottom = _gen_ro_property( "bottom" ) 1466 center = _gen_ro_property( "center" ) 1467 preferences = _gen_ro_property( "preferences" ) 1468 statusbar = _gen_ro_property( "statusbar" ) 1469 _widgets = _gen_ro_property( "_widgets" ) 1470
1471 - def __init__( self, title, id=None, 1472 center=None, left=None, right=None, top=None, bottom=None, 1473 preferences=None, 1474 quit_callback=None, data_changed_callback=None, 1475 author=None, description=None, help=None, version=None, 1476 license=None, copyright=None, 1477 statusbar=False ):
1478 """App Constructor. 1479 1480 @param title: application name, to be displayed in the title bar. 1481 @param id: unique id to this application, or None to generate one 1482 automatically. 1483 @param center: list of widgets to be laid out vertically in the 1484 window's center. 1485 @param left: list of widgets to be laid out vertically in the 1486 window's left side. 1487 @param right: list of widgets to be laid out vertically in the 1488 window's right side. 1489 @param top: list of widgets to be laid out horizontally in the 1490 window's top. 1491 @param bottom: list of widgets to be laid out horizontally in the 1492 window's bottom. 1493 @param preferences: list of widgets to be laid out vertically in 1494 another window, this can be shown with L{PreferencesButton}. 1495 @param statusbar: if C{True}, an statusbar will be available and 1496 usable with L{status_message} method. 1497 @param author: the application author or list of author, used in 1498 L{AboutDialog}, this can be shown with L{AboutButton}. 1499 @param description: application description, used in L{AboutDialog}. 1500 @param help: help text, used in L{AboutDialog} and L{HelpDialog}, this 1501 can be shown with L{HelpButton}. 1502 @param version: application version, used in L{AboutDialog}. 1503 @param license: application license, used in L{AboutDialog}. 1504 @param copyright: application copyright, used in L{AboutDialog}. 1505 @param quit_callback: function (or list of functions) that will be 1506 called when application is closed. Function will receive as 1507 parameter the reference to App. If return value is False, 1508 it will abort closing the window. 1509 @param data_changed_callback: function (or list of functions) that will 1510 be called when some widget that holds data have its data 1511 changed. Function will receive as parameters: 1512 - App reference 1513 - Widget reference 1514 - new value 1515 """ 1516 _EGObject.__init__( self, id ) 1517 self.title = title 1518 self.left = left 1519 self.right = right 1520 self.top = top 1521 self.bottom = bottom 1522 self.center = center 1523 self.preferences = preferences 1524 self.author = _str_tuple( author ) 1525 self.description = _str_tuple( description ) 1526 self.help = _str_tuple( help ) 1527 self.version = _str_tuple( version ) 1528 self.license = _str_tuple( license ) 1529 self.copyright = _str_tuple( copyright ) 1530 self.statusbar = statusbar 1531 self._widgets = {} 1532 1533 self.quit_callback = _callback_tuple( quit_callback ) 1534 self.data_changed_callback = _callback_tuple( data_changed_callback ) 1535 1536 self.__add_to_app_list__() 1537 self.__setup_gui__() 1538 self.__setup_connections__() 1539 self.load()
1540 # __init__() 1541 1542
1543 - def __getitem__( self, name ):
1544 w = self.get_widget_by_id( name ) 1545 if isinstance( w, _EGDataWidget ): 1546 return w.get_value() 1547 else: 1548 return w
1549 # __getitem__() 1550 1551
1552 - def __setitem__( self, name, value ):
1553 w = self.get_widget_by_id( name ) 1554 if w is None: 1555 raise ValueError( "Could not find any widget with id=%r" % name ) 1556 elif isinstance( w, _EGDataWidget ): 1557 return w.set_value( value ) 1558 else: 1559 raise TypeError( 1560 "Could not set value of widget '%s' of type '%s'." % \ 1561 ( name, type( w ).__name__ ) )
1562 # __setitem__() 1563 1564
1565 - def get_widget_by_id( self, widget_id ):
1566 """Return referece to widget with provided id or None if not found.""" 1567 if isinstance( widget_id, _EGWidget ) and \ 1568 widget_id in self._widgets.itervalues(): 1569 return widget_id 1570 else: 1571 return self._widgets.get( widget_id, None )
1572 # get_widget_by_id() 1573 1574
1575 - def show_about_dialog( self ):
1576 """Show L{AboutDialog} of this App.""" 1577 diag = AboutDialog( app=self, 1578 title=self.title, 1579 author=self.author, 1580 description=self.description, 1581 help=self.help, 1582 version=self.version, 1583 license=self.license, 1584 copyright=self.copyright, 1585 ) 1586 diag.run()
1587 # show_about_dialog() 1588 1589
1590 - def show_help_dialog( self ):
1591 """Show L{HelpDialog} of this App.""" 1592 diag = HelpDialog( app=self, 1593 title=self.title, 1594 help=self.help, 1595 ) 1596 diag.run()
1597 # show_help_dialog() 1598 1599
1600 - def file_chooser( self, action, filename=None, 1601 filter=None, multiple=False ):
1602 """Show L{FileChooser} and return selected file(s). 1603 1604 @param action: must be one of ACTION_* as defined in L{FileChooser}. 1605 @param filter: a pattern (ie: '*.png'), mime type or a list. 1606 1607 @see: L{FileChooser} 1608 """ 1609 diag = FileChooser( app=self, action=action, 1610 filename=filename, filter=filter, 1611 multiple=multiple ) 1612 return diag.run()
1613 # file_chooser() 1614 1615
1616 - def show_preferences_dialog( self ):
1617 """Show L{PreferencesDialog} associated with this App.""" 1618 return self._preferences.run()
1619 # show_preferences_dialog() 1620 1621
1622 - def __get_window__( self ):
1623 return self._win
1624 # __get_window__() 1625 1626
1627 - def __add_to_app_list__( self ):
1628 if not self.id: 1629 self.id = self.__get_id__() 1630 1631 if self.id in _apps: 1632 raise ValueError( "App id '%s' already existent!" % self.id ) 1633 1634 _apps[ self.id ] = self
1635 # __add_to_app_list__() 1636 1637
1638 - def __add_widget__( self, widget ):
1639 if widget.id in self._widgets: 1640 w = self._widgets[ widget.id ] 1641 raise ValueError( ( "Object id \"%s\" (type: %s) already in " 1642 "application \"%s\" as type: %s!" ) % \ 1643 ( widget.id, 1644 widget.__class__.__name__, 1645 self.id, 1646 w.__class__.__name__ ) ) 1647 else: 1648 if widget.app is None: 1649 self._widgets[ widget.id ] = widget 1650 widget.app = self 1651 elif widget.app != self: 1652 try: 1653 id = widget.app.id 1654 except: 1655 id = widget.app 1656 raise ValueError( ( "Object \"%s\" already is in another " 1657 "App: \"%s\"" ) % \ 1658 ( widget.id, id ) )
1659 # __add_widget__() 1660 1661
1662 - def __setup_gui__( self ):
1663 self._win = hildon.App() 1664 self._win.set_name( self.id ) 1665 self._win.set_title( self.title ) 1666 1667 settings = self._win.get_settings() 1668 settings.set_property( "gtk-button-images", False ) 1669 1670 self._mv = hildon.AppView( self.title ) 1671 self._win.set_appview( self._mv ) 1672 self._mv.set_fullscreen_key_allowed( True ) 1673 1674 self._top_layout = gtk.VBox( False ) 1675 self._mv.add( self._top_layout ) 1676 1677 self._hbox = gtk.HBox( False, self.spacing ) 1678 self._hbox.set_border_width( self.border_width ) 1679 self._top_layout.pack_start( self._hbox, expand=True, fill=True ) 1680 1681 self.__setup_menus__() 1682 self.__setup_gui_left__() 1683 self.__setup_gui_right__() 1684 self.__setup_gui_center__() 1685 self.__setup_gui_top__() 1686 self.__setup_gui_bottom__() 1687 self.__setup_gui_preferences__() 1688 1689 self._vbox = gtk.VBox( False, self.spacing ) 1690 1691 if self._top.__get_widgets__(): 1692 self._vbox.pack_start( self._top, expand=False, fill=True ) 1693 if self._center.__get_widgets__() or \ 1694 self._bottom.__get_widgets__(): 1695 self._vbox.pack_start( gtk.HSeparator(), expand=False, 1696 fill=True ) 1697 1698 if self._center.__get_widgets__(): 1699 self._vbox.pack_start( self._center, expand=True, fill=True ) 1700 1701 if self._bottom.__get_widgets__(): 1702 if self._center.__get_widgets__(): 1703 self._vbox.pack_start( gtk.HSeparator(), expand=False, 1704 fill=True ) 1705 self._vbox.pack_start( self._bottom, expand=False, fill=True ) 1706 1707 if self._left.__get_widgets__(): 1708 self._hbox.pack_start( self._left, expand=False, fill=True ) 1709 if self._center.__get_widgets__() or \ 1710 self._top.__get_widgets__() or \ 1711 self._bottom.__get_widgets__() or \ 1712 self._right.__get_widgets__(): 1713 self._hbox.pack_start( gtk.VSeparator(), 1714 expand=False, fill=False ) 1715 1716 self._hbox.pack_start( self._vbox, expand=True, fill=True ) 1717 1718 if self._right.__get_widgets__(): 1719 if self._center.__get_widgets__() or \ 1720 self._top.__get_widgets__() or \ 1721 self._bottom.__get_widgets__(): 1722 self._hbox.pack_start( gtk.VSeparator(), 1723 expand=False, fill=False ) 1724 self._hbox.pack_end( self._right, expand=False, fill=True ) 1725 1726 if self.statusbar: 1727 self._statusbar = gtk.Statusbar() 1728 self._statusbar_ctx = self._statusbar.get_context_id( self.title ) 1729 self._statusbar.set_has_resize_grip( True ) 1730 self._top_layout.pack_end( self._statusbar, 1731 expand=False, fill=True ) 1732 1733 self._win.show_all()
1734 # __setup_gui__() 1735 1736
1737 - def __setup_menus__( self ):
1738 self._menu = self._mv.get_menu() 1739 1740 mi = gtk.MenuItem( "Help" ) 1741 mi.connect( "activate", lambda *x: self.show_help_dialog() ) 1742 self._menu.append( mi ) 1743 1744 mi = gtk.MenuItem( "About" ) 1745 mi.connect( "activate", lambda *x: self.show_about_dialog() ) 1746 self._menu.append( mi ) 1747 1748 mi = gtk.MenuItem( "Preferences" ) 1749 mi.connect( "activate", lambda *x: self.show_preferences_dialog() ) 1750 self._menu.append( mi ) 1751 1752 mi = gtk.MenuItem( "Close" ) 1753 mi.connect( "activate", lambda *x: self.close() ) 1754 self._menu.append( mi ) 1755 1756 mi = gtk.MenuItem( "Quit" ) 1757 mi.connect( "activate", lambda *x: quit() ) 1758 self._menu.append( mi ) 1759 1760 self._menu.show_all()
1761 # __setup_menus__() 1762 1763
1764 - def __setup_gui_left__( self ):
1765 self._left = _VPanel( self, id="left", children=self.left )
1766 # __setup_gui_left__() 1767 1768
1769 - def __setup_gui_right__( self ):
1770 self._right =_VPanel( self, id="right", children=self.right )
1771 # __setup_gui_right__() 1772 1773
1774 - def __setup_gui_center__( self ):
1775 self._center = _VPanel( self, id="center", children=self.center )
1776 # __setup_gui_center__() 1777 1778
1779 - def __setup_gui_top__( self ):
1780 self._top = _HPanel( self, id="top", children=self.top )
1781 # __setup_gui_top__() 1782 1783
1784 - def __setup_gui_bottom__( self ):
1785 self._bottom = _HPanel( self, id="bottom", children=self.bottom )
1786 # __setup_gui_bottom__() 1787 1788
1789 - def __setup_gui_preferences__( self ):
1790 self._preferences = PreferencesDialog( self, 1791 children=self.preferences )
1792 # __setup_gui_preferences__() 1793 1794
1795 - def __setup_connections__( self ):
1796 self._win.connect( "delete_event", self.__delete_event__ )
1797 # __setup_connections__() 1798 1799
1800 - def data_changed( self, widget, value ):
1801 """Notify that widget changed it's value. 1802 1803 Probably you will not need to call this directly. 1804 """ 1805 self.save() 1806 for c in self.data_changed_callback: 1807 c( self, widget, value )
1808 # data_changed() 1809 1810
1811 - def __do_close__( self ):
1812 self.save() 1813 1814 for c in self.quit_callback: 1815 if not c( self ): 1816 return False 1817 1818 del _apps[ self.id ] 1819 if not _apps: 1820 gtk.main_quit() 1821 1822 return True
1823 # __do_close__() 1824 1825
1826 - def __delete_event__( self, *args ):
1827 if self.__do_close__(): 1828 return False 1829 else: 1830 return True
1831 # __delete_event__() 1832 1833
1834 - def __persistence_filename__( self ):
1835 return "%s.save_data" % self.id
1836 # __persistence_filename__() 1837 1838
1839 - def save( self ):
1840 """Save data from widgets to file. 1841 1842 Probably you will not need to call this directly. 1843 """ 1844 d = {} 1845 for id, w in self._widgets.iteritems(): 1846 if isinstance( w, _EGDataWidget ) and w.persistent: 1847 d[ id ] = w.get_value() 1848 1849 if d: 1850 f = open( self.__persistence_filename__(), "wb" ) 1851 pickle.dump( d, f, pickle.HIGHEST_PROTOCOL ) 1852 f.close()
1853 # save() 1854 1855
1856 - def load( self ):
1857 """Load data to widgets from file. 1858 1859 Probably you will not need to call this directly. 1860 """ 1861 try: 1862 f = open( self.__persistence_filename__(), "rb" ) 1863 except IOError: 1864 return 1865 1866 d = pickle.load( f ) 1867 f.close() 1868 1869 for id, v in d.iteritems(): 1870 try: 1871 w = self._widgets[ id ] 1872 except KeyError: 1873 w = None 1874 if isinstance( w, _EGDataWidget ) and w.persistent: 1875 w.set_value( v )
1876 # load() 1877 1878
1879 - def close( self ):
1880 """Close application window.""" 1881 if self.__do_close__(): 1882 self._win.destroy()
1883 # close() 1884 1885
1886 - def status_message( self, message ):
1887 """Display a message in status bar and retrieve its identifier for 1888 later removal. 1889 1890 @see: L{remove_status_message} 1891 @note: this only active if statusbar=True 1892 """ 1893 if self.statusbar: 1894 return self._statusbar.push( self._statusbar_ctx, message ) 1895 else: 1896 raise ValueError( "App '%s' doesn't use statusbar!" % self.id )
1897 # status_message() 1898 1899
1900 - def remove_status_message( self, message_id ):
1901 """Remove a previously displayed message. 1902 1903 @see: L{status_message} 1904 @note: this only active if statusbar=True 1905 """ 1906 if self.statusbar: 1907 self._statusbar.remove( self._statusbar_ctx, message_id ) 1908 else: 1909 raise ValueError( "App '%s' doesn't use statusbar!" % self.id )
1910 # remove_status_message() 1911 1912
1913 - def timeout_add( self, interval, callback ):
1914 """Register a function to be called after a given timeout/interval. 1915 1916 @param interval: milliseconds between calls. 1917 @param callback: function to call back. This function gets as 1918 argument the app reference and must return C{True} to 1919 keep running, if C{False} is returned, it will not be 1920 called anymore. 1921 @return: id number to be used in L{remove_event_source} 1922 """
1923 - def wrap( *args ):
1924 return callback( self )
1925 # wrap() 1926 return gobject.timeout_add( interval, wrap )
1927 # timeout_add() 1928 1929
1930 - def idle_add( self, callback ):
1931 """Register a function to be called when system is idle. 1932 1933 System is idle if there is no other event pending. 1934 1935 @param callback: function to call back. This function gets as 1936 argument the app reference and must return C{True} to 1937 keep running, if C{False} is returned, it will not be 1938 called anymore. 1939 @return: id number to be used in L{remove_event_source} 1940 """
1941 - def wrap( *args ):
1942 return callback( self )
1943 # wrap() 1944 return gobject.idle_add( wrap )
1945 # idle_add() 1946 1947 1948
1949 - def io_watch( self, file, callback, 1950 on_in=False, on_out=False, on_urgent=False, on_error=False, 1951 on_hungup=False ):
1952 """Register a function to be called after an Input/Output event. 1953 1954 @param file: any file object or file descriptor (integer). 1955 @param callback: function to be called back, parameters will be the 1956 application that generated the event, the file that triggered 1957 it and on_in, on_out, on_urgent, on_error or on_hungup, 1958 being True those that triggered the event. 1959 The function must return C{True} to be called back again, 1960 otherwise it is automatically removed. 1961 @param on_in: there is data to read. 1962 @param on_out: data can be written without blocking. 1963 @param on_urgent: there is urgent data to read. 1964 @param on_error: error condition. 1965 @param on_hungup: hung up (the connection has been broken, usually for 1966 pipes and sockets). 1967 @return: id number to be used in L{remove_event_source} 1968 """
1969 - def wrap( source, cb_condition ):
1970 on_in = bool( cb_condition & gobject.IO_IN ) 1971 on_out = bool( cb_condition & gobject.IO_OUT ) 1972 on_urgent = bool( cb_condition & gobject.IO_PRI ) 1973 on_error = bool( cb_condition & gobject.IO_ERR ) 1974 on_hungup = bool( cb_condition & gobject.IO_HUP ) 1975 return callback( self, source, on_in=on_in, 1976 on_out=on_out, on_urgent=on_urgent, 1977 on_error=on_error, on_hungup=on_hungup )
1978 # wrap() 1979 1980 condition = 0 1981 if on_in: 1982 condition |= gobject.IO_IN 1983 if on_out: 1984 condition |= gobject.IO_OUT 1985 if on_urgent: 1986 condition |= gobject.IO_PRI 1987 if on_error: 1988 condition |= gobject.IO_ERR 1989 if on_hungup: 1990 condition |= gobject.IO_HUP 1991 return gobject.io_add_watch( file, condition, wrap )
1992 # io_watch() 1993 1994
1995 - def remove_event_source( self, event_id ):
1996 """Remove an event generator like those created by L{timeout_add}, 1997 L{idle_add} or L{io_watch}. 1998 1999 @param event_id: value returned from L{timeout_add}, 2000 L{idle_add} or L{io_watch}. 2001 2002 @return: C{True} if it was removed. 2003 """ 2004 return gobject.source_remove( event_id )
2005 # remove_event_source() 2006 # App 2007 2008
2009 -class Canvas( _EGWidget ):
2010 """The drawing area. 2011 2012 Eagle's drawing area (Canvas) is provided with a frame and an optional 2013 label, together with scrollbars, to make it fit everywhere. 2014 2015 """ 2016 padding = 5 2017 bgcolor= "black" 2018 2019 LEFT = -1 2020 CENTER = 0 2021 RIGHT = 1 2022 2023 FONT_OPTION_BOLD = 1 2024 FONT_OPTION_OBLIQUE = 2 2025 FONT_OPTION_ITALIC = 4 2026 2027 FONT_NAME_NORMAL = "normal" 2028 FONT_NAME_SERIF = "serif" 2029 FONT_NAME_SANS = "sans" 2030 FONT_NAME_MONO = "monospace" 2031 2032 MOUSE_BUTTON_1 = 1 2033 MOUSE_BUTTON_2 = 2 2034 MOUSE_BUTTON_3 = 4 2035 MOUSE_BUTTON_4 = 8 2036 MOUSE_BUTTON_5 = 16 2037 2038 label = _gen_ro_property( "label" ) 2039
2040 - def __init__( self, id, width, height, label="", bgcolor=None, 2041 callback=None ):
2042 """Canvas Constructor. 2043 2044 @param id: unique identifier. 2045 @param width: width of the drawing area in pixels, widget can be 2046 larger or smaller because and will use scrollbars if need. 2047 @param height: height of the drawing area in pixels, widget can be 2048 larger or smaller because and will use scrollbars if need. 2049 @param label: label to display in the widget frame around the 2050 drawing area. 2051 @param bgcolor: color to paint background. 2052 @param callback: function (or list of functions) to call when 2053 mouse state changed in the drawing area. Function will get 2054 as parameters: 2055 - App reference 2056 - Canvas reference 2057 - Button state (or'ed MOUSE_BUTTON_*) 2058 - horizontal positon (x) 2059 - vertical positon (y) 2060 2061 @todo: honor the alpha value while drawing colors. 2062 """ 2063 _EGWidget.__init__( self, id ) 2064 self.label = label 2065 self.width = width 2066 self.height = height 2067 2068 self._pixmap = None 2069 self._callback = _callback_tuple( callback ) 2070 2071 # style and color context must be set just after drawing area is 2072 # attached to a window, otherwise they'll be empty and useless. 2073 # This is done in configure_event. 2074 self._style = None 2075 self._fg_gc_normal = None 2076 self._bg_gc_normal = None 2077 2078 if bgcolor is not None: 2079 self.bgcolor = self.__color_from__( bgcolor ) 2080 2081 self.__setup_gui__( width, height ) 2082 self.__setup_connections__()
2083 # __init__() 2084 2085
2086 - def __setup_gui__( self, width, height ):
2087 self._frame = gtk.Frame( self.label ) 2088 self._sw = gtk.ScrolledWindow() 2089 self._area = gtk.DrawingArea() 2090 2091 self._sw.set_border_width( self.padding ) 2092 2093 self._frame.add( self._sw ) 2094 self._frame.set_shadow_type( gtk.SHADOW_OUT ) 2095 2096 self._area.set_size_request( width, height ) 2097 self._sw.add_with_viewport( self._area ) 2098 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 2099 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 2100 self._sw.show_all() 2101 2102 self._widgets = ( self._frame, )
2103 # __setup_gui__() 2104 2105
2106 - def __set_useful_attributes__( self ):
2107 self._style = self._area.get_style() 2108 self._fg_gc_normal = self._style.fg_gc[ gtk.STATE_NORMAL ] 2109 self._bg_gc_normal = self._style.bg_gc[ gtk.STATE_NORMAL ]
2110 # __set_useful_attributes__() 2111 2112
2113 - def __setup_connections__( self ):
2114 - def configure_event( widget, event ):
2115 if self._pixmap is None: 2116 self.__set_useful_attributes__() 2117 w, h = self._area.size_request() 2118 self.resize( w, h ) 2119 return True
2120 # configure_event() 2121 self._area.connect( "configure_event", configure_event ) 2122 2123
2124 - def expose_event( widget, event ):
2125 x , y, width, height = event.area 2126 gc = widget.get_style().fg_gc[ gtk.STATE_NORMAL ] 2127 widget.window.draw_drawable( gc, self._pixmap, x, y, x, y, 2128 width, height ) 2129 return False
2130 # expose_event() 2131 self._area.connect( "expose_event", expose_event ) 2132 2133
2134 - def get_buttons( state ):
2135 buttons = 0 2136 if state & gtk.gdk.BUTTON1_MASK: 2137 buttons |= self.MOUSE_BUTTON_1 2138 if state & gtk.gdk.BUTTON2_MASK: 2139 buttons |= self.MOUSE_BUTTON_2 2140 if state & gtk.gdk.BUTTON3_MASK: 2141 buttons |= self.MOUSE_BUTTON_3 2142 if state & gtk.gdk.BUTTON4_MASK: 2143 buttons |= self.MOUSE_BUTTON_4 2144 if state & gtk.gdk.BUTTON5_MASK: 2145 buttons |= self.MOUSE_BUTTON_5 2146 return buttons
2147 # get_buttons() 2148 2149 buttons_map = { 2150 1: self.MOUSE_BUTTON_1, 2151 2: self.MOUSE_BUTTON_2, 2152 3: self.MOUSE_BUTTON_3, 2153 4: self.MOUSE_BUTTON_4, 2154 5: self.MOUSE_BUTTON_5, 2155 } 2156
2157 - def button_press_event( widget, event ):
2158 if self._pixmap != None: 2159 btns = get_buttons( event.state ) 2160 btns |= buttons_map[ event.button ] 2161 2162 x = int( event.x ) 2163 y = int( event.y ) 2164 2165 for c in self._callback: 2166 c( self.app, self, btns, x, y ) 2167 return True
2168 # button_press_event() 2169 if self._callback: 2170 self._area.connect( "button_press_event", button_press_event ) 2171 2172
2173 - def button_release_event( widget, event ):
2174 if self._pixmap != None: 2175 btns = get_buttons( event.state ) 2176 btns &= ~buttons_map[ event.button ] 2177 2178 x = int( event.x ) 2179 y = int( event.y ) 2180 2181 for c in self._callback: 2182 c( self.app, self, btns, x, y ) 2183 return True
2184 # button_press_event() 2185 if self._callback: 2186 self._area.connect( "button_release_event", button_release_event ) 2187 2188
2189 - def motion_notify_event( widget, event ):
2190 if self._pixmap is None: 2191 return True 2192 2193 if event.is_hint: 2194 x, y, state = event.window.get_pointer() 2195 else: 2196 x = event.x 2197 y = event.y 2198 state = event.state 2199 2200 2201 btns = get_buttons( state ) 2202 x = int( x ) 2203 y = int( y ) 2204 2205 if btns: 2206 for c in self._callback: 2207 c( self.app, self, btns, x, y ) 2208 2209 return True
2210 # motion_notify_event() 2211 if self._callback: 2212 self._area.connect( "motion_notify_event", motion_notify_event ) 2213 2214 2215 # Enable events 2216 self._area.set_events( gtk.gdk.EXPOSURE_MASK | 2217 gtk.gdk.LEAVE_NOTIFY_MASK | 2218 gtk.gdk.BUTTON_PRESS_MASK | 2219 gtk.gdk.BUTTON_RELEASE_MASK | 2220 gtk.gdk.POINTER_MOTION_MASK | 2221 gtk.gdk.POINTER_MOTION_HINT_MASK )
2222 # __setup_connections__() 2223 2224
2225 - def __get_resize_mode__( self ):
2226 return ( gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND )
2227 # __get_resize_mode__() 2228 2229 2230
2231 - def __color_from__( color ):
2232 """Convert from color to internal representation. 2233 2234 Gets a string, integer or tuple/list arguments and converts into 2235 internal color representation. 2236 """ 2237 a = 255 2238 2239 if isinstance( color, str ): 2240 try: 2241 c = gtk.gdk.color_parse( color ) 2242 r = int( c.red / 65535.0 * 255 ) 2243 g = int( c.green / 65535.0 * 255 ) 2244 b = int( c.blue / 65535.0 * 255 ) 2245 except ValueError, e: 2246 raise ValueError( "%s. color=%r" % ( e, color ) ) 2247 elif isinstance( color, gtk.gdk.Color ): 2248 r = int( color.red / 65535.0 * 255 ) 2249 g = int( color.green / 65535.0 * 255 ) 2250 b = int( color.blue / 65535.0 * 255 ) 2251 elif isinstance( color, int ): 2252 r = ( color >> 16 ) & 0xff 2253 g = ( color >> 8 ) & 0xff 2254 b = ( color & 0xff ) 2255 elif isinstance( color, ( tuple, list ) ): 2256 if len( color) == 3: 2257 r, g, b = color 2258 else: 2259 a, r, g, b = color 2260 2261 return a, r, g, b
2262 # __color_from__() 2263 __color_from__ = staticmethod( __color_from__ ) 2264 2265
2266 - def __to_gtk_color__( self, color ):
2267 r = int( color[ 1 ] / 255.0 * 65535 ) 2268 g = int( color[ 2 ] / 255.0 * 65535 ) 2269 b = int( color[ 3 ] / 255.0 * 65535 ) 2270 return gtk.gdk.Color( r, g, b )
2271 # __to_gtk_color__() 2272 2273
2274 - def __configure_gc__( self, fgcolor=None, bgcolor=None, fill=None, 2275 line_width=None, line_style=None ):
2276 if fgcolor is not None: 2277 fgcolor = self.__color_from__( fgcolor ) 2278 if bgcolor is not None: 2279 bgcolor = self.__color_from__ ( bgcolor ) 2280 2281 k = {} 2282 if fill is not None: 2283 k[ "fill" ] = gtk.gdk.SOLID 2284 if line_width is not None: 2285 k[ "line_width" ] = line_width 2286 if line_style is not None: 2287 k[ "line_style" ] = line_style 2288 2289 gc = self._pixmap.new_gc( **k ) 2290 2291 if fgcolor is not None: 2292 gc.set_rgb_fg_color( self.__to_gtk_color__( fgcolor ) ) 2293 if bgcolor is not None: 2294 gc.set_rgb_bg_color( self.__to_gtk_color__( bgcolor ) ) 2295 return gc
2296 # __configure_gc__() 2297 2298
2299 - def resize( self, width, height ):
2300 """Resize the drawing area.""" 2301 old = self._pixmap 2302 self._pixmap = gtk.gdk.Pixmap( self._area.window, width, height ) 2303 if old is None: 2304 # Paint with bg color 2305 self.clear() 2306 else: 2307 # copy old contents over this 2308 w, h = old.get_size() 2309 self._pixmap.draw_drawable( self._fg_gc_normal, old, 2310 0, 0, 0, 0, w, h )
2311 # resize() 2312 2313
2314 - def draw_image( self, image, x=0, y=0, 2315 width=None, height=None, 2316 src_x=0, src_y=0 ):
2317 """Draw image on canvas. 2318 2319 By default it draws entire image at top canvas corner. 2320 2321 You may restrict which image areas to use with src_x, src_y, width 2322 and height. 2323 2324 You may choose position on canvas with x and y. 2325 """ 2326 if not isinstance( image, Image ): 2327 raise TypeError( ( "image must be instance of Image class, " 2328 "but %s found!" ) % ( type( image ).__name__ ) ) 2329 2330 p = image.__get_gtk_pixbuf__() 2331 2332 if src_x >= p.get_width(): 2333 raise ValueError( "src_x is greater or equal width!" ) 2334 2335 if src_y >= p.get_height(): 2336 raise ValueError( "src_y is greater or equal height!" ) 2337 2338 if width is None or width < 1: 2339 width = p.get_width() 2340 2341 if height is None or height < 1: 2342 height = p.get_height() 2343 2344 if src_x + width > p.get_width(): 2345 width = p.get_width() - src_x 2346 if src_y + height > p.get_height(): 2347 height = p.get_height() - src_y 2348 2349 self._pixmap.draw_pixbuf( self._fg_gc_normal, 2350 p, src_x, src_y, x, y, width, height ) 2351 self._area.queue_draw_area( x, y, width, width )
2352 # draw_image() 2353 2354
2355 - def draw_text( self, text, x=0, y=0, 2356 fgcolor=None, bgcolor=None, 2357 font_name=None, font_size=None, font_options=0, 2358 font_family=None, 2359 width=None, wrap_word=False, 2360 alignment=LEFT, justify=True ):
2361 """Draw text on canvas. 2362 2363 By default text is draw with current font and colors at top canvas 2364 corner. 2365 2366 You may limit width providing a value and choose if it should wrap 2367 at words (wrap_word=True) or characters (wrap_word=False). 2368 2369 2370 Colors can be specified with fgcolor an bgcolor. If not provided, the 2371 system foreground color is used and no background color is used. 2372 2373 Font name, family, size and options may be specified using 2374 font_name, font_family, font_size and font_options, respectively. 2375 Try to avoid using system specific font fames, use those provided 2376 by FONT_NAME_*. 2377 2378 Font options is OR'ed values from FONT_OPTIONS_*. 2379 2380 Font name is a string that have all the information, like 2381 "sans bold 12". This is returned by L{Font}. 2382 2383 Text alignment is one of LEFT, RIGHT or CENTER. 2384 """ 2385 if fgcolor is not None: 2386 fgcolor = self.__to_gtk_color__( self.__color_from__( fgcolor ) ) 2387 if bgcolor is not None: 2388 bgcolor = self.__to_gtk_color__( self.__color_from__( bgcolor ) ) 2389 2390 layout = self._area.create_pango_layout( text ) 2391 if width is not None: 2392 layout.set_width( width * pango.SCALE ) 2393 if wrap_word: 2394 layout.set_wrap( pango.WRAP_WORD ) 2395 2396 layout.set_justify( justify ) 2397 alignment = { self.LEFT: pango.ALIGN_LEFT, 2398 self.CENTER: pango.ALIGN_CENTER, 2399 self.RIGHT: pango.ALIGN_RIGHT }.get( alignment, 2400 pango.ALIGN_CENTER ) 2401 layout.set_alignment( alignment ) 2402 2403 if font_name or font_size or font_options or font_family: 2404 if font_name: 2405 fd = pango.FontDescription( font_name ) 2406 else: 2407 fd = layout.get_context().get_font_description() 2408 2409 if font_size: 2410 fd.set_size( font_size * pango.SCALE) 2411 if font_options: 2412 if font_options & self.FONT_OPTION_BOLD: 2413 fd.set_weight( pango.WEIGHT_BOLD ) 2414 if font_options & self.FONT_OPTION_ITALIC: 2415 fd.set_style( pango.STYLE_ITALIC ) 2416 if font_options & self.FONT_OPTION_OBLIQUE: 2417 fd.set_style( pango.STYLE_OBLIQUE ) 2418 layout.set_font_description( fd ) 2419 2420 self._pixmap.draw_layout( self._fg_gc_normal, x, y, layout, 2421 fgcolor, bgcolor ) 2422 w, h = layout.get_pixel_size() 2423 self._area.queue_draw_area( x, y, w, h )
2424 # draw_text() 2425 2426 2427
2428 - def draw_point( self, x, y, color=None ):
2429 """Draw point.""" 2430 gc = self.__configure_gc__( fgcolor=color ) 2431 self._pixmap.draw_point( gc, x, y ) 2432 self._area.queue_draw_area( x, y, 1, 1 )
2433 # draw_point() 2434 2435
2436 - def draw_points( self, points, color=None ):
2437 """Draw points. 2438 2439 Efficient way to draw more than one point with the same 2440 characteristics. 2441 """ 2442 gc = self.__configure_gc__( fgcolor=color ) 2443 self._pixmap.draw_points( gc, points ) 2444 w, h = self._pixmap.get_size() 2445 self._area.queue_draw_area( 0, 0, w, h )
2446 # draw_poinst() 2447 2448
2449 - def draw_line( self, x0, y0, x1, y1, color=None, size=1 ):
2450 """Draw line.""" 2451 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2452 self._pixmap.draw_line( gc, x0, y0, x1, y1 ) 2453 2454 size2 = size * 2 2455 2456 w, h = abs( x1 - x0 ) + size2, abs( y1 - y0 ) + size2 2457 x, y = max( min( x0, x1 ) - size, 0 ), max( min( y0, y1 ) - size, 0 ) 2458 self._area.queue_draw_area( x, y, w, h )
2459 # draw_line() 2460 2461
2462 - def draw_segments( self, segments, color=None, size=1 ):
2463 """Draw line segments. 2464 2465 Efficient way to draw more than one line with the same 2466 characteristics. 2467 2468 Lines are not connected, use L{draw_lines} instead. 2469 """ 2470 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2471 self._pixmap.draw_segments( gc, segments ) 2472 w, h = self._pixmap.get_size() 2473 self._area.queue_draw_area( 0, 0, w, h )
2474 # draw_segments() 2475 2476
2477 - def draw_lines( self, points, color=None, size=1 ):
2478 """Draw lines connecting points. 2479 2480 Points are connected using lines, but first and last points 2481 are not connected, use L{draw_polygon} instead. 2482 """ 2483 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2484 self._pixmap.draw_lines( gc, points ) 2485 w, h = self._pixmap.get_size() 2486 self._area.queue_draw_area( 0, 0, w, h )
2487 # draw_lines() 2488 2489
2490 - def draw_rectangle( self, x, y, width, height, color=None, size=1, 2491 fillcolor=None, filled=False ):
2492 """Draw rectagle. 2493 2494 If L{filled} is C{True}, it will be filled with L{fillcolor}. 2495 2496 If L{color} is provided, it will draw the rectangle's frame, even 2497 if L{filled} is C{True}. 2498 """ 2499 if filled: 2500 gc = self.__configure_gc__( fgcolor=fillcolor, fill=filled ) 2501 self._pixmap.draw_rectangle( gc, True, x, y, width, height ) 2502 2503 if size > 0: 2504 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2505 self._pixmap.draw_rectangle( gc, False, x, y, width, height ) 2506 else: 2507 size = 0 2508 2509 half = size / 2 2510 self._area.queue_draw_area( x-half, y-half, width+size, height+size )
2511 # draw_rectangle() 2512 2513
2514 - def draw_arc( self, x, y, width, height, start_angle, end_angle, 2515 color=None, size=1, fillcolor=None, filled=False ):
2516 """Draw arc on canvas. 2517 2518 Arc will be the part of an ellipse that starts at ( L{x}, L{y} ) 2519 and have size of L{width}xL{height}. 2520 2521 L{start_angle} and L{end_angle} are in radians, starts from the 2522 positive x-axis and are counter-clockwise. 2523 2524 If L{filled} is C{True}, it will be filled with L{fillcolor}. 2525 2526 If L{color} is provided, it will draw the arc's frame, even 2527 if L{filled} is C{True}. Frame here is just the curve, not the 2528 straight lines that are limited by L{start_angle} and L{end_angle}. 2529 """ 2530 # GTK: angles are in 1/64 of degree and must be integers 2531 mult = 180.0 / 3.1415926535897931 * 64.0 2532 start_angle = int( mult * start_angle ) 2533 end_angle = int( mult * end_angle ) 2534 2535 if filled: 2536 gc = self.__configure_gc__( fgcolor=fillcolor, fill=filled ) 2537 self._pixmap.draw_arc( gc, True, x, y, width, height, 2538 start_angle, end_angle ) 2539 if size > 0: 2540 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2541 self._pixmap.draw_arc( gc, False, x, y, width, height, 2542 start_angle, end_angle ) 2543 else: 2544 size = 0 2545 2546 half = size / 2 2547 self._area.queue_draw_area( x-half, y-half, width+size, height+size )
2548 # draw_arc() 2549 2550
2551 - def draw_polygon( self, points, color=None, size=1, 2552 fillcolor=None, filled=False ):
2553 """Draw polygon on canvas. 2554 2555 If L{filled} is C{True}, it will be filled with L{fillcolor}. 2556 2557 If L{color} is provided, it will draw the polygon's frame, even 2558 if L{filled} is C{True}. 2559 """ 2560 if filled: 2561 gc = self.__configure_gc__( fgcolor=fillcolor, fill=filled ) 2562 self._pixmap.draw_polygon( gc, True, points ) 2563 2564 if size > 0: 2565 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2566 self._pixmap.draw_polygon( gc, False, points ) 2567 else: 2568 size = 0 2569 2570 w, h = self._pixmap.get_size() 2571 self._area.queue_draw_area( 0, 0, w, h )
2572 # draw_polygon() 2573 2574
2575 - def clear( self ):
2576 """Clear using bgcolor.""" 2577 self.fill( self.bgcolor )
2578 # clear() 2579 2580
2581 - def fill( self, color ):
2582 w, h = self.get_size() 2583 self.draw_rectangle( 0, 0, w, h, color, size=0, fillcolor=color, 2584 filled=True )
2585 # fill() 2586 2587
2588 - def get_size( self ):
2589 return self._pixmap.get_size()
2590 # get_size() 2591 2592
2593 - def get_image( self ):
2594 """Get the L{Image} that represents this drawing area.""" 2595 w, h = self._pixmap.get_size() 2596 img = gtk.gdk.Pixbuf( gtk.gdk.COLORSPACE_RGB, True, 8, w, h ) 2597 img.get_from_drawable( self._pixmap, self._area.get_colormap(), 2598 0, 0, 0, 0, w, h ) 2599 return Image( __int_image__=img )
2600 # get_image() 2601 2602
2603 - def __str__( self ):
2604 return "%s( id=%r, width=%r, height=%r, label=%r )" % \ 2605 ( self.__class__.__name__, self.id, self.width, self.height, 2606 self.label )
2607 # __str__() 2608 __repr__ = __str__
2609 # Canvas 2610 2611
2612 -class _MultiLineEntry( gtk.ScrolledWindow ):
2613 - def __init__( self ):
2614 gtk.ScrolledWindow.__init__( self ) 2615 self.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 2616 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 2617 self.set_shadow_type( gtk.SHADOW_IN ) 2618 2619 self.textview = gtk.TextView() 2620 self.add( self.textview ) 2621 2622 self.textview.set_editable( True ) 2623 self.textview.set_cursor_visible( True ) 2624 self.textview.set_left_margin( 2 ) 2625 self.textview.set_right_margin( 2 ) 2626 self.textview.get_buffer().connect( "changed", self.__emit_changed__ )
2627 # __init__() 2628 2629
2630 - def __emit_changed__( self, *args ):
2631 self.emit( "changed" )
2632 # __emit_changed__ 2633 2634
2635 - def set_text( self, value ):
2636 self.textview.get_buffer().set_text( value )
2637 # set_text() 2638 2639
2640 - def get_text( self ):
2641 b = self.textview.get_buffer() 2642 return b.get_text( b.get_start_iter(), b.get_end_iter() )
2643 # get_text() 2644 # _MultiLineEntry 2645 gobject.signal_new( "changed", 2646 _MultiLineEntry, 2647 gobject.SIGNAL_RUN_LAST, 2648 gobject.TYPE_NONE, 2649 tuple() ) 2650 2651
2652 -class Entry( _EGWidLabelEntry ):
2653 """Text entry. 2654 2655 The simplest user input component. Its contents are free-form and not 2656 filtered/masked. 2657 """ 2658 value = _gen_ro_property( "value" ) 2659 callback = _gen_ro_property( "callback" ) 2660 multiline = _gen_ro_property( "multiline" ) 2661
2662 - def __init__( self, id, label="", value="", callback=None, 2663 persistent=False, multiline=False ):
2664 """Entry constructor. 2665 2666 @param id: unique identifier. 2667 @param label: what to show on a label on the left side of the widget. 2668 @param value: initial content. 2669 @param callback: function (or list of functions) that will 2670 be called when this widget have its data changed. 2671 Function will receive as parameters: 2672 - App reference 2673 - Widget reference 2674 - new value 2675 @param persistent: if this widget should save its data between 2676 sessions. 2677 @param multiline: if this entry can be multi-line. 2678 """ 2679 self.value = value 2680 self.callback = _callback_tuple( callback ) 2681 self.multiline = bool( multiline ) 2682 2683 _EGWidLabelEntry.__init__( self, id, persistent, label ) 2684 2685 self.__setup_gui__() 2686 self.__setup_connections__()
2687 # __init__() 2688 2689
2690 - def __setup_gui__( self ):
2691 if self.multiline: 2692 self._entry = _MultiLineEntry() 2693 else: 2694 self._entry = gtk.Entry() 2695 2696 self._entry.set_name( self.id ) 2697 self._entry.set_text( self.value ) 2698 2699 _EGWidLabelEntry.__setup_gui__( self )
2700 # __setup_gui__() 2701 2702
2703 - def __setup_connections__( self ):
2704 - def callback( obj ):
2705 v = self.get_value() 2706 self.app.data_changed( self, v ) 2707 for c in self.callback: 2708 c( self.app, self, v )
2709 # callback() 2710 self._entry.connect( "changed", callback )
2711 # __setup_connections__() 2712 2713
2714 - def get_value( self ):
2715 return self._entry.get_text()
2716 # get_value() 2717 2718
2719 - def set_value( self, value ):
2720 self._entry.set_text( str( value ) )
2721 # set_value() 2722 2723
2724 - def __get_resize_mode__( self ):
2725 """Resize mode. 2726 2727 First tuple of tuple is about horizontal mode for label and entry. 2728 Second tuple of tuple is about vertical mode for label and entry. 2729 """ 2730 if self.multiline: 2731 return ( ( gtk.FILL, gtk.FILL | gtk.EXPAND ), 2732 ( gtk.FILL, gtk.FILL | gtk.EXPAND ) ) 2733 else: 2734 return ( ( gtk.FILL, gtk.FILL | gtk.EXPAND ), 2735 ( gtk.FILL, gtk.FILL ) )
2736 # __get_resize_mode__() 2737 # Entry 2738 2739
2740 -class Password( Entry ):
2741 """Password entry. 2742 2743 Like L{Entry}, but will show '*' instead of typed chars. 2744 """
2745 - def __init__( self, id, label="", value="", callback=None, 2746 persistent=False ):
2747 """Password constructor. 2748 2749 @param id: unique identifier. 2750 @param label: what to show on a label on the left side of the widget. 2751 @param value: initial content. 2752 @param callback: function (or list of functions) that will 2753 be called when this widget have its data changed. 2754 Function will receive as parameters: 2755 - App reference 2756 - Widget reference 2757 - new value 2758 @param persistent: if this widget should save its data between 2759 sessions. 2760 """ 2761 Entry.__init__( self, id, label, value, callback, persistent ) 2762 self._entry.set_visibility( False )
2763 # __init__() 2764 # Password 2765 2766
2767 -class Spin( _EGWidLabelEntry ):
2768 """Spin button entry. 2769 2770 Spin buttons are numeric user input that checks if value is inside 2771 a specified range. It also provides small buttons to help incrementing/ 2772 decrementing value. 2773 """ 2774 default_min = -1e60 2775 default_max = 1e60 2776 2777 value = _gen_ro_property( "value" ) 2778 min = _gen_ro_property( "min" ) 2779 max = _gen_ro_property( "max" ) 2780 step = _gen_ro_property( "step" ) 2781 digits = _gen_ro_property( "digits" ) 2782 2783 callback = _gen_ro_property( "callback" ) 2784
2785 - def __init__( self, id, label="", 2786 value=None, min=None, max=None, step=None, digits=3, 2787 callback=None, persistent=False ):
2788 """Spin constructor. 2789 2790 @param id: unique identifier. 2791 @param label: what to show on a label on the left side of the widget. 2792 @param value: initial content. 2793 @param min: minimum value. If None, L{default_min} will be used. 2794 @param max: maximum value. If None, L{default_max} will be used. 2795 @param step: step to use to decrement/increment using buttons. 2796 @param digits: how many digits to show. 2797 @param callback: function (or list of functions) that will 2798 be called when this widget have its data changed. 2799 Function will receive as parameters: 2800 - App reference 2801 - Widget reference 2802 - new value 2803 @param persistent: if this widget should save its data between 2804 sessions. 2805 """ 2806 self.value = value 2807 self.min = min 2808 self.max = max 2809 self.step = step 2810 self.digits = digits 2811 self.callback = _callback_tuple( callback ) 2812 2813 _EGWidLabelEntry.__init__( self, id, persistent, label ) 2814 2815 self.__setup_connections__()
2816 # __init__() 2817 2818
2819 - def __setup_gui__( self ):
2820 k = {} 2821 2822 if self.value is not None: 2823 k[ "value" ] = self.value 2824 2825 if self.min is not None: 2826 k[ "lower" ] = self.min 2827 else: 2828 k[ "lower" ] = self.default_min 2829 2830 if self.max is not None: 2831 k[ "upper" ] = self.max 2832 else: 2833 k[ "upper" ] = self.default_max 2834 2835 if self.step is not None: 2836 k[ "step_incr" ] = self.step 2837 k[ "page_incr" ] = self.step * 2 2838 else: 2839 k[ "step_incr" ] = 1 2840 k[ "page_incr" ] = 2 2841 2842 adj = gtk.Adjustment( **k ) 2843 self._entry = gtk.SpinButton( adjustment=adj, digits=self.digits ) 2844 self._entry.set_name( self.id ) 2845 self._entry.set_numeric( True ) 2846 self._entry.set_snap_to_ticks( False ) 2847 2848 _EGWidLabelEntry.__setup_gui__( self )
2849 # __setup_gui__() 2850 2851
2852 - def __setup_connections__( self ):
2853 - def callback( obj, *args ):
2854 v = self.get_value() 2855 self.app.data_changed( self, v ) 2856 for c in self.callback: 2857 c( self.app, self, v )
2858 # callback() 2859 self._entry.connect( "notify::value", callback )
2860 # __setup_connections__() 2861 2862
2863 - def set_value( self, value ):
2864 self._entry.set_value( float( value ) )
2865 # set_value() 2866 # Spin 2867 2868
2869 -class IntSpin( Spin ):
2870 """Integer-only Spin button. 2871 2872 Convenience widget that behaves like L{Spin} with digits set to 2873 zero and also ensure L{set_value} and L{get_value} operates on 2874 integers. 2875 """ 2876 default_min = -sys.maxint 2877 default_max = sys.maxint 2878
2879 - def __init__( self, id, label="", 2880 value=None, min=None, max=None, step=None, 2881 callback=None, persistent=False ):
2882 """Integer Spin constructor. 2883 2884 @param id: unique identifier. 2885 @param label: what to show on a label on the left side of the widget. 2886 @param value: initial content. 2887 @param min: minimum value. If None, L{default_min} will be used. 2888 @param max: maximum value. If None, L{default_max} will be used. 2889 @param step: step to use to decrement/increment using buttons. 2890 @param callback: function (or list of functions) that will 2891 be called when this widget have its data changed. 2892 Function will receive as parameters: 2893 - App reference 2894 - Widget reference 2895 - new value 2896 @param persistent: if this widget should save its data between 2897 sessions. 2898 """ 2899 if value is not None: 2900 value = int( value ) 2901 if min is not None: 2902 min = int( min ) 2903 if max is not None: 2904 max = int( max ) 2905 if step is not None: 2906 step = int( step ) 2907 Spin.__init__( self, id, label, value, min, max, step, 0, callback, 2908 persistent )
2909 # __init__() 2910 2911
2912 - def __setup_gui__( self ):
2913 if self.min is not None: 2914 vmin = self.min 2915 else: 2916 vmin = self.default_min 2917 2918 if self.max is not None: 2919 vmax = self.max 2920 else: 2921 vmax = self.default_max 2922 2923 if self.step is not None: 2924 sys.stderr.write( "Maemo HildonNumberEditor doesn't support step\n" ) 2925 2926 self._entry = hildon.NumberEditor( vmin, vmax ) 2927 self._entry.set_name( self.id ) 2928 if self.value: 2929 self._entry.set_value( self.value ) 2930 2931 _EGWidLabelEntry.__setup_gui__( self )
2932 # __setup_gui__() 2933 2934
2935 - def __setup_connections__( self ):
2936 - def callback( obj, *args ):
2937 v = self.get_value() 2938 self.app.data_changed( self, v ) 2939 for c in self.callback: 2940 c( self.app, self, v )
2941 # callback() 2942 self._entry.connect( "notify::value", callback )
2943 # __setup_connections__() 2944 2945
2946 - def get_value( self ):
2947 return int( Spin.get_value( self ) )
2948 # get_value() 2949 2950
2951 - def set_value( self, value ):
2952 self._entry.set_value( int( value ) )
2953 # set_value() 2954 # IntSpin 2955 2956
2957 -class UIntSpin( IntSpin ):
2958 """Unsigned integer-only Spin button. 2959 2960 Convenience widget that behaves like L{IntSpin} with minimum value 2961 always greater or equal zero. 2962 """ 2963 default_min = 0 2964
2965 - def __init__( self, id, label="", 2966 value=None, min=0, max=None, step=None, 2967 callback=None, persistent=False ):
2968 """Unsigned Integer Spin constructor. 2969 2970 @param id: unique identifier. 2971 @param label: what to show on a label on the left side of the widget. 2972 @param value: initial content. 2973 @param min: minimum value, must be greater or equal zero. 2974 @param max: maximum value. If None, L{default_max} will be used. 2975 @param step: step to use to decrement/increment using buttons. 2976 @param callback: function (or list of functions) that will 2977 be called when this widget have its data changed. 2978 Function will receive as parameters: 2979 - App reference 2980 - Widget reference 2981 - new value 2982 @param persistent: if this widget should save its data between 2983 sessions. 2984 """ 2985 if min < 0: 2986 raise ValueError( "UIntSpin cannot have min < 0!" ) 2987 Spin.__init__( self, id, label, value, min, max, step, 0, callback, 2988 persistent )
2989 # __init__() 2990 # UIntSpin 2991 2992
2993 -class Color( _EGWidLabelEntry ):
2994 """Button to select colors. 2995 2996 It show current/last selected color and may pop-up a new dialog to 2997 select a new one. 2998 """ 2999 color = _gen_ro_property( "color" ) 3000 callback = _gen_ro_property( "callback" ) 3001
3002 - def __init__( self, id, label="", color=0, 3003 callback=None, 3004 persistent=False ):
3005 """Color selector constructor. 3006 3007 @param id: unique identifier. 3008 @param label: what to show on a label on the left side of the widget. 3009 @param color: initial content. May be a triple with elements within 3010 the range 0-255, an string with color in HTML format or even 3011 a color present in X11's rgb.txt. 3012 @param callback: function (or list of functions) that will 3013 be called when this widget have its data changed. 3014 Function will receive as parameters: 3015 - App reference 3016 - Widget reference 3017 - new value 3018 @param persistent: if this widget should save its data between 3019 sessions. 3020 """ 3021 self.color = self.color_from( color ) 3022 self.callback = _callback_tuple( callback ) 3023 _EGWidLabelEntry.__init__( self, id, persistent, label ) 3024 3025 self.__setup_connections__()
3026 # __init__() 3027 3028
3029 - def color_from( color ):
3030 a = 255 3031 if isinstance( color, str ): 3032 try: 3033 c = gtk.gdk.color_parse( color ) 3034 r = int( c.red / 65535.0 * 255 ) 3035 g = int( c.green / 65535.0 * 255 ) 3036 b = int( c.blue / 65535.0 * 255 ) 3037 except ValueError, e: 3038 raise ValueError( "%s. color=%r" % ( e, color ) ) 3039 3040 if isinstance( color, int ): 3041 r = ( color >> 16 ) & 0xff 3042 g = ( color >> 8 ) & 0xff 3043 b = ( color & 0xff ) 3044 elif isinstance( color, ( tuple, list ) ): 3045 if len( color ) == 3: 3046 r, g, b = color 3047 else: 3048 a, r, g, b = color 3049 3050 return a, r, g, b
3051 # color_from() 3052 color_from = staticmethod( color_from ) 3053 3054
3055 - def __setup_gui__( self ):
3056 r = int( self.color[ 1 ] / 255.0 * 65535 ) 3057 g = int( self.color[ 2 ] / 255.0 * 65535 ) 3058 b = int( self.color[ 3 ] / 255.0 * 65535 ) 3059 3060 c = gtk.gdk.Color( r, g, b ) 3061 3062 self._entry = hildon.ColorButton() 3063 self._entry.set_color( c ) 3064 self._entry.set_name( self.id ) 3065 _EGWidLabelEntry.__setup_gui__( self )
3066 # __setup_gui__() 3067 3068
3069 - def __setup_connections__( self ):
3070 - def callback( obj, *args ):
3071 v = self.get_value() 3072 self.app.data_changed( self, v ) 3073 for c in self.callback: 3074 c( self.app, self, v )
3075 # callback() 3076 self._entry.connect( "notify::color", callback )
3077 # __setup_connections__() 3078 3079
3080 - def get_value( self ):
3081 """Return a tuple with ( alpha, red, green, blue ) 3082 each in 0-255 range. 3083 """ 3084 c = self._entry.get_color() 3085 r = int( c.red / 65535.0 * 255 ) 3086 g = int( c.green / 65535.0 * 255 ) 3087 b = int( c.blue / 65535.0 * 255 ) 3088 3089 return ( r, g, b )
3090 # get_value() 3091 3092
3093 - def set_value( self, value ):
3094 """ 3095 @param value: May be a triple with elements within 3096 the range 0-255, an string with color in HTML format or even 3097 a color present in X11's rgb.txt. 3098 """ 3099 a, r, g, b = self.color_from( value ) 3100 3101 r = int( r / 255.0 * 65535 ) 3102 g = int( g / 255.0 * 65535 ) 3103 b = int( b / 255.0 * 65535 ) 3104 3105 c = gtk.gdk.Color( r, g, b ) 3106 self._entry.set_color( c )
3107 # set_value() 3108 # Color 3109 3110
3111 -class Font( _EGWidLabelEntry ):
3112 """Button to select fonts. 3113 3114 It show current/last selected font and may pop-up a new dialog to 3115 select a new one. 3116 """ 3117 font = _gen_ro_property( "font" ) 3118 callback = _gen_ro_property( "callback" ) 3119
3120 - def __init__( self, id, label="", font="sans 12", callback=None, 3121 persistent=False ):
3122 """Font selector constructor. 3123 3124 @param id: unique identifier. 3125 @param label: what to show on a label on the left side of the widget. 3126 @param font: initial content. 3127 @param callback: function (or list of functions) that will 3128 be called when this widget have its data changed. 3129 Function will receive as parameters: 3130 - App reference 3131 - Widget reference 3132 - new value 3133 @param persistent: if this widget should save its data between 3134 sessions. 3135 """ 3136 self.font = font 3137 self.callback = _callback_tuple( callback ) 3138 _EGWidLabelEntry.__init__( self, id, persistent, label ) 3139 3140 self.__setup_connections__()
3141 # __init__() 3142 3143
3144 - def __setup_gui__( self ):
3145 self._entry = gtk.FontButton( self.font ) 3146 self._entry.set_name( self.id ) 3147 self._entry.set_show_style( True ) 3148 _EGWidLabelEntry.__setup_gui__( self )
3149 # __setup_gui__() 3150 3151
3152 - def __setup_connections__( self ):
3153 - def callback( obj ):
3154 v = self.get_value() 3155 self.app.data_changed( self, v ) 3156 for c in self.callback: 3157 c( self.app, self, v )
3158 # callback() 3159 self._entry.connect( "font-set", callback )
3160 # __setup_connections__() 3161 3162
3163 - def get_value( self ):
3164 return self._entry.get_font_name()
3165 # get_value() 3166 3167
3168 - def set_value( self, value ):
3169 self._entry.set_font_name( value )
3170 # set_value() 3171 # Font 3172 3173
3174 -class Selection( _EGWidLabelEntry ):
3175 """Selection box (aka Combo box). 3176 3177 Selection or combo box is an element that allow you to select one of 3178 various pre-defined values. 3179 """ 3180 options = _gen_ro_property( "options" ) 3181 active = _gen_ro_property( "active" ) 3182
3183 - def __init__( self, id, label="", options=None, active=None, 3184 callback=None, persistent=False ):
3185 """Selection constructor. 3186 3187 @param id: unique identifier. 3188 @param label: what to show on a label on the left side of the widget. 3189 @param options: list of possible values. 3190 @param active: selected element. 3191 @param callback: function (or list of functions) that will 3192 be called when this widget have its data changed. 3193 Function will receive as parameters: 3194 - App reference 3195 - Widget reference 3196 - new value 3197 @param persistent: if this widget should save its data between 3198 sessions. 3199 """ 3200 self.options = options or [] 3201 self.active = active 3202 self.callback = _callback_tuple( callback ) 3203 _EGWidLabelEntry.__init__( self, id, persistent, label ) 3204 3205 self.__setup_connections__()
3206 # __init__() 3207 3208
3209 - def __setup_gui__( self ):
3210 self._entry = gtk.combo_box_new_text() 3211 self._entry.set_name( self.id ) 3212 for i, o in enumerate( self.options ): 3213 self._entry.append_text( str( o ) ) 3214 if self.active == o: 3215 self._entry.set_active( i ) 3216 3217 _EGWidLabelEntry.__setup_gui__( self )
3218 # __setup_gui__() 3219 3220
3221 - def __setup_connections__( self ):
3222 - def callback( obj ):
3223 v = self.get_value() 3224 self.app.data_changed( self, v ) 3225 for c in self.callback: 3226 c( self.app, self, v )
3227 # callback() 3228 self._entry.connect( "changed", callback )
3229 # __setup_connections__() 3230 3231
3232 - def get_value( self ):
3233 return self._entry.get_active_text()
3234 # get_value() 3235 3236
3237 - def set_value( self, value ):
3238 for i, o in enumerate( self._entry.get_model() ): 3239 if o[ 0 ] == value: 3240 self._entry.set_active( i )
3241 # set_value() 3242 3243
3244 - def append( self, value, set_active=False ):
3245 """Append new value to available options. 3246 3247 @param value: string that is not already an option. 3248 3249 @raise: ValueError: if value is already an option. 3250 """ 3251 if value not in self.items(): 3252 self._entry.append_text( value ) 3253 if set_active: 3254 self.set_value( value ) 3255 else: 3256 raise ValueError( "value already in selection" )
3257 # append() 3258 3259
3260 - def prepend( self, value ):
3261 """Prepend new value to available options. 3262 3263 @param value: string that is not already an option. 3264 3265 @raise: ValueError: if value is already an option. 3266 """ 3267 if value not in self.items(): 3268 self._entry.prepend_text( value ) 3269 if set_active: 3270 self.set_value( value ) 3271 else: 3272 raise ValueError( "value already in selection" )
3273 # prepend() 3274 3275
3276 - def insert( self, position, value ):
3277 """Insert new option at position. 3278 3279 @param value: string that is not already an option. 3280 3281 @raise: ValueError: if value is already an option. 3282 """ 3283 if value not in self.items(): 3284 self._entry.insert_text( position, value ) 3285 if set_active: 3286 self.set_value( value ) 3287 else: 3288 raise ValueError( "value already in selection" )
3289 # insert() 3290 3291
3292 - def remove( self, value ):
3293 """Remove given value from available options. 3294 3295 @param value: string that is an option. 3296 3297 @raise ValueError: if value is not already an option. 3298 """ 3299 for i, o in enumerate( self._entry.get_model() ): 3300 if o[ 0 ] == value: 3301 self._entry.remove_text( i ) 3302 return 3303 3304 raise ValueError( "value not in selection" )
3305 # remove() 3306 3307
3308 - def items( self ):
3309 """Returns every item/option in this selection.""" 3310 return [ str( x[ 0 ] ) for x in self._entry.get_model() ]
3311 # items() 3312 options = items 3313 3314
3315 - def __len__( self ):
3316 return len( self._entry.get_model() )
3317 # __len__() 3318 3319
3320 - def __contains__( self, value ):
3321 return value in self.items()
3322 # __contains__() 3323 3324
3325 - def __iadd__( self, value ):
3326 """Same as L{append}""" 3327 self.append( value )
3328 # __iadd__() 3329 3330
3331 - def __isub__( self, value ):
3332 """Same as L{remove}""" 3333 self.remove( value )
3334 # __isub__() 3335 # Selection 3336 3337
3338 -class Progress( _EGWidLabelEntry ):
3339 """Progress bar.""" 3340 value = _gen_ro_property( "value" ) 3341
3342 - def __init__( self, id, label="", value=0.0 ):
3343 """Progress bar constructor. 3344 3345 0 <= value <= 1.0 3346 3347 @param id: unique identifier. 3348 @param label: what to show on a label on the left side of the widget. 3349 @param value: initial content ( 0.0 <= value <= 1.0 ) 3350 """ 3351 self.value = value 3352 _EGWidLabelEntry.__init__( self, id, False, label )
3353 # __init__() 3354
3355 - def __setup_gui__( self ):
3356 self._entry = gtk.ProgressBar() 3357 self._entry.set_name( self.id ) 3358 self.set_value( self.value ) 3359 _EGWidLabelEntry.__setup_gui__( self )
3360 # __setup_gui__() 3361 3362
3363 - def get_value( self ):
3364 return self._entry.get_fraction()
3365 # get_value() 3366 3367
3368 - def set_value( self, value ):
3369 if 1.0 < value <= 100.0: 3370 value /= 100.0 3371 elif not ( 0.0 <= value <= 1.0 ): 3372 raise ValueError( ( "Progress value of \"%s\" must be " 3373 "between 0.0 and 1.0!" ) % self.id ) 3374 self._entry.set_fraction( value ) 3375 self._entry.set_text( "%d%%" % ( int( value * 100 ), ) )
3376 # set_value() 3377 3378
3379 - def pulse( self ):
3380 """Animate progress bar.""" 3381 self._entry.pulse()
3382 # pulse() 3383 # Progress 3384 3385
3386 -class CheckBox( _EGDataWidget ):
3387 """Check box. 3388 3389 Check box is an component that have only two values: True (checked) or 3390 False (unchecked). 3391 """ 3392 state = _gen_ro_property( "state" ) 3393
3394 - def __init__( self, id, label="", state=False, callback=None, 3395 persistent=False ):
3396 """Check box constructor. 3397 3398 @param id: unique identifier. 3399 @param label: what to show on a label on the left side of the widget. 3400 @param state: initial state. 3401 @param callback: function (or list of functions) that will 3402 be called when this widget have its data changed. 3403 Function will receive as parameters: 3404 - App reference 3405 - Widget reference 3406 - new value 3407 @param persistent: if this widget should save its data between 3408 sessions. 3409 """ 3410 self.label = label 3411 self.state = state 3412 self.callback = _callback_tuple( callback ) 3413 3414 _EGDataWidget.__init__( self, id, persistent ) 3415 3416 self.__setup_gui__() 3417 self.__setup_connections__()
3418 # __init__() 3419 3420
3421 - def __setup_gui__( self ):
3422 self._wid = gtk.CheckButton( self.label ) 3423 self._wid.set_name( self.id ) 3424 self._wid.set_active( self.state ) 3425 self._widgets = ( self._wid, )
3426 # __setup_gui__() 3427 3428
3429 - def __setup_connections__( self ):
3430 - def callback( obj ):
3431 v = self.get_value() 3432 self.app.data_changed( self, v ) 3433 for c in self.callback: 3434 c( self.app, self, v )
3435 # callback() 3436 self._wid.connect( "toggled", callback )
3437 # __setup_connections__() 3438 3439
3440 - def get_value( self ):
3441 return self._wid.get_active()
3442 # get_value() 3443 # CheckBox 3444 3445
3446 -class Group( _EGWidget ):
3447 """Group of various components. 3448 3449 Group is a component that holds other components, always in a vertical 3450 layout. 3451 3452 Group has a frame and may show a label. 3453 """ 3454 children = _gen_ro_property( "children" ) 3455
3456 - def _get_app( self ):
3457 try: 3458 return self.__ro_app 3459 except AttributeError: 3460 return None
3461 # _get_app() 3462
3463 - def _set_app( self, value ):
3464 # We need to overload app setter in order to add 3465 # children widgets to application as soon as we know the app 3466 try: 3467 v = self.__ro_app 3468 except AttributeError: 3469 v = None 3470 if v is None: 3471 self.__ro_app = value 3472 self.__add_widgets_to_app__() 3473 else: 3474 raise Exception( "Read Only property 'app'." )
3475 # _set_app() 3476 app = property( _get_app, _set_app ) 3477 3478
3479 - def __init__( self, id, label="", children=None ):
3480 """Group constructor. 3481 3482 @param id: unique identified. 3483 @param label: displayed at top-left. 3484 @param children: a list of eagle widgets that this group contains. 3485 They're presented in vertical layout. 3486 """ 3487 _EGWidget.__init__( self, id ) 3488 self.label = label 3489 self.children = children or tuple() 3490 3491 self.__setup_gui__()
3492 # __init__() 3493 3494
3495 - def __setup_gui__( self ):
3496 self._frame = gtk.Frame( self.label ) 3497 self._frame.set_name( self.id ) 3498 self._contents = _Table( id=( "%s-contents" % self.id ), 3499 children=self.children ) 3500 self._frame.add( self._contents ) 3501 self._widgets = ( self._frame, )
3502 # __setup_gui__() 3503 3504
3505 - def __add_widgets_to_app__( self ):
3506 for w in self.children: 3507 self.app.__add_widget__( w )
3508 # __add_widgets_to_app__() 3509 # Group 3510 3511
3512 -class Table( _EGWidget ):
3513 """Data table. 3514 3515 Each column should have only one type, it will be checked. 3516 Can be accessed as a python list: 3517 3518 >>> t = Table( 't', 'table', [ 1, 2, 3 ] ) 3519 >>> t[ 0 ] 3520 [ 1 ] 3521 >>> del t[ 1 ] 3522 >>> t[ : ] 3523 [ 1, 3 ] 3524 """ 3525 spacing = 3 3526 3527
3528 - class Row( object ):
3529 # Used to hide gtk.ListStore
3530 - def __init__( self, items ):
3531 self.__items = items
3532 # __init__() 3533 3534
3535 - def __str__( self ):
3536 return "[" + ", ".join( [ str( x ) for x in self.__items ] ) + "]"
3537 # __str__() 3538 __repr__ = __str__ 3539 3540
3541 - def __len__( self ):
3542 return len( self.__items )
3543 # __len__() 3544 3545
3546 - def __nonzero__( self ):
3547 return self.__items.__nonzero__()
3548 # __nonzero__() 3549 3550
3551 - def __getitem__( self, index ):
3552 return self.__items[ index ]
3553 # __getitem__() 3554 3555
3556 - def __setitem__( self, index, value ):
3557 self.__items[ index ] = value
3558 # __setitem__() 3559 3560
3561 - def __delitem__( self, index ):
3562 del self.__items[ index ]
3563 # __delitem__() 3564 3565
3566 - def __contains__( self, element ):
3567 return element in self.__items
3568 # __contains__() 3569 3570
3571 - def __getslice__( self, start, end ):
3572 slice = [] 3573 3574 l = len( self.__items ) 3575 while start < 0: 3576 start += l 3577 while end < 0: 3578 end += l 3579 3580 start = min( start, l ) 3581 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 3582 3583 for i in xrange( start, end ): 3584 slice.append( self.__items[ i ] ) 3585 return slice
3586 # __getslice__() 3587 3588
3589 - def __setslice__( self, start, end, items ):
3590 l = len( self.__items ) 3591 while start < 0: 3592 start += l 3593 while end < 0: 3594 end += l 3595 3596 start = min( start, l ) 3597 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 3598 3599 l2 = len( items ) 3600 if end - start > l2: 3601 end = start + l2 3602 3603 j = 0 3604 for i in xrange( start, end ): 3605 self.__items[ i ] = items[ j ] 3606 j += 1
3607 # __setslice__() 3608 # Row 3609 3610 3611
3612 - def __init__( self, id, label, items=None, types=None, 3613 headers=None, show_headers=True, editable=False, 3614 expand_columns_indexes=None, 3615 selection_callback=None, data_changed_callback=None ):
3616 """Table constructor. 3617 3618 @param id: unique identifier. 3619 @param label: what to show on table frame 3620 @param items: a list (single column) or list of lists (multiple 3621 columns) 3622 @param types: a list of types (str, int, long, float, unicode, bool) 3623 for columns, if omitted, will be guessed from items. 3624 @param headers: what to use as table header. 3625 @param show_headers: whenever to show table headers 3626 @param editable: if table is editable. If editable, user can change 3627 values inline or double-clicking, also edit buttons will 3628 show after the table. 3629 @param expand_columns_indexes: list of indexes that can expand size 3630 @param selection_callback: the function (or list of functions) to 3631 call when selection changes. Function will get as parameters: 3632 - App reference 3633 - Table reference 3634 - List of pairs ( index, row_contents ) 3635 @param data_changed_callback: the function (or list of functions) to 3636 call when data changes. Function will get as parameters: 3637 - App reference 3638 - Table reference 3639 - Pair ( index, row_contents ) 3640 3641 @warning: although this widget contains data, it's not a 3642 _EGDataWidget and thus will not notify application that 3643 data changed, also it cannot persist it's data 3644 automatically, if you wish, do it manually. This behavior 3645 may change in future if Table show to be useful as 3646 _EGDataWidget. 3647 """ 3648 _EGWidget.__init__( self, id ) 3649 self.editable = editable or False 3650 self.label = str( label or "" ) 3651 self.headers = headers or tuple() 3652 self.show_headers = bool( show_headers ) 3653 3654 if isinstance( expand_columns_indexes, ( int, long ) ): 3655 expand_columns_indexes = ( expand_columns_indexes, ) 3656 elif isinstance( expand_columns_indexes, ( tuple, list ) ): 3657 expand_columns_indexes = tuple( expand_columns_indexes ) 3658 elif expand_columns_indexes is None: 3659 expand_columns_indexes = tuple() 3660 else: 3661 raise ValueError( \ 3662 "expand_columns_indexes must be a sequence of integers" ) 3663 self.expand_columns_indexes = expand_columns_indexes 3664 3665 3666 if not ( types or items ): 3667 raise ValueError( "Must provide items or types!" ) 3668 elif not types: 3669 items = items or [] 3670 if not isinstance( items[ 0 ], ( list, tuple ) ): 3671 # just one column, convert to generic representation 3672 items = [ [ i ] for i in items ] 3673 3674 types = [ type( i ) for i in items[ 0 ] ] 3675 self.types = types 3676 self.items = items 3677 3678 self.selection_callback = _callback_tuple( selection_callback ) 3679 self.data_changed_callback = _callback_tuple( data_changed_callback ) 3680 3681 self.__setup_gui__() 3682 self.__setup_connections__()
3683 # __init__() 3684 3685
3686 - def __setup_gui__( self ):
3687 self._frame = gtk.Frame( self.label ) 3688 self._frame.set_name( self.id ) 3689 3690 self._vbox = gtk.VBox( False, self.spacing ) 3691 self._vbox.set_border_width( self.spacing ) 3692 self._vbox.set_name( "vbox-%s" % self.id ) 3693 3694 self._frame.add( self._vbox ) 3695 self._widgets = ( self._frame, ) 3696 3697 self.__setup_table__() 3698 3699 if self.editable: 3700 self._hbox = gtk.HBox( False, self.spacing ) 3701 self._vbox.pack_start( self._hbox, expand=False, fill=True ) 3702 3703 self._btn_add = gtk.Button( stock=gtk.STOCK_ADD ) 3704 self._btn_del = gtk.Button( stock=gtk.STOCK_REMOVE ) 3705 self._btn_edit = gtk.Button( stock=gtk.STOCK_EDIT ) 3706 3707 self._hbox.pack_start( self._btn_add ) 3708 self._hbox.pack_start( self._btn_del ) 3709 self._hbox.pack_start( self._btn_edit )
3710 # __setup_gui__() 3711 3712
3713 - def __setup_connections__( self ):
3714 if self.data_changed_callback: 3715 self.__setup_connections_changed__() 3716 3717 if self.editable: 3718 self.__setup_connections_editable__() 3719 3720 if self.selection_callback: 3721 self.__setup_connections_selection__()
3722 # __setup_connections__() 3723 3724
3725 - def __setup_connections_changed__( self ):
3726 - def row_changed( model, path, itr ):
3727 index = path[ 0 ] 3728 v = ( index, Table.Row( model[ path ] ) ) 3729 for c in self.data_changed_callback: 3730 c( self.app, self, v )
3731 # row_changed() 3732 3733
3734 - def row_deleted( model, path ):
3735 index = path[ 0 ] 3736 v = ( index, None ) 3737 for c in self.data_changed_callback: 3738 c( self.app, self, v )
3739 # row_deleted() 3740 3741 self._model.connect( "row-changed", row_changed ) 3742 self._model.connect( "row-deleted", row_deleted ) 3743 self._model.connect( "row-inserted", row_changed)
3744 # __setup_connections_changed__() 3745 3746
3748 - def edit_dialog( data ):
3749 title = "Edit data from table %s" % self.label 3750 buttons = ( gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, 3751 gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT ) 3752 d = gtk.Dialog( title=title, 3753 flags=gtk.DIALOG_MODAL, 3754 buttons=buttons ) 3755 d.set_default_response( gtk.RESPONSE_ACCEPT ) 3756 3757 l = len( data ) 3758 t = gtk.Table( l, 2 ) 3759 t.set_border_width( 0 ) 3760 w = [] 3761 for i, v in enumerate( data ): 3762 title = self._table.get_column( i ).get_title() 3763 label = gtk.Label( title ) 3764 label.set_justify( gtk.JUSTIFY_RIGHT ) 3765 label.set_alignment( xalign=1.0, yalign=0.5 ) 3766 3767 tp = self.types[ i ] 3768 if tp == bool: 3769 entry = gtk.CheckButton() 3770 entry.set_active( data[ i ] ) 3771 elif tp in ( int, long ): 3772 entry = gtk.SpinButton( digits=0 ) 3773 adj = entry.get_adjustment() 3774 adj.lower = Spin.default_min 3775 adj.upper = Spin.default_max 3776 adj.step_increment = 1 3777 adj.page_increment = 5 3778 entry.set_value( data[ i ] ) 3779 elif tp == float: 3780 entry = gtk.SpinButton( digits=6 ) 3781 adj = entry.get_adjustment() 3782 adj.lower = Spin.default_min 3783 adj.upper = Spin.default_max 3784 adj.step_increment = 1 3785 adj.page_increment = 5 3786 entry.set_value( data[ i ] ) 3787 elif tp in ( str, unicode ): 3788 entry = gtk.Entry() 3789 entry.set_text( data[ i ] ) 3790 else: 3791 try: 3792 name = tp.__name__ 3793 except: 3794 name = tp 3795 raise ValueError( "Unsuported column (%d) type: %s" % 3796 ( i, name ) ) 3797 3798 t.attach( label, 0, 1, i, i + 1, 3799 xoptions=gtk.FILL, 3800 xpadding=self.spacing, ypadding=self.spacing ) 3801 t.attach( entry, 1, 2, i, i + 1, 3802 xoptions=gtk.EXPAND|gtk.FILL, 3803 xpadding=self.spacing, ypadding=self.spacing ) 3804 w.append( entry ) 3805 3806 t.show_all() 3807 3808 sw = gtk.ScrolledWindow() 3809 sw.add_with_viewport( t ) 3810 sw.get_child().set_shadow_type( gtk.SHADOW_NONE ) 3811 d.vbox.pack_start( sw ) 3812 # Hack, disable scrollbars so we get the window to the 3813 # best size 3814 sw.set_policy( hscrollbar_policy=gtk.POLICY_NEVER, 3815 vscrollbar_policy=gtk.POLICY_NEVER ) 3816 d.show_all() 3817 # Scrollbars are automatic 3818 sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 3819 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 3820 3821 r = d.run() 3822 d.destroy() 3823 if r in ( gtk.RESPONSE_REJECT, gtk.RESPONSE_DELETE_EVENT ) : 3824 return None 3825 else: 3826 result = [] 3827 for i in xrange( len( data ) ): 3828 tp = self.types[ i ] 3829 wid = w[ i ] 3830 if tp == bool: 3831 r = bool( wid.get_active() ) 3832 elif tp in ( int, long ): 3833 r = tp( wid.get_value() ) 3834 elif tp == float: 3835 r = float( wid.get_value() ) 3836 elif tp in ( str, unicode ): 3837 r = tp( wid.get_text() ) 3838 else: 3839 try: 3840 name = tp.__name__ 3841 except: 3842 name = tp 3843 raise ValueError( \ 3844 "Unsuported column (%d) type: %s" % 3845 ( i, name ) ) 3846 result.append( r ) 3847 3848 return result
3849 # edit_dialog() 3850 3851
3852 - def clicked_add( button ):
3853 entry = [] 3854 for i, t in enumerate( self.types ): 3855 if t == bool: 3856 v = False 3857 elif t in ( int, long, float ): 3858 v = 0 3859 elif t in ( str, unicode ): 3860 v = '' 3861 else: 3862 try: 3863 name = t.__name__ 3864 except: 3865 name = t 3866 raise ValueError( "Unsuported column (%d) type: %s" % 3867 ( i, name ) ) 3868 entry.append( v ) 3869 result = edit_dialog( entry ) 3870 if result: 3871 self.append( result )
3872 # clicked_add() 3873 3874
3875 - def clicked_edit( button ):
3876 selected = self.selected() 3877 if not selected: 3878 return 3879 3880 for index, data in selected: 3881 print data 3882 result = edit_dialog( data ) 3883 if result: 3884 self[ index ] = result
3885 # clicked_edit() 3886 3887
3888 - def clicked_del( button ):
3889 selected = self.selected() 3890 if not selected: 3891 return 3892 3893 for index, data in selected: 3894 del self[ index ]
3895 # clicked_del() 3896 3897 self._btn_add.connect( "clicked", clicked_add ) 3898 self._btn_del.connect( "clicked", clicked_del ) 3899 self._btn_edit.connect( "clicked", clicked_edit ) 3900
3901 - def row_activated( treeview, path, column ):
3902 data = treeview.get_model()[ path ] 3903 result = edit_dialog( data ) 3904 if result: 3905 self[ path[ 0 ] ] = result
3906 # row_activated() 3907 3908 3909 self._table.connect( "row-activated", row_activated )
3910 # __setup_connections_editable__() 3911 3912
3914 - def selection_changed( selection ):
3915 result = self.selected() 3916 for c in self.selection_callback: 3917 c( self.app, self, result )
3918 # selection_changed() 3919 3920 selection = self._table.get_selection() 3921 selection.connect( "changed", selection_changed )
3922 # __setup_connections_selection__() 3923 3924
3925 - def __setup_table__( self ):
3926 self.__setup_model__() 3927 self._table = gtk.TreeView( self._model ) 3928 self._table.set_property( "allow-checkbox-mode", False ) 3929 self._table.set_name( "table-%s" % self.id ) 3930 self._table.get_selection().set_mode( gtk.SELECTION_MULTIPLE ) 3931
3932 - def column_clicked( column ):
3933 cid, order = self._model.get_sort_column_id() 3934 self._model.set_sort_column_id( cid, order )
3935 # column_clicked() 3936
3937 - def toggled( cell_render, path, col ):
3938 self._model[ path ][ col ] = not self._model[ path ][ col ]
3939 # toggled() 3940 3941
3942 - def edited( cell_render, path, text, col ):
3943 t = self.types[ col ] 3944 try: 3945 value = t( text ) 3946 except ValueError, e: 3947 name = t.__name__ 3948 error( "Invalid contents for column of type '%s': %s" % 3949 ( name, text ) ) 3950 else: 3951 self._model[ path ][ col ] = value
3952 # edited() 3953 3954 3955 for i, t in enumerate( self.types ): 3956 if t == bool: 3957 cell_rend = gtk.CellRendererToggle() 3958 props = { "active": i } 3959 if self.editable: 3960 cell_rend.set_property( "activatable", True) 3961 cell_rend.connect( "toggled", toggled, i ) 3962 3963 elif t in ( int, long, float, str, unicode ): 3964 cell_rend = gtk.CellRendererText() 3965 if self.editable: 3966 cell_rend.set_property( "editable", True ) 3967 cell_rend.connect( "edited", edited, i ) 3968 3969 props = { "text": i } 3970 if t in ( int, long, float ): 3971 cell_rend.set_property( "xalign", 1.0 ) 3972 else: 3973 try: 3974 name = t.__name__ 3975 except: 3976 name = t 3977 raise ValueError( "Unsuported column (%d) type: %s" % 3978 ( i, name ) ) 3979 3980 try: 3981 title = self.headers[ i ] 3982 except IndexError: 3983 title = "Col-%d (%s)" % ( i, t.__name__ ) 3984 3985 col = gtk.TreeViewColumn( title, cell_rend, **props ) 3986 col.set_resizable( True ) 3987 col.set_sort_column_id( i ) 3988 col.connect( "clicked", column_clicked ) 3989 if i in self.expand_columns_indexes: 3990 col.set_expand( True ) 3991 else: 3992 col.set_expand( False ) 3993 self._table.append_column( col ) 3994 3995 3996 self._table.set_headers_visible( self.show_headers ) 3997 self._table.set_headers_clickable( True ) 3998 self._table.set_reorderable( True ) 3999 self._table.set_enable_search( True ) 4000 4001 4002 if self.items: 4003 for row in self.items: 4004 self.append( row, select=False, autosize=False ) 4005 self.columns_autosize() 4006 4007 self._sw = gtk.ScrolledWindow() 4008 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 4009 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 4010 self._sw.set_shadow_type( gtk.SHADOW_IN ) 4011 self._sw.add( self._table ) 4012 self._vbox.pack_start( self._sw )
4013 # __setup_table__() 4014 4015
4016 - def __setup_model__( self ):
4017 gtk_types = [] 4018 for i, t in enumerate( self.types ): 4019 if t == bool: 4020 gtk_types.append( gobject.TYPE_BOOLEAN ) 4021 elif t == int: 4022 gtk_types.append( gobject.TYPE_INT ) 4023 elif t == long: 4024 gtk_types.append( gobject.TYPE_LONG ) 4025 elif t == float: 4026 gtk_types.append( gobject.TYPE_FLOAT ) 4027 elif t in ( str, unicode ): 4028 gtk_types.append( gobject.TYPE_STRING ) 4029 else: 4030 try: 4031 name = t.__name__ 4032 except: 4033 name = t 4034 raise ValueError( "Unsuported column (%d) type: %s" % 4035 ( i, name ) ) 4036 self._model = gtk.ListStore( *gtk_types ) 4037
4038 - def sort_fn( model, itr1, itr2, id ):
4039 return cmp( model[ itr1 ][ id ], model[ itr2 ][ id ] )
4040 # sort_fn() 4041 4042 for i in xrange( len( self.types ) ): 4043 self._model.set_sort_func( i, sort_fn, i )
4044 # __setup_model__() 4045 4046
4047 - def __get_resize_mode__( self ):
4048 return ( gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND )
4049 # __get_resize_mode__() 4050 4051
4052 - def columns_autosize( self ):
4053 self._table.columns_autosize()
4054 # columns_autosize() 4055 4056
4057 - def selected( self ):
4058 model, paths = self._table.get_selection().get_selected_rows() 4059 if paths: 4060 result = [] 4061 for p in paths: 4062 result.append( ( p[ 0 ], Table.Row( model[ p ] ) ) ) 4063 return result 4064 else: 4065 return None
4066 # selected() 4067 4068
4069 - def append( self, row, select=True, autosize=True ):
4070 if not isinstance( row, ( list, tuple ) ): 4071 row = ( row, ) 4072 4073 itr = self._model.append( row ) 4074 4075 if autosize: 4076 self._table.columns_autosize() 4077 if select: 4078 self._table.set_cursor( self._model[ itr ].path )
4079 # append() 4080 4081
4082 - def insert( self, index, row, select=True, autosize=True ):
4083 if not isinstance( row, ( list, tuple ) ): 4084 row = ( row, ) 4085 4086 itr = self._model.insert( index, row ) 4087 self._table.columns_autosize() 4088 4089 if autosize: 4090 self._table.columns_autosize() 4091 if select: 4092 self._table.set_cursor( self._model[ itr ].path )
4093 # insert() 4094 4095
4096 - def __nonzero__( self ):
4097 return self._model.__nonzero__()
4098 # __nonzero__() 4099
4100 - def __len__( self ):
4101 return len( self._model )
4102 # __len__() 4103 4104
4105 - def __iadd__( self, other ):
4106 self.append( other ) 4107 return self
4108 # __iadd__() 4109 4110
4111 - def __setitem__( self, index, other ):
4112 if not isinstance( other, ( list, tuple ) ): 4113 other = ( other, ) 4114 try: 4115 self._model[ index ] = other 4116 except TypeError, e: 4117 raise IndexError( "index out of range" )
4118 # __setitem__() 4119 4120
4121 - def __getitem__( self, index ):
4122 try: 4123 items = self._model[ index ] 4124 except TypeError, e: 4125 raise IndexError( "index out of range" ) 4126 4127 return Table.Row( items )
4128 # __getitem__() 4129 4130
4131 - def __delitem__( self, index ):
4132 try: 4133 del self._model[ index ] 4134 except TypeError, e: 4135 raise IndexError( "index out of range" )
4136 # __delitem__() 4137 4138
4139 - def __contains__( self, row ):
4140 for r in self._model: 4141 if row in r: 4142 return True 4143 return False
4144 # __contains__() 4145 4146
4147 - def __getslice__( self, start, end ):
4148 slice = [] 4149 4150 l = len( self._model ) 4151 while start < 0: 4152 start += l 4153 while end < 0: 4154 end += l 4155 4156 start = min( start, l ) 4157 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 4158 4159 for i in xrange( start, end ): 4160 slice.append( Table.Row( self._model[ i ] ) ) 4161 return slice
4162 # __getslice__() 4163 4164
4165 - def __setslice__( self, start, end, slice ):
4166 l = len( self._model ) 4167 while start < 0: 4168 start += l 4169 while end < 0: 4170 end += l 4171 4172 del self[ start : end ] 4173 4174 # just insert len( slice ) items 4175 l2 = len( slice ) 4176 if end - start > l2: 4177 end = start + l2 4178 for j, i in enumerate( xrange( start, end ) ): 4179 row = list( slice[ j ] ) 4180 4181 4182 # extend row if necessary 4183 lr = len( row ) 4184 lt = len( self.types ) 4185 if lr < lt: 4186 for i in xrange( lr, lt ): 4187 t = self.types[ i ] 4188 row.append( t() ) 4189 4190 self.insert( i, row, select=False, autosize=False )
4191 # __setslice__() 4192 4193
4194 - def __delslice__( self, start, end ):
4195 l = len( self._model ) 4196 while start < 0: 4197 start += l 4198 while end < 0: 4199 end += l 4200 4201 start = min( start, l ) 4202 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 4203 4204 while end > start: 4205 end -= 1 4206 del self._model[ end ]
4207 # __delslice__() 4208 # Table 4209 4210
4211 -class Button( _EGWidget ):
4212 """A push button. 4213 """ 4214 stock = _gen_ro_property( "stock" ) 4215 callback = _gen_ro_property( "callback" ) 4216 4217 stock_items = ( 4218 "about", 4219 "help", 4220 "quit", 4221 "add", 4222 "remove", 4223 "refresh", 4224 "update", 4225 "yes", 4226 "no", 4227 "zoom_100", 4228 "zoom_in", 4229 "zoom_out", 4230 "zoom_fit", 4231 "undo", 4232 "execute", 4233 "stop", 4234 "open", 4235 "save", 4236 "save_as", 4237 "properties", 4238 "preferences", 4239 "print", 4240 "print_preview", 4241 "ok", 4242 "cancel", 4243 "apply", 4244 "close", 4245 "clear", 4246 "convert", 4247 "next", 4248 "back", 4249 "up", 4250 "down", 4251 "font", 4252 "color", 4253 ) 4254 4255 4256 _gtk_stock_map = { 4257 "about": gtk.STOCK_ABOUT, 4258 "help": gtk.STOCK_HELP, 4259 "quit": gtk.STOCK_QUIT, 4260 "add": gtk.STOCK_ADD, 4261 "remove": gtk.STOCK_REMOVE, 4262 "refresh": gtk.STOCK_REFRESH, 4263 "update": gtk.STOCK_REFRESH, 4264 "yes": gtk.STOCK_YES, 4265 "no": gtk.STOCK_NO, 4266 "zoom_100": gtk.STOCK_ZOOM_100, 4267 "zoom_in": gtk.STOCK_ZOOM_IN, 4268 "zoom_out": gtk.STOCK_ZOOM_OUT, 4269 "zoom_fit": gtk.STOCK_ZOOM_FIT, 4270 "undo": gtk.STOCK_UNDO, 4271 "execute": gtk.STOCK_EXECUTE, 4272 "stop": gtk.STOCK_STOP, 4273 "open": gtk.STOCK_OPEN, 4274 "save": gtk.STOCK_SAVE, 4275 "save_as": gtk.STOCK_SAVE_AS, 4276 "properties": gtk.STOCK_PROPERTIES, 4277 "preferences": gtk.STOCK_PREFERENCES, 4278 "print": gtk.STOCK_PRINT, 4279 "print_preview": gtk.STOCK_PRINT_PREVIEW, 4280 "ok": gtk.STOCK_OK, 4281 "cancel": gtk.STOCK_CANCEL, 4282 "apply": gtk.STOCK_APPLY, 4283 "close": gtk.STOCK_CLOSE, 4284 "clear": gtk.STOCK_CLEAR, 4285 "convert": gtk.STOCK_CONVERT, 4286 "next": gtk.STOCK_GO_FORWARD, 4287 "back": gtk.STOCK_GO_BACK, 4288 "up": gtk.STOCK_GO_UP, 4289 "down": gtk.STOCK_GO_DOWN, 4290 "font": gtk.STOCK_SELECT_FONT, 4291 "color": gtk.STOCK_SELECT_COLOR, 4292 } 4293
4294 - def __init__( self, id, label="", stock=None, callback=None ):
4295 """Push button constructor. 4296 4297 @param label: what text to show, if stock isn't provided. 4298 @param stock: optional. One of L{stock_items}. 4299 @param callback: the function (or list of functions) to call 4300 when button is pressed. Function will get as parameter: 4301 - App reference 4302 - Button reference 4303 """ 4304 self.label = label 4305 self.stock = stock 4306 self.callback = _callback_tuple( callback ) 4307 4308 # Check if provided stock items are implemented 4309 for i in self.stock_items: 4310 if i not in self._gtk_stock_map: 4311 print >> sys.stderr, \ 4312 "Stock item %s missing in implementation map!" % ( i, ) 4313 4314 _EGWidget.__init__( self, id ) 4315 4316 self.__setup_gui__() 4317 self.__setup_connections__()
4318 # __init__() 4319 4320
4321 - def __setup_gui__( self ):
4322 k = {} 4323 try: 4324 k[ "stock" ] = self._gtk_stock_map[ self.stock ] 4325 except KeyError: 4326 k[ "label" ] = self.label or self.stock 4327 4328 self._button = gtk.Button( **k ) 4329 self._button.set_name( self.id ) 4330 self._widgets = ( self._button, )
4331 # __setup_gui__() 4332 4333
4334 - def __setup_connections__( self ):
4335 - def callback( obj ):
4336 for c in self.callback: 4337 c( self.app, self )
4338 # callback() 4339 self._button.connect( "clicked", callback )
4340 # __setup_connections__() 4341 # Button 4342 4343
4344 -class AboutButton( Button, AutoGenId ):
4345 """Push button to show L{AboutDialog} of L{App}."""
4346 - def __init__( self, id=None ):
4347 """You may not provide id, it will be generated automatically"""
4348 - def show_about( app_id, wid_id ):
4349 self.app.show_about_dialog()
4350 # show_about() 4351 Button.__init__( self, id or self.__get_id__(), 4352 stock="about", callback=show_about )
4353 # __init__() 4354 # AboutButton 4355 4356
4357 -class CloseButton( Button, AutoGenId ):
4358 """Push button to close L{App}."""
4359 - def __init__( self, id=None ):
4360 """You may not provide id, it will be generated automatically"""
4361 - def close( app_id, wid_id ):
4362 self.app.close()
4363 # close() 4364 Button.__init__( self, id or self.__get_id__(), 4365 stock="close", callback=close )
4366 # __init__() 4367 # CloseButton 4368 4369
4370 -class QuitButton( Button, AutoGenId ):
4371 """Push button to quit all L{App}s."""
4372 - def __init__( self, id=None ):
4373 """You may not provide id, it will be generated automatically"""
4374 - def c( app_id, wid_id ):
4375 quit()
4376 # c() 4377 Button.__init__( self, id or self.__get_id__(), 4378 stock="quit", callback=c )
4379 # __init__() 4380 # QuitButton 4381 4382
4383 -class HelpButton( Button, AutoGenId ):
4384 """Push button to show L{HelpDialog} of L{App}."""
4385 - def __init__( self, id=None ):
4386 """You may not provide id, it will be generated automatically"""
4387 - def c( app_id, wid_id ):
4388 self.app.show_help_dialog()
4389 # c() 4390 Button.__init__( self, id or self.__get_id__(), 4391 stock="help", callback=c )
4392 # __init__() 4393 # HelpButton 4394 4395
4396 -class OpenFileButton( Button, AutoGenId ):
4397 """Push button to show dialog to choose an existing file."""
4398 - def __init__( self, id=None, 4399 filter=None, multiple=False, 4400 callback=None ):
4401 """Constructor. 4402 4403 @param id: may not be provided, it will be generated automatically. 4404 @param filter: filter files to show, see L{FileChooser}. 4405 @param multiple: enable selection of multiple files. 4406 @param callback: function (or list of functions) to call back 4407 when file is selected. Function will get as parameters: 4408 - app reference. 4409 - widget reference. 4410 - file name or file list (if multiple). 4411 4412 @see: L{FileChooser} 4413 """
4414 - def c( app_id, wid_id ):
4415 f = self.app.file_chooser( FileChooser.ACTION_OPEN, 4416 filter=filter, multiple=multiple ) 4417 if f is not None and callback: 4418 callback( self.app, self, f )
4419 # c() 4420 Button.__init__( self, id or self.__get_id__(), 4421 stock="open", callback=c )
4422 # __init__() 4423 # OpenFileButton 4424 4425
4426 -class SelectFolderButton( Button, AutoGenId ):
4427 """Push button to show dialog to choose an existing folder/directory."""
4428 - def __init__( self, id=None, callback=None ):
4429 """Constructor. 4430 4431 @param id: may not be provided, it will be generated automatically. 4432 @param callback: function (or list of functions) to call back 4433 when file is selected. Function will get as parameters: 4434 - app reference. 4435 - widget reference. 4436 - directory/folder name. 4437 4438 @see: L{FileChooser} 4439 """
4440 - def c( app_id, wid_id ):
4441 f = self.app.file_chooser( FileChooser.ACTION_SELECT_FOLDER ) 4442 if f is not None and callback: 4443 callback( self.app, self, f )
4444 # c() 4445 Button.__init__( self, id or self.__get_id__(), 4446 stock="open", callback=c )
4447 # __init__() 4448 # SelectFolderButton 4449 4450
4451 -class SaveFileButton( Button, AutoGenId ):
4452 """Push button to show dialog to choose a file to save."""
4453 - def __init__( self, id=None, filename=None, 4454 filter=None, callback=None ):
4455 """Constructor. 4456 4457 @param id: may not be provided, it will be generated automatically. 4458 @param filename: default filename. 4459 @param filter: filter files to show, see L{FileChooser}. 4460 @param callback: function (or list of functions) to call back 4461 when file is selected. Function will get as parameters: 4462 - app reference. 4463 - widget reference. 4464 - file name. 4465 4466 @see: L{FileChooser} 4467 """
4468 - def c( app_id, wid_id ):
4469 f = self.app.file_chooser( FileChooser.ACTION_SAVE, 4470 filename=filename, 4471 filter=filter ) 4472 if f is not None and callback: 4473 callback( self.app, self, f )
4474 # c() 4475 Button.__init__( self, id or self.__get_id__(), 4476 stock="save", callback=c )
4477 # __init__() 4478 # SaveFileButton 4479 4480
4481 -class PreferencesButton( Button, AutoGenId ):
4482 """Push button to show L{PreferencesDialog} of L{App}."""
4483 - def __init__( self, id=None ):
4484 """You may not provide id, it will be generated automatically"""
4485 - def c( app_id, wid_id ):
4486 f = self.app.show_preferences_dialog()
4487 # c() 4488 Button.__init__( self, id or self.__get_id__(), 4489 stock="preferences", callback=c )
4490 # __init__() 4491 # PreferencesButton 4492 4493
4494 -class HSeparator( _EGWidget, AutoGenId ):
4495 """Horizontal separator"""
4496 - def __init__( self, id=None ):
4497 """You may not provide id, it will be generated automatically""" 4498 _EGWidget.__init__( self, id or self.__get_id__() ) 4499 self._wid = gtk.HSeparator() 4500 self._wid.set_name( self.id ) 4501 self._widgets = ( self._wid, )
4502 # __init__() 4503 # HSeparator 4504 4505
4506 -class VSeparator( _EGWidget ):
4507 """Horizontal separator"""
4508 - def __init__( self, id=None ):
4509 """You may not provide id, it will be generated automatically""" 4510 _EGWidget.__init__( self, id or self.__get_id__() ) 4511 self._wid = gtk.VSeparator() 4512 self._wid.set_name( self.id ) 4513 self._widgets = ( self._wid, )
4514 # __init__() 4515 # VSeparator 4516 4517
4518 -class Label( _EGDataWidget, AutoGenId ):
4519 """Text label""" 4520 label = _gen_ro_property( "label" ) 4521 4522 LEFT = 0.0 4523 RIGHT = 1.0 4524 CENTER = 0.5 4525 TOP = 0.0 4526 MIDDLE = 0.5 4527 BOTTOM = 1.0 4528
4529 - def __init__( self, id=None, label="", 4530 halignment=LEFT, valignment=MIDDLE ):
4531 """Label constructor. 4532 4533 @param id: may not be provided, it will be generated automatically. 4534 @param label: what this label will show. 4535 @param halignment: horizontal alignment, like L{LEFT}, L{RIGHT} or 4536 L{CENTER}. 4537 @param valignment: vertical alignment, like L{TOP}, L{BOTTOM} or 4538 L{MIDDLE}. 4539 """ 4540 _EGDataWidget.__init__( self, id or self.__get_id__(), False ) 4541 self.label = label 4542 4543 self._wid = gtk.Label( self.label ) 4544 self._wid.set_name( self.id ) 4545 self._wid.set_alignment( xalign=halignment, yalign=valignment ) 4546 self._widgets = ( self._wid, )
4547 # __init__() 4548 4549
4550 - def get_value( self ):
4551 return self._wid.get_text()
4552 # get_value() 4553 4554
4555 - def set_value( self, value ):
4556 self._wid.set_text( str( value ) )
4557 # set_value() 4558 4559
4560 - def __str__( self ):
4561 return "%s( id=%r, label=%r )" % \ 4562 ( self.__class__.__name__, self.id, self.label )
4563 # __str__() 4564 # Label 4565 4566
4567 -def information( message ):
4568 """Show info message to user.""" 4569 4570 d = gtk.MessageDialog( type=gtk.MESSAGE_INFO, 4571 message_format=message, 4572 buttons=gtk.BUTTONS_CLOSE ) 4573 d.run() 4574 d.destroy() 4575 return
4576 # information() 4577 info = information 4578 4579
4580 -def warning( message ):
4581 """Show warning message to user.""" 4582 4583 d = gtk.MessageDialog( type=gtk.MESSAGE_WARNING, 4584 message_format=message, 4585 buttons=gtk.BUTTONS_CLOSE ) 4586 d.run() 4587 d.destroy() 4588 return
4589 # warning() 4590 warn = warning 4591 4592
4593 -def error( message ):
4594 """Show error message to user.""" 4595 4596 d = gtk.MessageDialog( type=gtk.MESSAGE_ERROR, 4597 message_format=message, 4598 buttons=gtk.BUTTONS_CLOSE ) 4599 d.run() 4600 d.destroy() 4601 return
4602 # error() 4603 err = error 4604 4605
4606 -def yesno( message, yesdefault=False ):
4607 """Show yes/no message to user. 4608 4609 @param yesdefault: if yes should be the default action. 4610 """ 4611 4612 d = gtk.MessageDialog( type=gtk.MESSAGE_QUESTION, 4613 message_format=message, 4614 buttons=gtk.BUTTONS_YES_NO ) 4615 if yesdefault: 4616 d.set_default_response( gtk.RESPONSE_YES ) 4617 else: 4618 d.set_default_response( gtk.RESPONSE_NO ) 4619 4620 r = d.run() 4621 d.destroy() 4622 4623 if r == gtk.RESPONSE_YES: 4624 return True 4625 elif r == gtk.RESPONSE_NO: 4626 return False 4627 else: 4628 return yesdefault
4629 # yesno() 4630 4631 4632
4633 -def confirm( message, okdefault=False ):
4634 """Show confirm message to user. 4635 4636 @param okdefault: if ok should be the default action. 4637 """ 4638 4639 d = gtk.MessageDialog( type=gtk.MESSAGE_QUESTION, 4640 message_format=message, 4641 buttons=gtk.BUTTONS_OK_CANCEL ) 4642 if okdefault: 4643 d.set_default_response( gtk.RESPONSE_OK ) 4644 else: 4645 d.set_default_response( gtk.RESPONSE_CANCEL ) 4646 4647 r = d.run() 4648 d.destroy() 4649 4650 if r == gtk.RESPONSE_OK: 4651 return True 4652 elif r == gtk.RESPONSE_CANCEL: 4653 return False 4654 else: 4655 return okdefault
4656 # confirm() 4657 4658 4659
4660 -def run():
4661 """Enter the event loop""" 4662 try: 4663 gtk.main() 4664 except KeyboardInterrupt: 4665 raise SystemExit( "User quit using Control-C" )
4666 # run() 4667
4668 -def quit():
4669 """Quit the event loop""" 4670 gtk.main_quit()
4671 # quit() 4672 4673
4674 -def get_app_by_id( app_id ):
4675 """Given an App unique identifier, return the reference to it.""" 4676 if app_id is None: 4677 try: 4678 return _apps.values()[ 0 ] 4679 except IndexError, e: 4680 raise ValueError( "No application defined!" ) 4681 elif isinstance( app_id, ( str, unicode ) ): 4682 try: 4683 return _apps[ app_id ] 4684 except KeyError, e: 4685 raise ValueError( "Application id \"%s\" doesn't exists!" % \ 4686 app_id ) 4687 elif isinstance( app_id, App ): 4688 return app_id 4689 else: 4690 raise ValueError( "app_id must be string or App instance!" )
4691 # get_app_by_id() 4692 4693
4694 -def get_widget_by_id( widget_id, app_id=None ):
4695 """Given an Widget unique identifier, return the reference to it. 4696 4697 If app_id is not provided, will use the first App found. 4698 4699 @attention: Try to always provide app_id since it may lead to problems if 4700 your program have more than one App. 4701 """ 4702 app = get_app_by_id( app_id ) 4703 4704 if app: 4705 w = app.get_widget_by_id( widget_id ) 4706 if not w: 4707 raise ValueError( "Widget id \"%s\" doesn't exists!" % widget_id ) 4708 else: 4709 return w
4710 # get_widget_by_id() 4711 4712
4713 -def get_value( widget_id, app_id=None ):
4714 """Convenience function to get widget and call its get_value() method.""" 4715 try: 4716 wid = get_widget_by_id( widget_id, app_id ) 4717 return wid.get_value() 4718 except ValueError, e: 4719 raise ValueError( e )
4720 # get_value() 4721 4722
4723 -def set_value( widget_id, value, app_id=None ):
4724 """Convenience function to get widget and call its set_value() method.""" 4725 try: 4726 wid = get_widget_by_id( widget_id, app_id ) 4727 wid.set_value( value ) 4728 except ValueError, e: 4729 raise ValueError( e )
4730 # set_value() 4731 4732
4733 -def show( widget_id, app_id=None ):
4734 """Convenience function to get widget and call its show() method.""" 4735 try: 4736 wid = get_widget_by_id( widget_id, app_id ) 4737 wid.show() 4738 except ValueError, e: 4739 raise ValueError( e )
4740 # show() 4741 4742
4743 -def hide( widget_id, app_id=None ):
4744 """Convenience function to get widget and call its hide() method.""" 4745 try: 4746 wid = get_widget_by_id( widget_id, app_id ) 4747 wid.hide() 4748 except ValueError, e: 4749 raise ValueError( e )
4750 # hide() 4751 4752
4753 -def set_active( widget_id, active=True, app_id=None ):
4754 """Convenience function to get widget and call its set_active() method.""" 4755 try: 4756 wid = get_widget_by_id( widget_id, app_id ) 4757 wid.set_active( active ) 4758 except ValueError, e: 4759 raise ValueError( e )
4760 # set_active() 4761 4762
4763 -def set_inactive( widget_id, app_id=None ):
4764 """ 4765 Convenience function to get widget and call its set_inactive() method. 4766 """ 4767 try: 4768 wid = get_widget_by_id( widget_id, app_id ) 4769 wid.set_inactive() 4770 except ValueError, e: 4771 raise ValueError( e )
4772 # set_inactive() 4773 4774
4775 -def close( app_id=None ):
4776 """Convenience function to get app and call its close() method.""" 4777 try: 4778 app = get_app_by_id( app_id ) 4779 app.close() 4780 except ValueError, e: 4781 raise ValueError( e )
4782 # close() 4783