Salome HOME
Modif V6_4_°
[tools/eficas.git] / Pmw / Pmw_1_2 / lib / PmwScrolledCanvas.py
1 import Tkinter
2 import Pmw
3
4 class ScrolledCanvas(Pmw.MegaWidget):
5     def __init__(self, parent = None, **kw):
6
7         # Define the megawidget options.
8         INITOPT = Pmw.INITOPT
9         optiondefs = (
10             ('borderframe',    0,            INITOPT),
11             ('canvasmargin',   0,            INITOPT),
12             ('hscrollmode',    'dynamic',    self._hscrollMode),
13             ('labelmargin',    0,            INITOPT),
14             ('labelpos',       None,         INITOPT),
15             ('scrollmargin',   2,            INITOPT),
16             ('usehullsize',    0,            INITOPT),
17             ('vscrollmode',    'dynamic',    self._vscrollMode),
18         )
19         self.defineoptions(kw, optiondefs)
20
21         # Initialise the base class (after defining the options).
22         Pmw.MegaWidget.__init__(self, parent)
23
24         # Create the components.
25         self.origInterior = Pmw.MegaWidget.interior(self)
26
27         if self['usehullsize']:
28             self.origInterior.grid_propagate(0)
29
30         if self['borderframe']:
31             # Create a frame widget to act as the border of the canvas. 
32             self._borderframe = self.createcomponent('borderframe',
33                     (), None,
34                     Tkinter.Frame, (self.origInterior,),
35                     relief = 'sunken',
36                     borderwidth = 2,
37             )
38             self._borderframe.grid(row = 2, column = 2, sticky = 'news')
39
40             # Create the canvas widget.
41             self._canvas = self.createcomponent('canvas',
42                     (), None,
43                     Tkinter.Canvas, (self._borderframe,),
44                     highlightthickness = 0,
45                     borderwidth = 0,
46             )
47             self._canvas.pack(fill = 'both', expand = 1)
48         else:
49             # Create the canvas widget.
50             self._canvas = self.createcomponent('canvas',
51                     (), None,
52                     Tkinter.Canvas, (self.origInterior,),
53                     relief = 'sunken',
54                     borderwidth = 2,
55             )
56             self._canvas.grid(row = 2, column = 2, sticky = 'news')
57
58         self.origInterior.grid_rowconfigure(2, weight = 1, minsize = 0)
59         self.origInterior.grid_columnconfigure(2, weight = 1, minsize = 0)
60         
61         # Create the horizontal scrollbar
62         self._horizScrollbar = self.createcomponent('horizscrollbar',
63                 (), 'Scrollbar',
64                 Tkinter.Scrollbar, (self.origInterior,),
65                 orient='horizontal',
66                 command=self._canvas.xview
67         )
68
69         # Create the vertical scrollbar
70         self._vertScrollbar = self.createcomponent('vertscrollbar',
71                 (), 'Scrollbar',
72                 Tkinter.Scrollbar, (self.origInterior,),
73                 orient='vertical',
74                 command=self._canvas.yview
75         )
76
77         self.createlabel(self.origInterior, childCols = 3, childRows = 3)
78
79         # Initialise instance variables.
80         self._horizScrollbarOn = 0
81         self._vertScrollbarOn = 0
82         self.scrollTimer = None
83         self._scrollRecurse = 0
84         self._horizScrollbarNeeded = 0
85         self._vertScrollbarNeeded = 0
86         self.setregionTimer = None
87
88         # Check keywords and initialise options.
89         self.initialiseoptions()
90
91     def destroy(self):
92         if self.scrollTimer is not None:
93             self.after_cancel(self.scrollTimer)
94             self.scrollTimer = None
95         if self.setregionTimer is not None:
96             self.after_cancel(self.setregionTimer)
97             self.setregionTimer = None
98         Pmw.MegaWidget.destroy(self)
99
100     # ======================================================================
101
102     # Public methods.
103
104     def interior(self):
105         return self._canvas
106
107     def resizescrollregion(self):
108         if self.setregionTimer is None:
109             self.setregionTimer = self.after_idle(self._setRegion)
110
111     # ======================================================================
112
113     # Configuration methods.
114
115     def _hscrollMode(self):
116         # The horizontal scroll mode has been configured.
117
118         mode = self['hscrollmode']
119
120         if mode == 'static':
121             if not self._horizScrollbarOn:
122                 self._toggleHorizScrollbar()
123         elif mode == 'dynamic':
124             if self._horizScrollbarNeeded != self._horizScrollbarOn:
125                 self._toggleHorizScrollbar()
126         elif mode == 'none':
127             if self._horizScrollbarOn:
128                 self._toggleHorizScrollbar()
129         else:
130             message = 'bad hscrollmode option "%s": should be static, dynamic, or none' % mode
131             raise ValueError, message
132
133         self._configureScrollCommands()
134
135     def _vscrollMode(self):
136         # The vertical scroll mode has been configured.
137
138         mode = self['vscrollmode']
139
140         if mode == 'static':
141             if not self._vertScrollbarOn:
142                 self._toggleVertScrollbar()
143         elif mode == 'dynamic':
144             if self._vertScrollbarNeeded != self._vertScrollbarOn:
145                 self._toggleVertScrollbar()
146         elif mode == 'none':
147             if self._vertScrollbarOn:
148                 self._toggleVertScrollbar()
149         else:
150             message = 'bad vscrollmode option "%s": should be static, dynamic, or none' % mode
151             raise ValueError, message
152
153         self._configureScrollCommands()
154
155     # ======================================================================
156
157     # Private methods.
158
159     def _configureScrollCommands(self):
160         # If both scrollmodes are not dynamic we can save a lot of
161         # time by not having to create an idle job to handle the
162         # scroll commands.
163
164         # Clean up previous scroll commands to prevent memory leak.
165         tclCommandName = str(self._canvas.cget('xscrollcommand'))
166         if tclCommandName != '':   
167             self._canvas.deletecommand(tclCommandName)
168         tclCommandName = str(self._canvas.cget('yscrollcommand'))
169         if tclCommandName != '':   
170             self._canvas.deletecommand(tclCommandName)
171
172         if self['hscrollmode'] == self['vscrollmode'] == 'dynamic':
173             self._canvas.configure(
174                     xscrollcommand=self._scrollBothLater,
175                     yscrollcommand=self._scrollBothLater
176             )
177         else:
178             self._canvas.configure(
179                     xscrollcommand=self._scrollXNow,
180                     yscrollcommand=self._scrollYNow
181             )
182
183     def _scrollXNow(self, first, last):
184         self._horizScrollbar.set(first, last)
185         self._horizScrollbarNeeded = ((first, last) != ('0', '1'))
186
187         if self['hscrollmode'] == 'dynamic':
188             if self._horizScrollbarNeeded != self._horizScrollbarOn:
189                 self._toggleHorizScrollbar()
190
191     def _scrollYNow(self, first, last):
192         self._vertScrollbar.set(first, last)
193         self._vertScrollbarNeeded = ((first, last) != ('0', '1'))
194
195         if self['vscrollmode'] == 'dynamic':
196             if self._vertScrollbarNeeded != self._vertScrollbarOn:
197                 self._toggleVertScrollbar()
198
199     def _scrollBothLater(self, first, last):
200         # Called by the canvas to set the horizontal or vertical
201         # scrollbar when it has scrolled or changed scrollregion.
202
203         if self.scrollTimer is None:
204             self.scrollTimer = self.after_idle(self._scrollBothNow)
205
206     def _scrollBothNow(self):
207         # This performs the function of _scrollXNow and _scrollYNow.
208         # If one is changed, the other should be updated to match.
209         self.scrollTimer = None
210
211         # Call update_idletasks to make sure that the containing frame
212         # has been resized before we attempt to set the scrollbars. 
213         # Otherwise the scrollbars may be mapped/unmapped continuously.
214         self._scrollRecurse = self._scrollRecurse + 1
215         self.update_idletasks()
216         self._scrollRecurse = self._scrollRecurse - 1
217         if self._scrollRecurse != 0:
218             return
219
220         xview = self._canvas.xview()
221         yview = self._canvas.yview()
222         self._horizScrollbar.set(xview[0], xview[1])
223         self._vertScrollbar.set(yview[0], yview[1])
224
225         self._horizScrollbarNeeded = (xview != (0.0, 1.0))
226         self._vertScrollbarNeeded = (yview != (0.0, 1.0))
227
228         # If both horizontal and vertical scrollmodes are dynamic and
229         # currently only one scrollbar is mapped and both should be
230         # toggled, then unmap the mapped scrollbar.  This prevents a
231         # continuous mapping and unmapping of the scrollbars. 
232         if (self['hscrollmode'] == self['vscrollmode'] == 'dynamic' and
233                 self._horizScrollbarNeeded != self._horizScrollbarOn and
234                 self._vertScrollbarNeeded != self._vertScrollbarOn and
235                 self._vertScrollbarOn != self._horizScrollbarOn):
236             if self._horizScrollbarOn:
237                 self._toggleHorizScrollbar()
238             else:
239                 self._toggleVertScrollbar()
240             return
241
242         if self['hscrollmode'] == 'dynamic':
243             if self._horizScrollbarNeeded != self._horizScrollbarOn:
244                 self._toggleHorizScrollbar()
245
246         if self['vscrollmode'] == 'dynamic':
247             if self._vertScrollbarNeeded != self._vertScrollbarOn:
248                 self._toggleVertScrollbar()
249
250     def _toggleHorizScrollbar(self):
251
252         self._horizScrollbarOn = not self._horizScrollbarOn
253
254         interior = self.origInterior
255         if self._horizScrollbarOn:
256             self._horizScrollbar.grid(row = 4, column = 2, sticky = 'news')
257             interior.grid_rowconfigure(3, minsize = self['scrollmargin'])
258         else:
259             self._horizScrollbar.grid_forget()
260             interior.grid_rowconfigure(3, minsize = 0)
261
262     def _toggleVertScrollbar(self):
263
264         self._vertScrollbarOn = not self._vertScrollbarOn
265
266         interior = self.origInterior
267         if self._vertScrollbarOn:
268             self._vertScrollbar.grid(row = 2, column = 4, sticky = 'news')
269             interior.grid_columnconfigure(3, minsize = self['scrollmargin'])
270         else:
271             self._vertScrollbar.grid_forget()
272             interior.grid_columnconfigure(3, minsize = 0)
273
274     def _setRegion(self):
275         self.setregionTimer = None
276
277         region = self._canvas.bbox('all')
278         if region is not None:
279             canvasmargin = self['canvasmargin']
280             region = (region[0] - canvasmargin, region[1] - canvasmargin,
281                 region[2] + canvasmargin, region[3] + canvasmargin)
282             self._canvas.configure(scrollregion = region)
283
284     # Need to explicitly forward this to override the stupid
285     # (grid_)bbox method inherited from Tkinter.Frame.Grid.
286     def bbox(self, *args):
287         return apply(self._canvas.bbox, args)
288
289 Pmw.forwardmethods(ScrolledCanvas, Tkinter.Canvas, '_canvas')