~ubuntu-branches/ubuntu/utopic/python-traitsui/utopic

« back to all changes in this revision

Viewing changes to traitsui/wx/image_panel.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-09 13:57:39 UTC
  • Revision ID: james.westby@ubuntu.com-20110709135739-x5u20q86huissmn1
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#-------------------------------------------------------------------------------
 
2
#
 
3
#  Copyright (c) 2007, Enthought, Inc.
 
4
#  All rights reserved.
 
5
#
 
6
#  This software is provided without warranty under the terms of the BSD
 
7
#  license included in enthought/LICENSE.txt and may be redistributed only
 
8
#  under the conditions described in the aforementioned license.  The license
 
9
#  is also available online at http://www.enthought.com/licenses/BSD.txt
 
10
#
 
11
#  Thanks for using Enthought open source!
 
12
#
 
13
#  Author: David C. Morrill
 
14
#  Date:   08/11/2007
 
15
#
 
16
#-------------------------------------------------------------------------------
 
17
 
 
18
""" Defines a themed panel that wraps itself around a single child widget.
 
19
"""
 
20
 
 
21
#-------------------------------------------------------------------------------
 
22
#  Imports:
 
23
#-------------------------------------------------------------------------------
 
24
 
 
25
import wx
 
26
 
 
27
from traits.api \
 
28
    import Str, Property, Instance, Bool, cached_property
 
29
 
 
30
from themed_window \
 
31
    import ThemedWindow
 
32
 
 
33
#-------------------------------------------------------------------------------
 
34
#  Constants:
 
35
#-------------------------------------------------------------------------------
 
36
 
 
37
# Size of an empty text string:
 
38
ZeroTextSize = ( 0, 0, 0, 0 )
 
39
 
 
40
#-------------------------------------------------------------------------------
 
41
#  'ImagePanel' class:
 
42
#-------------------------------------------------------------------------------
 
43
 
 
44
class ImagePanel ( ThemedWindow ):
 
45
 
 
46
    # The optional text to display in the top or bottom of the image slice:
 
47
    text = Str( event = 'updated' )
 
48
 
 
49
    # Can the application change the theme contents?
 
50
    mutable_theme = Bool( False )
 
51
 
 
52
    # Is the image panel capable of displaying text?
 
53
    can_show_text = Property
 
54
 
 
55
    # The adjusted size of the panel, taking into account the size of its
 
56
    # current children and the image border:
 
57
    adjusted_size = Property
 
58
 
 
59
    # The best size of the panel, taking into account the best size of its
 
60
    # children and the image border:
 
61
    best_size = Property
 
62
 
 
63
    # The underlying wx control:
 
64
    control = Instance( wx.Window )
 
65
 
 
66
    #-- Private Traits ---------------------------------------------------------
 
67
 
 
68
    # The size of the current text:
 
69
    text_size = Property( depends_on = 'text, control' )
 
70
 
 
71
    #-- Public Methods ---------------------------------------------------------
 
72
 
 
73
    def create_control ( self, parent ):
 
74
        """ Creates the underlying wx.Panel control.
 
75
        """
 
76
        self.control = control = wx.Panel( parent, -1,
 
77
                          style = wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE )
 
78
 
 
79
        # Set up the sizer for the control:
 
80
        control.SetSizer( ImageSizer( self.theme ) )
 
81
 
 
82
        # Initialize the control (set-up event handlers, ...)
 
83
        self.init_control()
 
84
 
 
85
        # Attach the image slice to the control:
 
86
        control._image_slice = self.theme.image_slice
 
87
 
 
88
        # Set the panel's background colour to the image slice bg_color:
 
89
        control.SetBackgroundColour( control._image_slice.bg_color )
 
90
 
 
91
        return control
 
92
 
 
93
    def layout ( self ):
 
94
        """ Lays out the contents of the image panel.
 
95
        """
 
96
        self.control.Layout()
 
97
        self.control.Refresh()
 
98
 
 
99
    #-- Property Implementations -----------------------------------------------
 
100
 
 
101
    def _get_adjusted_size ( self ):
 
102
        """ Returns the adjusted size of the panel taking into account the
 
103
            size of its current children and the image border.
 
104
        """
 
105
        control = self.control
 
106
        dx, dy  = 0, 0
 
107
        for child in control.GetChildren():
 
108
            dx, dy = child.GetSizeTuple()
 
109
 
 
110
        size = self._adjusted_size_of( dx, dy )
 
111
        control.SetSize( size )
 
112
 
 
113
        return size
 
114
 
 
115
    def _get_best_size ( self ):
 
116
        """ Returns the best size of the panel taking into account the
 
117
            best size of its current children and the image border.
 
118
        """
 
119
        control = self.control
 
120
        dx, dy  = 0, 0
 
121
        for child in control.GetChildren():
 
122
            dx, dy = child.GetBestSize()
 
123
 
 
124
        return self._adjusted_size_of( dx, dy )
 
125
 
 
126
    @cached_property
 
127
    def _get_can_show_text ( self ):
 
128
        """ Returns whether or not the image panel is capable of displaying
 
129
            text.
 
130
        """
 
131
        tdx, tdy, descent, leading = self.control.GetFullTextExtent( 'Myj' )
 
132
        slice = self.theme.image_slice
 
133
        tdy  += 4
 
134
        return ((tdy <= slice.xtop) or (tdy <= slice.xbottom) or
 
135
                (slice.xleft >= 40) or (slice.xright >= 40))
 
136
 
 
137
    @cached_property
 
138
    def _get_text_size ( self ):
 
139
        """ Returns the text size information for the window.
 
140
        """
 
141
        if (self.text == '') or (self.control is None):
 
142
            return ZeroTextSize
 
143
 
 
144
        return self.control.GetFullTextExtent( self.text )
 
145
 
 
146
    #-- Trait Event Handlers ---------------------------------------------------
 
147
 
 
148
    def _updated_changed ( self ):
 
149
        """ Handles a change that requires the control to be updated.
 
150
        """
 
151
        if self.control is not None:
 
152
            self.control.Refresh()
 
153
 
 
154
    def _mutable_theme_changed ( self, state ):
 
155
        """ Handles a change to the 'mutable_theme' trait.
 
156
        """
 
157
        self.on_trait_change( self._theme_modified,
 
158
            'theme.[border.-,content.-,label.-,alignment,content_color,'
 
159
            'label_color]', remove = not state )
 
160
 
 
161
    def _theme_modified ( self ):
 
162
        if self.control is not None:
 
163
            self.layout()
 
164
 
 
165
    def _theme_changed ( self, theme ):
 
166
        """ Handles the 'theme' trait being changed.
 
167
        """
 
168
        super( ImagePanel, self )._theme_changed()
 
169
 
 
170
        control = self.control
 
171
        if (control is not None) and (theme is not None):
 
172
            # Attach the image slice to the control:
 
173
            control._image_slice = theme.image_slice
 
174
 
 
175
            # Set the panel's background colour to the image slice bg_color:
 
176
            control.SetBackgroundColour( control._image_slice.bg_color )
 
177
 
 
178
    #-- wx.Python Event Handlers -----------------------------------------------
 
179
 
 
180
    def _paint_fg ( self, dc ):
 
181
        """ Paints the foreground into the specified device context.
 
182
        """
 
183
        # If we have text and have room to draw it, then do so:
 
184
        text = self.text
 
185
        if (text != '') and self.can_show_text:
 
186
            theme = self.theme
 
187
            dc.SetBackgroundMode( wx.TRANSPARENT )
 
188
            dc.SetTextForeground( theme.label_color )
 
189
            dc.SetFont( self.control.GetFont() )
 
190
 
 
191
            alignment = theme.alignment
 
192
            label     = theme.label
 
193
            wdx, wdy  = self.control.GetClientSizeTuple()
 
194
            tdx, tdy, descent, leading = self.text_size
 
195
            tx      = None
 
196
            slice   = theme.image_slice
 
197
            xleft   = slice.xleft
 
198
            xright  = slice.xright
 
199
            xtop    = slice.xtop
 
200
            xbottom = slice.xbottom
 
201
            ltop    = label.top
 
202
            lbottom = label.bottom
 
203
            tdyp    = tdy + ltop + lbottom
 
204
            cl      = xleft + label.left
 
205
            cr      = wdx - xright - label.right
 
206
            if (tdyp <= xtop) and (xtop >= xbottom):
 
207
                ty = ((ltop + xtop - lbottom - tdy) / 2) + 1
 
208
            elif tdy <= xbottom:
 
209
                ty = wdy + ((ltop - xbottom - lbottom - tdy) / 2)
 
210
            else:
 
211
                ty = (wdy + xtop + label.top - xbottom - label.bottom - tdy) / 2
 
212
                if xleft >= xright:
 
213
                    cl = label.left
 
214
                    cr = xleft - label.right
 
215
                else:
 
216
                    cl = wdx - xright + label.left
 
217
                    cr = wdx - label.right
 
218
 
 
219
            # Calculate the x coordinate for the specified alignment type:
 
220
            if alignment == 'left':
 
221
                tx = cl
 
222
            elif alignment == 'right':
 
223
                tx = cr - tdx
 
224
            else:
 
225
                tx = (cl + cr - tdx) / 2
 
226
 
 
227
            # Draw the (clipped) text string:
 
228
            dc.SetClippingRegion( cl, ty, cr - cl, tdy )
 
229
            dc.DrawText( text, tx, ty )
 
230
            dc.DestroyClippingRegion()
 
231
 
 
232
    #-- Private Methods --------------------------------------------------------
 
233
 
 
234
    def _adjusted_size_of ( self, dx, dy ):
 
235
        """ Returns the adjusted size of its children, taking into account the
 
236
            image slice border.
 
237
        """
 
238
        slice   = self.theme.image_slice
 
239
        content = self.theme.content
 
240
        sizer   = self.control.GetSizer()
 
241
        return wx.Size( dx + min( slice.left, slice.xleft )   +
 
242
                             min( slice.right, slice.xright ) +
 
243
                             content.left + content.right,
 
244
                        dy + min( slice.top, slice.xtop )       +
 
245
                             min( slice.bottom, slice.xbottom ) +
 
246
                             content.top + content.bottom )
 
247
 
 
248
#-------------------------------------------------------------------------------
 
249
#  'ImageSizer' class:
 
250
#-------------------------------------------------------------------------------
 
251
 
 
252
class ImageSizer ( wx.PySizer ):
 
253
    """ Defines a sizer that correctly sizes a window's children to fit within
 
254
        the borders implicitly defined by a background ImageSlice object,
 
255
    """
 
256
 
 
257
    #---------------------------------------------------------------------------
 
258
    #  Initializes the object:
 
259
    #---------------------------------------------------------------------------
 
260
 
 
261
    def __init__ ( self, theme ):
 
262
        """ Initializes the object.
 
263
        """
 
264
        super( ImageSizer, self ).__init__()
 
265
 
 
266
        # Save a reference to the theme:
 
267
        self._theme = theme
 
268
 
 
269
        # Save the ImageSlice object which determines the inset border size:
 
270
        self._image_slice = theme.image_slice
 
271
 
 
272
    #---------------------------------------------------------------------------
 
273
    #  Calculates the minimum size needed by the sizer:
 
274
    #---------------------------------------------------------------------------
 
275
 
 
276
    def CalcMin ( self ):
 
277
        """ Calculates the minimum size of the control by adding its contents
 
278
            minimum size to the ImageSlice object's border size.
 
279
        """
 
280
        dx, dy = 0, 0
 
281
        for item in self.GetChildren():
 
282
            if item.IsSizer():
 
283
                dx, dy = item.GetSizer().CalcMin()
 
284
            else:
 
285
                dx, dy = item.GetWindow().GetBestSize()
 
286
 
 
287
        slice   = self._image_slice
 
288
        content = self._theme.content
 
289
 
 
290
        return wx.Size( max( slice.left + slice.right,
 
291
                             slice.xleft + slice.xright +
 
292
                             content.left + content.right + dx ),
 
293
                        max( slice.top + slice.bottom,
 
294
                             slice.xtop + slice.xbottom +
 
295
                             content.top + content.bottom + dy ) )
 
296
 
 
297
    #---------------------------------------------------------------------------
 
298
    #  Layout the contents of the sizer based on the sizer's current size and
 
299
    #  position:
 
300
    #---------------------------------------------------------------------------
 
301
 
 
302
    def RecalcSizes ( self ):
 
303
        """ Layout the contents of the sizer based on the sizer's current size
 
304
            and position.
 
305
        """
 
306
        x,   y  = self.GetPositionTuple()
 
307
        dx, dy  = self.GetSizeTuple()
 
308
        slice   = self._image_slice
 
309
        content = self._theme.content
 
310
        left    = slice.xleft + content.left
 
311
        top     = slice.xtop  + content.top
 
312
        ix, iy, idx, idy = ( x + left,
 
313
                             y + top,
 
314
                             dx - left - slice.xright  - content.right,
 
315
                             dy - top  - slice.xbottom - content.bottom )
 
316
 
 
317
        for item in self.GetChildren():
 
318
            if item.IsSizer():
 
319
                item.GetSizer().SetDimension( ix, iy, idx, idy )
 
320
            else:
 
321
                item.GetWindow().SetDimensions( ix, iy, idx, idy )
 
322