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-07-28 16:12:25 -0300 (Sex, 28 Jul 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.7" 
  27  __revision__ = "$Rev: 94 $" 
  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", "Tabs", "Table", 
  73      "HSeparator", "VSeparator", 
  74      "Label", 
  75      "Canvas", "Image", 
  76      "information", "info", "error", "err", "warning", "warn", 
  77      "yesno", "confirm", 
  78      "AboutDialog", "HelpDialog", "FileChooser", 
  79      "RichText", 
  80      "ExpandPolicy", 
  81      ] 
  82   
  83  import os 
  84  import sys 
  85  import gc 
  86  import cPickle as pickle 
  87  import htmllib 
  88  import formatter 
  89  import weakref 
  90   
  91  try: 
  92      import pygtk 
  93      pygtk.require( "2.0" ) 
  94      import gtk 
  95      import pango 
  96      import gobject 
  97  except ImportError, e: 
  98      sys.stderr.writelines( 
  99          ( "Missing module: ", str( e ), "\n", 
 100            "This module is part of pygtk (http://pygtk.org).\n", 
 101            ) ) 
 102      sys.exit( -1 ) 
 103   
 104  required_gtk = ( 2, 6, 0 ) 
 105  m = gtk.check_version( *required_gtk ) 
 106  if m: 
 107      sys.stderr.writelines( 
 108          ( "Error checking GTK version: %s\n" 
 109            "This system requires pygtk >= %s, you have %s installed.\n" ) 
 110          % ( m, 
 111              ".".join( [ str( v ) for v in required_gtk ] ), 
 112              ".".join( [ str( v ) for v in gtk.pygtk_version ] ) 
 113              ) ) 
 114      sys.exit( -1 ) 
 115   
 116   
 117  if not sys.platform.startswith( "win" ): 
 118      gtk.gdk.threads_init() # enable threads 
 119   
 120  _apps = {} 
 121   
 122   
123 -def _gen_ro_property( name, doc="" ):
124 """Generates a Read-Only property. 125 126 The generated property can be assigned only one value, if this value 127 is not None, it cannot be changed. 128 """ 129 naming = "__ro_%s__" % ( name, )
130 - def get( self ):
131 try: 132 return getattr( self, naming ) 133 except AttributeError: 134 return None
135 # get()
136 - def set( self, value ):
137 try: 138 v = getattr( self, naming ) 139 except AttributeError: 140 v = None 141 if v is None: 142 setattr( self, naming, value ) 143 else: 144 raise Exception( "Read Only property '%s'." % ( name, ) )
145 # set() 146 return property( get, set, None, doc )
147 # _gen_ro_property() 148 149
150 -def _callback_tuple( callback ):
151 if not isinstance( callback, ( tuple, list ) ): 152 if callback is None: 153 return tuple() 154 elif callable( callback ): 155 return ( callback, ) 156 else: 157 raise TypeError( "Callback '%s' is not callable!" % ( callback, ) ) 158 else: 159 for c in callback: 160 if not callable( c ): 161 raise TypeError( "Callback '%s' is not callable!" % ( c, ) ) 162 return callback
163 # _callback_tuple() 164 165
166 -def _str_tuple( string ):
167 if not isinstance( string, ( tuple, list ) ): 168 if string is None: 169 return tuple() 170 else: 171 return ( str( string ), ) 172 else: 173 return tuple( [ str( s ) for s in string ] )
174 # _str_tuple() 175 176
177 -def _obj_tuple( obj ):
178 if not isinstance( obj, ( tuple, list ) ): 179 if obj is None: 180 return tuple() 181 else: 182 return ( obj, ) 183 else: 184 return tuple( obj )
185 # _obj_tuple() 186 187
188 -def _set_icon_list( gtkwidget, stock_id ):
189 style = gtkwidget.get_style() 190 iconset = style.lookup_icon_set( stock_id ) 191 if iconset: 192 icons = [] 193 for s in iconset.get_sizes(): 194 i = iconset.render_icon( style, 195 gtk.TEXT_DIR_NONE, 196 gtk.STATE_NORMAL, 197 s, 198 gtkwidget, 199 None 200 ) 201 icons.append( i ) 202 gtkwidget.set_icon_list( *icons )
203 # _set_icon_list() 204 205
206 -class _ExpandRule( object ):
207 __slots__ = ( "fill", "expand" )
208 - def __init__( self, expand=False, fill=True ):
209 self.expand = expand 210 self.fill = fill
211 # __init__() 212 213
214 - def __get_gtk_resize_policy__( self ):
215 p = 0 216 if self.expand: 217 p |= gtk.EXPAND 218 if self.fill: 219 p |= gtk.FILL 220 return p
221 # __get_gtk_resize_policy__() 222 223
224 - def __str__( self ):
225 return "ExpandRule( expand=%s, fill=%s )" % \ 226 ( self.expand, self.fill )
227 # __str__() 228 __repr__ = __str__
229 # _ExpandRule 230
231 -class ExpandPolicy( object ):
232 - class Policy( object ):
233 Rule = _ExpandRule 234 __slots__ = ( "horizontal", "vertical" ) 235
236 - def __init__( self, **kargs ):
237 - def conv_arg( arg ):
238 if isinstance( arg, _ExpandRule ): 239 return arg 240 elif isinstance( arg, ( tuple, list ) ): 241 return _ExpandRule( *arg ) 242 elif isinstance( arg, dict ): 243 return _ExpandRule( **arg )
244 # conv_arg() 245 246 h = kargs.get( "horizontal", None ) 247 if h is not None: 248 self.horizontal = conv_arg( h ) 249 else: 250 self.horizontal = _ExpandRule() 251 252 v = kargs.get( "vertical", None ) 253 if v is not None: 254 self.vertical = conv_arg( v ) 255 else: 256 self.vertical = _ExpandRule()
257 # __init__() 258 259
260 - def __str__( self ):
261 return "%s( horizontal=%s, vertical=%s )" % \ 262 ( self.__class__.__name__, self.horizontal, self.vertical )
263 # __str__() 264 __repr__ = __str__
265 # Policy 266 267
268 - class All( Policy ):
269 horizontal = _ExpandRule( expand=True, fill=True ) 270 vertical = _ExpandRule( expand=True, fill=True )
271 - def __init__( self ): pass
272 # All 273 274
275 - class Nothing( Policy ):
276 horizontal = _ExpandRule( expand=False, fill=False ) 277 vertical = _ExpandRule( expand=False, fill=False )
278 - def __init__( self ): pass
279 # Nothing 280 281
282 - class Horizontal( Policy ):
283 horizontal = _ExpandRule( expand=True, fill=True ) 284 vertical = _ExpandRule( expand=False, fill=True )
285 - def __init__( self ): pass
286 # Horizontal 287 288
289 - class Vertical( Policy ):
290 horizontal = _ExpandRule( expand=False, fill=True ) 291 vertical = _ExpandRule( expand=True, fill=True )
292 - def __init__( self ): pass
293 # Vertical 294 295
296 - class FillHorizontal( Policy ):
297 horizontal = _ExpandRule( expand=False, fill=True ) 298 vertical = _ExpandRule( expand=False, fill=False )
299 - def __init__( self ): pass
300 # FillHorizontal 301 302
303 - class FillVertical( Policy ):
304 horizontal = _ExpandRule( expand=False, fill=False ) 305 vertical = _ExpandRule( expand=False, fill=True )
306 - def __init__( self ): pass
307 # FillVertical 308 309
310 - class Fill( Policy ):
311 horizontal = _ExpandRule( expand=False, fill=True ) 312 vertical = _ExpandRule( expand=False, fill=True )
313 - def __init__( self ): pass
314 # Fill 315 # ExpandPolicy 316 317 318
319 -class _Table( gtk.Table ):
320 """Internal widget to arrange components in tabular form. 321 322 @warning: never use it directly in Eagle applications! 323 """ 324 325 padding = 3 326 id = _gen_ro_property( "id" ) 327 children = _gen_ro_property( "children" ) 328 horizontal = _gen_ro_property( "horizontal" ) 329 330
331 - def __init__( self, id, children, horizontal=False ):
332 self.id = id 333 self.horizontal = horizontal 334 self.children = _obj_tuple( children ) 335 336 gtk.Table.__init__( self ) 337 338 self.set_name( id ) 339 self.set_homogeneous( False ) 340 self.__setup_gui__()
341 # __init__() 342 343
344 - def __setup_gui__( self ):
345 """Lay out components in a horizontal or vertical table.""" 346 if not self.children: 347 return 348 349 n = len( self.children ) 350 351 if self.horizontal: 352 self.resize( 2, n ) 353 else: 354 self.resize( n, 2 ) 355 356 if self.horizontal: 357 orientation = _EGWidget.ORIENTATION_HORIZONTAL 358 else: 359 orientation = _EGWidget.ORIENTATION_VERTICAL 360 361 for idx, c in enumerate( self.children ): 362 c.__configure_orientation__( orientation ) 363 w = c.__get_widgets__() 364 policy = c.expand_policy 365 366 if len( w ) == 1: 367 # use last one, in case of LabelEntry without label 368 if isinstance( policy, ( tuple, list ) ): 369 policy = policy[ -1 ] 370 371 xrm = policy.horizontal.__get_gtk_resize_policy__() 372 yrm = policy.vertical.__get_gtk_resize_policy__() 373 374 if self.horizontal: 375 row0 = 0 376 row1 = 2 377 col0 = idx 378 col1 = idx + 1 379 else: 380 row0 = idx 381 row1 = idx + 1 382 col0 = 0 383 col1 = 2 384 385 self.attach( w[ 0 ], col0, col1, row0, row1, 386 xoptions=xrm, 387 yoptions=yrm, 388 xpadding=self.padding, 389 ypadding=self.padding ) 390 391 elif len( w ) == 2: 392 if not isinstance( policy, ( tuple, list ) ): 393 policy = ( policy, policy ) 394 395 xrm = ( policy[ 0 ].horizontal.__get_gtk_resize_policy__(), 396 policy[ 1 ].horizontal.__get_gtk_resize_policy__() ) 397 yrm = ( policy[ 0 ].vertical.__get_gtk_resize_policy__(), 398 policy[ 1 ].vertical.__get_gtk_resize_policy__() ) 399 400 if self.horizontal: 401 row0 = 0 402 row1 = 1 403 row2 = 1 404 row3 = 2 405 col0 = idx 406 col1 = idx + 1 407 col2 = idx 408 col3 = idx + 1 409 else: 410 row0 = idx 411 row1 = idx + 1 412 row2 = idx 413 row3 = idx + 1 414 col0 = 0 415 col1 = 1 416 col2 = 1 417 col3 = 2 418 self.attach( w[ 0 ], col0, col1, row0, row1, 419 xoptions=xrm[ 0 ], 420 yoptions=yrm[ 0 ], 421 xpadding=self.padding, 422 ypadding=self.padding ) 423 self.attach( w[ 1 ], col2, col3, row2, row3, 424 xoptions=xrm[ 1 ], 425 yoptions=yrm[ 1 ], 426 xpadding=self.padding, 427 ypadding=self.padding )
428 # __setup_gui__() 429 430
431 - def __get_widgets__( self ):
432 return self.children
433 # __get_widgets__() 434 # _Table 435 436
437 -class _Panel( gtk.ScrolledWindow ):
438 """Internal widget to arrange components. 439 440 @warning: never use it directly in Eagle applications! 441 """ 442 443 spacing = 5 444 app = _gen_ro_property( "app" ) 445 id = _gen_ro_property( "id" ) 446 children = _gen_ro_property( "children" ) 447 448 _horizontal = None 449 _hscrollbar_policy = None 450 _vscrollbar_policy = None 451
452 - def __init__( self, app, id, children ):
453 self.app = app 454 self.id = id 455 self.children = _obj_tuple( children ) 456 457 gtk.ScrolledWindow.__init__( self ) 458 self.set_name( id ) 459 self.__setup_gui__() 460 self.__add_widgets_to_app__()
461 # __init__() 462 463
464 - def __setup_gui__( self ):
465 self.set_shadow_type( gtk.SHADOW_NONE ) 466 self.set_policy( hscrollbar_policy=self._hscrollbar_policy, 467 vscrollbar_policy=self._vscrollbar_policy ) 468 self._tab = _Table( self.id, self.children, self._horizontal ) 469 self.add_with_viewport( self._tab ) 470 self.get_child().set_shadow_type( gtk.SHADOW_NONE )
471 # __setup_gui__() 472 473
474 - def __add_widgets_to_app__( self ):
475 for w in self.children: 476 self.app.__add_widget__( w )
477 # __add_widgets_to_app__() 478 479
480 - def __get_widgets__( self ):
481 return self._tab.__get_widgets__()
482 # __get_widgets__() 483 # _Panel 484 485
486 -class _VPanel( _Panel ):
487 """Internal widget to arrange components vertically. 488 489 @warning: never use it directly in Eagle applications! 490 """ 491 492 _horizontal = False 493 _hscrollbar_policy = gtk.POLICY_NEVER 494 _vscrollbar_policy = gtk.POLICY_AUTOMATIC
495 # _VPanel 496 497
498 -class _HPanel( _Panel ):
499 """Internal widget to arrange components horizontally. 500 501 @warning: never use it directly in Eagle applications! 502 """ 503 504 _horizontal = True 505 _hscrollbar_policy = gtk.POLICY_AUTOMATIC 506 _vscrollbar_policy = gtk.POLICY_NEVER
507 # _HPanel 508 509
510 -class _EGObject( object ):
511 """The basic Eagle Object. 512 513 All eagle objects provides an attribute "id". 514 515 @warning: never use it directly in Eagle applications! 516 """ 517 518 id = _gen_ro_property( "id" ) 519
520 - def __init__( self, id ):
521 self.id = id
522 # __init__() 523 524
525 - def __str__( self ):
526 return "%s( id=%r )" % ( self.__class__.__name__, self.id )
527 # __str__() 528 __repr__ = __str__
529 # _EGObject 530 531
532 -class AutoGenId( object ):
533 """Mix-In to auto-generate ids. 534 535 @warning: never use it directly in Eagle applications! 536 """ 537 last_id_num = 0 538
539 - def __get_id__( classobj ):
540 n = "%s-%d" % ( classobj.__name__, classobj.last_id_num ) 541 classobj.last_id_num += 1 542 return n
543 # __get_id__() 544 __get_id__ = classmethod( __get_id__ )
545 # AutoGenId 546 547
548 -class Image( _EGObject, AutoGenId ):
549 """ 550 An image that can be loaded from files or binary data and saved to files. 551 """ 552 _id2obj_ = weakref.WeakValueDictionary() 553
554 - def __init__( self, **kargs ):
555 """Image constructor. 556 557 Images can be constructed in 2 ways using keyword arguments: 558 - from files, in this case you give it B{filename} keyword: 559 560 >>> Image( filename='myfile.png' ) 561 562 - from raw data, in this case you need to provide at least 563 B{data}, B{width} and B{height} as arguments. Optional 564 arguments are I{depth}, I{has_alpha} and I{row_stride}. 565 See L{load_data()} for more information: 566 567 >>> Image( data=data, width=200, height=200, depth=32, has_alpha=False ) 568 569 @see: L{load_data()} 570 @see: L{load_file()} 571 """ 572 id = kargs.get( "id" ) or self.__get_id__() 573 _EGObject.__init__( self, id ) 574 575 self._img = None 576 577 if "filename" in kargs: 578 self.load_file( kargs[ "filename" ] ) 579 elif "data" in kargs and "width" in kargs and "height" in kargs: 580 k = { "data": kargs[ "data" ], 581 "width": kargs[ "width" ], 582 "height": kargs[ "height" ], 583 } 584 if "depth" in kargs: 585 k[ "depth" ] = kargs[ "depth" ] 586 if "has_alpha" in kargs: 587 k[ "has_alpha" ] = kargs[ "has_alpha" ] 588 if "rowstride" in kargs: 589 k[ "rowstride" ] = kargs[ "rowstride" ] 590 self.load_data( **k ) 591 elif "__int_image__" in kargs: 592 if isinstance( kargs[ "__int_image__" ], gtk.gdk.Pixbuf ): 593 self._img = kargs[ "__int_image__" ] 594 else: 595 raise ValueError( "Wrong internal image given!" ) 596 elif len( kargs ) > 0: 597 params = [ "%s=%r" % kv for kv in kargs.iteritems() ] 598 raise ValueError( "Unknow parameters: %s" % params ) 599 600 Image._id2obj_[ self.id ] = self
601 # __init__() 602 603
604 - def __get_gtk_pixbuf__( self ):
605 return self._img
606 # __get_gtk_pixbuf__() 607 608
609 - def __get_by_id__( klass, id ):
610 return klass._id2obj_[ id ]
611 # __get_by_id__() 612 __get_by_id__ = classmethod( __get_by_id__ ) 613 614
615 - def __del__( self ):
616 gc.collect()
617 # __del__() 618 619
620 - def save( self, filename, format=None, **options ):
621 """Save image to a file. 622 623 If format is not specified, it will be guessed from filename. 624 625 Format may be an extension or a mime type, see 626 L{get_writable_formats()}. 627 628 @see: L{get_writable_formats()}. 629 @raise Exception: if errors happened during write 630 @raise ValueError: if format is unsupported 631 """ 632 if isinstance( filename, ( tuple, list ) ): 633 filename = os.path.join( *filename ) 634 635 if format is None: 636 format = filename.split( os.path.extsep )[ -1 ] 637 638 format = format.lower() 639 t = None 640 for f in self.get_writable_formats(): 641 if format == f[ "name" ] or \ 642 format in f[ "extensions" ] or \ 643 format in f[ "mime_types" ]: 644 t = f[ "name" ] 645 break 646 if t: 647 try: 648 self._img.save( filename, t, options ) 649 except gobject.GError, e: 650 raise Exception( e ) 651 else: 652 raise ValueError( "Unsupported file format: \"%s\"" % format )
653 # save() 654 655
656 - def get_formats( self ):
657 """Get supported image format information. 658 659 @return: list of dicts with keys: 660 - B{name}: format name 661 - B{description}: format description 662 - B{extensions}: extensions that match format 663 - B{mime_types}: mime types that match format 664 - B{is_writable}: if it is possible to write in this format, otherwise 665 it's just readable 666 """ 667 return gtk.gdk.pixbuf_get_formats()
668 # get_formats() 669 670
671 - def get_writable_formats( self ):
672 """Get formats that support saving/writing. 673 674 @see: L{get_formats()} 675 """ 676 k = [] 677 for f in self.get_formats(): 678 if f[ "is_writable" ]: 679 k.append( f ) 680 return k
681 # get_writable_formats() 682 683
684 - def load_file( self, filename ):
685 """Load image from file given its filename. 686 687 filename may be a string or a tuple/list with path elements, 688 this helps your program to stay portable across different platforms. 689 690 >>> i = Image() 691 >>> i.load_file( 'img.png' ) 692 >>> i.load_file( ( 'test', 'img.png' ) ) 693 """ 694 if isinstance( filename, ( tuple, list ) ): 695 filename = os.path.join( *filename ) 696 697 try: 698 self._img = gtk.gdk.pixbuf_new_from_file( filename ) 699 except gobject.GError, e: 700 raise Exception( e )
701 # load_file() 702 703
704 - def load_data( self, data, width, height, 705 depth=24, has_alpha=None, rowstride=None ):
706 """Load image from raw data. 707 708 If no value is provided as B{has_alpha}, then it's set to C{False} 709 if B{depth} is less or equal 24 or set to C{True} if depth is 32. 710 711 If no value is provided as B{rowstride}, then it's set to 712 M{width * depth / bits_per_sample}. 713 714 >>> i = Image() 715 >>> i.load_data( my_data1, 800, 600, depth=32, has_alpha=False ) 716 >>> i.load_data( my_data2, 400, 300, depth=24 ) 717 """ 718 colorspace = gtk.gdk.COLORSPACE_RGB 719 bits_per_sample = 8 720 721 if has_alpha is None: 722 if depth <= 24: 723 has_alpha=False 724 else: 725 has_alpha=True 726 727 if rowstride is None: 728 rowstride = width * depth / bits_per_sample 729 730 if len( data ) < height * rowstride: 731 raise ValueError( ( "data must be at least " 732 "width * height * rowstride long." 733 "Values are: data size=%d, required=%d" ) % 734 ( len( data ), height * rowstride ) ) 735 736 if isinstance( data, list ): 737 # Convert to allowed types, from fastest to slower 738 try: 739 import Numeric 740 data = Numeric.array( data, typecode=Numeric.Character ) 741 except ImportError: 742 try: 743 import array 744 data = array.array( 'c', data ) 745 except: 746 data = tuple( data ) 747 748 self._img = gtk.gdk.pixbuf_new_from_data( data, colorspace, 749 has_alpha, bits_per_sample, 750 width, height, rowstride )
751 # load_data() 752 753
754 - def get_data( self ):
755 """Return raw data and information about this image. 756 757 @return: a tuple of: 758 - width 759 - height 760 - depth 761 - has alpha? 762 - rowstride 763 - raw pixel data 764 """ 765 return ( self.get_width(), self.get_height(), self.get_depth(), 766 self.has_alpha(), self.get_rowstride(), 767 self._img.get_pixels() )
768 # get_data() 769
770 - def get_width( self ):
771 return self._img.get_width()
772 # get_width() 773 774
775 - def get_height( self ):
776 return self._img.get_height()
777 # get_height() 778 779
780 - def get_size( self ):
781 """Return a tuple ( width, heigt )""" 782 return ( self.get_width(), self.get_height() )
783 # get_size() 784 785
786 - def get_rowstride( self ):
787 """Row stride is the allocated size of a row. 788 789 Generally, rowstride is the number of elements in a row multiplied 790 by the size of each element (bits per pixel). 791 792 But there are cases that there is more space left, a padding, to 793 align it to some boundary, so you may get different value for 794 row stride. 795 """ 796 return self._img.get_rowstride()
797 # get_rowstride() 798 799
800 - def get_n_channels( self ):
801 """Number of channels.""" 802 return self._img.get_n_channels()
803 # get_n_channels() 804 805
806 - def get_bits_per_pixel( self ):
807 """Bits per pixel""" 808 return self.get_n_channels() * self._img.get_bits_per_sample()
809 # get_bits_per_pixel() 810 get_depth = get_bits_per_pixel 811 812
813 - def has_alpha( self ):
814 """If it has an alpha channel""" 815 return self._img.get_has_alpha()
816 # has_alpha() 817 # Image 818 819 820
821 -class _EGWidget( _EGObject ):
822 """The base of every Graphical Component in Eagle. 823 824 @warning: never use it directly in Eagle applications! 825 """ 826 app = _gen_ro_property( "app" ) 827
828 - def __init__( self, id, app=None, expand_policy=None ):
829 _EGObject.__init__( self, id ) 830 if app is not None: 831 self.app = app 832 self._widgets = tuple() 833 self.expand_policy = expand_policy or ExpandPolicy.All()
834 # __init__() 835 836
837 - def __get_widgets__( self ):
838 """Return a list of B{internal} widgets this Eagle widget contains. 839 840 @warning: never use it directly in Eagle applications! 841 """ 842 return self._widgets
843 # __get_widgets__() 844 845 846 ORIENTATION_VERTICAL = 0 847 ORIENTATION_HORIZONTAL = 1
848 - def __configure_orientation__( self, setting ):
849 pass
850 # __configure_orientation__() 851 852 853
854 - def set_active( self, active=True ):
855 """Set the widget as active. 856 857 An active widget have their actions enabled, while an inactive 858 (active=False) will be grayed and actions disabled. 859 """ 860 for w in self.__get_widgets__(): 861 w.set_sensitive( active )
862 # set_active() 863 864
865 - def set_inactive( self ):
866 """Same as L{set_active}( False )""" 867 self.set_active( False )
868 # set_inactive() 869 870
871 - def show( self ):
872 """Make widget visible.""" 873 for w in self.__get_widgets__(): 874 w.show()
875 # show() 876 877
878 - def hide( self ):
879 """Make widget invisible.""" 880 for w in self.__get_widgets__(): 881 w.hide()
882 # hide() 883 # _EGWidget 884 885
886 -class _EGDataWidget( _EGWidget ):
887 """The base of every Eagle widget that holds data. 888 889 Widgets that holds data implement the interface with L{get_value}() and 890 L{set_value}(). 891 892 It can be made persistent with L{persistent}=True 893 """ 894 persistent = False 895
896 - def __init__( self, id, persistent, app=None, expand_policy=None ):
897 _EGWidget.__init__( self, id, expand_policy=expand_policy ) 898 if app is not None: 899 self.app = app 900 self.persistent = persistent 901 self._widgets = tuple()
902 # __init__() 903 904
905 - def get_value( self ):
906 """Get data from this widget.""" 907 raise NotImplementedError( "%s doesn't implement get_value()" % 908 self.__class__.__name__ )
909 # get_value() 910
911 - def set_value( self, value ):
912 """Set data to this widget.""" 913 raise NotImplementedError( "%s doesn't implement set_value()" % 914 self.__class__.__name__ )
915 # set_value() 916 # _EGDataWidget 917 918
919 -class AboutDialog( _EGWidget, AutoGenId ):
920 """A window that displays information about the application. 921 922 @attention: avoid using this directly, use L{AboutButton} instead. 923 """ 924 border = 12 925 spacing = 6 926 width = 600 927 height = 400 928 margin = 6 929
930 - def __init__( self, app, 931 title, author=None, description=None, help=None, 932 version=None, license=None, copyright=None ):
933 _EGWidget.__init__( self, self.__get_id__(), app ) 934 self.title = str( title ) 935 self.author = _str_tuple( author ) 936 self.description = _str_tuple( description ) 937 self.help = _str_tuple( help ) 938 self.version = _str_tuple( version ) 939 self.license = _str_tuple( license ) 940 self.copyright = _str_tuple( copyright ) 941 942 self.__setup_gui__()
943 # __init__() 944 945
946 - def __del__( self ):
947 self._diag.destroy()
948 # __del__() 949 950
951 - def __setup_gui__( self ):
952 win = self.app.__get_window__() 953 btns = ( gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE ) 954 flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT 955 self._diag = gtk.Dialog( title=( "About: %s" % self.title ), 956 parent=win, 957 flags=flags, buttons=btns ) 958 959 self._diag.set_border_width( self.border ) 960 self._diag.set_default_size( self.width, self.height ) 961 self._diag.set_has_separator( False ) 962 self._diag.vbox.set_spacing( self.spacing ) 963 964 _set_icon_list( self._diag, gtk.STOCK_ABOUT ) 965 966 self._text = RichText( id="About-%s" % self.app.id ) 967 self._diag.vbox.pack_start( self._text._widgets[ 0 ], True, True ) 968 969 self.__setup_text__()
970 # __setup_gui__() 971 972
973 - def __setup_text__( self ):
974 self._text.append( "<h1>%s</h1>" % self.title ) 975 976 if self.version: 977 v = ".".join( self.version ) 978 self._text.append( "<i>%s</i>" % v ) 979 980 self._text.append( "<hr />" ) 981 982 if self.description: 983 self._text.append( "<h2>Description</h2>" ) 984 for l in self.description: 985 self._text.append( "<p>%s</p>" % l ) 986 987 if self.license: 988 self._text.append( "<h2>License</h2><p>" ) 989 self._text.append( ", ".join( self.license ) ) 990 self._text.append( "</p>" ) 991 992 if self.author: 993 if len( self.author ) == 1: 994 self._text.append( "<h2>Author</h2>" ) 995 else: 996 self._text.append( "<h2>Authors</h2>" ) 997 998 self._text.append( "<ul>" ) 999 for a in self.author: 1000 self._text.append( "<li>%s</li>" % a ) 1001 self._text.append( "</ul>" ) 1002 1003 if self.help: 1004 self._text.append( "<h2>Help</h2>" ) 1005 for l in self.help: 1006 self._text.append( "<p>%s</p>" % l ) 1007 1008 if self.copyright: 1009 self._text.append( "<h2>Copyright</h2>" ) 1010 for l in self.copyright: 1011 self._text.append( "<p>%s</p>" % l )
1012 # __setup_text__() 1013 1014
1015 - def run( self ):
1016 self._diag.show_all() 1017 self._diag.run() 1018 self._diag.hide()
1019 # run() 1020 # AboutDialog 1021 1022
1023 -class HelpDialog( _EGWidget, AutoGenId ):
1024 """A window that displays application help. 1025 1026 @attention: avoid using this directly, use L{HelpButton} instead. 1027 """ 1028 border = 12 1029 spacing = 6 1030 width = 600 1031 height = 400 1032 margin = 6 1033
1034 - def __init__( self, app, title, help=None ):
1035 _EGWidget.__init__( self, self.__get_id__(), app ) 1036 self.title = title 1037 self.help = _str_tuple( help ) 1038 self.__setup_gui__()
1039 # __init__() 1040 1041
1042 - def __del__( self ):
1043 self._diag.destroy()
1044 # __del__() 1045 1046
1047 - def __setup_gui__( self ):
1048 win = self.app.__get_window__() 1049 btns = ( gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE ) 1050 flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT 1051 self._diag = gtk.Dialog( title=( "Help: %s" % self.title ), 1052 parent=win, 1053 flags=flags, buttons=btns ) 1054 self._diag.set_border_width( self.border ) 1055 self._diag.set_default_size( self.width, self.height ) 1056 self._diag.set_has_separator( False ) 1057 self._diag.vbox.set_spacing( self.spacing ) 1058 _set_icon_list( self._diag, gtk.STOCK_HELP ) 1059 1060 self._text = RichText( id="About-%s" % self.app.id ) 1061 self._diag.vbox.pack_start( self._text._widgets[ 0 ], True, True ) 1062 1063 self.__setup_text__()
1064 # __setup_gui__() 1065 1066
1067 - def __setup_text__( self ):
1068 self._text.append( "<h1>%s</h1>" % self.title ) 1069 self._text.append( "<h2>Help</h2>" ) 1070 for l in self.help: 1071 self._text.append( "<p>%s</p>" % l )
1072 # __setup_text__() 1073 1074
1075 - def run( self ):
1076 self._diag.show_all() 1077 self._diag.run() 1078 self._diag.hide()
1079 # run() 1080 # HelpDialog 1081 1082
1083 -class FileChooser( _EGWidget, AutoGenId ):
1084 """A dialog to choose a file. 1085 1086 @attention: avoid using this directly, use L{App.file_chooser}, 1087 L{OpenFileButton}, L{SaveFileButton} or L{SelectFolderButton} instead. 1088 """ 1089 ACTION_OPEN = 0 1090 ACTION_SAVE = 1 1091 ACTION_SELECT_FOLDER = 2 1092 ACTION_CREATE_FOLDER = 3 1093
1094 - def __init__( self, app, action, filename=None, 1095 title=None, filter=None, multiple=False ):
1096 """Dialog to choose files. 1097 1098 filter may be a single pattern (ie: '*.png'), mime type 1099 (ie: 'text/html') or a list of patterns or mime types or 1100 a list of lists, each sub list with a filter name and mime type/ 1101 patterns accepted. Examples: 1102 [ [ 'Images', '*.ppm', 'image/jpeg', 'image/png' ], 1103 [ 'Text', '*.text', 'text/plain' ], 1104 ] 1105 """ 1106 _EGWidget.__init__( self, self.__get_id__(), app ) 1107 self.action = action 1108 self.filter = filter 1109 self.multiple = multiple or False 1110 self.filename = filename 1111 self.title = title or self.__gen_title__() 1112 1113 self.__setup_gui__()
1114 # __init__() 1115 1116
1117 - def __gen_title__( self ):
1118 t = { self.ACTION_OPEN: "Open: %s", 1119 self.ACTION_SAVE: "Save: %s", 1120 self.ACTION_SELECT_FOLDER: "Open Folder: %s", 1121 self.ACTION_CREATE_FOLDER: "Create Folder: %s", 1122 } 1123 title = t.get( self.action, t[ self.ACTION_OPEN ] ) 1124 return title % self.app.title
1125 # __gen_title__() 1126 1127
1128 - def __del__( self ):
1129 self._diag.destroy()
1130 # __del__() 1131 1132
1133 - def __setup_gui__( self ):
1134 win = self.app.__get_window__() 1135 a = { self.ACTION_OPEN: gtk.FILE_CHOOSER_ACTION_OPEN, 1136 self.ACTION_SAVE: gtk.FILE_CHOOSER_ACTION_SAVE, 1137 self.ACTION_SELECT_FOLDER: gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, 1138 self.ACTION_CREATE_FOLDER: gtk.FILE_CHOOSER_ACTION_CREATE_FOLDER, 1139 }.get( self.action, gtk.FILE_CHOOSER_ACTION_OPEN ) 1140 1141 b = ( gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT, 1142 gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL ) 1143 self._diag = gtk.FileChooserDialog( title=self.title, 1144 parent=win, 1145 action=a, 1146 buttons=b ) 1147 _set_icon_list( self._diag, gtk.STOCK_OPEN ) 1148 self._diag.set_select_multiple( self.multiple ) 1149 if self.filter: 1150 if isinstance( self.filter, ( tuple, list ) ): 1151 for f in self.filter: 1152 filter = gtk.FileFilter() 1153 if isinstance( f, ( tuple, list ) ): 1154 filter.set_name( f[ 0 ] ) 1155 for e in f[ 1 : ]: 1156 if '/' in e: 1157 filter.add_mime_type( e ) 1158 else: 1159 filter.add_pattern( e ) 1160 elif isinstance( f, ( str, unicode ) ): 1161 filter.set_name( f ) 1162 if '/' in f: 1163 filter.add_mime_type( f ) 1164 else: 1165 filter.add_pattern( f ) 1166 else: 1167 raise ValueError( "invalid filter!" ) 1168 self._diag.add_filter( filter ) 1169 1170 elif isinstance( self.filter, ( str, unicode ) ): 1171 filter = gtk.FileFilter() 1172 filter.set_name( self.filter ) 1173 if '/' in self.filter: 1174 filter.add_mime_type( self.filter ) 1175 else: 1176 filter.add_pattern( self.filter ) 1177 self._diag.set_filter( filter ) 1178 else: 1179 raise ValueError( "invalid filter!" ) 1180 if self.filename: 1181 self._diag.set_filename( self.filename )
1182 # __setup_gui__() 1183 1184
1185 - def run( self ):
1186 self._diag.show_all() 1187 r = self._diag.run() 1188 self._diag.hide() 1189 if r == gtk.RESPONSE_ACCEPT: 1190 return self._diag.get_filename() 1191 else: 1192 return None
1193 # run() 1194 # FileChooser 1195 1196
1197 -class PreferencesDialog( _EGWidget, AutoGenId ):
1198 """A dialog to present user with preferences. 1199 1200 Preferences is another L{App} area, just like C{left}, C{right}, C{center}, 1201 C{top} or C{bottom}, but will be displayed in a separate window. 1202 1203 @attention: avoid using this directly, use L{PreferencesButton} instead. 1204 """
1205 - def __init__( self, app, children ):
1206 _EGWidget.__init__( self, self.__get_id__(), app ) 1207 self.children = _obj_tuple( children ) 1208 self.__setup_gui__() 1209 self.__add_widgets_to_app__()
1210 # __init__() 1211 1212
1213 - def __del__( self ):
1214 self._diag.destroy()
1215 # __del__() 1216 1217
1218 - def __setup_gui__( self ):
1219 win = self.app.__get_window__() 1220 btns = ( gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE ) 1221 flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT 1222 self._diag = gtk.Dialog( title=( "Preferences: %s" % self.app.title ), 1223 parent=win, 1224 flags=flags, buttons=btns ) 1225 self._diag.set_default_size( 400, 300 ) 1226 _set_icon_list( self._diag, gtk.STOCK_PREFERENCES ) 1227 1228 self._sw = gtk.ScrolledWindow() 1229 self._diag.get_child().pack_start( self._sw, expand=True, fill=True ) 1230 1231 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 1232 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 1233 1234 self._tab = _Table( self.id, self.children ) 1235 self._sw.add_with_viewport( self._tab ) 1236 self._sw.get_child().set_shadow_type( gtk.SHADOW_NONE ) 1237 self._sw.set_shadow_type( gtk.SHADOW_NONE )
1238 # __setup_gui__() 1239 1240
1241 - def __add_widgets_to_app__( self ):
1242 for w in self.children: 1243 if isinstance( w, _EGDataWidget ): 1244 w.persistent = True 1245 self.app.__add_widget__( w )
1246 # __add_widgets_to_app__() 1247 1248
1249 - def run( self ):
1250 self._diag.show_all() 1251 self._diag.run() 1252 self._diag.hide()
1253 # run() 1254 # PreferencesDialog 1255 1256
1257 -class DebugDialog( _EGObject, AutoGenId ):
1258 """Dialog to show uncaught exceptions. 1259 1260 This dialog shows information about uncaught exceptions and also save 1261 the traceback to a file. 1262 """ 1263 # Most of DebugDialog code came from Gazpacho code! Thanks! 1264 border = 12 1265 spacing = 6 1266 width = 600 1267 height = 400 1268 margin = 6 1269
1270 - def __init__( self ):
1271 _EGObject.__init__( self, self.__get_id__() ) 1272 self.__setup_gui__()
1273 # __init__() 1274 1275
1276 - def __setup_gui__( self ):
1277 b = ( gtk.STOCK_QUIT, gtk.RESPONSE_CLOSE ) 1278 self._diag = gtk.Dialog( "Application Crashed!", 1279 parent=None, 1280 flags=gtk.DIALOG_MODAL, 1281 buttons=b ) 1282 self._diag.set_border_width( self.border ) 1283 self._diag.set_default_size( self.width, self.height ) 1284 self._diag.set_has_separator( False ) 1285 self._diag.vbox.set_spacing( self.spacing ) 1286 1287 self._hbox1 = gtk.HBox() 1288 1289 self._label1 = gtk.Label( "<b>Exception type:</b>" ) 1290 self._label1.set_use_markup( True ) 1291 self._hbox1.pack_start( self._label1, False, False, self.spacing ) 1292 self._label1.show() 1293 1294 self._exctype = gtk.Label() 1295 self._hbox1.pack_start( self._exctype, False, True ) 1296 self._exctype.show() 1297 1298 self._diag.vbox.pack_start( self._hbox1, False, False ) 1299 self._hbox1.show() 1300 1301 self._hbox2 = gtk.HBox() 1302 1303 self._label2 = gtk.Label( "<b>This info was saved to:</b>" ) 1304 self._label2.set_use_markup( True ) 1305 self._hbox2.pack_start( self._label2, False, False, self.spacing ) 1306 self._label2.show() 1307 1308 self._save_name = gtk.Label() 1309 self._hbox2.pack_start( self._save_name, False, True ) 1310 self._save_name.show() 1311 1312 self._diag.vbox.pack_start( self._hbox2, False, False ) 1313 self._hbox2.show() 1314 1315 self._sw = gtk.ScrolledWindow() 1316 self._sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC ) 1317 self._sw.set_shadow_type( gtk.SHADOW_IN ) 1318 self._text = gtk.TextView() 1319 self._text.set_editable( False ) 1320 self._text.set_cursor_visible( False ) 1321 self._text.set_wrap_mode( gtk.WRAP_WORD ) 1322 self._text.set_left_margin( self.margin ) 1323 self._text.set_right_margin( self.margin ) 1324 1325 self._sw.add( self._text ) 1326 self._text.show() 1327 self._diag.vbox.pack_start( self._sw, expand=True, fill=True ) 1328 self._sw.show() 1329 self.__setup_text__()
1330 # __setup_gui__() 1331 1332
1333 - def __setup_text__( self ):
1334 self._buf = self._text.get_buffer() 1335 self._buf.create_tag( "label", weight=pango.WEIGHT_BOLD ) 1336 self._buf.create_tag( "code", foreground="gray25", 1337 family="monospace" ) 1338 self._buf.create_tag( "exc", foreground="#880000", 1339 weight=pango.WEIGHT_BOLD )
1340 # __setup_text__() 1341 1342
1343 - def show_exception( self, exctype, value, tb ):
1344 import traceback 1345 self._exctype.set_text( str( exctype ) ) 1346 self.print_tb( tb ) 1347 1348 lines = traceback.format_exception_only( exctype, value ) 1349 msg = lines[ 0 ] 1350 result = msg.split( ' ', 1 ) 1351 if len( result ) == 1: 1352 msg = result[ 0 ] 1353 arguments = "" 1354 else: 1355 msg, arguments = result 1356 1357 self._insert_text( "\n" ) 1358 self._insert_text( msg, "exc" ) 1359 self._insert_text( " " ) 1360 self._insert_text( arguments )
1361 # show_exception() 1362 1363
1364 - def save_exception( self, exctype, value, tb ):
1365 import traceback 1366 import time 1367 progname = os.path.split( sys.argv[ 0 ] )[ -1 ] 1368 filename = "%s-%s-%s.tb" % ( progname, 1369 os.getuid(), 1370 int( time.time() ) ) 1371 filename = os.path.join( os.path.sep, "tmp", filename ) 1372 f = open( filename, "wb" ) 1373 try: 1374 os.chmod( filename, 0600 ) 1375 except: 1376 pass 1377 1378 for e in traceback.format_exception( exctype, value, tb ): 1379 f.write( e ) 1380 f.close() 1381 self._save_name.set_text( filename ) 1382 sys.stderr.write( "Traceback saved to '%s'.\n" % filename )
1383 # save_exception() 1384 1385
1386 - def print_tb( self, tb, limit=None ):
1387 import linecache 1388 1389 if limit is None: 1390 if hasattr( sys, "tracebacklimit" ): 1391 limit = sys.tracebacklimit 1392 n = 0 1393 while tb is not None and ( limit is None or n < limit ): 1394 f = tb.tb_frame 1395 lineno = tb.tb_lineno 1396 co = f.f_code 1397 filename = co.co_filename 1398 name = co.co_name 1399 self._print_file( filename, lineno, name ) 1400 line = linecache.getline( filename, lineno ) 1401 if line: 1402 self._insert_text( " " + line.strip() + "\n\n", "code" ) 1403 tb = tb.tb_next 1404 n = n+1
1405 # print_tb() 1406 1407
1408 - def _insert_text( self, text, *tags ):
1409 end_iter = self._buf.get_end_iter() 1410 self._buf.insert_with_tags_by_name( end_iter, text, *tags )
1411 # _insert_text() 1412 1413
1414 - def _print_file( self, filename, lineno, name ):
1415 if filename.startswith( os.getcwd() ): 1416 filename = filename.replace( os.getcwd(), '' )[ 1 : ] 1417 1418 self._insert_text( "File: ", "label" ) 1419 self._insert_text( filename ) 1420 self._insert_text( "\n" ) 1421 self._insert_text( "Line: ", "label" ) 1422 self._insert_text( str( lineno ) ) 1423 self._insert_text( "\n" ) 1424 self._insert_text( "Function: ", "label" ) 1425 self._insert_text( name ) 1426 self._insert_text( "\n" )
1427 # _print_file() 1428
1429 - def _start_debugger( self ):
1430 import pdb 1431 pdb.pm()
1432 # _start_debugger() 1433 1434 1435
1436 - def run( self, error=None ):
1437 r = self._diag.run() 1438 if r == gtk.RESPONSE_CLOSE or gtk.RESPONSE_DELETE_EVENT: 1439 raise SystemExit( error )
1440 # run() 1441 1442
1443 - def except_hook( exctype, value, tb ):
1444 if exctype is not KeyboardInterrupt: 1445 d = DebugDialog() 1446 d.save_exception( exctype, value, tb ) 1447 d.show_exception( exctype, value, tb ) 1448 d.run() 1449 d.destroy() 1450 1451 raise SystemExit
1452 # except_hook() 1453 except_hook = staticmethod( except_hook )
1454 # DebugDialog 1455 sys.excepthook = DebugDialog.except_hook 1456 1457
1458 -class _EGWidLabelEntry( _EGDataWidget ):
1459 """Widget that holds a label and an associated Entry. 1460 1461 @note: _EGWidLabelEntry must B{NOT} be used directly! You should use 1462 a widget that specialize this instead. 1463 1464 @attention: B{Widget Developers:} You must setup an instance attribute 1465 C{_entry} before using it, since this will be set as mnemonic for this 1466 label and also returned in L{__get_widgets__}(). 1467 """ 1468
1469 - def __init__( self, id, persistent, label="", expand_policy=None ):
1470 """ 1471 @param expand_policy: one or two ExpandPolicy elements. If just 1472 one is provided, it will be used for both inner elements. 1473 If two are provided, first will be used for label and 1474 second for entry. 1475 """ 1476 if expand_policy is None: 1477 expand_policy = ( ExpandPolicy.Fill(), 1478 ExpandPolicy.Horizontal() ) 1479 elif not isinstance( expand_policy, ( list, tuple ) ): 1480 expand_policy = ( expand_policy, expand_policy ) 1481 1482 _EGDataWidget.__init__( self, id, persistent, 1483 expand_policy=expand_policy ) 1484 self.__label = label 1485 self.__setup_gui__()
1486 # __init__() 1487 1488
1489 - def __setup_gui__( self ):
1490 if self.__label is not None: 1491 self._label = gtk.Label( self.__label ) 1492 self._label.set_mnemonic_widget( self._entry ) 1493 self._widgets = ( self._label, self._entry ) 1494 else: 1495 self._widgets = ( self._entry, )
1496 # __setup_gui__() 1497 1498
1499 - def __configure_orientation__( self, setting ):
1500 if self.label: 1501 if setting == self.ORIENTATION_VERTICAL: 1502 self._label.set_justify( gtk.JUSTIFY_RIGHT ) 1503 self._label.set_alignment( xalign=1.0, yalign=0.5 ) 1504 elif setting == self.ORIENTATION_HORIZONTAL: 1505 self._label.set_justify( gtk.JUSTIFY_LEFT ) 1506 self._label.set_alignment( xalign=0.0, yalign=1.0 )
1507 # __configure_orientation__() 1508 1509
1510 - def get_value( self ):
1511 return self._entry.get_value()
1512 # get_value() 1513 1514
1515 - def set_value( self, value ):
1516 self._entry.set_value( value )
1517 # set_value() 1518 1519
1520 - def set_label( self, label ):
1521 if self.__label is None: 1522 raise ValueError( "You cannot change label of widget created " 1523 "without one. Create it with placeholder! " 1524 "(label='')" ) 1525 self.__label = label 1526 self._label.set_text( self.__label )
1527 # set_label() 1528 1529
1530 - def get_label( self ):
1531 return self.__label
1532 # get_label() 1533 1534 label = property( get_label, set_label ) 1535 1536
1537 - def __str__( self ):
1538 return "%s( id=%r, label=%r, value=%r )" % \ 1539 ( self.__class__.__name__, self.id, self.label, 1540 self.get_value() )
1541 # __str__() 1542 __repr__ = __str__
1543 # _EGWidLabelEntry 1544 1545
1546 -class App( _EGObject, AutoGenId ):
1547 """An application window. 1548 1549 This is the base of Eagle programs, since it will hold every graphical 1550 component. 1551 1552 An App window is split in 5 areas: 1553 - left 1554 - right 1555 - center 1556 - top 1557 - bottom 1558 the first 3 have a vertical layout, the other have horizontal layout. 1559 Every area has its own scroll bars that are shown automatically when 1560 need. 1561 1562 Also provided is an extra area, that is shown in another window. This is 1563 the preferences area. It have a vertical layout and components that 1564 hold data are made persistent automatically. You should use 1565 L{PreferencesButton} to show this area. 1566 1567 Extra information like author, description, help, version, license and 1568 copyright are used in specialized dialogs. You may show these dialogs 1569 with L{AboutButton} and L{HelpButton}. 1570 1571 Widgets can be reach with L{get_widget_by_id}, example: 1572 >>> app = App( "My App", left=Entry( id="entry" ) ) 1573 >>> app.get_widget_by_id( "entry" ) 1574 Entry( id='entry', label='entry', value='' ) 1575 1576 You may also reach widgets using dict-like syntax, but with the 1577 special case for widgets that hold data, these will be provided 1578 using their L{set_data<_EGDataWidget.set_data>} and 1579 L{get_data<_EGDataWidget.get_data>}, it make things easier, but 1580 B{be careful to don't misuse it!}. Example: 1581 1582 >>> app= App( "My App", left=Entry( id="entry" ), 1583 ... right=Canvas( "canvas", 300, 300 ) ) 1584 >>> app[ "entry" ] 1585 '' 1586 >>> app[ "entry" ] = "text" 1587 >>> app[ "entry" ] 1588 'text' 1589 >>> app[ "canvas" ] 1590 Canvas( id='canvas', width=300, height=300, label='' ) 1591 >>> app[ "canvas" ].draw_text( "hello" ) 1592 >>> app[ "entry" ].get_value() # will fail, since it's a data widget 1593 1594 """ 1595 border_width = 10 1596 spacing = 3 1597 1598 title = _gen_ro_property( "title" ) 1599 left = _gen_ro_property( "left" ) 1600 right = _gen_ro_property( "right" ) 1601 top = _gen_ro_property( "top" ) 1602 bottom = _gen_ro_property( "bottom" ) 1603 center = _gen_ro_property( "center" ) 1604 preferences = _gen_ro_property( "preferences" ) 1605 statusbar = _gen_ro_property( "statusbar" ) 1606 _widgets = _gen_ro_property( "_widgets" ) 1607
1608 - def __init__( self, title, id=None, 1609 center=None, left=None, right=None, top=None, bottom=None, 1610 preferences=None, window_size=( 800, 600 ), 1611 quit_callback=None, data_changed_callback=None, 1612 author=None, description=None, help=None, version=None, 1613 license=None, copyright=None, 1614 statusbar=False ):
1615 """App Constructor. 1616 1617 @param title: application name, to be displayed in the title bar. 1618 @param id: unique id to this application, or None to generate one 1619 automatically. 1620 @param center: list of widgets to be laid out vertically in the 1621 window's center. 1622 @param left: list of widgets to be laid out vertically in the 1623 window's left side. 1624 @param right: list of widgets to be laid out vertically in the 1625 window's right side. 1626 @param top: list of widgets to be laid out horizontally in the 1627 window's top. 1628 @param bottom: list of widgets to be laid out horizontally in the 1629 window's bottom. 1630 @param preferences: list of widgets to be laid out vertically in 1631 another window, this can be shown with L{PreferencesButton}. 1632 @param window_size: tuple of ( width, height ) or None to use the 1633 minimum size. 1634 @param statusbar: if C{True}, an statusbar will be available and 1635 usable with L{status_message} method. 1636 @param author: the application author or list of author, used in 1637 L{AboutDialog}, this can be shown with L{AboutButton}. 1638 @param description: application description, used in L{AboutDialog}. 1639 @param help: help text, used in L{AboutDialog} and L{HelpDialog}, this 1640 can be shown with L{HelpButton}. 1641 @param version: application version, used in L{AboutDialog}. 1642 @param license: application license, used in L{AboutDialog}. 1643 @param copyright: application copyright, used in L{AboutDialog}. 1644 @param quit_callback: function (or list of functions) that will be 1645 called when application is closed. Function will receive as 1646 parameter the reference to App. If return value is False, 1647 it will abort closing the window. 1648 @param data_changed_callback: function (or list of functions) that will 1649 be called when some widget that holds data have its data 1650 changed. Function will receive as parameters: 1651 - App reference 1652 - Widget reference 1653 - new value 1654 """ 1655 _EGObject.__init__( self, id ) 1656 self.title = title 1657 self.left = left 1658 self.right = right 1659 self.top = top 1660 self.bottom = bottom 1661 self.center = center 1662 self.preferences = preferences 1663 self.window_size = window_size 1664 self.author = _str_tuple( author ) 1665 self.description = _str_tuple( description ) 1666 self.help = _str_tuple( help ) 1667 self.version = _str_tuple( version ) 1668 self.license = _str_tuple( license ) 1669 self.copyright = _str_tuple( copyright ) 1670 self.statusbar = statusbar 1671 self._widgets = {} 1672 1673 self.quit_callback = _callback_tuple( quit_callback ) 1674 self.data_changed_callback = _callback_tuple( data_changed_callback ) 1675 1676 self.__add_to_app_list__() 1677 self.__setup_gui__() 1678 self.__setup_connections__() 1679 self.load()
1680 # __init__() 1681 1682
1683 - def __getitem__( self, name ):
1684 w = self.get_widget_by_id( name ) 1685 if isinstance( w, _EGDataWidget ): 1686 return w.get_value() 1687 else: 1688 return w
1689 # __getitem__() 1690 1691
1692 - def __setitem__( self, name, value ):
1693 w = self.get_widget_by_id( name ) 1694 if w is None: 1695 raise ValueError( "Could not find any widget with id=%r" % name ) 1696 elif isinstance( w, _EGDataWidget ): 1697 return w.set_value( value ) 1698 else: 1699 raise TypeError( 1700 "Could not set value of widget '%s' of type '%s'." % \ 1701 ( name, type( w ).__name__ ) )
1702 # __setitem__() 1703 1704
1705 - def get_widget_by_id( self, widget_id ):
1706 """Return referece to widget with provided id or None if not found.""" 1707 if isinstance( widget_id, _EGWidget ) and \ 1708 widget_id in self._widgets.itervalues(): 1709 return widget_id 1710 else: 1711 return self._widgets.get( widget_id, None )
1712 # get_widget_by_id() 1713 1714
1715 - def show_about_dialog( self ):
1716 """Show L{AboutDialog} of this App.""" 1717 diag = AboutDialog( app=self, 1718 title=self.title, 1719 author=self.author, 1720 description=self.description, 1721 help=self.help, 1722 version=self.version, 1723 license=self.license, 1724 copyright=self.copyright, 1725 ) 1726 diag.run()
1727 # show_about_dialog() 1728 1729
1730 - def show_help_dialog( self ):
1731 """Show L{HelpDialog} of this App.""" 1732 diag = HelpDialog( app=self, 1733 title=self.title, 1734 help=self.help, 1735 ) 1736 diag.run()
1737 # show_help_dialog() 1738 1739
1740 - def file_chooser( self, action, filename=None, 1741 filter=None, multiple=False ):
1742 """Show L{FileChooser} and return selected file(s). 1743 1744 @param action: must be one of ACTION_* as defined in L{FileChooser}. 1745 @param filter: a pattern (ie: '*.png'), mime type or a list. 1746 1747 @see: L{FileChooser} 1748 """ 1749 diag = FileChooser( app=self, action=action, 1750 filename=filename, filter=filter, 1751 multiple=multiple ) 1752 return diag.run()
1753 # file_chooser() 1754 1755
1756 - def show_preferences_dialog( self ):
1757 """Show L{PreferencesDialog} associated with this App.""" 1758 return self._preferences.run()
1759 # show_preferences_dialog() 1760 1761
1762 - def __get_window__( self ):
1763 return self._win
1764 # __get_window__() 1765 1766
1767 - def __add_to_app_list__( self ):
1768 if not self.id: 1769 self.id = self.__get_id__() 1770 1771 if self.id in _apps: 1772 raise ValueError( "App id '%s' already existent!" % self.id ) 1773 1774 _apps[ self.id ] = self
1775 # __add_to_app_list__() 1776 1777
1778 - def __add_widget__( self, widget ):
1779 if widget.id in self._widgets: 1780 w = self._widgets[ widget.id ] 1781 raise ValueError( ( "Object id \"%s\" (type: %s) already in " 1782 "application \"%s\" as type: %s!" ) % \ 1783 ( widget.id, 1784 widget.__class__.__name__, 1785 self.id, 1786 w.__class__.__name__ ) ) 1787 else: 1788 if widget.app is None: 1789 self._widgets[ widget.id ] = widget 1790 widget.app = self 1791 elif widget.app != self: 1792 try: 1793 id = widget.app.id 1794 except: 1795 id = widget.app 1796 raise ValueError( ( "Object \"%s\" already is in another " 1797 "App: \"%s\"" ) % \ 1798 ( widget.id, id ) )
1799 # __add_widget__() 1800 1801
1802 - def __setup_gui__( self ):
1803 self._win = gtk.Window( gtk.WINDOW_TOPLEVEL ) 1804 self._win.set_name( self.id ) 1805 self._win.set_title( self.title ) 1806 if self.window_size: 1807 self._win.set_default_size( *self.window_size ) 1808 1809 self._top_layout = gtk.VBox( False ) 1810 self._win.add( self._top_layout ) 1811 1812 self._vbox = gtk.VBox( False, self.spacing ) 1813 self._hbox = gtk.HBox( False, self.spacing ) 1814 self._hbox.set_border_width( self.border_width ) 1815 self._top_layout.pack_start( self._vbox, expand=True, fill=True ) 1816 1817 self.__setup_gui_left__() 1818 self.__setup_gui_right__() 1819 self.__setup_gui_center__() 1820 self.__setup_gui_top__() 1821 self.__setup_gui_bottom__() 1822 self.__setup_gui_preferences__() 1823 1824 has_top = bool( self._top.__get_widgets__() ) 1825 has_bottom = bool( self._bottom.__get_widgets__() ) 1826 has_left = bool( self._left.__get_widgets__() ) 1827 has_right = bool( self._right.__get_widgets__() ) 1828 has_center = bool( self._center.__get_widgets__() ) 1829 1830 expand_top = False 1831 expand_bottom = False 1832 expand_left = False 1833 expand_right = False 1834 expand_center = has_center 1835 has_hl = has_left or has_center or has_right 1836 1837 # Left and Right just expand if there is no center 1838 if has_left and not has_center: 1839 expand_left = True 1840 if has_right and not has_center: 1841 expand_right = True 1842 1843 # Top and Bottom just expand if there is no center 1844 if has_top and not has_hl: 1845 expand_top = True 1846 if has_bottom and not has_hl: 1847 expand_bottom = True 1848 1849 # Create Horizontal layout with ( Left | Center | Right ) 1850 if has_hl: 1851 if has_left: 1852 self._hbox.pack_start( self._left, expand_left, True ) 1853 if has_center or has_right: 1854 self._hbox.pack_start( gtk.VSeparator(), False, True ) 1855 1856 if has_center: 1857 self._hbox.pack_start( self._center, expand_center, True ) 1858 if has_right: 1859 self._hbox.pack_start( gtk.VSeparator(), False, True ) 1860 1861 if has_right: 1862 self._hbox.pack_start( self._right, expand_right, True ) 1863 1864 # Create Vertical layout with ( TOP | HL | Bottom ) 1865 # where HL is Horizontal layout created before 1866 if has_top: 1867 self._vbox.pack_start( self._top, expand_top, True ) 1868 if has_hl or has_bottom: 1869 self._vbox.pack_start( gtk.HSeparator(), False, True ) 1870 1871 if has_hl: 1872 self._vbox.pack_start( self._hbox, True, True ) 1873 if has_bottom: 1874 self._vbox.pack_start( gtk.HSeparator(), False, True ) 1875 1876 if has_bottom: 1877 self._vbox.pack_start( self._bottom, expand_bottom, True ) 1878 1879 1880 if self.statusbar: 1881 self._statusbar = gtk.Statusbar() 1882 self._statusbar_ctx = self._statusbar.get_context_id( self.title ) 1883 self._statusbar.set_has_resize_grip( True ) 1884 self._top_layout.pack_end( self._statusbar, 1885 expand=False, fill=True ) 1886 1887 self._win.show_all()
1888 # __setup_gui__() 1889 1890
1891 - def __setup_gui_left__( self ):
1892 self._left = _VPanel( self, id="left", children=self.left )
1893 # __setup_gui_left__() 1894 1895
1896 - def __setup_gui_right__( self ):
1897 self._right =_VPanel( self, id="right", children=self.right )
1898 # __setup_gui_right__() 1899 1900
1901 - def __setup_gui_center__( self ):
1902 self._center = _VPanel( self, id="center", children=self.center )
1903 # __setup_gui_center__() 1904 1905
1906 - def __setup_gui_top__( self ):
1907 self._top = _HPanel( self, id="top", children=self.top )
1908 # __setup_gui_top__() 1909 1910
1911 - def __setup_gui_bottom__( self ):
1912 self._bottom = _HPanel( self, id="bottom", children=self.bottom )
1913 # __setup_gui_bottom__() 1914 1915
1916 - def __setup_gui_preferences__( self ):
1917 self._preferences = PreferencesDialog( self, 1918 children=self.preferences )
1919 # __setup_gui_preferences__() 1920 1921
1922 - def __setup_connections__( self ):
1923 self._win.connect( "delete_event", self.__delete_event__ )
1924 # __setup_connections__() 1925 1926
1927 - def data_changed( self, widget, value ):
1928 """Notify that widget changed it's value. 1929 1930 Probably you will not need to call this directly. 1931 """ 1932 self.save() 1933 for c in self.data_changed_callback: 1934 c( self, widget, value )
1935 # data_changed() 1936 1937
1938 - def __do_close__( self ):
1939 self.save() 1940 1941 for c in self.quit_callback: 1942 if not c( self ): 1943 return False 1944 1945 del _apps[ self.id ] 1946 if not _apps: 1947 gtk.main_quit() 1948 1949 return True
1950 # __do_close__() 1951 1952
1953 - def __delete_event__( self, *args ):
1954 if self.__do_close__(): 1955 return False 1956 else: 1957 return True
1958 # __delete_event__() 1959 1960
1961 - def __persistence_filename__( self ):
1962 fname = "%s.save_data" % self.id 1963 1964 if sys.platform.startswith( "win" ): 1965 appdata = os.environ.get( "APPDATA", "C:" ) 1966 binname = os.path.realpath( sys.argv[ 0 ] ).replace( ":", "" ) 1967 d = os.path.join( appdata, "Eagle", binname ) 1968 else: 1969 home = os.environ.get( "HOME", "." ) 1970 binname = os.path.realpath( sys.argv[ 0 ] )[ 1 : ] 1971 d = os.path.join( home, ".eagle", binname ) 1972 1973 if not os.path.exists( d ): 1974 os.makedirs( d ) 1975 1976 return os.path.join( d, fname )
1977 # __persistence_filename__() 1978 1979
1980 - def save( self ):
1981 """Save data from widgets to file. 1982 1983 Probably you will not need to call this directly. 1984 """ 1985 d = {} 1986 for id, w in self._widgets.iteritems(): 1987 if isinstance( w, _EGDataWidget ) and w.persistent: 1988 d[ id ] = w.get_value() 1989 1990 if d: 1991 f = open( self.__persistence_filename__(), "wb" ) 1992 pickle.dump( d, f, pickle.HIGHEST_PROTOCOL ) 1993 f.close()
1994 # save() 1995 1996
1997 - def load( self ):
1998 """Load data to widgets from file. 1999 2000 Probably you will not need to call this directly. 2001 """ 2002 try: 2003 f = open( self.__persistence_filename__(), "rb" ) 2004 except IOError: 2005 return 2006 2007 d = pickle.load( f ) 2008 f.close() 2009 2010 for id, v in d.iteritems(): 2011 try: 2012 w = self._widgets[ id ] 2013 except KeyError: 2014 w = None 2015 if isinstance( w, _EGDataWidget ) and w.persistent: 2016 w.set_value( v )
2017 # load() 2018 2019
2020 - def close( self ):
2021 """Close application window.""" 2022 if self.__do_close__(): 2023 self._win.destroy()
2024 # close() 2025 2026
2027 - def status_message( self, message ):
2028 """Display a message in status bar and retrieve its identifier for 2029 later removal. 2030 2031 @see: L{remove_status_message} 2032 @note: this only active if statusbar=True 2033 """ 2034 if self.statusbar: 2035 return self._statusbar.push( self._statusbar_ctx, message ) 2036 else: 2037 raise ValueError( "App '%s' doesn't use statusbar!" % self.id )
2038 # status_message() 2039 2040
2041 - def remove_status_message( self, message_id ):
2042 """Remove a previously displayed message. 2043 2044 @see: L{status_message} 2045 @note: this only active if statusbar=True 2046 """ 2047 if self.statusbar: 2048 self._statusbar.remove( self._statusbar_ctx, message_id ) 2049 else: 2050 raise ValueError( "App '%s' doesn't use statusbar!" % self.id )
2051 # remove_status_message() 2052 2053
2054 - def timeout_add( self, interval, callback ):
2055 """Register a function to be called after a given timeout/interval. 2056 2057 @param interval: milliseconds between calls. 2058 @param callback: function to call back. This function gets as 2059 argument the app reference and must return C{True} to 2060 keep running, if C{False} is returned, it will not be 2061 called anymore. 2062 @return: id number to be used in L{remove_event_source} 2063 """
2064 - def wrap( *args ):
2065 return callback( self )
2066 # wrap() 2067 return gobject.timeout_add( interval, wrap )
2068 # timeout_add() 2069 2070
2071 - def idle_add( self, callback ):
2072 """Register a function to be called when system is idle. 2073 2074 System is idle if there is no other event pending. 2075 2076 @param callback: function to call back. This function gets as 2077 argument the app reference and must return C{True} to 2078 keep running, if C{False} is returned, it will not be 2079 called anymore. 2080 @return: id number to be used in L{remove_event_source} 2081 """
2082 - def wrap( *args ):
2083 return callback( self )
2084 # wrap() 2085 return gobject.idle_add( wrap )
2086 # idle_add() 2087 2088 2089
2090 - def io_watch( self, file, callback, 2091 on_in=False, on_out=False, on_urgent=False, on_error=False, 2092 on_hungup=False ):
2093 """Register a function to be called after an Input/Output event. 2094 2095 @param file: any file object or file descriptor (integer). 2096 @param callback: function to be called back, parameters will be the 2097 application that generated the event, the file that triggered 2098 it and on_in, on_out, on_urgent, on_error or on_hungup, 2099 being True those that triggered the event. 2100 The function must return C{True} to be called back again, 2101 otherwise it is automatically removed. 2102 @param on_in: there is data to read. 2103 @param on_out: data can be written without blocking. 2104 @param on_urgent: there is urgent data to read. 2105 @param on_error: error condition. 2106 @param on_hungup: hung up (the connection has been broken, usually for 2107 pipes and sockets). 2108 @return: id number to be used in L{remove_event_source} 2109 """
2110 - def wrap( source, cb_condition ):
2111 on_in = bool( cb_condition & gobject.IO_IN ) 2112 on_out = bool( cb_condition & gobject.IO_OUT ) 2113 on_urgent = bool( cb_condition & gobject.IO_PRI ) 2114 on_error = bool( cb_condition & gobject.IO_ERR ) 2115 on_hungup = bool( cb_condition & gobject.IO_HUP ) 2116 return callback( self, source, on_in=on_in, 2117 on_out=on_out, on_urgent=on_urgent, 2118 on_error=on_error, on_hungup=on_hungup )
2119 # wrap() 2120 2121 condition = 0 2122 if on_in: 2123 condition |= gobject.IO_IN 2124 if on_out: 2125 condition |= gobject.IO_OUT 2126 if on_urgent: 2127 condition |= gobject.IO_PRI 2128 if on_error: 2129 condition |= gobject.IO_ERR 2130 if on_hungup: 2131 condition |= gobject.IO_HUP 2132 return gobject.io_add_watch( file, condition, wrap )
2133 # io_watch() 2134 2135
2136 - def remove_event_source( self, event_id ):
2137 """Remove an event generator like those created by L{timeout_add}, 2138 L{idle_add} or L{io_watch}. 2139 2140 @param event_id: value returned from L{timeout_add}, 2141 L{idle_add} or L{io_watch}. 2142 2143 @return: C{True} if it was removed. 2144 """ 2145 return gobject.source_remove( event_id )
2146 # remove_event_source() 2147 # App 2148 2149
2150 -class Canvas( _EGWidget ):
2151 """The drawing area. 2152 2153 Eagle's drawing area (Canvas) is provided with a frame and an optional 2154 label, together with scrollbars, to make it fit everywhere. 2155 2156 """ 2157 padding = 5 2158 bgcolor= "black" 2159 2160 LEFT = -1 2161 CENTER = 0 2162 RIGHT = 1 2163 2164 FONT_OPTION_BOLD = 1 2165 FONT_OPTION_OBLIQUE = 2 2166 FONT_OPTION_ITALIC = 4 2167 2168 FONT_NAME_NORMAL = "normal" 2169 FONT_NAME_SERIF = "serif" 2170 FONT_NAME_SANS = "sans" 2171 FONT_NAME_MONO = "monospace" 2172 2173 MOUSE_BUTTON_1 = 1 2174 MOUSE_BUTTON_2 = 2 2175 MOUSE_BUTTON_3 = 4 2176 MOUSE_BUTTON_4 = 8 2177 MOUSE_BUTTON_5 = 16 2178 2179 label = _gen_ro_property( "label" ) 2180
2181 - def __init__( self, id, width, height, label="", bgcolor=None, 2182 scrollbars=True, callback=None, expand_policy=None ):
2183 """Canvas Constructor. 2184 2185 @param id: unique identifier. 2186 @param width: width of the drawing area in pixels, widget can be 2187 larger or smaller because and will use scrollbars if need. 2188 @param height: height of the drawing area in pixels, widget can be 2189 larger or smaller because and will use scrollbars if need. 2190 @param label: label to display in the widget frame around the 2191 drawing area. If None, no label or frame will be shown. 2192 @param bgcolor: color to paint background. 2193 @param scrollbars: whenever to use scrollbars and make canvas 2194 fit small places. 2195 @param callback: function (or list of functions) to call when 2196 mouse state changed in the drawing area. Function will get 2197 as parameters: 2198 - App reference 2199 - Canvas reference 2200 - Button state (or'ed MOUSE_BUTTON_*) 2201 - horizontal positon (x) 2202 - vertical positon (y) 2203 @param expand_policy: how this widget should fit space, see 2204 L{ExpandPolicy.Policy.Rule}. 2205 2206 @todo: honor the alpha value while drawing colors. 2207 """ 2208 _EGWidget.__init__( self, id, expand_policy=expand_policy ) 2209 self.__label = label 2210 self.width = width 2211 self.height = height 2212 self.scrollbars = scrollbars 2213 2214 self._pixmap = None 2215 self._callback = _callback_tuple( callback ) 2216 2217 # style and color context must be set just after drawing area is 2218 # attached to a window, otherwise they'll be empty and useless. 2219 # This is done in configure_event. 2220 self._style = None 2221 self._fg_gc_normal = None 2222 self._bg_gc_normal = None 2223 2224 if bgcolor is not None: 2225 self.bgcolor = self.__color_from__( bgcolor ) 2226 2227 self.__setup_gui__( width, height ) 2228 self.__setup_connections__()
2229 # __init__() 2230 2231
2232 - def __setup_gui__( self, width, height ):
2233 self._sw = gtk.ScrolledWindow() 2234 self._area = gtk.DrawingArea() 2235 2236 self._sw.set_border_width( self.padding ) 2237 2238 if self.label is not None: 2239 self._frame = gtk.Frame( self.label ) 2240 self._frame.add( self._sw ) 2241 self._frame.set_shadow_type( gtk.SHADOW_OUT ) 2242 root = self._frame 2243 else: 2244 root = self._sw 2245 2246 self._area.set_size_request( width, height ) 2247 self._sw.add_with_viewport( self._area ) 2248 if self.scrollbars: 2249 policy = gtk.POLICY_AUTOMATIC 2250 border = gtk.SHADOW_IN 2251 else: 2252 policy = gtk.POLICY_NEVER 2253 border = gtk.SHADOW_NONE 2254 2255 self._sw.set_policy( hscrollbar_policy=policy, 2256 vscrollbar_policy=policy ) 2257 self._sw.child.set_shadow_type( border ) 2258 self._sw.show_all() 2259 2260 self._widgets = ( root, )
2261 # __setup_gui__() 2262 2263
2264 - def __set_useful_attributes__( self ):
2265 self._style = self._area.get_style() 2266 self._fg_gc_normal = self._style.fg_gc[ gtk.STATE_NORMAL ] 2267 self._bg_gc_normal = self._style.bg_gc[ gtk.STATE_NORMAL ]
2268 # __set_useful_attributes__() 2269 2270
2271 - def __setup_connections__( self ):
2272 - def configure_event( widget, event ):
2273 if self._pixmap is None: 2274 self.__set_useful_attributes__() 2275 w, h = self._area.size_request() 2276 self.resize( w, h ) 2277 return True
2278 # configure_event() 2279 self._area.connect( "configure_event", configure_event ) 2280 2281
2282 - def expose_event( widget, event ):
2283 x , y, width, height = event.area 2284 gc = widget.get_style().fg_gc[ gtk.STATE_NORMAL ] 2285 widget.window.draw_drawable( gc, self._pixmap, x, y, x, y, 2286 width, height ) 2287 return False
2288 # expose_event() 2289 self._area.connect( "expose_event", expose_event ) 2290 2291
2292 - def get_buttons( state ):
2293 buttons = 0 2294 if state & gtk.gdk.BUTTON1_MASK: 2295 buttons |= self.MOUSE_BUTTON_1 2296 if state & gtk.gdk.BUTTON2_MASK: 2297 buttons |= self.MOUSE_BUTTON_2 2298 if state & gtk.gdk.BUTTON3_MASK: 2299 buttons |= self.MOUSE_BUTTON_3 2300 if state & gtk.gdk.BUTTON4_MASK: 2301 buttons |= self.MOUSE_BUTTON_4 2302 if state & gtk.gdk.BUTTON5_MASK: 2303 buttons |= self.MOUSE_BUTTON_5 2304 return buttons
2305 # get_buttons() 2306 2307 buttons_map = { 2308 1: self.MOUSE_BUTTON_1, 2309 2: self.MOUSE_BUTTON_2, 2310 3: self.MOUSE_BUTTON_3, 2311 4: self.MOUSE_BUTTON_4, 2312 5: self.MOUSE_BUTTON_5, 2313 } 2314
2315 - def button_press_event( widget, event ):
2316 if self._pixmap != None: 2317 btns = get_buttons( event.state ) 2318 btns |= buttons_map[ event.button ] 2319 2320 x = int( event.x ) 2321 y = int( event.y ) 2322 2323 for c in self._callback: 2324 c( self.app, self, btns, x, y ) 2325 return True
2326 # button_press_event() 2327 if self._callback: 2328 self._area.connect( "button_press_event", button_press_event ) 2329 2330
2331 - def button_release_event( widget, event ):
2332 if self._pixmap != None: 2333 btns = get_buttons( event.state ) 2334 btns &= ~buttons_map[ event.button ] 2335 2336 x = int( event.x ) 2337 y = int( event.y ) 2338 2339 for c in self._callback: 2340 c( self.app, self, btns, x, y ) 2341 return True
2342 # button_press_event() 2343 if self._callback: 2344 self._area.connect( "button_release_event", button_release_event ) 2345 2346
2347 - def motion_notify_event( widget, event ):
2348 if self._pixmap is None: 2349 return True 2350 2351 if event.is_hint: 2352 x, y, state = event.window.get_pointer() 2353 else: 2354 x = event.x 2355 y = event.y 2356 state = event.state 2357 2358 2359 btns = get_buttons( state ) 2360 x = int( x ) 2361 y = int( y ) 2362 2363 if btns: 2364 for c in self._callback: 2365 c( self.app, self, btns, x, y ) 2366 2367 return True
2368 # motion_notify_event() 2369 if self._callback: 2370 self._area.connect( "motion_notify_event", motion_notify_event ) 2371 2372 2373 # Enable events 2374 self._area.set_events( gtk.gdk.EXPOSURE_MASK | 2375 gtk.gdk.LEAVE_NOTIFY_MASK | 2376 gtk.gdk.BUTTON_PRESS_MASK | 2377 gtk.gdk.BUTTON_RELEASE_MASK | 2378 gtk.gdk.POINTER_MOTION_MASK | 2379 gtk.gdk.POINTER_MOTION_HINT_MASK )
2380 # __setup_connections__() 2381 2382 2383
2384 - def __color_from__( color ):
2385 """Convert from color to internal representation. 2386 2387 Gets a string, integer or tuple/list arguments and converts into 2388 internal color representation. 2389 """ 2390 a = 255 2391 2392 if isinstance( color, str ): 2393 try: 2394 c = gtk.gdk.color_parse( color ) 2395 r = int( c.red / 65535.0 * 255 ) 2396 g = int( c.green / 65535.0 * 255 ) 2397 b = int( c.blue / 65535.0 * 255 ) 2398 except ValueError, e: 2399 raise ValueError( "%s. color=%r" % ( e, color ) ) 2400 elif isinstance( color, gtk.gdk.Color ): 2401 r = int( color.red / 65535.0 * 255 ) 2402 g = int( color.green / 65535.0 * 255 ) 2403 b = int( color.blue / 65535.0 * 255 ) 2404 elif isinstance( color, int ): 2405 r = ( color >> 16 ) & 0xff 2406 g = ( color >> 8 ) & 0xff 2407 b = ( color & 0xff ) 2408 elif isinstance( color, ( tuple, list ) ): 2409 if len( color) == 3: 2410 r, g, b = color 2411 else: 2412 a, r, g, b = color 2413 2414 return a, r, g, b
2415 # __color_from__() 2416 __color_from__ = staticmethod( __color_from__ ) 2417 2418
2419 - def __to_gtk_color__( color ):
2420 r = int( color[ 1 ] / 255.0 * 65535 ) 2421 g = int( color[ 2 ] / 255.0 * 65535 ) 2422 b = int( color[ 3 ] / 255.0 * 65535 ) 2423 return gtk.gdk.Color( r, g, b )
2424 # __to_gtk_color__() 2425 __to_gtk_color__ = staticmethod( __to_gtk_color__ ) 2426 2427
2428 - def __configure_gc__( self, fgcolor=None, bgcolor=None, fill=None, 2429 line_width=None, line_style=None ):
2430 if fgcolor is not None: 2431 fgcolor = self.__color_from__( fgcolor ) 2432 if bgcolor is not None: 2433 bgcolor = self.__color_from__ ( bgcolor ) 2434 2435 k = {} 2436 if fill is not None: 2437 k[ "fill" ] = gtk.gdk.SOLID 2438 if line_width is not None: 2439 k[ "line_width" ] = line_width 2440 if line_style is not None: 2441 k[ "line_style" ] = line_style 2442 2443 gc = self._pixmap.new_gc( **k ) 2444 2445 if fgcolor is not None: 2446 gc.set_rgb_fg_color( self.__to_gtk_color__( fgcolor ) ) 2447 if bgcolor is not None: 2448 gc.set_rgb_bg_color( self.__to_gtk_color__( bgcolor ) ) 2449 return gc
2450 # __configure_gc__() 2451 2452
2453 - def resize( self, width, height ):
2454 """Resize the drawing area.""" 2455 old = self._pixmap 2456 self._pixmap = gtk.gdk.Pixmap( self._area.window, width, height ) 2457 if old is None: 2458 # Paint with bg color 2459 self.clear() 2460 else: 2461 # copy old contents over this 2462 w, h = old.get_size() 2463 self._pixmap.draw_drawable( self._fg_gc_normal, old, 2464 0, 0, 0, 0, w, h )
2465 # resize() 2466 2467
2468 - def draw_image( self, image, x=0, y=0, 2469 width=None, height=None, 2470 src_x=0, src_y=0 ):
2471 """Draw image on canvas. 2472 2473 By default it draws entire image at top canvas corner. 2474 2475 You may restrict which image areas to use with src_x, src_y, width 2476 and height. 2477 2478 You may choose position on canvas with x and y. 2479 """ 2480 if not isinstance( image, Image ): 2481 raise TypeError( ( "image must be instance of Image class, " 2482 "but %s found!" ) % ( type( image ).__name__ ) ) 2483 2484 p = image.__get_gtk_pixbuf__() 2485 2486 if src_x >= p.get_width(): 2487 raise ValueError( "src_x is greater or equal width!" ) 2488 2489 if src_y >= p.get_height(): 2490 raise ValueError( "src_y is greater or equal height!" ) 2491 2492 if width is None or width < 1: 2493 width = p.get_width() 2494 2495 if height is None or height < 1: 2496 height = p.get_height() 2497 2498 if src_x + width > p.get_width(): 2499 width = p.get_width() - src_x 2500 if src_y + height > p.get_height(): 2501 height = p.get_height() - src_y 2502 2503 self._pixmap.draw_pixbuf( self._fg_gc_normal, 2504 p, src_x, src_y, x, y, width, height ) 2505 self._area.queue_draw_area( x, y, width, width )
2506 # draw_image() 2507 2508
2509 - def draw_text( self, text, x=0, y=0, 2510 fgcolor=None, bgcolor=None, 2511 font_name=None, font_size=None, font_options=0, 2512 font_family=None, 2513 width=None, wrap_word=False, 2514 alignment=LEFT, justify=True ):
2515 """Draw text on canvas. 2516 2517 By default text is draw with current font and colors at top canvas 2518 corner. 2519 2520 You may limit width providing a value and choose if it should wrap 2521 at words (wrap_word=True) or characters (wrap_word=False). 2522 2523 2524 Colors can be specified with fgcolor an bgcolor. If not provided, the 2525 system foreground color is used and no background color is used. 2526 2527 Font name, family, size and options may be specified using 2528 font_name, font_family, font_size and font_options, respectively. 2529 Try to avoid using system specific font fames, use those provided 2530 by FONT_NAME_*. 2531 2532 Font options is OR'ed values from FONT_OPTIONS_*. 2533 2534 Font name is a string that have all the information, like 2535 "sans bold 12". This is returned by L{Font}. 2536 2537 Text alignment is one of LEFT, RIGHT or CENTER. 2538 """ 2539 if fgcolor is not None: 2540 fgcolor = self.__to_gtk_color__( self.__color_from__( fgcolor ) ) 2541 if bgcolor is not None: 2542 bgcolor = self.__to_gtk_color__( self.__color_from__( bgcolor ) ) 2543 2544 layout = self._area.create_pango_layout( text ) 2545 if width is not None: 2546 layout.set_width( width * pango.SCALE ) 2547 if wrap_word: 2548 layout.set_wrap( pango.WRAP_WORD ) 2549 2550 layout.set_justify( justify ) 2551 alignment = { self.LEFT: pango.ALIGN_LEFT, 2552 self.CENTER: pango.ALIGN_CENTER, 2553 self.RIGHT: pango.ALIGN_RIGHT }.get( alignment, 2554 pango.ALIGN_CENTER ) 2555 layout.set_alignment( alignment ) 2556 2557 if font_name or font_size or font_options or font_family: 2558 if font_name: 2559 fd = pango.FontDescription( font_name ) 2560 else: 2561 fd = layout.get_context().get_font_description() 2562 2563 if font_size: 2564 fd.set_size( font_size * pango.SCALE) 2565 if font_options: 2566 if font_options & self.FONT_OPTION_BOLD: 2567 fd.set_weight( pango.WEIGHT_BOLD ) 2568 if font_options & self.FONT_OPTION_ITALIC: 2569 fd.set_style( pango.STYLE_ITALIC ) 2570 if font_options & self.FONT_OPTION_OBLIQUE: 2571 fd.set_style( pango.STYLE_OBLIQUE ) 2572 layout.set_font_description( fd ) 2573 2574 self._pixmap.draw_layout( self._fg_gc_normal, x, y, layout, 2575 fgcolor, bgcolor ) 2576 w, h = layout.get_pixel_size() 2577 self._area.queue_draw_area( x, y, w, h )
2578 # draw_text() 2579 2580 2581
2582 - def draw_point( self, x, y, color=None ):
2583 """Draw point.""" 2584 gc = self.__configure_gc__( fgcolor=color ) 2585 self._pixmap.draw_point( gc, x, y ) 2586 self._area.queue_draw_area( x, y, 1, 1 )
2587 # draw_point() 2588 2589
2590 - def draw_points( self, points, color=None ):
2591 """Draw points. 2592 2593 Efficient way to draw more than one point with the same 2594 characteristics. 2595 """ 2596 gc = self.__configure_gc__( fgcolor=color ) 2597 self._pixmap.draw_points( gc, points ) 2598 w, h = self._pixmap.get_size() 2599 self._area.queue_draw_area( 0, 0, w, h )
2600 # draw_poinst() 2601 2602
2603 - def draw_line( self, x0, y0, x1, y1, color=None, size=1 ):
2604 """Draw line.""" 2605 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2606 self._pixmap.draw_line( gc, x0, y0, x1, y1 ) 2607 2608 size2 = size * 2 2609 2610 w, h = abs( x1 - x0 ) + size2, abs( y1 - y0 ) + size2 2611 x, y = max( min( x0, x1 ) - size, 0 ), max( min( y0, y1 ) - size, 0 ) 2612 self._area.queue_draw_area( x, y, w, h )
2613 # draw_line() 2614 2615
2616 - def draw_segments( self, segments, color=None, size=1 ):
2617 """Draw line segments. 2618 2619 Efficient way to draw more than one line with the same 2620 characteristics. 2621 2622 Lines are not connected, use L{draw_lines} instead. 2623 """ 2624 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2625 self._pixmap.draw_segments( gc, segments ) 2626 w, h = self._pixmap.get_size() 2627 self._area.queue_draw_area( 0, 0, w, h )
2628 # draw_segments() 2629 2630
2631 - def draw_lines( self, points, color=None, size=1 ):
2632 """Draw lines connecting points. 2633 2634 Points are connected using lines, but first and last points 2635 are not connected, use L{draw_polygon} instead. 2636 """ 2637 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2638 self._pixmap.draw_lines( gc, points ) 2639 w, h = self._pixmap.get_size() 2640 self._area.queue_draw_area( 0, 0, w, h )
2641 # draw_lines() 2642 2643
2644 - def draw_rectangle( self, x, y, width, height, color=None, size=1, 2645 fillcolor=None, filled=False ):
2646 """Draw rectagle. 2647 2648 If L{filled} is C{True}, it will be filled with L{fillcolor}. 2649 2650 If L{color} is provided, it will draw the rectangle's frame, even 2651 if L{filled} is C{True}. 2652 """ 2653 if filled: 2654 gc = self.__configure_gc__( fgcolor=fillcolor, fill=filled ) 2655 self._pixmap.draw_rectangle( gc, True, x, y, width, height ) 2656 2657 if size > 0: 2658 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2659 self._pixmap.draw_rectangle( gc, False, x, y, width, height ) 2660 else: 2661 size = 0 2662 2663 half = size / 2 2664 self._area.queue_draw_area( x-half, y-half, width+size, height+size )
2665 # draw_rectangle() 2666 2667
2668 - def draw_arc( self, x, y, width, height, start_angle, end_angle, 2669 color=None, size=1, fillcolor=None, filled=False ):
2670 """Draw arc on canvas. 2671 2672 Arc will be the part of an ellipse that starts at ( L{x}, L{y} ) 2673 and have size of L{width}xL{height}. 2674 2675 L{start_angle} and L{end_angle} are in radians, starts from the 2676 positive x-axis and are counter-clockwise. 2677 2678 If L{filled} is C{True}, it will be filled with L{fillcolor}. 2679 2680 If L{color} is provided, it will draw the arc's frame, even 2681 if L{filled} is C{True}. Frame here is just the curve, not the 2682 straight lines that are limited by L{start_angle} and L{end_angle}. 2683 """ 2684 # GTK: angles are in 1/64 of degree and must be integers 2685 mult = 180.0 / 3.1415926535897931 * 64.0 2686 start_angle = int( mult * start_angle ) 2687 end_angle = int( mult * end_angle ) 2688 2689 if filled: 2690 gc = self.__configure_gc__( fgcolor=fillcolor, fill=filled ) 2691 self._pixmap.draw_arc( gc, True, x, y, width, height, 2692 start_angle, end_angle ) 2693 if size > 0: 2694 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2695 self._pixmap.draw_arc( gc, False, x, y, width, height, 2696 start_angle, end_angle ) 2697 else: 2698 size = 0 2699 2700 half = size / 2 2701 self._area.queue_draw_area( x-half, y-half, width+size, height+size )
2702 # draw_arc() 2703 2704
2705 - def draw_polygon( self, points, color=None, size=1, 2706 fillcolor=None, filled=False ):
2707 """Draw polygon on canvas. 2708 2709 If L{filled} is C{True}, it will be filled with L{fillcolor}. 2710 2711 If L{color} is provided, it will draw the polygon's frame, even 2712 if L{filled} is C{True}. 2713 """ 2714 if filled: 2715 gc = self.__configure_gc__( fgcolor=fillcolor, fill=filled ) 2716 self._pixmap.draw_polygon( gc, True, points ) 2717 2718 if size > 0: 2719 gc = self.__configure_gc__( fgcolor=color, line_width=size ) 2720 self._pixmap.draw_polygon( gc, False, points ) 2721 else: 2722 size = 0 2723 2724 w, h = self._pixmap.get_size() 2725 self._area.queue_draw_area( 0, 0, w, h )
2726 # draw_polygon() 2727 2728
2729 - def clear( self ):
2730 """Clear using bgcolor.""" 2731 self.fill( self.bgcolor )
2732 # clear() 2733 2734
2735 - def fill( self, color ):
2736 w, h = self.get_size() 2737 self.draw_rectangle( 0, 0, w, h, color, size=0, fillcolor=color, 2738 filled=True )
2739 # fill() 2740 2741
2742 - def get_size( self ):
2743 return self._pixmap.get_size()
2744 # get_size() 2745 2746
2747 - def get_image( self ):
2748 """Get the L{Image} that represents this drawing area.""" 2749 w, h = self._pixmap.get_size() 2750 img = gtk.gdk.Pixbuf( gtk.gdk.COLORSPACE_RGB, True, 8, w, h ) 2751 img.get_from_drawable( self._pixmap, self._area.get_colormap(), 2752 0, 0, 0, 0, w, h ) 2753 return Image( __int_image__=img )
2754 # get_image() 2755 2756
2757 - def set_label( self, label ):
2758 if self.__label is None: 2759 raise ValueError( "You cannot change label of widget created " 2760 "without one. Create it with placeholder! " 2761 "(label='')" ) 2762 self.__label = label 2763 self._frame.set_label( self.__label )
2764 # set_label() 2765 2766
2767 - def get_label( self ):
2768 return self.__label
2769 # get_label() 2770 2771 label = property( get_label, set_label ) 2772 2773
2774 - def __str__( self ):
2775 return "%s( id=%r, width=%r, height=%r, label=%r )" % \ 2776 ( self.__class__.__name__, self.id, self.width, self.height, 2777 self.label )
2778 # __str__() 2779 __repr__ = __str__
2780 # Canvas 2781 2782
2783 -class _MultiLineEntry( gtk.ScrolledWindow ):
2784 - def __init__( self ):
2785 gtk.ScrolledWindow.__init__( self ) 2786 self.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 2787 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 2788 self.set_shadow_type( gtk.SHADOW_IN ) 2789 2790 self.textview = gtk.TextView() 2791 self.add( self.textview ) 2792 2793 self.textview.set_editable( True ) 2794 self.textview.set_cursor_visible( True ) 2795 self.textview.set_left_margin( 2 ) 2796 self.textview.set_right_margin( 2 ) 2797 self.textview.get_buffer().connect( "changed", self.__emit_changed__ )
2798 # __init__() 2799 2800
2801 - def __emit_changed__( self, *args ):
2802 self.emit( "changed" )
2803 # __emit_changed__ 2804 2805
2806 - def set_text( self, value ):
2807 self.textview.get_buffer().set_text( value )
2808 # set_text() 2809 2810
2811 - def get_text( self ):
2812 b = self.textview.get_buffer() 2813 return b.get_text( b.get_start_iter(), b.get_end_iter() )
2814 # get_text() 2815 2816
2817 - def set_editable( self, setting ):
2818 self.textview.set_editable( setting )
2819 # set_editable() 2820 # _MultiLineEntry 2821 gobject.signal_new( "changed", 2822 _MultiLineEntry, 2823 gobject.SIGNAL_RUN_LAST, 2824 gobject.TYPE_NONE, 2825 tuple() ) 2826 2827
2828 -class Entry( _EGWidLabelEntry ):
2829 """Text entry. 2830 2831 The simplest user input component. Its contents are free-form and not 2832 filtered/masked. 2833 """ 2834 value = _gen_ro_property( "value" ) 2835 callback = _gen_ro_property( "callback" ) 2836 multiline = _gen_ro_property( "multiline" ) 2837 2838
2839 - def __init__( self, id, label="", value="", callback=None, 2840 editable=True, persistent=False, multiline=False, 2841 expand_policy=None ):
2842 """Entry constructor. 2843 2844 @param id: unique identifier. 2845 @param label: what to show on a label on the left side of the widget. 2846 @param value: initial content. 2847 @param editable: if this field is editable by user. 2848 @param callback: function (or list of functions) that will 2849 be called when this widget have its data changed. 2850 Function will receive as parameters: 2851 - App reference 2852 - Widget reference 2853 - new value 2854 @param persistent: if this widget should save its data between 2855 sessions. 2856 @param multiline: if this entry can be multi-line. 2857 @param expand_policy: how this widget should fit space, see 2858 L{ExpandPolicy.Policy.Rule}. 2859 """ 2860 self.value = value 2861 self.callback = _callback_tuple( callback ) 2862 self.multiline = bool( multiline ) 2863 self._editable = editable 2864 2865 if expand_policy is None: 2866 if self.multiline: 2867 p = ExpandPolicy.All() 2868 else: 2869 p = ExpandPolicy.Horizontal() 2870 2871 expand_policy = ( ExpandPolicy.Fill(), p ) 2872 2873 _EGWidLabelEntry.__init__( self, id, persistent, label, 2874 expand_policy=expand_policy ) 2875 2876 self.__setup_gui__() 2877 self.__setup_connections__()
2878 # __init__() 2879 2880
2881 - def __setup_gui__( self ):
2882 if self.multiline: 2883 self._entry = _MultiLineEntry() 2884 else: 2885 self._entry = gtk.Entry() 2886 2887 self._entry.set_name( self.id ) 2888 self._entry.set_text( self.value ) 2889 self.set_editable( self._editable ) 2890 2891 _EGWidLabelEntry.__setup_gui__( self )
2892 # __setup_gui__() 2893 2894
2895 - def __configure_orientation__( self, setting ):
2896 super( Entry, self ).__configure_orientation__( setting ) 2897 if self.multiline and self.label and \ 2898 setting == self.ORIENTATION_VERTICAL: 2899 self._label.set_alignment( xalign=1.0, yalign=0 )
2900 # __configure_orientation__() 2901 2902
2903 - def __setup_connections__( self ):
2904 - def callback( obj ):
2905 v = self.get_value() 2906 self.app.data_changed( self, v ) 2907 for c in self.callback: 2908 c( self.app, self, v )
2909 # callback() 2910 self._entry.connect( "changed", callback )
2911 # __setup_connections__() 2912 2913
2914 - def get_value( self ):
2915 return self._entry.get_text()
2916 # get_value() 2917 2918
2919 - def set_value( self, value ):
2920 self._entry.set_text( str( value ) )
2921 # set_value() 2922 2923
2924 - def set_editable( self, value ):
2925 self._editable = bool( value ) 2926 self._entry.set_editable( self._editable )
2927 # set_editable() 2928 2929
2930 - def get_editable( self ):
2931 return self._editable
2932 # set_editable() 2933 2934 editable = property( get_editable, set_editable )
2935 # Entry 2936 2937
2938 -class Password( Entry ):
2939 """Password entry. 2940 2941 Like L{Entry}, but will show '*' instead of typed chars. 2942 """
2943 - def __init__( self, id, label="", value="", callback=None, 2944 persistent=False, expand_policy=None ):
2945 """Password constructor. 2946 2947 @param id: unique identifier. 2948 @param label: what to show on a label on the left side of the widget. 2949 @param value: initial content. 2950 @param callback: function (or list of functions) that will 2951 be called when this widget have its data changed. 2952 Function will receive as parameters: 2953 - App reference 2954 - Widget reference 2955 - new value 2956 @param persistent: if this widget should save its data between 2957 sessions. 2958 @param expand_policy: how this widget should fit space, see 2959 L{ExpandPolicy.Policy.Rule}. 2960 """ 2961 Entry.__init__( self, id, label, value, callback, persistent, 2962 expand_policy=expand_policy ) 2963 self._entry.set_visibility( False )
2964 # __init__() 2965 # Password 2966 2967
2968 -class Spin( _EGWidLabelEntry ):
2969 """Spin button entry. 2970 2971 Spin buttons are numeric user input that checks if value is inside 2972 a specified range. It also provides small buttons to help incrementing/ 2973 decrementing value. 2974 """ 2975 default_min = -1e60 2976 default_max = 1e60 2977 2978 value = _gen_ro_property( "value" ) 2979 min = _gen_ro_property( "min" ) 2980 max = _gen_ro_property( "max" ) 2981 step = _gen_ro_property( "step" ) 2982 digits = _gen_ro_property( "digits" ) 2983 2984 callback = _gen_ro_property( "callback" ) 2985
2986 - def __init__( self, id, label="", 2987 value=None, min=None, max=None, step=None, digits=3, 2988 callback=None, persistent=False, expand_policy=None ):
2989 """Spin constructor. 2990 2991 @param id: unique identifier. 2992 @param label: what to show on a label on the left side of the widget. 2993 @param value: initial content. 2994 @param min: minimum value. If None, L{default_min} will be used. 2995 @param max: maximum value. If None, L{default_max} will be used. 2996 @param step: step to use to decrement/increment using buttons. 2997 @param digits: how many digits to show. 2998 @param callback: function (or list of functions) that will 2999 be called when this widget have its data changed. 3000 Function will receive as parameters: 3001 - App reference 3002 - Widget reference 3003 - new value 3004 @param persistent: if this widget should save its data between 3005 sessions. 3006 @param expand_policy: how this widget should fit space, see 3007 L{ExpandPolicy.Policy.Rule}. 3008 """ 3009 self.value = value 3010 self.min = min 3011 self.max = max 3012 self.step = step 3013 self.digits = digits 3014 self.callback = _callback_tuple( callback ) 3015 3016 _EGWidLabelEntry.__init__( self, id, persistent, label, 3017 expand_policy=expand_policy ) 3018 3019 self.__setup_connections__()
3020 # __init__() 3021 3022
3023 - def __setup_gui__( self ):
3024 k = {} 3025 3026 if self.value is not None: 3027 k[ "value" ] = self.value 3028 3029 if self.min is not None: 3030 k[ "lower" ] = self.min 3031 else: 3032 k[ "lower" ] = self.default_min 3033 3034 if self.max is not None: 3035 k[ "upper" ] = self.max 3036 else: 3037 k[ "upper" ] = self.default_max 3038 3039 if self.step is not None: 3040 k[ "step_incr" ] = self.step 3041 k[ "page_incr" ] = self.step * 2 3042 else: 3043 k[ "step_incr" ] = 1 3044 k[ "page_incr" ] = 2 3045 3046 adj = gtk.Adjustment( **k ) 3047 self._entry = gtk.SpinButton( adjustment=adj, digits=self.digits ) 3048 self._entry.set_name( self.id ) 3049 self._entry.set_numeric( True ) 3050 self._entry.set_snap_to_ticks( False ) 3051 3052 _EGWidLabelEntry.__setup_gui__( self )
3053 # __setup_gui__() 3054 3055
3056 - def __setup_connections__( self ):
3057 - def callback( obj ):
3058 v = self.get_value() 3059 self.app.data_changed( self, v ) 3060 for c in self.callback: 3061 c( self.app, self, v )
3062 # callback() 3063 self._entry.connect( "value-changed", callback )
3064 # __setup_connections__() 3065 3066
3067 - def set_value( self, value ):
3068 self._entry.set_value( float( value ) )
3069 # set_value() 3070 # Spin 3071 3072
3073 -class IntSpin( Spin ):
3074 """Integer-only Spin button. 3075 3076 Convenience widget that behaves like L{Spin} with digits set to 3077 zero and also ensure L{set_value} and L{get_value} operates on 3078 integers. 3079 """ 3080 default_min = -sys.maxint 3081 default_max = sys.maxint 3082
3083 - def __init__( self, id, label="", 3084 value=None, min=None, max=None, step=None, 3085 callback=None, persistent=False, expand_policy=None ):
3086 """Integer Spin constructor. 3087 3088 @param id: unique identifier. 3089 @param label: what to show on a label on the left side of the widget. 3090 @param value: initial content. 3091 @param min: minimum value. If None, L{default_min} will be used. 3092 @param max: maximum value. If None, L{default_max} will be used. 3093 @param step: step to use to decrement/increment using buttons. 3094 @param callback: function (or list of functions) that will 3095 be called when this widget have its data changed. 3096 Function will receive as parameters: 3097 - App reference 3098 - Widget reference 3099 - new value 3100 @param persistent: if this widget should save its data between 3101 sessions. 3102 @param expand_policy: how this widget should fit space, see 3103 L{ExpandPolicy.Policy.Rule}. 3104 """ 3105 if value is not None: 3106 value = int( value ) 3107 if min is not None: 3108 min = int( min ) 3109 if max is not None: 3110 max = int( max ) 3111 if step is not None: 3112 step = int( step ) 3113 Spin.__init__( self, id, label, value, min, max, step, 0, callback, 3114 persistent, expand_policy=expand_policy )
3115 # __init__() 3116 3117
3118 - def get_value( self ):
3119 return int( Spin.get_value( self ) )
3120 # get_value() 3121 3122
3123 - def set_value( self, value ):
3124 Spin.set_value( self, int( value ) )
3125 # set_value() 3126 # IntSpin 3127 3128
3129 -class UIntSpin( IntSpin ):
3130 """Unsigned integer-only Spin button. 3131 3132 Convenience widget that behaves like L{IntSpin} with minimum value 3133 always greater or equal zero. 3134 """ 3135 default_min = 0 3136
3137 - def __init__( self, id, label="", 3138 value=None, min=0, max=None, step=None, 3139 callback=None, persistent=False, expand_policy=None ):
3140 """Unsigned Integer Spin constructor. 3141 3142 @param id: unique identifier. 3143 @param label: what to show on a label on the left side of the widget. 3144 @param value: initial content. 3145 @param min: minimum value, must be greater or equal zero. 3146 @param max: maximum value. If None, L{default_max} will be used. 3147 @param step: step to use to decrement/increment using buttons. 3148 @param callback: function (or list of functions) that will 3149 be called when this widget have its data changed. 3150 Function will receive as parameters: 3151 - App reference 3152 - Widget reference 3153 - new value 3154 @param persistent: if this widget should save its data between 3155 sessions. 3156 @param expand_policy: how this widget should fit space, see 3157 L{ExpandPolicy.Policy.Rule}. 3158 """ 3159 if min < 0: 3160 raise ValueError( "UIntSpin cannot have min < 0!" ) 3161 Spin.__init__( self, id, label, value, min, max, step, 0, callback, 3162 persistent, expand_policy=expand_policy )
3163 # __init__() 3164 # UIntSpin 3165 3166
3167 -class Color( _EGWidLabelEntry ):
3168 """Button to select colors. 3169 3170 It show current/last selected color and may pop-up a new dialog to 3171 select a new one. 3172 """ 3173 color = _gen_ro_property( "color" ) 3174 use_alpha = _gen_ro_property( "use_alpha" ) 3175 callback = _gen_ro_property( "callback" ) 3176
3177 - def __init__( self, id, label="", color=0, use_alpha=False, 3178 callback=None, persistent=False, expand_policy=None ):
3179 """Color selector constructor. 3180 3181 @param id: unique identifier. 3182 @param label: what to show on a label on the left side of the widget. 3183 @param color: initial content. May be a triple with elements within 3184 the range 0-255, an string with color in HTML format or even 3185 a color present in X11's rgb.txt. 3186 @param use_alpha: if the alpha channel should be used, it will be 3187 the first value in the tuple representing the color. 3188 @param callback: function (or list of functions) that will 3189 be called when this widget have its data changed. 3190 Function will receive as parameters: 3191 - App reference 3192 - Widget reference 3193 - new value 3194 @param persistent: if this widget should save its data between 3195 sessions. 3196 @param expand_policy: how this widget should fit space, see 3197 L{ExpandPolicy.Policy.Rule}. 3198 """ 3199 self.color = self.color_from( color ) 3200 self.use_alpha = use_alpha 3201 self.callback = _callback_tuple( callback ) 3202 _EGWidLabelEntry.__init__( self, id, persistent, label, 3203 expand_policy=expand_policy ) 3204 3205 self.__setup_connections__()
3206 # __init__() 3207 3208
3209 - def color_from( color ):
3210 a = 255 3211 if isinstance( color, str ): 3212 try: 3213 c = gtk.gdk.color_parse( color ) 3214 r = int( c.red / 65535.0 * 255 ) 3215 g = int( c.green / 65535.0 * 255 ) 3216 b = int( c.blue / 65535.0 * 255 ) 3217 except ValueError, e: 3218 raise ValueError( "%s. color=%r" % ( e, color ) ) 3219 3220 if isinstance( color, int ): 3221 r = ( color >> 16 ) & 0xff 3222 g = ( color >> 8 ) & 0xff 3223 b = ( color & 0xff ) 3224 elif isinstance( color, ( tuple, list ) ): 3225 if len( color ) == 3: 3226 r, g, b = color 3227 else: 3228 a, r, g, b = color 3229 3230 return a, r, g, b
3231 # color_from() 3232 color_from = staticmethod( color_from ) 3233 3234
3235 - def __setup_gui__( self ):
3236 r = int( self.color[ 1 ] / 255.0 * 65535 ) 3237 g = int( self.color[ 2 ] / 255.0 * 65535 ) 3238 b = int( self.color[ 3 ] / 255.0 * 65535 ) 3239 3240 c = gtk.gdk.Color( r, g, b ) 3241 3242 self._entry = gtk.ColorButton( c ) 3243 self._entry.set_name( self.id ) 3244 self._entry.set_use_alpha( self.use_alpha ) 3245 if self.use_alpha: 3246 alpha = int( self.color[ 0 ] / 255.0 * 65535 ) 3247 self._entry.set_alpha( alpha ) 3248 _EGWidLabelEntry.__setup_gui__( self )
3249 # __setup_gui__() 3250 3251
3252 - def __setup_connections__( self ):
3253 - def callback( obj ):
3254 v = self.get_value() 3255 self.app.data_changed( self, v ) 3256 for c in self.callback: 3257 c( self.app, self, v )
3258 # callback() 3259 self._entry.connect( "color-set", callback )
3260 # __setup_connections__() 3261 3262
3263 - def get_value( self ):
3264 """Return a tuple with ( alpha, red, green, blue ) 3265 ( alpha, red, green, blue ) (use_alpha=True), each in 0-255 range. 3266 """ 3267 c = self._entry.get_color() 3268 r = int( c.red / 65535.0 * 255 ) 3269 g = int( c.green / 65535.0 * 255 ) 3270 b = int( c.blue / 65535.0 * 255 ) 3271 3272 if self.use_alpha: 3273 a = int( self._entry.get_alpha() / 65535.0 * 255 ) 3274 return ( a, r, g, b ) 3275 else: 3276 return ( r, g, b )
3277 # get_value() 3278 3279
3280 - def set_value( self, value ):
3281 """ 3282 @param value: May be a triple with elements within 3283 the range 0-255, an string with color in HTML format or even 3284 a color present in X11's rgb.txt. 3285 """ 3286 a, r, g, b = self.color_from( value ) 3287 if self.use_alpha: 3288 self._entry.set_alpha( int( a / 255.0 * 65535.0 ) ) 3289 3290 r = int( r / 255.0 * 65535 ) 3291 g = int( g / 255.0 * 65535 ) 3292 b = int( b / 255.0 * 65535 ) 3293 3294 c = gtk.gdk.Color( r, g, b ) 3295 self._entry.set_color( c )
3296 # set_value() 3297 # Color 3298 3299
3300 -class Font( _EGWidLabelEntry ):
3301 """Button to select fonts. 3302 3303 It show current/last selected font and may pop-up a new dialog to 3304 select a new one. 3305 """ 3306 font = _gen_ro_property( "font" ) 3307 callback = _gen_ro_property( "callback" ) 3308
3309 - def __init__( self, id, label="", font="sans 12", callback=None, 3310 persistent=False, expand_policy=None ):
3311 """Font selector constructor. 3312 3313 @param id: unique identifier. 3314 @param label: what to show on a label on the left side of the widget. 3315 @param font: initial content. 3316 @param callback: function (or list of functions) that will 3317 be called when this widget have its data changed. 3318 Function will receive as parameters: 3319 - App reference 3320 - Widget reference 3321 - new value 3322 @param persistent: if this widget should save its data between 3323 sessions. 3324 @param expand_policy: how this widget should fit space, see 3325 L{ExpandPolicy.Policy.Rule}. 3326 """ 3327 self.font = font 3328 self.callback = _callback_tuple( callback ) 3329 _EGWidLabelEntry.__init__( self, id, persistent, label, 3330 expand_policy=expand_policy ) 3331 3332 self.__setup_connections__()
3333 # __init__() 3334 3335
3336 - def __setup_gui__( self ):
3337 self._entry = gtk.FontButton( self.font ) 3338 self._entry.set_name( self.id ) 3339 self._entry.set_show_style( True ) 3340 _EGWidLabelEntry.__setup_gui__( self )
3341 # __setup_gui__() 3342 3343
3344 - def __setup_connections__( self ):
3345 - def callback( obj ):
3346 v = self.get_value() 3347 self.app.data_changed( self, v ) 3348 for c in self.callback: 3349 c( self.app, self, v )
3350 # callback() 3351 self._entry.connect( "font-set", callback )
3352 # __setup_connections__() 3353 3354
3355 - def get_value( self ):
3356 return self._entry.get_font_name()
3357 # get_value() 3358 3359
3360 - def set_value( self, value ):
3361 self._entry.set_font_name( value )
3362 # set_value() 3363 # Font 3364 3365
3366 -class Selection( _EGWidLabelEntry ):
3367 """Selection box (aka Combo box). 3368 3369 Selection or combo box is an element that allow you to select one of 3370 various pre-defined values. 3371 """ 3372 options = _gen_ro_property( "options" ) 3373 active = _gen_ro_property( "active" ) 3374
3375 - def __init__( self, id, label="", options=None, active=None, 3376 callback=None, persistent=False, expand_policy=None ):
3377 """Selection constructor. 3378 3379 @param id: unique identifier. 3380 @param label: what to show on a label on the left side of the widget. 3381 @param options: list of possible values. 3382 @param active: selected element. 3383 @param callback: function (or list of functions) that will 3384 be called when this widget have its data changed. 3385 Function will receive as parameters: 3386 - App reference 3387 - Widget reference 3388 - new value 3389 @param persistent: if this widget should save its data between 3390 sessions. 3391 @param expand_policy: how this widget should fit space, see 3392 L{ExpandPolicy.Policy.Rule}. 3393 """ 3394 self.options = options or [] 3395 self.active = active 3396 self.callback = _callback_tuple( callback ) 3397 _EGWidLabelEntry.__init__( self, id, persistent, label, 3398 expand_policy=expand_policy ) 3399 3400 self.__setup_connections__()
3401 # __init__() 3402 3403
3404 - def __setup_gui__( self ):
3405 self._entry = gtk.combo_box_new_text() 3406 self._entry.set_name( self.id ) 3407 for i, o in enumerate( self.options ): 3408 self._entry.append_text( str( o ) ) 3409 if self.active == o: 3410 self._entry.set_active( i ) 3411 3412 _EGWidLabelEntry.__setup_gui__( self )
3413 # __setup_gui__() 3414 3415
3416 - def __setup_connections__( self ):
3417 - def callback( obj ):
3418 v = self.get_value() 3419 self.app.data_changed( self, v ) 3420 for c in self.callback: 3421 c( self.app, self, v )
3422 # callback() 3423 self._entry.connect( "changed", callback )
3424 # __setup_connections__() 3425 3426
3427 - def get_value( self ):
3428 return self._entry.get_active_text()
3429 # get_value() 3430 3431
3432 - def set_value( self, value ):
3433 for i, o in enumerate( self._entry.get_model() ): 3434 if o[ 0 ] == value: 3435 self._entry.set_active( i )
3436 # set_value() 3437 3438
3439 - def append( self, value, set_active=False ):
3440 """Append new value to available options. 3441 3442 @param value: string that is not already an option. 3443 3444 @raise: ValueError: if value is already an option. 3445 """ 3446 if value not in self.items(): 3447 self._entry.append_text( value ) 3448 if set_active: 3449 self.set_value( value ) 3450 else: 3451 raise ValueError( "value already in selection" )
3452 # append() 3453 3454
3455 - def prepend( self, value ):
3456 """Prepend new value to available options. 3457 3458 @param value: string that is not already an option. 3459 3460 @raise: ValueError: if value is already an option. 3461 """ 3462 if value not in self.items(): 3463 self._entry.prepend_text( value ) 3464 if set_active: 3465 self.set_value( value ) 3466 else: 3467 raise ValueError( "value already in selection" )
3468 # prepend() 3469 3470
3471 - def insert( self, position, value ):
3472 """Insert new option at position. 3473 3474 @param value: string that is not already an option. 3475 3476 @raise: ValueError: if value is already an option. 3477 """ 3478 if value not in self.items(): 3479 self._entry.insert_text( position, value ) 3480 if set_active: 3481 self.set_value( value ) 3482 else: 3483 raise ValueError( "value already in selection" )
3484 # insert() 3485 3486
3487 - def remove( self, value ):
3488 """Remove given value from available options. 3489 3490 @param value: string that is an option. 3491 3492 @raise ValueError: if value is not already an option. 3493 """ 3494 for i, o in enumerate( self._entry.get_model() ): 3495 if o[ 0 ] == value: 3496 self._entry.remove_text( i ) 3497 return 3498 3499 raise ValueError( "value not in selection" )
3500 # remove() 3501 3502
3503 - def items( self ):
3504 """Returns every item/option in this selection.""" 3505 return [ str( x[ 0 ] ) for x in self._entry.get_model() ]
3506 # items() 3507 options = items 3508 3509
3510 - def __len__( self ):
3511 return len( self._entry.get_model() )
3512 # __len__() 3513 3514
3515 - def __contains__( self, value ):
3516 return value in self.items()
3517 # __contains__() 3518 3519
3520 - def __iadd__( self, value ):
3521 """Same as L{append}""" 3522 self.append( value )
3523 # __iadd__() 3524 3525
3526 - def __isub__( self, value ):
3527 """Same as L{remove}""" 3528 self.remove( value )
3529 # __isub__() 3530 # Selection 3531 3532
3533 -class Progress( _EGWidLabelEntry ):
3534 """Progress bar.""" 3535 value = _gen_ro_property( "value" ) 3536
3537 - def __init__( self, id, label="", value=0.0, expand_policy=None ):
3538 """Progress bar constructor. 3539 3540 0 <= value <= 1.0 3541 3542 @param id: unique identifier. 3543 @param label: what to show on a label on the left side of the widget. 3544 @param value: initial content ( 0.0 <= value <= 1.0 ) 3545 @param expand_policy: how this widget should fit space, see 3546 L{ExpandPolicy.Policy.Rule}. 3547 """ 3548 self.value = value 3549 _EGWidLabelEntry.__init__( self, id, False, label, 3550 expand_policy=expand_policy )
3551 # __init__() 3552
3553 - def __setup_gui__( self ):
3554 self._entry = gtk.ProgressBar() 3555 self._entry.set_name( self.id ) 3556 self.set_value( self.value ) 3557 _EGWidLabelEntry.__setup_gui__( self )
3558 # __setup_gui__() 3559 3560
3561 - def get_value( self ):
3562 return self._entry.get_fraction()
3563 # get_value() 3564 3565
3566 - def set_value( self, value ):
3567 if 1.0 < value <= 100.0: 3568 value /= 100.0 3569 elif not ( 0.0 <= value <= 1.0 ): 3570 raise ValueError( ( "Progress value of \"%s\" must be " 3571 "between 0.0 and 1.0!" ) % self.id ) 3572 self._entry.set_fraction( value ) 3573 self._entry.set_text( "%d%%" % ( int( value * 100 ), ) )
3574 # set_value() 3575 3576
3577 - def pulse( self ):
3578 """Animate progress bar.""" 3579 self._entry.pulse()
3580 # pulse() 3581 # Progress 3582 3583
3584 -class CheckBox( _EGDataWidget ):
3585 """Check box. 3586 3587 Check box is an component that have only two values: True (checked) or 3588 False (unchecked). 3589 """ 3590 state = _gen_ro_property( "state" ) 3591
3592 - def __init__( self, id, label="", state=False, callback=None, 3593 persistent=False, expand_policy=None ):
3594 """Check box constructor. 3595 3596 @param id: unique identifier. 3597 @param label: what to show on a label on the left side of the widget. 3598 @param state: initial state. 3599 @param callback: function (or list of functions) that will 3600 be called when this widget have its data changed. 3601 Function will receive as parameters: 3602 - App reference 3603 - Widget reference 3604 - new value 3605 @param persistent: if this widget should save its data between 3606 sessions. 3607 @param expand_policy: how this widget should fit space, see 3608 L{ExpandPolicy.Policy.Rule}. 3609 """ 3610 self.__label = label 3611 self.state = state 3612 self.callback = _callback_tuple( callback ) 3613 3614 if expand_policy is None: 3615 expand_policy = ExpandPolicy.Fill() 3616 3617 _EGDataWidget.__init__( self, id, persistent, 3618 expand_policy=expand_policy ) 3619 3620 self.__setup_gui__() 3621 self.__setup_connections__()
3622 # __init__() 3623 3624
3625 - def __setup_gui__( self ):
3626 self._wid = gtk.CheckButton( self.__label ) 3627 self._wid.set_name( self.id ) 3628 self._wid.set_active( self.state ) 3629 self._widgets = ( self._wid, )
3630 # __setup_gui__() 3631 3632
3633 - def __setup_connections__( self ):
3634 - def callback( obj ):
3635 v = self.get_value() 3636 self.app.data_changed( self, v ) 3637 for c in self.callback: 3638 c( self.app, self, v )
3639 # callback() 3640 self._wid.connect( "toggled", callback )
3641 # __setup_connections__() 3642 3643
3644 - def get_value( self ):
3645 return self._wid.get_active()
3646 # get_value() 3647 3648
3649 - def set_value( self, value ):
3650 return self._wid.set_active( bool( value ) )
3651 # set_value() 3652 3653
3654 - def set_label( self, label ):
3655 if self.__label is None: 3656 raise ValueError( "You cannot change label of widget created " 3657 "without one. Create it with placeholder! " 3658 "(label='')" ) 3659 self.__label = label 3660 self._wid.set_label( self.__label )
3661 # set_label() 3662 3663
3664 - def get_label( self ):
3665 return self.__label
3666 # get_label() 3667 3668 label = property( get_label, set_label )
3669 # CheckBox 3670 3671
3672 -class Group( _EGWidget ):
3673 """Group of various components. 3674 3675 Group is a component that holds other components, always in a vertical 3676 layout. 3677 3678 Group has a frame and may show a label. 3679 """ 3680 children = _gen_ro_property( "children" ) 3681 horizontal = _gen_ro_property( "horizontal" ) 3682 3683 BORDER_NONE = gtk.SHADOW_NONE 3684 BORDER_IN = gtk.SHADOW_IN 3685 BORDER_OUT = gtk.SHADOW_OUT 3686 BORDER_ETCHED_IN = gtk.SHADOW_ETCHED_IN 3687 BORDER_ETCHED_OUT = gtk.SHADOW_ETCHED_OUT 3688
3689 - def _get_app( self ):
3690 try: 3691 return self.__ro_app 3692 except AttributeError: 3693 return None
3694 # _get_app() 3695
3696 - def _set_app( self, value ):
3697 # We need to overload app setter in order to add 3698 # children widgets to application as soon as we know the app 3699 try: 3700 v = self.__ro_app 3701 except AttributeError: 3702 v = None 3703 if v is None: 3704 self.__ro_app = value 3705 self.__add_widgets_to_app__() 3706 else: 3707 raise Exception( "Read Only property 'app'." )
3708 # _set_app() 3709 app = property( _get_app, _set_app ) 3710 3711
3712 - def __init__( self, id, label="", children=None, horizontal=False, 3713 border=BORDER_ETCHED_IN, expand_policy=None ):
3714 """Group constructor. 3715 3716 @param id: unique identified. 3717 @param label: displayed at top-left. 3718 @param children: a list of eagle widgets that this group contains. 3719 @param horizontal: if widgets should be laid out horizontally. 3720 @param border: can be one of Group.BORDER_* values or None to 3721 disable border and label completely. Note that some themes 3722 may have different appearance for borders and some may not 3723 respect BORDER_NONE, so if you really want no border, use 3724 None to disable it. Note that Groups without borders cannot 3725 have one added later. 3726 @param expand_policy: how this widget should fit space, see 3727 L{ExpandPolicy.Policy.Rule}. 3728 """ 3729 if expand_policy is None: 3730 expand_policy = ExpandPolicy.Horizontal() 3731 3732 _EGWidget.__init__( self, id, expand_policy=expand_policy ) 3733 self.__label = label 3734 self.children = _obj_tuple( children ) 3735 self.horizontal = bool( horizontal ) 3736 self.__border = border 3737 3738 self.__setup_gui__()
3739 # __init__() 3740 3741
3742 - def __setup_gui__( self ):
3743 self._contents = _Table( id=( "%s-contents" % self.id ), 3744 children=self.children, 3745 horizontal=self.horizontal ) 3746 3747 if self.border is not None: 3748 self._frame = gtk.Frame( self.__label ) 3749 self._frame.set_name( self.id ) 3750 self._frame.set_shadow_type( self.border ) 3751 self._frame.add( self._contents ) 3752 root = self._frame 3753 else: 3754 root = self._contents 3755 3756 self._widgets = ( root, )
3757 # __setup_gui__() 3758 3759
3760 - def __add_widgets_to_app__( self ):
3761 for w in self.children: 3762 self.app.__add_widget__( w )
3763 # __add_widgets_to_app__() 3764 3765
3766 - def set_label( self, label ):
3767 if self.__label is None: 3768 raise ValueError( "You cannot change label of widget created " 3769 "without one. Create it with placeholder! " 3770 "(label='')" ) 3771 self.__label = label 3772 if self.border is not None: 3773 self._frame.set_label( self.__label )
3774 # set_label() 3775 3776
3777 - def get_label( self ):
3778 return self.__label
3779 # get_label() 3780 3781 label = property( get_label, set_label ) 3782 3783
3784 - def set_border( self, border ):
3785 if self.__border is None: 3786 raise ValueError( "You cannot change border of widget created " 3787 "without one." ) 3788 3789 if border is not None: 3790 self._frame.set_shadow_type( border ) 3791 else: 3792 raise ValueError( "You cannot remove widget border" ) 3793 3794 self.__border = border
3795 # set_border() 3796 3797
3798 - def get_border( self ):
3799 return self.__border
3800 # get_border() 3801 3802 border = property( get_border, set_border )
3803 # Group 3804 3805
3806 -class Tabs( _EGWidget ):
3807 """Present widgets in Tabs. 3808 3809 This is also known as TabControl, TabWidget, Notebook, etc in 3810 other toolkits. 3811 3812 Tabs are composed from Pages (L{Tabs.Page}), which behave just 3813 like L{Group}s and hold a list of widgets. 3814 3815 Pages can be accessed by their index number (integer) or label, 3816 using the dictionary syntax or L{Tabs.get_page()} method. 3817 """ 3818
3819 - class Page( _EGWidget, AutoGenId ):
3820 """Page in Tabs component. 3821 3822 Pages must have a name and optionally an id, otherwise one id 3823 will be automatically generated. 3824 3825 It behaves just like L{Group} component. 3826 """ 3827 spacing = 3 3828 3829 children = _gen_ro_property( "children" ) 3830 horizontal = _gen_ro_property( "horizontal" ) 3831 parent = _gen_ro_property( "parent" ) 3832
3833 - def __init__( self, id=None, label="", children=None, 3834 horizontal=False ):
3835 """Tabs.Page constructor. 3836 3837 @param id: may not be provided, it will be generated automatically. 3838 @param label: displayed as Page label. 3839 @param children: a list of eagle widgets that this page contains. 3840 @param horizontal: if widgets should be laid out horizontally. 3841 """ 3842 _EGWidget.__init__( self, id or self.__get_id__() ) 3843 self.__label = label or "" 3844 self.horizontal = bool( horizontal ) 3845 self.children = _obj_tuple( children ) 3846 self.parent = None 3847 3848 self.__setup_gui__()
3849 # __init__() 3850 3851
3852 - def _get_app( self ):
3853 try: 3854 return self.__ro_app 3855 except AttributeError: 3856 return None
3857 # _get_app() 3858 3859
3860 - def _set_app( self, value ):
3861 # We need to overload app setter in order to add 3862 # children widgets to application as soon as we know the app 3863 try: 3864 v = self.__ro_app 3865 except AttributeError: 3866 v = None 3867 if v is None: 3868 self.__ro_app = value 3869 self.__add_widgets_to_app__() 3870 else: 3871 raise Exception( "Read Only property 'app'." )
3872 # _set_app() 3873 3874 app = property( _get_app, _set_app ) 3875 3876
3877 - def __add_widgets_to_app__( self ):
3878 for w in self.children: 3879 self.app.__add_widget__( w )
3880 # __add_widgets_to_app__() 3881 3882
3883 - def __setup_gui__( self ):
3884 self._wid = _Table( id=( "%s-contents" % self.id ), 3885 children=self.children, 3886 horizontal=self.horizontal ) 3887 self._widgets = ( self._wid, )
3888 # __setup_gui__() 3889 3890
3891 - def set_label( self, label ):
3892 if label is None: 3893 raise ValueError( "You cannot have 'None' labels for " 3894 "Tabs.Page!" ) 3895 self.__label = str( label ) 3896 3897 if self.parent is not None: 3898 self.parent.__set_page_label__( self, self.label )
3899 # set_label() 3900 3901
3902 - def get_label( self ):
3903 return self.__label
3904 # get_label() 3905 3906 label = property( get_label, set_label ) 3907 3908
3909 - def focus( self ):
3910 self.parent.__focus_page__( self )
3911 # focus() 3912 # Page 3913 3914 3915 children = _gen_ro_property( "children" ) 3916 3917
3918 - def __init__( self, id, children=None, expand_policy=None ):
3919 """Tabs constructor. 3920 3921 @param id: unique identified. 3922 @param children: an iterable with L{Tabs.Page} 3923 @param expand_policy: how this widget should fit space, see 3924 L{ExpandPolicy.Policy.Rule}. 3925 """ 3926 if expand_policy is None: 3927 expand_policy = ExpandPolicy.All() 3928 3929 _EGWidget.__init__( self, id, expand_policy=expand_policy ) 3930 if not children: 3931 self.children = tuple() 3932 else: 3933 lst = [] 3934 for i, widget in enumerate( children ): 3935 if isinstance( widget, Tabs.Page ): 3936 lst.append( widget ) 3937 else: 3938 sys.stderr.write( ( "children #%d (%s) is not " 3939 "instance of Tabs.Page, but %s" ) % 3940 ( i, widget, type( widget ).__name__ )) 3941 self.children = tuple( lst ) 3942 3943 self.__setup_gui__()
3944 # __init__() 3945 3946
3947 - def __setup_gui__( self ):
3948 self._wid = gtk.Notebook() 3949 for w in self.children: 3950 w.parent = self 3951 self._wid.append_page( w._widgets[ 0 ], 3952 gtk.Label( w.label ) ) 3953 self._widgets = ( self._wid, )
3954 # __setup_gui__() 3955 3956
3957 - def _get_app( self ):
3958 try: 3959 return self.__ro_app 3960 except AttributeError: 3961 return None
3962 # _get_app() 3963 3964
3965 - def _set_app( self, value ):
3966 # We need to overload app setter in order to add 3967 # children widgets to application as soon as we know the app 3968 try: 3969 v = self.__ro_app 3970 except AttributeError: 3971 v = None 3972 if v is None: 3973 self.__ro_app = value 3974 self.__add_widgets_to_app__() 3975 else: 3976 raise Exception( "Read Only property 'app'." )
3977 # _set_app() 3978 3979 app = property( _get_app, _set_app ) 3980 3981
3982 - def __add_widgets_to_app__( self ):
3983 for w in self.children: 3984 self.app.__add_widget__( w )
3985 # __add_widgets_to_app__() 3986 3987
3988 - def __set_page_label__( self, page, value ):
3989 self._wid.set_tab_label( page._widgets[ 0 ], gtk.Label( value ) )
3990 # __set_page_label__() 3991 3992
3993 - def __focus_page__( self, page ):
3994 for index, p in enumerate( self.children ): 3995 if p == page: 3996 self._wid.set_current_page( index ) 3997 return 3998 raise ValueError( "Page '%s' doesn't exist in this Tab." % ( page, ) )
3999 # __focus_page__() 4000 4001
4002 - def focus_page( self, index_or_name_or_page ):
4003 """Make given page visible.""" 4004 if not isinstance( index_or_name_or_page, Tabs.Page ): 4005 index_or_name = index_or_name_or_page 4006 page = self.get_page( index_or_name ) 4007 else: 4008 page = index_or_name_or_page 4009 page.focus()
4010 # focus_page() 4011 4012
4013 - def get_page( self, index_or_name ):
4014 """Get the Tabs.Page given its index or name. 4015 4016 @raise KeyError if index_or_name doesn't exists. 4017 """ 4018 if isinstance( index_or_name, basestring ): 4019 name = index_or_name 4020 for w in self.children: 4021 if w.label == name: 4022 return w 4023 raise KeyError( "No page labeled '%s'" % name ) 4024 else: 4025 index = index_or_name 4026 try: 4027 return self.children[ index ] 4028 except IndexError, e: 4029 raise KeyError( "No page numbered %s" % index )
4030 # get_page() 4031 4032
4033 - def __getitem__( self, name ):
4034 """Same as L{Tabs.get_page()}. 4035 4036 @raise KeyError see L{Tabs.get_page()} 4037 @see L{Tabs.get_page()} 4038 """ 4039 return self.get_page( name )
4040 # __getitem__() 4041 4042
4043 - def __setitem__( self, name, value ):
4044 """Set L{Tabs.Page.label} of a page get using 'name' for 4045 L{Tabs.get_page()}. 4046 4047 @raise KeyError see L{Tabs.get_page()} 4048 @see L{Tabs.get_page()} 4049 """ 4050 page = self[ name ] 4051 page.label = value
4052 # __setitem__() 4053 # Tabs 4054 4055
4056 -class Table( _EGWidget ):
4057 """Data table. 4058 4059 Each column should have only one type, it will be checked. 4060 Can be accessed as a python list: 4061 4062 >>> t = Table( 't', 'table', [ 1, 2, 3 ] ) 4063 >>> t[ 0 ] 4064 [ 1 ] 4065 >>> del t[ 1 ] 4066 >>> t[ : ] 4067 [ 1, 3 ] 4068 """ 4069 spacing = 3 4070 4071
4072 - class Row( object ):
4073 # Used to hide gtk.ListStore
4074 - def __init__( self, items ):
4075 self.__items = items
4076 # __init__() 4077 4078
4079 - def __str__( self ):
4080 return "[" + ", ".join( [ str( x ) for x in self.__items ] ) + "]"
4081 # __str__() 4082 __repr__ = __str__ 4083 4084
4085 - def __len__( self ):
4086 return len( self.__items )
4087 # __len__() 4088 4089
4090 - def __nonzero__( self ):
4091 return self.__items.__nonzero__()
4092 # __nonzero__() 4093 4094
4095 - def __getitem__( self, index ):
4096 return self.__items[ index ]
4097 # __getitem__() 4098 4099
4100 - def __setitem__( self, index, value ):
4101 self.__items[ index ] = value
4102 # __setitem__() 4103 4104
4105 - def __delitem__( self, index ):
4106 del self.__items[ index ]
4107 # __delitem__() 4108 4109
4110 - def __contains__( self, element ):
4111 return element in self.__items
4112 # __contains__() 4113 4114
4115 - def __getslice__( self, start, end ):
4116 slice = [] 4117 4118 l = len( self.__items ) 4119 while start < 0: 4120 start += l 4121 while end < 0: 4122 end += l 4123 4124 start = min( start, l ) 4125 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 4126 4127 for i in xrange( start, end ): 4128 slice.append( self.__items[ i ] ) 4129 return slice
4130 # __getslice__() 4131 4132
4133 - def __setslice__( self, start, end, items ):
4134 l = len( self.__items ) 4135 while start < 0: 4136 start += l 4137 while end < 0: 4138 end += l 4139 4140 start = min( start, l ) 4141 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 4142 4143 l2 = len( items ) 4144 if end - start > l2: 4145 end = start + l2 4146 4147 if self.__items.model._hid_row_changed is not None: 4148 hid_changed = self.__items.model._hid_row_changed 4149 self.__items.model.handler_block( hid_changed ) 4150 4151 j = 0 4152 for i in xrange( start, end ): 4153 self.__items[ i ] = items[ j ] 4154 j += 1 4155 4156 if self.__items.model._hid_row_changed is not None: 4157 hid_changed = self.__items.model._hid_row_changed 4158 self.__items.model.handler_unblock( hid_changed ) 4159 i = self.__items.iter 4160 p = self.__items.path 4161 self.__items.model.row_changed( p, i )
4162 4163 # __setslice__() 4164 # Row 4165 4166
4167 - class CellFormat( object ):
4168 __slots__ = ( "fgcolor", "bgcolor", "font", "bold", 4169 "italic", "underline", "strike", "contents" )
4170 - def __init__( self, **kargs ):
4171 for a in self.__slots__: 4172 v = kargs.get( a, None ) 4173 setattr( self, a, v )
4174 # __init__() 4175 # CellFormat() 4176 4177 4178
4179 - def __init__( self, id, label="", items=None, types=None, 4180 headers=None, show_headers=True, editable=False, 4181 repositioning=False, expand_columns_indexes=None, 4182 hidden_columns_indexes=None, cell_format_func=None, 4183 selection_callback=None, data_changed_callback=None, 4184 expand_policy=None ):
4185 """Table constructor. 4186 4187 @param id: unique identifier. 4188 @param label: what to show on table frame 4189 @param items: a list (single column) or list of lists (multiple 4190 columns) 4191 @param types: a list of types (str, int, long, float, unicode, bool) 4192 for columns, if omitted, will be guessed from items. 4193 @param headers: what to use as table header. 4194 @param show_headers: whenever to show table headers 4195 @param editable: if table is editable. If editable, user can change 4196 values inline or double-clicking, also edit buttons will 4197 show after the table. 4198 @param repositioning: allow items to be moved up and down. 4199 @param expand_columns_indexes: list of indexes that can expand size 4200 @param cell_format_func: if define, should return a CellFormat with 4201 properties to be applied to cell. Only non-None properties will 4202 be used. Function should have the following signature: 4203 def func( app, table, row, col, value ): 4204 return Table.CellFormat( ... ) 4205 where row and col are indexes in table. 4206 @param selection_callback: the function (or list of functions) to 4207 call when selection changes. Function will get as parameters: 4208 - App reference 4209 - Table reference 4210 - List of pairs ( index, row_contents ) 4211 @param data_changed_callback: the function (or list of functions) to 4212 call when data changes. Function will get as parameters: 4213 - App reference 4214 - Table reference 4215 - Pair ( index, row_contents ) 4216 @param expand_policy: how this widget should fit space, see 4217 L{ExpandPolicy.Policy.Rule}. 4218 4219 @warning: although this widget contains data, it's not a 4220 _EGDataWidget and thus will not notify application that 4221 data changed, also it cannot persist it's data 4222 automatically, if you wish, do it manually. This behavior 4223 may change in future if Table show to be useful as 4224 _EGDataWidget. 4225 """ 4226 if expand_policy is None: 4227 expand_policy = ExpandPolicy.All() 4228 4229 _EGWidget.__init__( self, id, expand_policy=expand_policy ) 4230 self.editable = editable or False 4231 self.repositioning = repositioning or False 4232 self.__label = label 4233 self.headers = headers or tuple() 4234 self.show_headers = bool( show_headers ) 4235 self.cell_format_func = cell_format_func 4236 4237 if isinstance( expand_columns_indexes, ( int, long ) ): 4238 expand_columns_indexes = ( expand_columns_indexes, ) 4239 elif isinstance( expand_columns_indexes, ( tuple, list ) ): 4240 expand_columns_indexes = tuple( expand_columns_indexes ) 4241 elif expand_columns_indexes is None: 4242 expand_columns_indexes = tuple() 4243 else: 4244 raise ValueError( \ 4245 "expand_columns_indexes must be a sequence of integers" ) 4246 self.expand_columns_indexes = expand_columns_indexes 4247 4248 if isinstance( hidden_columns_indexes, ( int, long ) ): 4249 hidden_columns_indexes = ( hidden_columns_indexes, ) 4250 elif isinstance( hidden_columns_indexes, ( tuple, list ) ): 4251 hidden_columns_indexes = tuple( hidden_columns_indexes ) 4252 elif hidden_columns_indexes is None: 4253 hidden_columns_indexes = tuple() 4254 else: 4255 raise ValueError( \ 4256 "hidden_columns_indexes must be a sequence of integers" ) 4257 self.hidden_columns_indexes = hidden_columns_indexes 4258 4259 if not ( types or items ): 4260 raise ValueError( "Must provide items or types!" ) 4261 elif not types: 4262 items = items or [] 4263 if not isinstance( items[ 0 ], ( list, tuple ) ): 4264 # just one column, convert to generic representation 4265 items = [ [ i ] for i in items ] 4266 4267 types = [ type( i ) for i in items[ 0 ] ] 4268 self.types = types 4269 self.items = items 4270 4271 self.selection_callback = _callback_tuple( selection_callback ) 4272 self.data_changed_callback = _callback_tuple( data_changed_callback ) 4273 4274 self.__setup_gui__() 4275 4276 self._model._hid_row_changed = None 4277 self._model._hid_row_deleted = None 4278 self._model._hid_row_inserted = None 4279 self.__setup_connections__() 4280 self.__setup_items__()
4281 # __init__() 4282 4283
4284 - def __setup_gui__( self ):
4285 self._vbox = gtk.VBox( False, self.spacing ) 4286 self._vbox.set_border_width( self.spacing ) 4287 self._vbox.set_name( "vbox-%s" % self.id ) 4288 4289 if self.label is not None: 4290 self._frame = gtk.Frame( self.label ) 4291 self._frame.set_name( self.id ) 4292 self._frame.add( self._vbox ) 4293 root = self._frame 4294 else: 4295 root = self._vbox 4296 self._widgets = ( root, ) 4297 4298 self.__setup_table__() 4299 4300 if self.editable or self.repositioning: 4301 self._hbox = gtk.HBox( False, self.spacing ) 4302 self._vbox.pack_start( self._hbox, expand=False, fill=True ) 4303 4304 if self.editable: 4305 self._btn_add = gtk.Button( stock=gtk.STOCK_ADD ) 4306 self._btn_del = gtk.Button( stock=gtk.STOCK_REMOVE ) 4307 self._btn_edit = gtk.Button( stock=gtk.STOCK_EDIT ) 4308 4309 self._hbox.pack_start( self._btn_add ) 4310 self._hbox.pack_start( self._btn_del ) 4311 self._hbox.pack_start( self._btn_edit ) 4312 4313 if self.repositioning: 4314 if self.editable: 4315 self._hbox.pack_start( gtk.VSeparator() ) 4316 4317 self._btn_up = gtk.Button( stock=gtk.STOCK_GO_UP ) 4318 self._btn_down = gtk.Button( stock=gtk.STOCK_GO_DOWN ) 4319 4320 self._btn_up.set_sensitive( False ) 4321 self._btn_down.set_sensitive( False ) 4322 4323 self._hbox.pack_start( self._btn_up ) 4324 self._hbox.pack_start( self._btn_down )
4325 # __setup_gui__() 4326 4327
4328 - def __setup_connections__( self ):
4329 if self.data_changed_callback: 4330 self.__setup_connections_changed__() 4331 4332 if self.editable: 4333 self.__setup_connections_editable__() 4334 4335 if self.repositioning: 4336 self.__setup_connections_repositioning__() 4337 4338 if self.selection_callback: 4339 self.__setup_connections_selection__()
4340 # __setup_connections__() 4341 4342
4343 - def __setup_connections_changed__( self ):
4344 - def row_changed( model, path, itr ):
4345 index = path[ 0 ] 4346 v = ( index, Table.Row( model[ path ] ) ) 4347 for c in self.data_changed_callback: 4348 c( self.app, self, v )
4349 # row_changed() 4350 4351
4352 - def row_deleted( model, path ):
4353 index = path[ 0 ] 4354 v = ( index, None ) 4355 for c in self.data_changed_callback: 4356 c( self.app, self, v )
4357 # row_deleted() 4358 4359 c = self._model.connect 4360 self._model._hid_row_changed = c( "row-changed", row_changed ) 4361 self._model._hid_row_deleted = c( "row-deleted", row_deleted ) 4362 self._model._hid_row_inserted = c( "row-inserted", row_changed)
4363 # __setup_connections_changed__() 4364 4365
4367 - def edit_dialog( data ):
4368 title = "Edit data from table %s" % ( self.label or self.id ) 4369 buttons = ( gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, 4370 gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT ) 4371 d = gtk.Dialog( title=title, 4372 flags=gtk.DIALOG_MODAL, 4373 buttons=buttons ) 4374 d.set_default_response( gtk.RESPONSE_ACCEPT ) 4375 4376 l = len( data ) 4377 t = gtk.Table( l, 2 ) 4378 t.set_border_width( 0 ) 4379 w = {} 4380 for i, v in enumerate( data ): 4381 if i in self.hidden_columns_indexes: 4382 continue 4383 4384 title = self._table.get_column( i ).get_title() 4385 label = gtk.Label( "%s:" % title ) 4386 label.set_justify( gtk.JUSTIFY_RIGHT ) 4387 label.set_alignment( xalign=1.0, yalign=0.5 ) 4388 4389 tp = self.types[ i ] 4390 if tp == bool: 4391 entry = gtk.CheckButton() 4392 entry.set_active( data[ i ] ) 4393 elif tp in ( int, long ): 4394 entry = gtk.SpinButton( digits=0 ) 4395 adj = entry.get_adjustment() 4396 adj.lower = Spin.default_min 4397 adj.upper = Spin.default_max 4398 adj.step_increment = 1 4399 adj.page_increment = 5 4400 entry.set_value( data[ i ] ) 4401 elif tp == float: 4402 entry = gtk.SpinButton( digits=6 ) 4403 adj = entry.get_adjustment() 4404 adj.lower = Spin.default_min 4405 adj.upper = Spin.default_max 4406 adj.step_increment = 1 4407 adj.page_increment = 5 4408 entry.set_value( data[ i ] ) 4409 elif tp in ( str, unicode ): 4410 entry = gtk.Entry() 4411 entry.set_text( data[ i ] ) 4412 elif tp == Image: 4413 entry = gtk.Image() 4414 entry.set_from_pixbuf( data[ i ].__get_gtk_pixbuf__() ) 4415 else: 4416 entry = gtk.Label( str( data[ i ] ) ) 4417 4418 t.attach( label, 0, 1, i, i + 1, 4419 xoptions=gtk.FILL, 4420 xpadding=self.spacing, ypadding=self.spacing ) 4421 t.attach( entry, 1, 2, i, i + 1, 4422 xoptions=gtk.EXPAND|gtk.FILL, 4423 xpadding=self.spacing, ypadding=self.spacing ) 4424 w[ i ] = entry 4425 4426 t.show_all() 4427 4428 sw = gtk.ScrolledWindow() 4429 sw.add_with_viewport( t ) 4430 sw.get_child().set_shadow_type( gtk.SHADOW_NONE ) 4431 d.vbox.pack_start( sw ) 4432 # Hack, disable scrollbars so we get the window to the 4433 # best size 4434 sw.set_policy( hscrollbar_policy=gtk.POLICY_NEVER, 4435 vscrollbar_policy=gtk.POLICY_NEVER ) 4436 d.show_all() 4437 # Scrollbars are automatic 4438 sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 4439 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 4440 4441 r = d.run() 4442 d.destroy() 4443 if r in ( gtk.RESPONSE_REJECT, gtk.RESPONSE_DELETE_EVENT ) : 4444 return None 4445 else: 4446 result = [] 4447 for i in xrange( len( data ) ): 4448 tp = self.types[ i ] 4449 4450 if i not in w: 4451 r = data[ i ] 4452 else: 4453 wid = w[ i ] 4454 4455 if tp == bool: 4456 r = bool( wid.get_active() ) 4457 elif tp in ( int, long ): 4458 r = tp( wid.get_value() ) 4459 elif tp == float: 4460 r = float( wid.get_value() ) 4461 elif tp in ( str, unicode ): 4462 r = tp( wid.get_text() ) 4463 else: 4464 r = data[ i ] 4465 result.append( r ) 4466 4467 return result
4468 # edit_dialog() 4469 4470
4471 - def clicked_add( button ):
4472 entry = [] 4473 for i, t in enumerate( self.types ): 4474 if t == bool: 4475 v = False 4476 elif t in ( int, long, float ): 4477 v = 0 4478 elif t in ( str, unicode ): 4479 v = '' 4480 else: 4481 try: 4482 v = t.default_value() 4483 except AttributeError: 4484 try: 4485 name = t.__name__ 4486 except: 4487 name = t 4488 raise ValueError( ( "Unsuported column (%d) type: %s." 4489 " Type should provide " 4490 "default_value() static method." ) 4491 % ( i, name ) ) 4492 entry.append( v ) 4493 result = edit_dialog( entry ) 4494 if result: 4495 self.append( result )
4496 # clicked_add() 4497 4498
4499 - def clicked_edit( button ):
4500 selected = self.selected() 4501 if not selected: 4502 return 4503 4504 for index, data in selected: 4505 print data 4506 result = edit_dialog( data ) 4507 if result: 4508 self[ index ] = result
4509 # clicked_edit() 4510 4511
4512 - def clicked_del( button ):
4513 selected = self.selected() 4514 if not selected: 4515 return 4516 4517 for index, data in selected: 4518 del self[ index ]
4519 # clicked_del() 4520 4521 self._btn_add.connect( "clicked", clicked_add ) 4522 self._btn_del.connect( "clicked", clicked_del ) 4523 self._btn_edit.connect( "clicked", clicked_edit ) 4524
4525 - def row_activated( treeview, path, column ):
4526 data = treeview.get_model()[ path ] 4527 result = edit_dialog( data ) 4528 if result: 4529 self[ path[ 0 ] ] = result
4530 # row_activated() 4531 4532 4533 self._table.connect( "row-activated", row_activated )
4534 # __setup_connections_editable__() 4535 4536
4538 - def selection_changed( selection ):
4539 result = self.selected() 4540 if not result: 4541 self._btn_up.set_sensitive( False ) 4542 self._btn_down.set_sensitive( False ) 4543 else: 4544 path = result[ 0 ][ 0 ] 4545 if path > 0: 4546 self._btn_up.set_sensitive( True ) 4547 else: 4548 self._btn_up.set_sensitive( False ) 4549 if path < len( self ) - 1: 4550 self._btn_down.set_sensitive( True ) 4551 else: 4552 self._btn_down.set_sensitive( False )
4553 # selection_changed() 4554
4555 - def move_up( button ):
4556 result = self.selected() 4557 a = result[ 0 ][ 0 ] 4558 if a <= 0: 4559 return 4560 4561 b = a - 1 4562 la = list( self[ a ] ) 4563 lb = list( self[ b ] ) 4564 self[ a ] = lb 4565 self[ b ] = la 4566 self.select( b )
4567 # move_up() 4568
4569 - def move_down( button ):
4570 result = self.selected() 4571 a = result[ 0 ][ 0 ] 4572 if a >= len( self ) - 1: 4573 return 4574 4575 b = a + 1 4576 la = list( self[ a ] ) 4577 lb = list( self[ b ] ) 4578 self[ a ] = lb 4579 self[ b ] = la 4580 self.select( b )
4581 # move_down() 4582 4583 selection = self._table.get_selection() 4584 selection.connect( "changed", selection_changed ) 4585 self._btn_up.connect( "clicked", move_up ) 4586 self._btn_down.connect( "clicked", move_down )
4587 # __setup_connections_repositioning__() 4588 4589
4591 - def selection_changed( selection ):
4592 result = self.selected() 4593 for c in self.selection_callback: 4594 c( self.app, self, result )
4595 # selection_changed() 4596 4597 selection = self._table.get_selection() 4598 selection.connect( "changed", selection_changed )
4599 # __setup_connections_selection__() 4600 4601
4602 - def __create_column_cell_format_func__( self, col, cell_rend ):
4603 - def get_color( c ):
4605 # get_color() 4606
4607 - def func( column, cell_renderer, model, itr, col_idx ):
4608 row_idx = model.get_path( itr )[ 0 ] 4609 value = model.get_value( itr, col_idx ) 4610 cf = self.cell_format_func( self.app, self, 4611 row_idx, col_idx, value ) 4612 if cf is None: 4613 cf = Table.CellFormat() 4614 4615 bgcolor = cf.bgcolor 4616 if bgcolor is not None: 4617 bgcolor = get_color( bgcolor ) 4618 else: 4619 bgcolor = self._table.style.base[ gtk.STATE_NORMAL ] 4620 cell_renderer.set_property( "cell-background-gdk", 4621 bgcolor ) 4622 4623 if isinstance( cell_renderer, gtk.CellRendererText ): 4624 v = None 4625 if cf.contents is not None: 4626 v = model[ itr ][ col_idx ] 4627 4628 if callable( cf.contents ): 4629 v = cf.contents( v ) 4630 else: 4631 v = cf.contents 4632 4633 if callable( cell_renderer.model_view_conv ): 4634 if v is None: 4635 v = model[ itr ][ col_idx ] 4636 v = cell_renderer.model_view_conv( v ) 4637 4638 if v is not None: 4639 cell_renderer.set_property( "text", v ) 4640 4641 font = cf.font 4642 if font is not None: 4643 font = pango.FontDescription( font ) 4644 else: 4645 font = self._table.style.font_desc 4646 cell_renderer.set_property( "font-desc", font ) 4647 4648 fgcolor = cf.fgcolor 4649 if fgcolor is not None: 4650 fgcolor = get_color( fgcolor ) 4651 else: 4652 fgcolor = self._table.style.text[ gtk.STATE_NORMAL ] 4653 cell_renderer.set_property( "foreground-gdk", fgcolor ) 4654 4655 if cf.underline: 4656 underline = pango.UNDERLINE_SINGLE 4657 else: 4658 underline = pango.UNDERLINE_NONE 4659 cell_renderer.set_property( "underline", underline ) 4660 4661 if cf.bold: 4662 bold = pango.WEIGHT_BOLD 4663 else: 4664 bold = pango.WEIGHT_NORMAL 4665 cell_renderer.set_property( "weight", bold ) 4666 4667 if cf.italic: 4668 italic = pango.STYLE_ITALIC 4669 else: 4670 italic = pango.STYLE_NORMAL 4671 cell_renderer.set_property( "style", italic ) 4672 4673 cell_renderer.set_property( "strikethrough", 4674 bool( cf.strike ) ) 4675 4676 elif isinstance( cell_renderer, gtk.CellRendererToggle ): 4677 v = None 4678 if cf.contents is not None: 4679 v = model[ itr ][ col_idx ] 4680 4681 if callable( cf.contents ): 4682 v = cf.contents( v ) 4683 else: 4684 v = cf.contents 4685 4686 if callable( cell_renderer.model_view_conv ): 4687 if v is None: 4688 v = model[ itr ][ col_idx ] 4689 v = cell_renderer.model_view_conv( v ) 4690 4691 if v is not None: 4692 cell_renderer.set_property( "active", v ) 4693 4694 elif isinstance( cell_renderer, gtk.CellRendererPixbuf ): 4695 v = None 4696 if cf.contents is not None: 4697 v = model[ itr ][ col_idx ] 4698 4699 if callable( cf.contents ): 4700 v = cf.contents( v ) 4701 else: 4702 v = cf.contents 4703 4704 if callable( cell_renderer.model_view_conv ): 4705 if v is None: 4706 v = model[ itr ][ col_idx ] 4707 v = cell_renderer.model_view_conv( v ) 4708 4709 if v is not None: 4710 cell_renderer.set_property( "pixbuf", v )
4711 # func() 4712 return func
4713 # __create_column_cell_format_func__() 4714 4715
4716 - def __setup_table__( self ):
4717 self.__setup_model__() 4718 self._table = gtk.TreeView( self._model ) 4719 self._table.set_name( "table-%s" % self.id ) 4720 self._table.get_selection().set_mode( gtk.SELECTION_MULTIPLE ) 4721
4722 - def column_clicked( column ):
4723 cid, order = self._model.get_sort_column_id() 4724 self._model.set_sort_column_id( cid, order )
4725 # column_clicked() 4726
4727 - def toggled( cell_render, path, col ):
4728 self._model[ path ][ col ] = not self._model[ path ][ col ]
4729 # toggled() 4730 4731
4732 - def edited( cell_render, path, text, col ):
4733 t = self.types[ col ] 4734 try: 4735 value = t( text ) 4736 except ValueError, e: 4737 name = t.__name__ 4738 error( "Invalid contents for column of type '%s': %s" % 4739 ( name, text ) ) 4740 else: 4741 self._model[ path ][ col ] = value
4742 # edited() 4743 4744 4745 for i, t in enumerate( self.types ): 4746 if t == bool: 4747 cell_rend = gtk.CellRendererToggle() 4748 props = { "active": i } 4749 cell_rend.model_view_conv = None 4750 if self.editable: 4751 cell_rend.set_property( "activatable", True) 4752 cell_rend.connect( "toggled", toggled, i ) 4753 4754 elif t in ( int, long, float, str, unicode ): 4755 cell_rend = gtk.CellRendererText() 4756 props = { "text": i } 4757 cell_rend.model_view_conv = None 4758 if self.editable: 4759 cell_rend.set_property( "editable", True ) 4760 cell_rend.connect( "edited", edited, i ) 4761 4762 if t in ( int, long, float ): 4763 cell_rend.set_property( "xalign", 1.0 ) 4764 elif t == Image: 4765 cell_rend = gtk.CellRendererPixbuf() 4766 cell_rend.model_view_conv = lambda x: x.__get_gtk_pixbuf__() 4767 props = {} 4768 else: 4769 cell_rend = gtk.CellRendererText() 4770 props = {} 4771 cell_rend.model_view_conv = str 4772 4773 try: 4774 title = self.headers[ i ] 4775 except IndexError: 4776 title = "Col-%d (%s)" % ( i, t.__name__ ) 4777 4778 col = gtk.TreeViewColumn( title, cell_rend, **props ) 4779 col.set_resizable( True ) 4780 col.set_sort_column_id( i ) 4781 col.connect( "clicked", column_clicked ) 4782 4783 if self.cell_format_func: 4784 f = self.__create_column_cell_format_func__( col, cell_rend ) 4785 col.set_cell_data_func( cell_rend, f, i ) 4786 4787 elif callable( cell_rend.model_view_conv ): 4788 def f( column, cell_rend, model, iter, model_idx ): 4789 o = model[ iter ][ model_idx ] 4790 v = cell_rend.model_view_conv( o ) 4791 if isinstance( cell_rend, gtk.CellRendererText ): 4792 cell_rend.set_property( "text", v ) 4793 elif isinstance( cell_rend, gtk.CellRendererToggle ): 4794 cell_rend.set_property( "active", v ) 4795 elif isinstance( cell_rend, gtk.CellRendererPixbuf ): 4796 cell_rend.set_property( "pixbuf", v )
4797 # f() 4798 col.set_cell_data_func( cell_rend, f, i ) 4799 4800 4801 if i in self.expand_columns_indexes: 4802 col.set_expand( True ) 4803 else: 4804 col.set_expand( False ) 4805 4806 if i not in self.hidden_columns_indexes: 4807 col.set_visible( True ) 4808 else: 4809 col.set_visible( False ) 4810 4811 self._table.append_column( col ) 4812 4813 4814 self._table.set_headers_visible( self.show_headers ) 4815 self._table.set_headers_clickable( True ) 4816 self._table.set_reorderable( self.repositioning ) 4817 self._table.set_enable_search( True ) 4818 4819 self._sw = gtk.ScrolledWindow() 4820 self._sw.set_policy( hscrollbar_policy=gtk.POLICY_AUTOMATIC, 4821 vscrollbar_policy=gtk.POLICY_AUTOMATIC ) 4822 self._sw.set_shadow_type( gtk.SHADOW_IN ) 4823 self._sw.add( self._table ) 4824 self._vbox.pack_start( self._sw )
4825 # __setup_table__() 4826 4827
4828 - def __setup_items__( self ):
4829 if self.items: 4830 for row in self.items: 4831 self.append( row, select=False, autosize=False ) 4832 self.columns_autosize()
4833 # __setup_items__() 4834 4835 4836
4837 - def __setup_model__( self ):
4838 gtk_types = [] 4839 for i, t in enumerate( self.types ): 4840 if t == bool: 4841 gtk_types.append( gobject.TYPE_BOOLEAN ) 4842 elif t == int: 4843 gtk_types.append( gobject.TYPE_INT ) 4844 elif t == long: 4845 gtk_types.append( gobject.TYPE_LONG ) 4846 elif t == float: 4847 gtk_types.append( gobject.TYPE_FLOAT ) 4848 elif t in ( str, unicode ): 4849 gtk_types.append( gobject.TYPE_STRING ) 4850 else: 4851 gtk_types.append( gobject.TYPE_PYOBJECT ) 4852 4853 self._model = gtk.ListStore( *gtk_types ) 4854
4855 - def sort_fn( model, itr1, itr2, id ):
4856 return cmp( model[ itr1 ][ id ], model[ itr2 ][ id ] )
4857 # sort_fn() 4858 4859 for i in xrange( len( self.types ) ): 4860 self._model.set_sort_func( i, sort_fn, i )
4861 # __setup_model__() 4862 4863
4864 - def set_label( self, label ):
4865 if self.__label is None: 4866 raise ValueError( "You cannot change label of widget created " 4867 "without one. Create it with placeholder! " 4868 "(label='')" ) 4869 self.__label = label 4870 self._frame.set_label( self.__label )
4871 # set_label() 4872 4873
4874 - def get_label( self ):
4875 return self.__label
4876 # get_label() 4877 4878 label = property( get_label, set_label ) 4879 4880
4881 - def columns_autosize( self ):
4882 self._table.columns_autosize()
4883 # columns_autosize() 4884 4885
4886 - def select( self, index ):
4887 selection = self._table.get_selection() 4888 selection.unselect_all() 4889 selection.select_path( index )
4890 # select() 4891 4892
4893 - def selected( self ):
4894 model, paths = self._table.get_selection().get_selected_rows() 4895 if paths: 4896 result = [] 4897 for p in paths: 4898 result.append( ( p[ 0 ], Table.Row( model[ p ] ) ) ) 4899 return result 4900 else: 4901 return None
4902 # selected() 4903 4904
4905 - def append( self, row, select=True, autosize=True ):
4906 if not isinstance( row, ( list, tuple ) ): 4907 row = ( row, ) 4908 4909 if self._model._hid_row_inserted is not None: 4910 self._model.handler_block( self._model._hid_row_inserted ) 4911 if self._model._hid_row_changed is not None: 4912 self._model.handler_block( self._model._hid_row_changed ) 4913 itr = self._model.append( row ) 4914 if self._model._hid_row_changed is not None: 4915 self._model.handler_unblock( self._model._hid_row_changed ) 4916 if self._model._hid_row_inserted is not None: 4917 self._model.handler_unblock( self._model._hid_row_inserted ) 4918 4919 self._model.row_changed( self._model.get_path( itr ), itr ) 4920 4921 if autosize: 4922 self._table.columns_autosize() 4923 if select: 4924 self._table.set_cursor( self._model[ itr ].path )
4925 # append() 4926 4927
4928 - def insert( self, index, row, select=True, autosize=True ):
4929 if not isinstance( row, ( list, tuple ) ): 4930 row = ( row, ) 4931 4932 if self._model._hid_row_inserted is not None: 4933 self._model.handler_block( self._model._hid_row_inserted ) 4934 if self._model._hid_row_changed is not None: 4935 self._model.handler_block( self._model._hid_row_changed ) 4936 itr = self._model.insert( index, row ) 4937 if self._model._hid_row_changed is not None: 4938 self._model.handler_unblock( self._model._hid_row_changed ) 4939 if self._model._hid_row_inserted is not None: 4940 self._model.handler_unblock( self._model._hid_row_inserted ) 4941 4942 self._model.row_changed( self._model.get_path( itr ), itr ) 4943 4944 if autosize: 4945 self._table.columns_autosize() 4946 if select: 4947 self._table.set_cursor( self._model[ itr ].path )
4948 # insert() 4949 4950
4951 - def __nonzero__( self ):
4952 return self._model.__nonzero__()
4953 # __nonzero__() 4954
4955 - def __len__( self ):
4956 return len( self._model )
4957 # __len__() 4958 4959
4960 - def __iadd__( self, other ):
4961 self.append( other ) 4962 return self
4963 # __iadd__() 4964 4965
4966 - def __setitem__( self, index, other ):
4967 if not isinstance( other, ( list, tuple, Table.Row ) ): 4968 other = ( other, ) 4969 try: 4970 if self._model._hid_row_inserted is not None: 4971 self._model.handler_block( self._model._hid_row_inserted ) 4972 if self._model._hid_row_changed is not None: 4973 self._model.handler_block( self._model._hid_row_changed ) 4974 self._model[ index ] = other 4975 if self._model._hid_row_changed is not None: 4976 self._model.handler_unblock( self._model._hid_row_changed ) 4977 if self._model._hid_row_inserted is not None: 4978 self._model.handler_unblock( self._model._hid_row_inserted ) 4979 4980 p = ( index, ) 4981 self._model.row_changed( p, self._model.get_iter( p ) ) 4982 4983 except TypeError, e: 4984 raise IndexError( "index out of range" )
4985 # __setitem__() 4986 4987
4988 - def __getitem__( self, index ):
4989 try: 4990 items = self._model[ index ] 4991 except TypeError, e: 4992 raise IndexError( "index out of range" ) 4993 4994 return Table.Row( items )
4995 # __getitem__() 4996 4997
4998 - def __delitem__( self, index ):
4999 try: 5000 del self._model[ index ] 5001 except TypeError, e: 5002 raise IndexError( "index out of range" )
5003 # __delitem__() 5004 5005
5006 - def __contains__( self, row ):
5007 for r in self._model: 5008 if row in r: 5009 return True 5010 return False
5011 # __contains__() 5012 5013
5014 - def __getslice__( self, start, end ):
5015 slice = [] 5016 5017 l = len( self._model ) 5018 while start < 0: 5019 start += l 5020 while end < 0: 5021 end += l 5022 5023 start = min( start, l ) 5024 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 5025 5026 for i in xrange( start, end ): 5027 slice.append( Table.Row( self._model[ i ] ) ) 5028 return slice
5029 # __getslice__() 5030 5031
5032 - def __setslice__( self, start, end, slice ):
5033 l = len( self._model ) 5034 while start < 0: 5035 start += l 5036 while end < 0: 5037 end += l 5038 5039 del self[ start : end ] 5040 5041 # just insert len( slice ) items 5042 l2 = len( slice ) 5043 if end - start > l2: 5044 end = start + l2 5045 for j, i in enumerate( xrange( start, end ) ): 5046 row = list( slice[ j ] ) 5047 5048 5049 # extend row if necessary 5050 lr = len( row ) 5051 lt = len( self.types ) 5052 if lr < lt: 5053 for i in xrange( lr, lt ): 5054 t = self.types[ i ] 5055 row.append( t() ) 5056 5057 self.insert( i, row, select=False, autosize=False )
5058 # __setslice__() 5059 5060
5061 - def __delslice__( self, start, end ):
5062 l = len( self._model ) 5063 while start < 0: 5064 start += l 5065 while end < 0: 5066 end += l 5067 5068 start = min( start, l ) 5069 end = min( end, l ) # l[ : ] -> l[ 0 : maxlistindex ] 5070 5071 while end > start: 5072 end -= 1 5073 del self._model[ end ]
5074 # __delslice__() 5075 # Table 5076 5077
5078 -class RichText( _EGWidget ):
5079 """A Rich Text viewer 5080 5081 Display text with basic formatting instructions. Formatting is 5082 done using a HTML subset. 5083 """ 5084
5085 - class Renderer( gtk.TextView ):
5086 """Specialized TextView to render formatted texts. 5087 5088 This class emits "follow-link" when user clicks somewhere. 5089 5090 It implements Writer interface as specified in standard library 5091 "formatter" module. 5092 """ 5093 bullet = None 5094 margin = 2 5095 signal_created = False 5096
5097 - def __init__( self, link_color="#0000ff", 5098 foreground=None, background=None, 5099 resource_provider=None ):
5100 """RichText.Renderer constructor. 5101 5102 @param link_color: color to use with links. String with color name 5103 or in internet format (3 pairs of RGB, in hexa, prefixed by 5104 #). 5105 @param foreground: default foreground color. Same spec as 5106 link_color. 5107 @param background: default background color. Same spec as 5108 link_color. 5109 @param resource_provider: function to provide unresolved resources. 5110 If some image could not be handled as a file, this function 5111 will be called and it should return an gtk.gdk.Pixbuf. 5112 Since http://url.com/file will always be unresolved, you 5113 may use this to provide remote file access to this class. 5114 """ 5115 self.link_color = link_color or "#0000ff" 5116 self.foreground = foreground 5117 self.background = background 5118 self.resource_provider = resource_provider 5119 self.hovering_over_link = False 5120 5121 b = gtk.TextBuffer() 5122 gtk.TextView.__init__( self, b ) 5123 5124 self.set_cursor_visible( False ) 5125 self.set_editable( False ) 5126 self.set_wrap_mode( gtk.WRAP_WORD ) 5127 self.set_left_margin( self.margin ) 5128 self.set_right_margin( self.margin ) 5129 5130 self.__setup_connections__() 5131 self.__setup_render__() 5132 self.__create_bullets__()
5133 # __init__() 5134 5135
5136 - def __create_bullets__( self ):
5137 klass = RichText.Renderer 5138 if klass.bullet is None: 5139 width = height = 16 5140 dx = dy = 4 5141 5142 colormap = gtk.gdk.colormap_get_system() 5143 visual = colormap.get_visual() 5144 5145 white = colormap.alloc_color( "#ffffff", True, True ) 5146 black = colormap.alloc_color( "#000000", True, True ) 5147 5148 pixmap = gtk.gdk.Pixmap( None, width, height, visual.depth ) 5149 white_gc = pixmap.new_gc( foreground=white, background=black, 5150 fill=gtk.gdk.SOLID, line_width=1, 5151 line_style=gtk.gdk.LINE_SOLID ) 5152 black_gc = pixmap.new_gc( foreground=black, background=white, 5153 fill=gtk.gdk.SOLID, line_width=1, 5154 line_style=gtk.gdk.LINE_SOLID ) 5155 pixmap.draw_rectangle( white_gc, True, 0, 0, width, height ) 5156 pixmap.draw_arc( black_gc, True, dx, dy, 5157 width - dx * 2, height - dy * 2, 5158 0, 23040 ) 5159 5160 5161 pixbuf = gtk.gdk.Pixbuf( gtk.gdk.COLORSPACE_RGB, True, 8, 5162 width, height ) 5163 pixbuf = pixbuf.get_from_drawable( pixmap, colormap, 0, 0, 0, 0, 5164 width, height ) 5165 pixbuf = pixbuf.add_alpha( True, chr(255), chr(255), chr(255) ) 5166 klass.bullet = pixbuf
5167 # __create_bullets__() 5168 5169
5170 - def __setup_connections__( self ):
5171 hand_cursor = gtk.gdk.Cursor( gtk.gdk.HAND2 ) 5172 regular_cursor = gtk.gdk.Cursor( gtk.gdk.XTERM ) 5173 K_Return = gtk.gdk.keyval_from_name( "Return" ) 5174 K_KP_Enter = gtk.gdk.keyval_from_name( "KP_Enter" ) 5175 K_Home = gtk.gdk.keyval_from_name( "Home" ) 5176 K_End = gtk.gdk.keyval_from_name( "End" ) 5177 5178 if not self.__class__.signal_created: 5179 gobject.signal_new( "follow-link", RichText.Renderer, 5180 gobject.SIGNAL_RUN_LAST, 5181 gobject.TYPE_NONE, 5182 ( gobject.TYPE_STRING, 5183 gobject.TYPE_ULONG ) ) 5184 self.__class__.signal_created = True 5185 5186 5194 # get_link() 5195 5200 # follow_if_link() 5201
5202 - def key_press_event( text_view, event ):
5203 if event.keyval in ( K_Return, K_KP_Enter ): 5204 b = text_view.get_buffer() 5205 itr = b.get_iter_at_mark( b.get_insert() ) 5206 follow_if_link( text_view, itr ) 5207 elif event.keyval == K_Home: 5208 itr = text_view.get_buffer().get_start_iter() 5209 text_view.scroll_to_iter( itr, 0.0, False ) 5210 elif event.keyval == K_End: 5211 itr = text_view.get_buffer().get_end_iter() 5212 text_view.scroll_to_iter( itr, 0.0, False )
5213 # key_press_event() 5214 self.connect( "key-press-event", key_press_event ) 5215
5216 - def event_after( text_view, event ):
5217 if event.type != gtk.gdk.BUTTON_RELEASE: 5218 return False 5219 5220 if event.button != 1: 5221 return False 5222 5223 b = text_view.get_buffer() 5224 5225 # we shouldn't follow a link if the user has selected something 5226 try: 5227 start, end = b.get_selection_bounds() 5228 except ValueError: 5229 pass 5230 else: 5231 if start.get_offset() != end.get_offset(): 5232 return False 5233 5234 x, y = text_view.window_to_buffer_coords( gtk.TEXT_WINDOW_WIDGET, 5235 int( event.x ), 5236 int( event.y ) ) 5237 itr = text_view.get_iter_at_location( x, y ) 5238 follow_if_link( text_view, itr ) 5239 return False
5240 # event_after() 5241 self.connect( "event-after", event_after ) 5242
5243 - def set_cursor_if_appropriate( text_view, x, y ):
5244 b = text_view.get_buffer() 5245 itr = text_view.get_iter_at_location( x, y ) 5246 5247 self.hovering_over_link = get_link( itr ) 5248 if self.hovering_over_link: 5249 cursor = hand_cursor 5250 else: 5251 cursor = regular_cursor 5252 5253 win = text_view.get_window( gtk.TEXT_WINDOW_TEXT ) 5254 win.set_cursor( cursor )
5255 # set_cursor_if_appropriate() 5256
5257 - def motion_notify_event( text_view, event ):
5258 x, y = text_view.window_to_buffer_coords( gtk.TEXT_WINDOW_WIDGET, 5259 int( event.x ), 5260 int( event.y ) ) 5261 set_cursor_if_appropriate( text_view, x, y ) 5262 text_view.window.get_pointer() 5263 return False
5264 # motion_notify_event() 5265 self.connect( "motion-notify-event", motion_notify_event ) 5266
5267 - def visibility_notify_event( text_view, event ):
5268 wx, wy, mod = text_view.window.get_pointer() 5269 x, y = text_view.window_to_buffer_coords( gtk.TEXT_WINDOW_WIDGET, 5270 wx, wy ) 5271 set_cursor_if_appropriate( text_view, x, y ) 5272 return False
5273 # visibility_notify_event() 5274 self.connect( "visibility-notify-event", visibility_notify_event ) 5275 5276
5277 - def after_realize( text_view ):
5278 colormap = self.get_colormap() 5279 if self.background: 5280 bg = colormap.alloc_color( self.background, True, True ) 5281 w = text_view.get_window( gtk.TEXT_WINDOW_TEXT ) 5282 w.set_background( bg ) 5283 w = text_view.get_window( gtk.TEXT_WINDOW_WIDGET ) 5284 w.set_background( bg )
5285 # after_realize() 5286 self.connect_after( "realize", after_realize )
5287 # __setup_connections__() 5288 5289
5290 - def __setup_render__( self ):
5291 self.buffer = self.get_buffer() 5292 itr = self.buffer.get_start_iter() 5293 5294 k = {} 5295 if self.foreground: 5296 k[ "foreground" ] = self.foreground 5297 5298 create_tag = self.buffer.create_tag 5299 self.tags = { 5300 "default": create_tag( "default", **k ), 5301 "bold": create_tag( "bold", weight=pango.WEIGHT_BOLD ), 5302 "italic": create_tag( "italic", style=pango.STYLE_ITALIC ), 5303 "link": create_tag( "link", foreground=self.link_color, 5304 underline=pango.UNDERLINE_SINGLE ), 5305 "h1": create_tag( "h1", scale=pango.SCALE_XX_LARGE ), 5306 "h2": create_tag( "h2", scale=pango.SCALE_X_LARGE ), 5307 "h3": create_tag( "h3", scale=pango.SCALE_LARGE ), 5308 "monospaced": create_tag( "monospaced", font="monospace" ), 5309 } 5310 self.tags[ "default" ].set_priority( 0 ) 5311 5312 self.font = [] 5313 self.link = [] 5314 self.margin = []
5315 # __setup_render__() 5316 5317
5318 - def send_paragraph( self, blankline ):
5319 if blankline: 5320 self.send_flowing_data( "\n" )
5321 # send_paragraph() 5322 5323
5324 - def send_line_break( self ):
5325 self.send_paragraph( 1 )
5326 # send_line_break() 5327 5328
5329 - def send_flowing_data( self, data ):
5330 itr = self.buffer.get_end_iter() 5331 t = [ self.tags[ "default" ] ] + self.font + self.link 5332 self.buffer.insert_with_tags( itr, data, *t )
5333 # send_flowing_data() 5334
5335 - def send_literal_data( self, data ):
5336 itr = self.buffer.get_end_iter() 5337 t = [ self.tags[ "default" ], self.tags[ "monospaced" ] ] + \ 5338 self.font + self.link 5339 self.buffer.insert_with_tags( itr, data, *t )
5340 # send_literal_data() 5341 5342
5343 - def send_hor_rule( self ):
5344 itr = self.buffer.get_end_iter() 5345 anchor = self.buffer.create_child_anchor( itr ) 5346 w = gtk.HSeparator()
5347 - def size_allocate( widget, rect ):
5348 lm = self.get_left_margin() 5349 rm = self.get_right_margin() 5350 width = max( rect.width - lm - rm - 1, 0 ) 5351 w.set_size_request( width, -1 )
5352 # size_allocate() 5353 5354 self.connect_after( "size-allocate", size_allocate ) 5355 self.add_child_at_anchor( w, anchor )
5356 # send_hor_rule() 5357 5358
5359 - def new_margin( self, margin, level ):
5360 itr = self.buffer.get_end_iter() 5361 self.margin.append( ( margin, level ) )
5362 # new_margin() 5363 5364
5365 - def send_label_data( self, data ):
5366 itr = self.buffer.get_end_iter() 5367 t = self.font + self.link + [ self.tags[ "bold" ] ] 5368 5369 margin, level = self.margin[ -1 ] 5370 self.buffer.insert_with_tags( itr, '\t' * level, *t ) 5371 if data == '*' and self.bullet: 5372 self.buffer.insert_pixbuf( itr, self.bullet ) 5373 else: 5374 self.buffer.insert_with_tags( itr, data, *t ) 5375 self.buffer.insert_with_tags( itr, ' ', *t )
5376 # send_label_data() 5377 5378
5379 - def add_image( self, filename, width, height ):
5380 try: 5381 pixbuf = gtk.gdk.pixbuf_new_from_file( filename ) 5382 except gobject.GError: 5383 if self.resource_provider: 5384 pixbuf = self.resource_provider( filename ) 5385 else: 5386 raise ValueError( "No resource provider for %r" % filename) 5387 5388 if not pixbuf: 5389 self.send_flowing_data( "[%s]" % filename ) 5390 return 5391 5392 ow = pixbuf.get_width() 5393 oh = pixbuf.get_height() 5394 p = float( ow ) / float( oh ) 5395 5396 if width > 0 and height < 1: 5397 height = int( width / p ) 5398 elif height > 0 and width < 1: 5399 width = int( height * p ) 5400 if width > 0 and height > 0: 5401 pixbuf = pixbuf.scale_simple( width, height, 5402 gtk.gdk.INTERP_BILINEAR ) 5403 itr = self.buffer.get_end_iter() 5404 self.buffer.insert_pixbuf( itr, pixbuf )
5405 # add_image() 5406 5407 5415 # new_link() 5416 5417 5420 # end_link() 5421 5422
5423 - def new_font( self, font ):
5424 if isinstance( font, ( tuple, list ) ): 5425 def append_unique( v ): 5426 f = self.font 5427 if v not in f: 5428 f.append( v )
5429 # append 5430 5431 size, is_italic, is_bold, is_tt = font 5432 if size == "h1": 5433 append_unique( self.tags[ "h1" ] ) 5434 elif size == "h2": 5435 append_unique( self.tags[ "h2" ] ) 5436 elif size == "h3": 5437 append_unique( self.tags[ "h3" ] ) 5438 5439 if is_italic: 5440 append_unique( self.tags[ "italic" ] ) 5441 if is_bold: 5442 append_unique( self.tags[ "bold" ] ) 5443 5444 elif isinstance( font, dict ): 5445 t = {} 5446 family = font.get( "family" ) 5447 size = font.get( "size" ) 5448 color = font.get( "color" ) 5449 background = font.get( "bgcolor" ) 5450 if family: 5451 t[ "family" ] = family 5452 if size: 5453 t[ "size-points" ] = int( size ) 5454 if color: 5455 t[ "foreground" ] = color 5456 if background: 5457 t[ "background" ] = background 5458 self.font.append( self.buffer.create_tag( None, **t ) ) 5459 else: 5460 self.font = []
5461 # new_font() 5462 5463
5464 - def goto( self, anchor ):
5465 mark = self.buffer.get_mark( anchor ) 5466 if mark is not None: 5467 self.scroll_mark_onscreen( mark ) 5468 else: 5469 raise ValueError( "Inexistent anchor: %r" % anchor )
5470 # goto() 5471 5472
5473 - def reset( self ):
5474 a = self.buffer.get_start_iter() 5475 b = self.buffer.get_end_iter() 5476 self.buffer.delete( a, b )
5477 # reset() 5478 # Renderer 5479 5480
5481 - class Parser( htmllib.HTMLParser ):
5482 """HTML subset parser"""
5483 - def anchor_bgn( self, href, name, type ):
5484 htmllib.HTMLParser.anchor_bgn( self, href, name, type ) 5485 self.formatter.push_link( href, name )
5486 # anchor_bgn() 5487 5488
5489 - def anchor_end( self ):
5490 self.formatter.pop_link()
5491 # anchor_end() 5492 5493
5494 - def handle_image( self, source, alt, ismap, align, width, height ):
5495 self.formatter.add_image( source, width, height )
5496 # handle_image() 5497 5498
5499 - def start_font( self, attrs ):
5500 k = dict( attrs ) 5501 self.formatter.push_font( k )
5502 # start_font() 5503 5504
5505 - def end_font( self ):
5506 self.formatter.pop_font()
5507 # end_font() 5508 # Parser 5509 5510
5511 - class Formatter( formatter.AbstractFormatter ):
5512 """HTML subset formatter"""
5513 - def add_image( self, filename, width, height ):
5514 self.writer.add_image( filename, width, height )
5515 # add_image() 5516 5519 # push_link() 5520 5523 # pop_link() 5524 5525
5526 - def push_font( self, font ):
5527 if isinstance( font, dict ): 5528 self.writer.new_font( font ) 5529 else: 5530 formatter.AbstractFormatter.push_font( self, font )
5531 # push_font() 5532 # Formatter 5533 5534 bgcolor = _gen_ro_property( "bgcolor" ) 5535 fgcolor = _gen_ro_property( "fgcolor" ) 5536 link_color = _gen_ro_property( "link_color" ) 5537 padding = 5 5538
5539 - def __init__( self, id, text="", label=None, link_color="blue", 5540 fgcolor=None, bgcolor=None, callback=None, 5541 img_provider=None, expand_policy=None ):
5542 """RichText constructor. 5543 5544 @param id: unique identifier. 5545 @param text: text to use in this viewer. 5546 @param label: label to display in the widget frame around the viewer. 5547 If None, no label or frame will be shown. 5548 @param link_color: color to use for links. 5549 @param fgcolor: color to use for foreground (text) 5550 @param bgcolor: color to use for background. 5551 @param callback: function (or list of functions) to call when 5552 user clicks a link. Links to anchor will automatically make 5553 the anchor/mark visible and then callback. Function will get 5554 as parameters: 5555 - App reference 5556 - RichText reference 5557 - href contents (string) 5558 - offset from buffer begin (integer) 5559 @param img_provider: if images could not be resolved, call this 5560 function. It should get an address (string) and return an 5561 eagle.Image. Eagle already provides a handle to addresses 5562 prefixed with "eagle://", the following part should be an 5563 eagle.Image id, and the image should be live (not garbage 5564 collected) when displaying it, so remember to keep a 5565 reference to it! You may use img_provider to download 5566 files from webservers and stuff like that. 5567 Function signature: 5568 def img_provider( filename ): 5569 return eagle.Image( ... ) 5570 @param expand_policy: how this widget should fit space, see 5571 L{ExpandPolicy.Policy.Rule}. 5572 """ 5573 _EGWidget.__init__( self, id, expand_policy ) 5574 self.__label = label 5575 self._callback = _callback_tuple( callback ) 5576 self.link_color = link_color 5577 self.foreground = fgcolor 5578 self.background = bgcolor 5579 self.img_provider = img_provider 5580 5581 self.__setup_gui__() 5582 self.__setup_parser__() 5583 self.__setup_connections__() 5584 self.set_text( text )
5585 # __init__() 5586 5587
5588 - def __setup_gui__( self ):
5589 - def img_provider( filename ):
5590 img = None 5591 if filename.startswith( "eagle://" ): 5592 id = filename[ len( "eagle://" ) : ] 5593 img = Image.__get_by_id__( id ) 5594 elif self.img_provider: 5595 img = self.img_provider( filename ) 5596 5597 if img: 5598 return img.__get_gtk_pixbuf__() 5599 else: 5600 error( "Could not find image %r" % filename )
5601 # img_provider() 5602 5603 self._sw = gtk.ScrolledWindow() 5604 self._renderer = RichText.Renderer( link_color=self.link_color, 5605 foreground=self.fgcolor, 5606 background=self.bgcolor, 5607 resource_provider=img_provider ) 5608 5609 self._sw.set_border_width( self.padding ) 5610 self._sw.set_shadow_type( gtk.SHADOW_IN ) 5611 if self.label is not None: 5612 self._frame = gtk.Frame( self.label ) 5613 self._frame.add( self._sw ) 5614 root = self._frame 5615 self._frame.set_shadow_type( gtk.SHADOW_OUT ) 5616 else: 5617 root = self._sw 5618 5619 self._sw.add( self._renderer ) 5620 self._sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC ) 5621 self._sw.show_all() 5622 self._widgets = ( root, )
5623 # __setup_gui__() 5624 5625
5626 - def __setup_parser__( self ):
5627 self._formatter = RichText.Formatter( self._renderer ) 5628 self._parser = RichText.Parser( self._formatter )
5629 # __setup_parser__() 5630 5631
5632 - def __setup_connections__( self ):
5633 - def callback( text_view, href, offset ):
5634 if href.startswith( "#" ): 5635 try: 5636 text_view.goto( href[ 1 : ] ) 5637 except ValueError, e: 5638 error( str( e ) ) 5639 5640 for c in self._callback: 5641 c( self.app, self, href, offset )
5642 # callback() 5643 self._renderer.connect( "follow-link", callback )
5644 # __setup_connections__() 5645 5646
5647 - def set_text( self, text ):
5648 """Replace current text""" 5649 self._text = text 5650 self._renderer.reset() 5651 self.__setup_parser__() 5652 self._parser.feed( self.text )
5653 # set_text() 5654 5655
5656 - def get_text( self ):
5657 """Return current text, with formatting tags""" 5658 return self._text
5659 # get_text() 5660 5661 text = property( get_text, set_text ) 5662 5663
5664 - def append( self, text ):
5665 self._text += text 5666 self._parser.feed( text )
5667 # append() 5668 5669
5670 - def set_label( self, label ):
5671 if self.__label is None: 5672 raise ValueError( "You cannot change label of widget created " 5673 "without one. Create it with placeholder! " 5674 "(label='')" ) 5675 self.__label = label 5676 self._frame.set_label( self.__label )
5677 # set_label() 5678 5679
5680 - def get_label( self ):
5681 return self.__label
5682 # get_label() 5683 5684 label = property( get_label, set_label ) 5685 5686
5687 - def __str__( self ):
5688 return "%s( id=%r, label=%r, link_color=%r, fgcolor=%r, bgcolor=%r )"%\ 5689 ( self.__class__.__name__, self.id, self.label, self.link_color, 5690 self.fgcolor, self.bgcolor )
5691 # __str__() 5692 __repr__ = __str__ 5693 # RichText 5694 5695
5696 -class Button( _EGWidget ):
5697 """A push button. 5698 """ 5699 stock = _gen_ro_property( "stock" ) 5700 callback = _gen_ro_property( "callback" ) 5701 5702 stock_items = ( 5703 "about", 5704 "help", 5705 "quit", 5706 "add", 5707 "remove", 5708 "refresh", 5709 "update", 5710 "yes", 5711 "no", 5712 "zoom_100", 5713 "zoom_in", 5714 "zoom_out", 5715 "zoom_fit", 5716 "undo", 5717 "execute", 5718 "stop", 5719 "open", 5720 "save", 5721 "save_as", 5722 "properties", 5723 "preferences", 5724 "print", 5725 "print_preview", 5726 "ok", 5727 "cancel", 5728 "apply", 5729 "close", 5730 "clear", 5731 "convert", 5732 "next", 5733 "back", 5734 "up", 5735 "down", 5736 "font", 5737 "color", 5738 "media:play", 5739 "media:pause", 5740 "media:stop", 5741 "media:previous", 5742 "media:next", 5743 "media:forward", 5744 "media:rewind", 5745 ) 5746 5747 5748 _gtk_stock_map = { 5749 "about": gtk.STOCK_ABOUT, 5750 "help": gtk.STOCK_HELP, 5751 "quit": gtk.STOCK_QUIT, 5752 "add": gtk.STOCK_ADD, 5753 "remove": gtk.STOCK_REMOVE, 5754 "refresh": gtk.STOCK_REFRESH, 5755 "update": gtk.STOCK_REFRESH, 5756 "yes": gtk.STOCK_YES, 5757 "no": gtk.STOCK_NO, 5758 "zoom_100": gtk.STOCK_ZOOM_100, 5759 "zoom_in": gtk.STOCK_ZOOM_IN, 5760 "zoom_out": gtk.STOCK_ZOOM_OUT, 5761 "zoom_fit": gtk.STOCK_ZOOM_FIT, 5762 "undo": gtk.STOCK_UNDO, 5763 "execute": gtk.STOCK_EXECUTE, 5764 "stop": gtk.STOCK_STOP, 5765 "open": gtk.STOCK_OPEN, 5766 "save": gtk.STOCK_SAVE, 5767 "save_as": gtk.STOCK_SAVE_AS, 5768 "properties": gtk.STOCK_PROPERTIES, 5769 "preferences": gtk.STOCK_PREFERENCES, 5770 "print": gtk.STOCK_PRINT, 5771 "print_preview": gtk.STOCK_PRINT_PREVIEW, 5772 "ok": gtk.STOCK_OK, 5773 "cancel": gtk.STOCK_CANCEL, 5774 "apply": gtk.STOCK_APPLY, 5775 "close": gtk.STOCK_CLOSE, 5776 "clear": gtk.STOCK_CLEAR, 5777 "convert": gtk.STOCK_CONVERT, 5778 "next": gtk.STOCK_GO_FORWARD, 5779 "back": gtk.STOCK_GO_BACK, 5780 "up": gtk.STOCK_GO_UP, 5781 "down": gtk.STOCK_GO_DOWN, 5782 "font": gtk.STOCK_SELECT_FONT, 5783 "color": gtk.STOCK_SELECT_COLOR, 5784 "media:play": gtk.STOCK_MEDIA_PLAY, 5785 "media:pause": gtk.STOCK_MEDIA_PAUSE, 5786 "media:stop": gtk.STOCK_MEDIA_STOP, 5787 "media:previous": gtk.STOCK_MEDIA_PREVIOUS, 5788 "media:next": gtk.STOCK_MEDIA_NEXT, 5789 "media:forward": gtk.STOCK_MEDIA_FORWARD, 5790 "media:rewind": gtk.STOCK_MEDIA_REWIND, 5791 } 5792
5793 - def __init__( self, id, label="", stock=None, callback=None, 5794 expand_policy=None ):
5795 """Push button constructor. 5796 5797 @param label: what text to show, if stock isn't provided. 5798 @param stock: optional. One of L{stock_items}. 5799 @param callback: the function (or list of functions) to call 5800 when button is pressed. Function will get as parameter: 5801 - App reference 5802 - Button reference 5803 @param expand_policy: how this widget should fit space, see 5804 L{ExpandPolicy.Policy.Rule}. 5805 """ 5806 if expand_policy is None: 5807 expand_policy = ExpandPolicy.Fill() 5808 self.label = label 5809 self.stock = stock 5810 self.callback = _callback_tuple( callback ) 5811 5812 # Check if provided stock items are implemented 5813 for i in self.stock_items: 5814 if i not in self._gtk_stock_map: 5815 print >> sys.stderr, \ 5816 "Stock item %s missing in implementation map!" % ( i, ) 5817 5818 _EGWidget.__init__( self, id, expand_policy=expand_policy ) 5819 5820 self.__setup_gui__() 5821 self.__setup_connections__()
5822 # __init__() 5823 5824
5825 - def __setup_gui__( self ):
5826 k = {} 5827 try: 5828 k[ "stock" ] = self._gtk_stock_map[ self.stock ] 5829 except KeyError, e: 5830 k[ "label" ] = self.label or self.stock 5831 5832 self._button = gtk.Button( **k ) 5833 self._button.set_name( self.id ) 5834 self._widgets = ( self._button, )
5835 # __setup_gui__() 5836 5837
5838 - def __setup_connections__( self ):
5839 - def callback( obj ):
5840 for c in self.callback: 5841 c( self.app, self )
5842 # callback() 5843 self._button.connect( "clicked", callback )
5844 # __setup_connections__() 5845 # Button 5846 5847
5848 -class AboutButton( Button, AutoGenId ):
5849 """Push button to show L{AboutDialog} of L{App}."""
5850 - def __init__( self, id=None, expand_policy=None ):
5851 """You may not provide id, it will be generated automatically"""
5852 - def show_about( app_id, wid_id ):
5853 self.app.show_about_dialog()
5854 # show_about() 5855 Button.__init__( self, id or self.__get_id__(), 5856 stock="about", callback=show_about, 5857 expand_policy=expand_policy )
5858 # __init__() 5859 # AboutButton 5860 5861
5862 -class CloseButton( Button, AutoGenId ):
5863 """Push button to close L{App}."""
5864 - def __init__( self, id=None, expand_policy=None ):
5865 """You may not provide id, it will be generated automatically"""
5866 - def close( app_id, wid_id ):
5867 self.app.close()
5868 # close() 5869 Button.__init__( self, id or self.__get_id__(), 5870 stock="close", callback=close, 5871 expand_policy=expand_policy )
5872 # __init__() 5873 # CloseButton 5874 5875
5876 -class QuitButton( Button, AutoGenId ):
5877 """Push button to quit all L{App}s."""
5878 - def __init__( self, id=None, expand_policy=None ):
5879 """You may not provide id, it will be generated automatically"""
5880 - def c( app_id, wid_id ):
5881 quit()
5882 # c() 5883 Button.__init__( self, id or self.__get_id__(), 5884 stock="quit", callback=c, 5885 expand_policy=expand_policy )
5886 # __init__() 5887 # QuitButton 5888 5889
5890 -class HelpButton( Button, AutoGenId ):
5891 """Push button to show L{HelpDialog} of L{App}."""
5892 - def __init__( self, id=None, expand_policy=None ):
5893 """You may not provide id, it will be generated automatically"""
5894 - def c( app_id, wid_id ):
5895 self.app.show_help_dialog()
5896 # c() 5897 Button.__init__( self, id or self.__get_id__(), 5898 stock="help", callback=c, 5899 expand_policy=expand_policy )
5900 # __init__() 5901 # HelpButton 5902 5903
5904 -class OpenFileButton( Button, AutoGenId ):
5905 """Push button to show dialog to choose an existing file."""
5906 - def __init__( self, id=None, 5907 filter=None, multiple=False, 5908 callback=None, expand_policy=None ):
5909 """Constructor. 5910 5911 @param id: may not be provided, it will be generated automatically. 5912 @param filter: filter files to show, see L{FileChooser}. 5913 @param multiple: enable selection of multiple files. 5914 @param callback: function (or list of functions) to call back 5915 when file is selected. Function will get as parameters: 5916 - app reference. 5917 - widget reference. 5918 - file name or file list (if multiple). 5919 @param expand_policy: how this widget should fit space, see 5920 L{ExpandPolicy.Policy.Rule}. 5921 5922 @see: L{FileChooser} 5923 """
5924 - def c( app_id, wid_id ):
5925 f = self.app.file_chooser( FileChooser.ACTION_OPEN, 5926 filter=filter, multiple=multiple ) 5927 if f is not None and callback: 5928 callback( self.app, self, f )
5929 # c() 5930 Button.__init__( self, id or self.__get_id__(), 5931 stock="open", callback=c, 5932 expand_policy=expand_policy )
5933 # __init__() 5934 # OpenFileButton 5935 5936
5937 -class SelectFolderButton( Button, AutoGenId ):
5938 """Push button to show dialog to choose an existing folder/directory."""
5939 - def __init__( self, id=None, callback=None, expand_policy=None ):
5940 """Constructor. 5941 5942 @param id: may not be provided, it will be generated automatically. 5943 @param callback: function (or list of functions) to call back 5944 when file is selected. Function will get as parameters: 5945 - app reference. 5946 - widget reference. 5947 - directory/folder name. 5948 @param expand_policy: how this widget should fit space, see 5949 L{ExpandPolicy.Policy.Rule}. 5950 5951 @see: L{FileChooser} 5952 """
5953 - def c( app_id, wid_id ):
5954 f = self.app.file_chooser( FileChooser.ACTION_SELECT_FOLDER ) 5955 if f is not None and callback: 5956 callback( self.app, self, f )
5957 # c() 5958 Button.__init__( self, id or self.__get_id__(), 5959 stock="open", callback=c, 5960 expand_policy=expand_policy )
5961 # __init__() 5962 # SelectFolderButton 5963 5964
5965 -class SaveFileButton( Button, AutoGenId ):
5966 """Push button to show dialog to choose a file to save."""
5967 - def __init__( self, id=None, filename=None, 5968 filter=None, callback=None, expand_policy=None ):
5969 """Constructor. 5970 5971 @param id: may not be provided, it will be generated automatically. 5972 @param filename: default filename. 5973 @param filter: filter files to show, see L{FileChooser}. 5974 @param callback: function (or list of functions) to call back 5975 when file is selected. Function will get as parameters: 5976 - app reference. 5977 - widget reference. 5978 - file name. 5979 @param expand_policy: how this widget should fit space, see 5980 L{ExpandPolicy.Policy.Rule}. 5981 5982 @see: L{FileChooser} 5983 """
5984 - def c( app_id, wid_id ):
5985 f = self.app.file_chooser( FileChooser.ACTION_SAVE, 5986 filename=filename, 5987 filter=filter ) 5988 if f is not None and callback: 5989 callback( self.app, self, f )
5990 # c() 5991 Button.__init__( self, id or self.__get_id__(), 5992 stock="save", callback=c, 5993 expand_policy=expand_policy )
5994 # __init__() 5995 # SaveFileButton 5996 5997
5998 -class PreferencesButton( Button, AutoGenId ):
5999 """Push button to show L{PreferencesDialog} of L{App}."""
6000 - def __init__( self, id=None, expand_policy=None ):
6001 """You may not provide id, it will be generated automatically"""
6002 - def c( app_id, wid_id ):
6003 f = self.app.show_preferences_dialog()
6004 # c() 6005 Button.__init__( self, id or self.__get_id__(), 6006 stock="preferences", callback=c )
6007 # __init__() 6008 # PreferencesButton 6009 6010
6011 -class HSeparator( _EGWidget, AutoGenId ):
6012 """Horizontal separator"""
6013 - def __init__( self, id=None, expand_policy=None ):
6014 """You may not provide id, it will be generated automatically""" 6015 _EGWidget.__init__( self, id or self.__get_id__(), 6016 expand_policy=expand_policy ) 6017 self._wid = gtk.HSeparator() 6018 self._wid.set_name( self.id ) 6019 self._widgets = ( self._wid, )
6020 # __init__() 6021 # HSeparator 6022 6023
6024 -class VSeparator( _EGWidget ):
6025 """Horizontal separator"""
6026 - def __init__( self, id=None, expand_policy=None ):
6027 """You may not provide id, it will be generated automatically""" 6028 _EGWidget.__init__( self, id or self.__get_id__(), 6029 expand_policy=expand_policy ) 6030 self._wid = gtk.VSeparator() 6031 self._wid.set_name( self.id ) 6032 self._widgets = ( self._wid, )
6033 # __init__() 6034 # VSeparator 6035 6036
6037 -class Label( _EGDataWidget, AutoGenId ):
6038 """Text label""" 6039 label = _gen_ro_property( "label" ) 6040 6041 LEFT = 0.0 6042 RIGHT = 1.0 6043 CENTER = 0.5 6044 TOP = 0.0 6045 MIDDLE = 0.5 6046 BOTTOM = 1.0 6047
6048 - def __init__( self, id=None, label="", 6049 halignment=LEFT, valignment=MIDDLE, expand_policy=None ):
6050 """Label constructor. 6051 6052 @param id: may not be provided, it will be generated automatically. 6053 @param label: what this label will show. 6054 @param halignment: horizontal alignment, like L{LEFT}, L{RIGHT} or 6055 L{CENTER}. 6056 @param valignment: vertical alignment, like L{TOP}, L{BOTTOM} or 6057 L{MIDDLE}. 6058 @param expand_policy: how this widget should fit space, see 6059 L{ExpandPolicy.Policy.Rule}. 6060 """ 6061 if expand_policy is None: 6062 expand_policy = ExpandPolicy.Nothing() 6063 6064 _EGDataWidget.__init__( self, id or self.__get_id__(), False, 6065 expand_policy=expand_policy ) 6066 self.label = label 6067 6068 self._wid = gtk.Label( self.label ) 6069 self._wid.set_name( self.id ) 6070 self._wid.set_alignment( xalign=halignment, yalign=valignment ) 6071 self._widgets = ( self._wid, )
6072 # __init__() 6073 6074
6075 - def get_value( self ):
6076 return self._wid.get_text()
6077 # get_value() 6078 6079
6080 - def set_value( self, value ):
6081 self._wid.set_text( str( value ) )
6082 # set_value() 6083 6084
6085 - def __str__( self ):
6086 return "%s( id=%r, label=%r )" % \ 6087 ( self.__class__.__name__, self.id, self.label )
6088 # __str__() 6089 # Label 6090 6091
6092 -def information( message ):
6093 """Show info message to user.""" 6094 6095 d = gtk.MessageDialog( type=gtk.MESSAGE_INFO, 6096 message_format=message, 6097 buttons=gtk.BUTTONS_CLOSE ) 6098 d.run() 6099 d.destroy() 6100 return
6101 # information() 6102 info = information 6103 6104
6105 -def warning( message ):
6106 """Show warning message to user.""" 6107 6108 d = gtk.MessageDialog( type=gtk.MESSAGE_WARNING, 6109 message_format=message, 6110 buttons=gtk.BUTTONS_CLOSE ) 6111 d.run() 6112 d.destroy() 6113 return
6114 # warning() 6115 warn = warning 6116 6117
6118 -def error( message ):
6119 """Show error message to user.""" 6120 6121 d = gtk.MessageDialog( type=gtk.MESSAGE_ERROR, 6122 message_format=message, 6123 buttons=gtk.BUTTONS_CLOSE ) 6124 d.run() 6125 d.destroy() 6126 return
6127 # error() 6128 err = error 6129 6130
6131 -def yesno( message, yesdefault=False ):
6132 """Show yes/no message to user. 6133 6134 @param yesdefault: if yes should be the default action. 6135 """ 6136 6137 d = gtk.MessageDialog( type=gtk.MESSAGE_QUESTION, 6138 message_format=message, 6139 buttons=gtk.BUTTONS_YES_NO ) 6140 if yesdefault: 6141 d.set_default_response( gtk.RESPONSE_YES ) 6142 else: 6143 d.set_default_response( gtk.RESPONSE_NO ) 6144 6145 r = d.run() 6146 d.destroy() 6147 6148 if r == gtk.RESPONSE_YES: 6149 return True 6150 elif r == gtk.RESPONSE_NO: 6151 return False 6152 else: 6153 return yesdefault
6154 # yesno() 6155 6156 6157
6158 -def confirm( message, okdefault=False ):
6159 """Show confirm message to user. 6160 6161 @param okdefault: if ok should be the default action. 6162 """ 6163 6164 d = gtk.MessageDialog( type=gtk.MESSAGE_QUESTION, 6165 message_format=message, 6166 buttons=gtk.BUTTONS_OK_CANCEL ) 6167 if okdefault: 6168 d.set_default_response( gtk.RESPONSE_OK ) 6169 else: 6170 d.set_default_response( gtk.RESPONSE_CANCEL ) 6171 6172 r = d.run() 6173 d.destroy() 6174 6175 if r == gtk.RESPONSE_OK: 6176 return True 6177 elif r == gtk.RESPONSE_CANCEL: 6178 return False 6179 else: 6180 return okdefault
6181 # confirm() 6182 6183 6184
6185 -def run():
6186 """Enter the event loop""" 6187 try: 6188 gtk.main() 6189 except KeyboardInterrupt: 6190 raise SystemExit( "User quit using Control-C" )
6191 # run() 6192
6193 -def quit():
6194 """Quit the event loop""" 6195 gtk.main_quit()
6196 # quit() 6197 6198
6199 -def get_app_by_id( app_id ):
6200 """Given an App unique identifier, return the reference to it.""" 6201 if app_id is None: 6202 try: 6203 return _apps.values()[ 0 ] 6204 except IndexError, e: 6205 raise ValueError( "No application defined!" ) 6206 elif isinstance( app_id, ( str, unicode ) ): 6207 try: 6208 return _apps[ app_id ] 6209 except KeyError, e: 6210 raise ValueError( "Application id \"%s\" doesn't exists!" % \ 6211 app_id ) 6212 elif isinstance( app_id, App ): 6213 return app_id 6214 else: 6215 raise ValueError( "app_id must be string or App instance!" )
6216 # get_app_by_id() 6217 6218
6219 -def get_widget_by_id( widget_id, app_id=None ):
6220 """Given an Widget unique identifier, return the reference to it. 6221 6222 If app_id is not provided, will use the first App found. 6223 6224 @attention: Try to always provide app_id since it may lead to problems if 6225 your program have more than one App. 6226 """ 6227 app = get_app_by_id( app_id ) 6228 6229 if app: 6230 w = app.get_widget_by_id( widget_id ) 6231 if not w: 6232 raise ValueError( "Widget id \"%s\" doesn't exists!" % widget_id ) 6233 else: 6234 return w
6235 # get_widget_by_id() 6236 6237
6238 -def get_value( widget_id, app_id=None ):
6239 """Convenience function to get widget and call its get_value() method.""" 6240 try: 6241 wid = get_widget_by_id( widget_id, app_id ) 6242 return wid.get_value() 6243 except ValueError, e: 6244 raise ValueError( e )
6245 # get_value() 6246 6247
6248 -def set_value( widget_id, value, app_id=None ):
6249 """Convenience function to get widget and call its set_value() method.""" 6250 try: 6251 wid = get_widget_by_id( widget_id, app_id ) 6252 wid.set_value( value ) 6253 except ValueError, e: 6254 raise ValueError( e )
6255 # set_value() 6256 6257
6258 -def show( widget_id, app_id=None ):
6259 """Convenience function to get widget and call its show() method.""" 6260 try: 6261 wid = get_widget_by_id( widget_id, app_id ) 6262 wid.show() 6263 except ValueError, e: 6264 raise ValueError( e )
6265 # show() 6266 6267
6268 -def hide( widget_id, app_id=None ):
6269 """Convenience function to get widget and call its hide() method.""" 6270 try: 6271 wid = get_widget_by_id( widget_id, app_id ) 6272 wid.hide() 6273 except ValueError, e: 6274 raise ValueError( e )
6275 # hide() 6276 6277
6278 -def set_active( widget_id, active=True, app_id=None ):
6279 """Convenience function to get widget and call its set_active() method.""" 6280 try: 6281 wid = get_widget_by_id( widget_id, app_id ) 6282 wid.set_active( active ) 6283 except ValueError, e: 6284 raise ValueError( e )
6285 # set_active() 6286 6287
6288 -def set_inactive( widget_id, app_id=None ):
6289 """ 6290 Convenience function to get widget and call its set_inactive() method. 6291 """ 6292 try: 6293 wid = get_widget_by_id( widget_id, app_id ) 6294 wid.set_inactive() 6295 except ValueError, e: 6296 raise ValueError( e )
6297 # set_inactive() 6298 6299
6300 -def close( app_id=None ):
6301 """Convenience function to get app and call its close() method.""" 6302 try: 6303 app = get_app_by_id( app_id ) 6304 app.close() 6305 except ValueError, e: 6306 raise ValueError( e )
6307 # close() 6308