]> SALOME platform Git repositories - tools/eficas.git/blob - Pmw/Pmw_1_2/lib/PmwCounter.py
Salome HOME
Modif V6_4_°
[tools/eficas.git] / Pmw / Pmw_1_2 / lib / PmwCounter.py
1 import string
2 import sys
3 import types
4 import Tkinter
5 import Pmw
6
7 class Counter(Pmw.MegaWidget):
8
9     def __init__(self, parent = None, **kw):
10
11         # Define the megawidget options.
12         INITOPT = Pmw.INITOPT
13         optiondefs = (
14             ('autorepeat',     1,             None),
15             ('buttonaspect',   1.0,           INITOPT),
16             ('datatype',       'numeric',     self._datatype),
17             ('increment',      1,             None),
18             ('initwait',       300,           None),
19             ('labelmargin',    0,             INITOPT),
20             ('labelpos',       None,          INITOPT),
21             ('orient',         'horizontal',  INITOPT),
22             ('padx',           0,             INITOPT),
23             ('pady',           0,             INITOPT),
24             ('repeatrate',     50,            None),
25             ('sticky',         'ew',          INITOPT),
26         )
27         self.defineoptions(kw, optiondefs)
28
29         # Initialise the base class (after defining the options).
30         Pmw.MegaWidget.__init__(self, parent)
31
32         # Initialise instance variables.
33         self._timerId = None
34         self._normalRelief = None
35
36         # Create the components.
37         interior = self.interior()
38
39         # If there is no label, put the arrows and the entry directly
40         # into the interior, otherwise create a frame for them.  In
41         # either case the border around the arrows and the entry will
42         # be raised (but not around the label).
43         if self['labelpos'] is None:
44             frame = interior
45             if not kw.has_key('hull_relief'):
46                 frame.configure(relief = 'raised')
47             if not kw.has_key('hull_borderwidth'):
48                 frame.configure(borderwidth = 1)
49         else:
50             frame = self.createcomponent('frame',
51                     (), None,
52                     Tkinter.Frame, (interior,),
53                     relief = 'raised', borderwidth = 1)
54             frame.grid(column=2, row=2, sticky=self['sticky'])
55             interior.grid_columnconfigure(2, weight=1)
56             interior.grid_rowconfigure(2, weight=1)
57
58         # Create the down arrow.
59         self._downArrowBtn = self.createcomponent('downarrow',
60                 (), 'Arrow',
61                 Tkinter.Canvas, (frame,),
62                 width = 16, height = 16, relief = 'raised', borderwidth = 2)
63
64         # Create the entry field.
65         self._counterEntry = self.createcomponent('entryfield',
66                 (('entry', 'entryfield_entry'),), None,
67                 Pmw.EntryField, (frame,))
68
69         # Create the up arrow.
70         self._upArrowBtn = self.createcomponent('uparrow',
71                 (), 'Arrow',
72                 Tkinter.Canvas, (frame,),
73                 width = 16, height = 16, relief = 'raised', borderwidth = 2)
74
75         padx = self['padx']
76         pady = self['pady']
77         orient = self['orient']
78         if orient == 'horizontal':
79             self._downArrowBtn.grid(column = 0, row = 0)
80             self._counterEntry.grid(column = 1, row = 0,
81                     sticky = self['sticky'])
82             self._upArrowBtn.grid(column = 2, row = 0)
83             frame.grid_columnconfigure(1, weight = 1)
84             frame.grid_rowconfigure(0, weight = 1)
85             if Tkinter.TkVersion >= 4.2:
86                 frame.grid_columnconfigure(0, pad = padx)
87                 frame.grid_columnconfigure(2, pad = padx)
88                 frame.grid_rowconfigure(0, pad = pady)
89         elif orient == 'vertical':
90             self._upArrowBtn.grid(column = 0, row = 0, sticky = 's')
91             self._counterEntry.grid(column = 0, row = 1,
92                     sticky = self['sticky'])
93             self._downArrowBtn.grid(column = 0, row = 2, sticky = 'n')
94             frame.grid_columnconfigure(0, weight = 1)
95             frame.grid_rowconfigure(0, weight = 1)
96             frame.grid_rowconfigure(2, weight = 1)
97             if Tkinter.TkVersion >= 4.2:
98                 frame.grid_rowconfigure(0, pad = pady)
99                 frame.grid_rowconfigure(2, pad = pady)
100                 frame.grid_columnconfigure(0, pad = padx)
101         else:
102             raise ValueError, 'bad orient option ' + repr(orient) + \
103                 ': must be either \'horizontal\' or \'vertical\''
104
105         self.createlabel(interior)
106
107         self._upArrowBtn.bind('<Configure>', self._drawUpArrow)
108         self._upArrowBtn.bind('<1>', self._countUp)
109         self._upArrowBtn.bind('<Any-ButtonRelease-1>', self._stopCounting)
110         self._downArrowBtn.bind('<Configure>', self._drawDownArrow)
111         self._downArrowBtn.bind('<1>', self._countDown)
112         self._downArrowBtn.bind('<Any-ButtonRelease-1>', self._stopCounting)
113         self._counterEntry.bind('<Configure>', self._resizeArrow)
114         entry = self._counterEntry.component('entry')
115         entry.bind('<Down>', lambda event, s = self: s._key_decrement(event))
116         entry.bind('<Up>', lambda event, s = self: s._key_increment(event))
117
118         # Need to cancel the timer if an arrow button is unmapped (eg: 
119         # its toplevel window is withdrawn) while the mouse button is
120         # held down.  The canvas will not get the ButtonRelease event
121         # if it is not mapped, since the implicit grab is cancelled.
122         self._upArrowBtn.bind('<Unmap>', self._stopCounting)
123         self._downArrowBtn.bind('<Unmap>', self._stopCounting)
124
125         # Check keywords and initialise options.
126         self.initialiseoptions()
127
128     def _resizeArrow(self, event):
129         for btn in (self._upArrowBtn, self._downArrowBtn):
130             bw = (string.atoi(btn['borderwidth']) +
131                     string.atoi(btn['highlightthickness']))
132             newHeight = self._counterEntry.winfo_reqheight() - 2 * bw
133             newWidth = int(newHeight * self['buttonaspect'])
134             btn.configure(width=newWidth, height=newHeight)
135             self._drawArrow(btn)
136
137     def _drawUpArrow(self, event):
138         self._drawArrow(self._upArrowBtn)
139
140     def _drawDownArrow(self, event):
141         self._drawArrow(self._downArrowBtn)
142
143     def _drawArrow(self, arrow):
144         if self['orient'] == 'vertical':
145             if arrow == self._upArrowBtn:
146                 direction = 'up'
147             else:
148                 direction = 'down'
149         else:
150             if arrow == self._upArrowBtn:
151                 direction = 'right'
152             else:
153                 direction = 'left'
154         Pmw.drawarrow(arrow, self['entry_foreground'], direction, 'arrow')
155
156     def _stopCounting(self, event = None):
157         if self._timerId is not None:
158             self.after_cancel(self._timerId)
159             self._timerId = None
160         if self._normalRelief is not None:
161             button, relief = self._normalRelief
162             button.configure(relief=relief)
163             self._normalRelief = None
164
165     def _countUp(self, event):
166         self._normalRelief = (self._upArrowBtn, self._upArrowBtn.cget('relief'))
167         self._upArrowBtn.configure(relief='sunken')
168         # Force arrow down (it may come up immediately, if increment fails).
169         self._upArrowBtn.update_idletasks()
170         self._count(1, 1)
171
172     def _countDown(self, event):
173         self._normalRelief = (self._downArrowBtn, self._downArrowBtn.cget('relief'))
174         self._downArrowBtn.configure(relief='sunken')
175         # Force arrow down (it may come up immediately, if increment fails).
176         self._downArrowBtn.update_idletasks()
177         self._count(-1, 1)
178
179     def increment(self):
180         self._forceCount(1)
181
182     def decrement(self):
183         self._forceCount(-1)
184
185     def _key_increment(self, event):
186         self._forceCount(1)
187         self.update_idletasks()
188
189     def _key_decrement(self, event):
190         self._forceCount(-1)
191         self.update_idletasks()
192
193     def _datatype(self):
194         datatype = self['datatype']
195
196         if type(datatype) is types.DictionaryType:
197             self._counterArgs = datatype.copy()
198             if self._counterArgs.has_key('counter'):
199                 datatype = self._counterArgs['counter']
200                 del self._counterArgs['counter']
201             else:
202                 datatype = 'numeric'
203         else:
204             self._counterArgs = {}
205
206         if _counterCommands.has_key(datatype):
207             self._counterCommand = _counterCommands[datatype]
208         elif callable(datatype):
209             self._counterCommand = datatype
210         else:
211             validValues = _counterCommands.keys()
212             validValues.sort()
213             raise ValueError, ('bad datatype value "%s":  must be a' +
214                     ' function or one of %s') % (datatype, validValues)
215
216     def _forceCount(self, factor):
217         if not self.valid():
218             self.bell()
219             return
220
221         text = self._counterEntry.get()
222         try:
223             value = apply(self._counterCommand,
224                     (text, factor, self['increment']), self._counterArgs)
225         except ValueError:
226             self.bell()
227             return
228
229         previousICursor = self._counterEntry.index('insert')
230         if self._counterEntry.setentry(value) == Pmw.OK:
231             self._counterEntry.xview('end')
232             self._counterEntry.icursor(previousICursor)
233
234     def _count(self, factor, first):
235         if not self.valid():
236             self.bell()
237             return
238
239         self._timerId = None
240         origtext = self._counterEntry.get()
241         try:
242             value = apply(self._counterCommand,
243                     (origtext, factor, self['increment']), self._counterArgs)
244         except ValueError:
245             # If text is invalid, stop counting.
246             self._stopCounting()
247             self.bell()
248             return
249
250         # If incrementing produces an invalid value, restore previous
251         # text and stop counting.
252         previousICursor = self._counterEntry.index('insert')
253         valid = self._counterEntry.setentry(value)
254         if valid != Pmw.OK:
255             self._stopCounting()
256             self._counterEntry.setentry(origtext)
257             if valid == Pmw.PARTIAL:
258                 self.bell()
259             return
260         self._counterEntry.xview('end')
261         self._counterEntry.icursor(previousICursor)
262
263         if self['autorepeat']:
264             if first:
265                 delay = self['initwait']
266             else:
267                 delay = self['repeatrate']
268             self._timerId = self.after(delay,
269                     lambda self=self, factor=factor: self._count(factor, 0))
270
271     def destroy(self):
272         self._stopCounting()
273         Pmw.MegaWidget.destroy(self)
274
275 Pmw.forwardmethods(Counter, Pmw.EntryField, '_counterEntry')
276
277 def _changeNumber(text, factor, increment):
278   value = string.atol(text)
279   if factor > 0:
280     value = (value / increment) * increment + increment
281   else:
282     value = ((value - 1) / increment) * increment
283
284   # Get rid of the 'L' at the end of longs (in python up to 1.5.2).
285   rtn = str(value)
286   if rtn[-1] == 'L':
287       return rtn[:-1]
288   else:
289       return rtn
290
291 def _changeReal(text, factor, increment, separator = '.'):
292   value = Pmw.stringtoreal(text, separator)
293   div = value / increment
294
295   # Compare reals using str() to avoid problems caused by binary
296   # numbers being only approximations to decimal numbers.
297   # For example, if value is -0.3 and increment is 0.1, then
298   # int(value/increment) = -2, not -3 as one would expect.
299   if str(div)[-2:] == '.0':
300     # value is an even multiple of increment.
301     div = round(div) + factor
302   else:
303     # value is not an even multiple of increment.
304     div = int(div) * 1.0
305     if value < 0:
306       div = div - 1
307     if factor > 0:
308       div = (div + 1)
309
310   value = div * increment
311
312   text = str(value)
313   if separator != '.':
314       index = string.find(text, '.')
315       if index >= 0:
316         text = text[:index] + separator + text[index + 1:]
317   return text
318
319 def _changeDate(value, factor, increment, format = 'ymd',
320         separator = '/', yyyy = 0):
321
322   jdn = Pmw.datestringtojdn(value, format, separator) + factor * increment
323
324   y, m, d = Pmw.jdntoymd(jdn)
325   result = ''
326   for index in range(3):
327     if index > 0:
328       result = result + separator
329     f = format[index]
330     if f == 'y':
331       if yyyy:
332         result = result + '%02d' % y
333       else:
334         result = result + '%02d' % (y % 100)
335     elif f == 'm':
336       result = result + '%02d' % m
337     elif f == 'd':
338       result = result + '%02d' % d
339
340   return result
341
342 _SECSPERDAY = 24 * 60 * 60
343 def _changeTime(value, factor, increment, separator = ':', time24 = 0):
344   unixTime = Pmw.timestringtoseconds(value, separator)
345   if factor > 0:
346     chunks = unixTime / increment + 1
347   else:
348     chunks = (unixTime - 1) / increment
349   unixTime = chunks * increment
350   if time24:
351       while unixTime < 0:
352           unixTime = unixTime + _SECSPERDAY
353       while unixTime >= _SECSPERDAY:
354           unixTime = unixTime - _SECSPERDAY
355   if unixTime < 0:
356     unixTime = -unixTime
357     sign = '-'
358   else:
359     sign = ''
360   secs = unixTime % 60
361   unixTime = unixTime / 60
362   mins = unixTime % 60
363   hours = unixTime / 60
364   return '%s%02d%s%02d%s%02d' % (sign, hours, separator, mins, separator, secs)
365
366 # hexadecimal, alphabetic, alphanumeric not implemented
367 _counterCommands = {
368     'numeric'   : _changeNumber,      # } integer
369     'integer'   : _changeNumber,      # } these two use the same function
370     'real'      : _changeReal,        # real number
371     'time'      : _changeTime,
372     'date'      : _changeDate,
373 }