1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()
128
129 _apps = {}
130
131 _prg = hildon.Program()
132
133
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, )
142 try:
143 return getattr( self, naming )
144 except AttributeError:
145 return None
146
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
157 return property( get, set, None, doc )
158
159
160
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
175
176
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
186
187
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
197
198
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
215
216
218 __slots__ = ( "fill", "expand" )
219 - def __init__( self, expand=False, fill=True ):
220 self.expand = expand
221 self.fill = fill
222
223
224
226 p = 0
227 if self.expand:
228 p |= gtk.EXPAND
229 if self.fill:
230 p |= gtk.FILL
231 return p
232
233
234
236 return "ExpandRule( expand=%s, fill=%s )" % \
237 ( self.expand, self.fill )
238
239 __repr__ = __str__
240
241
244 Rule = _ExpandRule
245 __slots__ = ( "horizontal", "vertical" )
246
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
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
269
270
272 return "%s( horizontal=%s, vertical=%s )" % \
273 ( self.__class__.__name__, self.horizontal, self.vertical )
274
275 __repr__ = __str__
276
277
278
279 - class All( Policy ):
283
284
285
290
291
292
297
298
299
304
305
306
311
312
313
318
319
320
321 - class Fill( Policy ):
325
326
327
328
329
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 ):
352
353
354
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
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
440
441
444
445
446
447
448 -class _Panel( gtk.ScrolledWindow ):
493
494
495
496
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
507
508
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
519
520
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
533
534
535
537 return "%s( id=%r )" % ( self.__class__.__name__, self.id )
538
539 __repr__ = __str__
540
541
542
544 """Mix-In to auto-generate ids.
545
546 @warning: never use it directly in Eagle applications!
547 """
548 last_id_num = 0
549
554
555 __get_id__ = classmethod( __get_id__ )
556
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
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
613
614
616 return self._img
617
618
619
622
623 __get_by_id__ = classmethod( __get_by_id__ )
624
625
627 gc.collect()
628
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
665
666
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
680
681
692
693
694
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
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
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
763
764
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
780
783
784
785
788
789
790
794
795
796
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
809
810
814
815
816
818 """Bits per pixel"""
819 return self.get_n_channels() * self._img.get_bits_per_sample()
820
821 get_depth = get_bits_per_pixel
822
823
825 """If it has an alpha channel"""
826 return self._img.get_has_alpha()
827
828
829
830
831
893
894
895
896
926
927
928
929
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 ):
954
955
956
958 self._diag.destroy()
959
960
961
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
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
1030
1031
1033 self._diag.show_all()
1034 self._diag.run()
1035 self._diag.hide()
1036
1037
1038
1039
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 ):
1056
1057
1058
1060 self._diag.destroy()
1061
1062
1063
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
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
1097
1098
1100 self._diag.show_all()
1101 self._diag.run()
1102 self._diag.hide()
1103
1104
1105
1106
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
1142
1143
1152
1153
1154
1156 self._diag.destroy()
1157
1158
1159
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
1177
1178
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
1188
1189
1190
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 """
1204
1205
1206
1208 self._diag.destroy()
1209
1210
1211
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
1240
1241
1247
1248
1249
1251 self._diag.show_all()
1252 self._diag.run()
1253 self._diag.hide()
1254
1255
1256
1257
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
1265 border = 3
1266 spacing = 6
1267 width = 600
1268 height = 400
1269 margin = 3
1270
1274
1275
1276
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
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
1349
1350
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
1370
1371
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
1392
1393
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
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
1420
1421
1435
1436
1438 import pdb
1439 pdb.pm()
1440
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
1449
1450
1460
1461 except_hook = staticmethod( except_hook )
1462
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
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
1505
1506
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
1516
1517
1518 - def get_value( self ):
1519 return self._entry.get_value()
1520
1521
1522
1523 - def set_value( self, value ):
1524 self._entry.set_value( value )
1525
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
1536
1537
1538 - def get_label( self ):
1539 return self.__label
1540
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
1550 __repr__ = __str__
1551
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
1689
1690
1697
1698
1699
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
1711
1712
1720
1721
1722
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
1736
1737
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
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
1762
1763
1765 """Show L{PreferencesDialog} associated with this App."""
1766 return self._preferences.run()
1767
1768
1769
1771 return self._win
1772
1773
1774
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
1784
1785
1807
1808
1809
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
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
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
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
1875
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
1904
1905
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
1933
1934
1937
1938
1939
1942
1943
1944
1947
1948
1949
1952
1953
1954
1957
1958
1959
1963
1964
1965
1967 self._win.connect( "delete_event", self.__delete_event__ )
1968
1969
1971 self._win_in_fullscreen = bool( event.new_window_state &
1972 gtk.gdk.WINDOW_STATE_FULLSCREEN )
1973
1974 self._win.connect( "window-state-event", on_window_state_event )
1975
1976
1978 if event.keyval == gtk.keysyms.F6:
1979 if self._win_in_fullscreen:
1980 self._win.unfullscreen()
1981 else:
1982 self._win.fullscreen()
1983
1984 self._win.connect( "key-press-event", on_key_press_event );
1985
1986
1987
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
1997
1998
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
2012
2013
2015 if self.__do_close__():
2016 return False
2017 else:
2018 return True
2019
2020
2021
2024 if not os.path.exists( d ):
2025 mkdir( os.path.dirname( d ) )
2026 os.mkdir( d )
2027
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
2038
2039
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
2055
2056
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
2078
2079
2081 """Close application window."""
2082 if self.__do_close__():
2083 self._win.destroy()
2084
2085
2086
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
2099
2100
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
2112
2113
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 ):
2126
2127 return gobject.timeout_add( interval, wrap )
2128
2129
2130
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 ):
2144
2145 return gobject.idle_add( wrap )
2146
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
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
2194
2195
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
2207
2208
2209
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
2278
2279
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
2290
2291
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
2322
2323
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
2329
2330
2338
2339 self._area.connect( "configure_event", configure_event )
2340
2341
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
2349 self._area.connect( "expose_event", expose_event )
2350
2351
2365
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
2386
2387 if self._callback:
2388 self._area.connect( "button_press_event", button_press_event )
2389
2390
2402
2403 if self._callback:
2404 self._area.connect( "button_release_event", button_release_event )
2405
2406
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
2429 if self._callback:
2430 self._area.connect( "motion_notify_event", motion_notify_event )
2431
2432
2433
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
2441
2442
2443
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
2476 __color_from__ = staticmethod( __color_from__ )
2477
2478
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
2485 __to_gtk_color__ = staticmethod( __to_gtk_color__ )
2486
2487
2510
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
2519 self.clear()
2520 else:
2521
2522 w, h = old.get_size()
2523 self._pixmap.draw_drawable( self._fg_gc_normal, old,
2524 0, 0, 0, 0, w, h )
2525
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
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
2639
2640
2641
2647
2648
2649
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
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
2674
2675
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
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
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
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
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
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
2787
2788
2790 """Clear using bgcolor."""
2791 self.fill( self.bgcolor )
2792
2793
2794
2795 - def fill( self, color ):
2799
2800
2801
2804
2805
2806
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
2815
2816
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
2825
2826
2828 return self.__label
2829
2830
2831 label = property( get_label, set_label )
2832
2833
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
2839 __repr__ = __str__
2840
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
2859
2860
2861 - def __emit_changed__( self, *args ):
2862 self.emit( "changed" )
2863
2864
2865
2866 - def set_text( self, value ):
2867 self.textview.get_buffer().set_text( value )
2868
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
2875
2876
2877 - def set_editable( self, setting ):
2878 self.textview.set_editable( setting )
2879
2880
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
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
2953
2954
2960
2961
2962
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
2970 self._entry.connect( "changed", callback )
2971
2972
2973
2974 - def get_value( self ):
2975 return self._entry.get_text()
2976
2977
2978
2979 - def set_value( self, value ):
2980 self._entry.set_text( str( value ) )
2981
2982
2983
2984 - def set_editable( self, value ):
2985 self._editable = bool( value )
2986 self._entry.set_editable( self._editable )
2987
2988
2989
2990 - def get_editable( self ):
2991 return self._editable
2992
2993
2994 editable = property( get_editable, set_editable )
2995
2996
2997
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
3025
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
3081
3082
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
3114
3115
3122
3123 self._entry.connect( "notify::value", callback )
3124
3125
3126
3129
3130
3131
3132
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
3176
3177
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
3193 if self.value:
3194 self._entry.set_value( self.value )
3195
3196 _EGWidLabelEntry.__setup_gui__( self )
3197
3198
3199
3206
3207 self._entry.connect( "notify::value", callback )
3208
3209
3210
3213
3214
3215
3218
3219
3220
3221
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
3257
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
3296
3297
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
3321 color_from = staticmethod( color_from )
3322
3323
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
3336
3337
3344
3345 self._entry.connect( "notify::color", callback )
3346
3347
3348
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
3360
3361
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
3377
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
3414
3415
3421
3422
3423
3430
3431 self._entry.connect( "font-set", callback )
3432
3433
3434
3436 return self._entry.get_font_name()
3437
3438
3439
3441 self._entry.set_font_name( value )
3442
3443
3444
3445
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
3482
3483
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
3494
3495
3502
3503 self._entry.connect( "changed", callback )
3504
3505
3506
3508 return self._entry.get_active_text()
3509
3510
3511
3513 for i, o in enumerate( self._entry.get_model() ):
3514 if o[ 0 ] == value:
3515 self._entry.set_active( i )
3516
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
3533
3534
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
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
3565
3566
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
3581
3582
3584 """Returns every item/option in this selection."""
3585 return [ str( x[ 0 ] ) for x in self._entry.get_model() ]
3586
3587 options = items
3588
3589
3591 return len( self._entry.get_model() )
3592
3593
3594
3597
3598
3599
3603
3604
3605
3609
3610
3611
3612
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
3632
3638
3639
3640
3642 return self._entry.get_fraction()
3643
3644
3645
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
3655
3656
3658 """Animate progress bar."""
3659 self._entry.pulse()
3660
3661
3662
3663
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
3703
3704
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
3711
3712
3719
3720 self._wid.connect( "toggled", callback )
3721
3722
3723
3725 return self._wid.get_active()
3726
3727
3728
3731
3732
3733
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
3742
3743
3745 return self.__label
3746
3747
3748 label = property( get_label, set_label )
3749
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
3770 try:
3771 return self.__ro_app
3772 except AttributeError:
3773 return None
3774
3775
3777
3778
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
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
3820
3821
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
3838
3839
3843
3844
3845
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
3855
3856
3858 return self.__label
3859
3860
3861 label = property( get_label, set_label )
3862
3863
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
3876
3877
3879 return self.__border
3880
3881
3882 border = property( get_border, set_border )
3883
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
3930
3931
3932 - def _get_app( self ):
3933 try:
3934 return self.__ro_app
3935 except AttributeError:
3936 return None
3937
3938
3939
3940 - def _set_app( self, value ):
3941
3942
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
3953
3954 app = property( _get_app, _set_app )
3955
3956
3960
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
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
3980
3981
3982 - def get_label( self ):
3983 return self.__label
3984
3985
3986 label = property( get_label, set_label )
3987
3988
3989 - def focus( self ):
3991
3992
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
4025
4026
4034
4035
4036
4038 try:
4039 return self.__ro_app
4040 except AttributeError:
4041 return None
4042
4043
4044
4046
4047
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
4058
4059 app = property( _get_app, _set_app )
4060
4061
4065
4066
4067
4068 - def __set_page_label__( self, page, value ):
4069 self._wid.set_tab_label( page._widgets[ 0 ], gtk.Label( value ) )
4070
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
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
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
4111
4112
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
4121
4122
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
4133
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
4155 self.__items = items
4156
4157
4158
4160 return "[" + ", ".join( [ str( x ) for x in self.__items ] ) + "]"
4161
4162 __repr__ = __str__
4163
4164
4166 return len( self.__items )
4167
4168
4169
4172
4173
4174
4176 return self.__items[ index ]
4177
4178
4179
4181 self.__items[ index ] = value
4182
4183
4184
4186 del self.__items[ index ]
4187
4188
4189
4191 return element in self.__items
4192
4193
4194
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 )
4206
4207 for i in xrange( start, end ):
4208 slice.append( self.__items[ i ] )
4209 return slice
4210
4211
4212
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 )
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
4244
4245
4246
4254
4255
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
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
4362
4363
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
4406
4407
4420
4421
4422
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
4430
4431
4433 index = path[ 0 ]
4434 v = ( index, None )
4435 for c in self.data_changed_callback:
4436 c( self.app, self, v )
4437
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
4444
4445
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
4520
4521 sw.set_policy( hscrollbar_policy=gtk.POLICY_NEVER,
4522 vscrollbar_policy=gtk.POLICY_NEVER )
4523 d.show_all()
4524
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
4556
4557
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
4584
4585
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
4597
4598
4606
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
4613 data = treeview.get_model()[ path ]
4614 result = edit_dialog( data )
4615 if result:
4616 self[ path[ 0 ] ] = result
4617
4618
4619
4620 self._table.connect( "row-activated", row_activated )
4621
4622
4623
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
4641
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
4655
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
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
4675
4676
4679 result = self.selected()
4680 for c in self.selection_callback:
4681 c( self.app, self, result )
4682
4683
4684 selection = self._table.get_selection()
4685 selection.connect( "changed", selection_changed )
4686
4687
4688
4800
4801
4802
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
4811 cid, order = self._model.get_sort_column_id()
4812 self._model.set_sort_column_id( cid, order )
4813
4814
4815 - def toggled( cell_render, path, col ):
4816 self._model[ path ][ col ] = not self._model[ path ][ col ]
4817
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
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
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
4913
4914
4920
4921
4922
4923
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
4945
4946 for i in xrange( len( self.types ) ):
4947 self._model.set_sort_func( i, sort_fn, i )
4948
4949
4950
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
4959
4960
4962 return self.__label
4963
4964
4965 label = property( get_label, set_label )
4966
4967
4970
4971
4972
4974 selection = self._table.get_selection()
4975 selection.unselect_all()
4976 selection.select_path( index )
4977
4978
4979
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
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
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
5036
5037
5040
5041
5043 return len( self._model )
5044
5045
5046
5048 self.append( other )
5049 return self
5050
5051
5052
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
5073
5074
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
5083
5084
5086 try:
5087 del self._model[ index ]
5088 except TypeError, e:
5089 raise IndexError( "index out of range" )
5090
5091
5092
5094 for r in self._model:
5095 if row in r:
5096 return True
5097 return False
5098
5099
5100
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 )
5112
5113 for i in xrange( start, end ):
5114 slice.append( Table.Row( self._model[ i ] ) )
5115 return slice
5116
5117
5118
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
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
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
5146
5147
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 )
5157
5158 while end > start:
5159 end -= 1
5160 del self._model[ end ]