]> SALOME platform Git repositories - tools/eficas.git/blob - Pmw/Pmw_1_2/lib/PmwScrolledFrame.py
Salome HOME
Modif V6_4_°
[tools/eficas.git] / Pmw / Pmw_1_2 / lib / PmwScrolledFrame.py
1 import string
2 import types
3 import Tkinter
4 import Pmw
5
6 class ScrolledFrame(Pmw.MegaWidget):
7     def __init__(self, parent = None, **kw):
8
9         # Define the megawidget options.
10         INITOPT = Pmw.INITOPT
11         optiondefs = (
12             ('borderframe',    1,            INITOPT),
13             ('horizflex',      'fixed',      self._horizflex),
14             ('horizfraction',  0.05,         INITOPT),
15             ('hscrollmode',    'dynamic',    self._hscrollMode),
16             ('labelmargin',    0,            INITOPT),
17             ('labelpos',       None,         INITOPT),
18             ('scrollmargin',   2,            INITOPT),
19             ('usehullsize',    0,            INITOPT),
20             ('vertflex',       'fixed',      self._vertflex),
21             ('vertfraction',   0.05,         INITOPT),
22             ('vscrollmode',    'dynamic',    self._vscrollMode),
23         )
24         self.defineoptions(kw, optiondefs)
25
26         # Initialise the base class (after defining the options).
27         Pmw.MegaWidget.__init__(self, parent)
28
29         # Create the components.
30         self.origInterior = Pmw.MegaWidget.interior(self)
31
32         if self['usehullsize']:
33             self.origInterior.grid_propagate(0)
34
35         if self['borderframe']:
36             # Create a frame widget to act as the border of the clipper. 
37             self._borderframe = self.createcomponent('borderframe',
38                     (), None,
39                     Tkinter.Frame, (self.origInterior,),
40                     relief = 'sunken',
41                     borderwidth = 2,
42             )
43             self._borderframe.grid(row = 2, column = 2, sticky = 'news')
44
45             # Create the clipping window.
46             self._clipper = self.createcomponent('clipper',
47                     (), None,
48                     Tkinter.Frame, (self._borderframe,),
49                     width = 400,
50                     height = 300,
51                     highlightthickness = 0,
52                     borderwidth = 0,
53             )
54             self._clipper.pack(fill = 'both', expand = 1)
55         else:
56             # Create the clipping window.
57             self._clipper = self.createcomponent('clipper',
58                     (), None,
59                     Tkinter.Frame, (self.origInterior,),
60                     width = 400,
61                     height = 300,
62                     relief = 'sunken',
63                     borderwidth = 2,
64             )
65             self._clipper.grid(row = 2, column = 2, sticky = 'news')
66
67         self.origInterior.grid_rowconfigure(2, weight = 1, minsize = 0)
68         self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
69         
70         # Create the horizontal scrollbar
71         self._horizScrollbar = self.createcomponent('horizscrollbar',
72                 (), 'Scrollbar',
73                 Tkinter.Scrollbar, (self.origInterior,),
74                 orient='horizontal',
75                 command=self.xview
76         )
77
78         # Create the vertical scrollbar
79         self._vertScrollbar = self.createcomponent('vertscrollbar',
80                 (), 'Scrollbar',
81                 Tkinter.Scrollbar, (self.origInterior,),
82                 orient='vertical',
83                 command=self.yview
84         )
85
86         self.createlabel(self.origInterior, childCols = 3, childRows = 3)
87
88         # Initialise instance variables.
89         self._horizScrollbarOn = 0
90         self._vertScrollbarOn = 0
91         self.scrollTimer = None
92         self._scrollRecurse = 0
93         self._horizScrollbarNeeded = 0
94         self._vertScrollbarNeeded = 0
95         self.startX = 0
96         self.startY = 0
97         self._flexoptions = ('fixed', 'expand', 'shrink', 'elastic')
98
99         # Create a frame in the clipper to contain the widgets to be
100         # scrolled.
101         self._frame = self.createcomponent('frame',
102                 (), None,
103                 Tkinter.Frame, (self._clipper,)
104         )
105
106         # Whenever the clipping window or scrolled frame change size,
107         # update the scrollbars.
108         self._frame.bind('<Configure>', self._reposition)
109         self._clipper.bind('<Configure>', self._reposition)
110
111         # Work around a bug in Tk where the value returned by the
112         # scrollbar get() method is (0.0, 0.0, 0.0, 0.0) rather than
113         # the expected 2-tuple.  This occurs if xview() is called soon
114         # after the Pmw.ScrolledFrame has been created.
115         self._horizScrollbar.set(0.0, 1.0)
116         self._vertScrollbar.set(0.0, 1.0)
117
118         # Check keywords and initialise options.
119         self.initialiseoptions()
120
121     def destroy(self):
122         if self.scrollTimer is not None:
123             self.after_cancel(self.scrollTimer)
124             self.scrollTimer = None
125         Pmw.MegaWidget.destroy(self)
126
127     # ======================================================================
128
129     # Public methods.
130
131     def interior(self):
132         return self._frame
133
134     # Set timer to call real reposition method, so that it is not
135     # called multiple times when many things are reconfigured at the
136     # same time.
137     def reposition(self):
138         if self.scrollTimer is None:
139             self.scrollTimer = self.after_idle(self._scrollBothNow)
140
141     # Called when the user clicks in the horizontal scrollbar. 
142     # Calculates new position of frame then calls reposition() to
143     # update the frame and the scrollbar.
144     def xview(self, mode = None, value = None, units = None):
145
146         if type(value) == types.StringType:
147             value = string.atof(value)
148         if mode is None:
149             return self._horizScrollbar.get()
150         elif mode == 'moveto':
151             frameWidth = self._frame.winfo_reqwidth()
152             self.startX = value * float(frameWidth)
153         else: # mode == 'scroll'
154             clipperWidth = self._clipper.winfo_width()
155             if units == 'units':
156                 jump = int(clipperWidth * self['horizfraction'])
157             else:
158                 jump = clipperWidth
159             self.startX = self.startX + value * jump
160
161         self.reposition()
162
163     # Called when the user clicks in the vertical scrollbar. 
164     # Calculates new position of frame then calls reposition() to
165     # update the frame and the scrollbar.
166     def yview(self, mode = None, value = None, units = None):
167
168         if type(value) == types.StringType:
169             value = string.atof(value)
170         if mode is None:
171             return self._vertScrollbar.get()
172         elif mode == 'moveto':
173             frameHeight = self._frame.winfo_reqheight()
174             self.startY = value * float(frameHeight)
175         else: # mode == 'scroll'
176             clipperHeight = self._clipper.winfo_height()
177             if units == 'units':
178                 jump = int(clipperHeight * self['vertfraction'])
179             else:
180                 jump = clipperHeight
181             self.startY = self.startY + value * jump
182
183         self.reposition()
184
185     # ======================================================================
186
187     # Configuration methods.
188
189     def _hscrollMode(self):
190         # The horizontal scroll mode has been configured.
191
192         mode = self['hscrollmode']
193
194         if mode == 'static':
195             if not self._horizScrollbarOn:
196                 self._toggleHorizScrollbar()
197         elif mode == 'dynamic':
198             if self._horizScrollbarNeeded != self._horizScrollbarOn:
199                 self._toggleHorizScrollbar()
200         elif mode == 'none':
201             if self._horizScrollbarOn:
202                 self._toggleHorizScrollbar()
203         else:
204             message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
205             raise ValueError, message
206
207     def _vscrollMode(self):
208         # The vertical scroll mode has been configured.
209
210         mode = self['vscrollmode']
211
212         if mode == 'static':
213             if not self._vertScrollbarOn:
214                 self._toggleVertScrollbar()
215         elif mode == 'dynamic':
216             if self._vertScrollbarNeeded != self._vertScrollbarOn:
217                 self._toggleVertScrollbar()
218         elif mode == 'none':
219             if self._vertScrollbarOn:
220                 self._toggleVertScrollbar()
221         else:
222             message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
223             raise ValueError, message
224
225     def _horizflex(self):
226         # The horizontal flex mode has been configured.
227
228         flex = self['horizflex']
229
230         if flex not in self._flexoptions:
231             message = 'bad horizflex option "%s": should be one of %s' % \
232                     (flex, str(self._flexoptions))
233             raise ValueError, message
234
235         self.reposition()
236
237     def _vertflex(self):
238         # The vertical flex mode has been configured.
239
240         flex = self['vertflex']
241
242         if flex not in self._flexoptions:
243             message = 'bad vertflex option "%s": should be one of %s' % \
244                     (flex, str(self._flexoptions))
245             raise ValueError, message
246
247         self.reposition()
248
249     # ======================================================================
250
251     # Private methods.
252
253     def _reposition(self, event):
254         self.reposition()
255
256     def _getxview(self):
257
258         # Horizontal dimension.
259         clipperWidth = self._clipper.winfo_width()
260         frameWidth = self._frame.winfo_reqwidth()
261         if frameWidth <= clipperWidth:
262             # The scrolled frame is smaller than the clipping window.
263
264             self.startX = 0
265             endScrollX = 1.0
266
267             if self['horizflex'] in ('expand', 'elastic'):
268                 relwidth = 1
269             else:
270                 relwidth = ''
271         else:
272             # The scrolled frame is larger than the clipping window.
273
274             if self['horizflex'] in ('shrink', 'elastic'):
275                 self.startX = 0
276                 endScrollX = 1.0
277                 relwidth = 1
278             else:
279                 if self.startX + clipperWidth > frameWidth:
280                     self.startX = frameWidth - clipperWidth
281                     endScrollX = 1.0
282                 else:
283                     if self.startX < 0:
284                         self.startX = 0
285                     endScrollX = (self.startX + clipperWidth) / float(frameWidth)
286                 relwidth = ''
287
288         # Position frame relative to clipper.
289         self._frame.place(x = -self.startX, relwidth = relwidth)
290         return (self.startX / float(frameWidth), endScrollX)
291
292     def _getyview(self):
293
294         # Vertical dimension.
295         clipperHeight = self._clipper.winfo_height()
296         frameHeight = self._frame.winfo_reqheight()
297         if frameHeight <= clipperHeight:
298             # The scrolled frame is smaller than the clipping window.
299
300             self.startY = 0
301             endScrollY = 1.0
302
303             if self['vertflex'] in ('expand', 'elastic'):
304                 relheight = 1
305             else:
306                 relheight = ''
307         else:
308             # The scrolled frame is larger than the clipping window.
309
310             if self['vertflex'] in ('shrink', 'elastic'):
311                 self.startY = 0
312                 endScrollY = 1.0
313                 relheight = 1
314             else:
315                 if self.startY + clipperHeight > frameHeight:
316                     self.startY = frameHeight - clipperHeight
317                     endScrollY = 1.0
318                 else:
319                     if self.startY < 0:
320                         self.startY = 0
321                     endScrollY = (self.startY + clipperHeight) / float(frameHeight)
322                 relheight = ''
323
324         # Position frame relative to clipper.
325         self._frame.place(y = -self.startY, relheight = relheight)
326         return (self.startY / float(frameHeight), endScrollY)
327
328     # According to the relative geometries of the frame and the
329     # clipper, reposition the frame within the clipper and reset the
330     # scrollbars.
331     def _scrollBothNow(self):
332         self.scrollTimer = None
333
334         # Call update_idletasks to make sure that the containing frame
335         # has been resized before we attempt to set the scrollbars. 
336         # Otherwise the scrollbars may be mapped/unmapped continuously.
337         self._scrollRecurse = self._scrollRecurse + 1
338         self.update_idletasks()
339         self._scrollRecurse = self._scrollRecurse - 1
340         if self._scrollRecurse != 0:
341             return
342
343         xview = self._getxview()
344         yview = self._getyview()
345         self._horizScrollbar.set(xview[0], xview[1])
346         self._vertScrollbar.set(yview[0], yview[1])
347
348         self._horizScrollbarNeeded = (xview != (0.0, 1.0))
349         self._vertScrollbarNeeded = (yview != (0.0, 1.0))
350
351         # If both horizontal and vertical scrollmodes are dynamic and
352         # currently only one scrollbar is mapped and both should be
353         # toggled, then unmap the mapped scrollbar.  This prevents a
354         # continuous mapping and unmapping of the scrollbars. 
355         if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
356                 self._horizScrollbarNeeded != self._horizScrollbarOn and
357                 self._vertScrollbarNeeded != self._vertScrollbarOn and
358                 self._vertScrollbarOn != self._horizScrollbarOn):
359             if self._horizScrollbarOn:
360                 self._toggleHorizScrollbar()
361             else:
362                 self._toggleVertScrollbar()
363             return
364
365         if self['hscrollmode'] == 'dynamic':
366             if self._horizScrollbarNeeded != self._horizScrollbarOn:
367                 self._toggleHorizScrollbar()
368
369         if self['vscrollmode'] == 'dynamic':
370             if self._vertScrollbarNeeded != self._vertScrollbarOn:
371                 self._toggleVertScrollbar()
372
373     def _toggleHorizScrollbar(self):
374
375         self._horizScrollbarOn = not self._horizScrollbarOn
376
377         interior = self.origInterior
378         if self._horizScrollbarOn:
379             self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
380             interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
381         else:
382             self._horizScrollbar.grid_forget()
383             interior.grid_rowconfigure(3, minsize = 0)
384
385     def _toggleVertScrollbar(self):
386
387         self._vertScrollbarOn = not self._vertScrollbarOn
388
389         interior = self.origInterior
390         if self._vertScrollbarOn:
391             self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
392             interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
393         else:
394             self._vertScrollbar.grid_forget()
395             interior.grid_columnconfigure(3, minsize = 0)