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