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

« back to all changes in this revision

Viewing changes to traitsui/qt4/range_editor.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
# Copyright (c) 2008, Riverbank Computing Limited
 
3
# All rights reserved.
 
4
#
 
5
# This software is provided without warranty under the terms of the BSD license.
 
6
# However, when used with the GPL version of PyQt the additional terms described in the PyQt GPL exception also apply
 
7
 
 
8
#
 
9
# Author: Riverbank Computing Limited
 
10
#------------------------------------------------------------------------------
 
11
 
 
12
""" Defines the various range editors and the range editor factory, for the
 
13
PyQt user interface toolkit.
 
14
"""
 
15
 
 
16
#-------------------------------------------------------------------------------
 
17
#  Imports:
 
18
#-------------------------------------------------------------------------------
 
19
 
 
20
from math \
 
21
    import log10
 
22
 
 
23
from pyface.qt import QtCore, QtGui
 
24
 
 
25
from traits.api \
 
26
     import TraitError, Str, Float, Any, Bool
 
27
 
 
28
# FIXME: ToolkitEditorFactory is a proxy class defined here just for backward
 
29
# compatibility. The class has been moved to the
 
30
# traitsui.editors.range_editor file.
 
31
from traitsui.editors.range_editor \
 
32
    import ToolkitEditorFactory
 
33
 
 
34
from editor_factory \
 
35
    import TextEditor
 
36
 
 
37
from editor \
 
38
    import Editor
 
39
 
 
40
from constants \
 
41
    import OKColor, ErrorColor
 
42
 
 
43
from helper \
 
44
    import IconButton
 
45
 
 
46
#-------------------------------------------------------------------------------
 
47
#  'BaseRangeEditor' class:
 
48
#-------------------------------------------------------------------------------
 
49
 
 
50
class BaseRangeEditor ( Editor ):
 
51
    """ The base class for Range editors. Using an evaluate trait, if specified,
 
52
        when assigning numbers the object trait.
 
53
    """
 
54
 
 
55
    #---------------------------------------------------------------------------
 
56
    #  Trait definitions:
 
57
    #---------------------------------------------------------------------------
 
58
 
 
59
    # Function to evaluate floats/ints
 
60
    evaluate = Any
 
61
 
 
62
    #---------------------------------------------------------------------------
 
63
    #  Sets the associated object trait's value:
 
64
    #---------------------------------------------------------------------------
 
65
 
 
66
    def _set_value ( self, value ):
 
67
        if self.evaluate is not None:
 
68
            value = self.evaluate( value )
 
69
        Editor._set_value( self, value )
 
70
 
 
71
#-------------------------------------------------------------------------------
 
72
#  'SimpleSliderEditor' class:
 
73
#-------------------------------------------------------------------------------
 
74
 
 
75
class SimpleSliderEditor ( BaseRangeEditor ):
 
76
    """ Simple style of range editor that displays a slider and a text field.
 
77
 
 
78
    The user can set a value either by moving the slider or by typing a value
 
79
    in the text field.
 
80
    """
 
81
 
 
82
    #---------------------------------------------------------------------------
 
83
    #  Trait definitions:
 
84
    #---------------------------------------------------------------------------
 
85
 
 
86
    # Low value for the slider range
 
87
    low = Any
 
88
 
 
89
    # High value for the slider range
 
90
    high = Any
 
91
 
 
92
    # Formatting string used to format value and labels
 
93
    format = Str
 
94
 
 
95
    #---------------------------------------------------------------------------
 
96
    #  Finishes initializing the editor by creating the underlying toolkit
 
97
    #  widget:
 
98
    #---------------------------------------------------------------------------
 
99
 
 
100
    def init ( self, parent ):
 
101
        """ Finishes initializing the editor by creating the underlying toolkit
 
102
            widget.
 
103
        """
 
104
        factory = self.factory
 
105
        if not factory.low_name:
 
106
            self.low = factory.low
 
107
 
 
108
        if not factory.high_name:
 
109
            self.high = factory.high
 
110
 
 
111
        self.format = factory.format
 
112
 
 
113
        self.evaluate = factory.evaluate
 
114
        self.sync_value( factory.evaluate_name, 'evaluate', 'from' )
 
115
 
 
116
        self.sync_value( factory.low_name,  'low',  'from' )
 
117
        self.sync_value( factory.high_name, 'high', 'from' )
 
118
 
 
119
        self.control = QtGui.QWidget()
 
120
        panel = QtGui.QHBoxLayout(self.control)
 
121
        panel.setContentsMargins(0, 0, 0, 0)
 
122
 
 
123
        fvalue = self.value
 
124
 
 
125
        try:
 
126
            fvalue_text = self.format % fvalue
 
127
            1 / (self.low <= fvalue <= self.high)
 
128
        except:
 
129
            fvalue_text = ''
 
130
            fvalue      = low
 
131
 
 
132
        ivalue = self._convert_to_slider(fvalue)
 
133
 
 
134
        self._label_lo = QtGui.QLabel()
 
135
        self._label_lo.setAlignment(QtCore.Qt.AlignRight |
 
136
                                    QtCore.Qt.AlignVCenter)
 
137
        if factory.label_width > 0:
 
138
            self._label_lo.setMinimumWidth(factory.label_width)
 
139
        panel.addWidget(self._label_lo)
 
140
 
 
141
        self.control.slider = slider = QtGui.QSlider(QtCore.Qt.Horizontal)
 
142
        slider.setTracking(factory.auto_set)
 
143
        slider.setMinimum(0)
 
144
        slider.setMaximum(10000)
 
145
        slider.setPageStep(1000)
 
146
        slider.setSingleStep(100)
 
147
        slider.setValue(ivalue)
 
148
        QtCore.QObject.connect(slider, QtCore.SIGNAL('valueChanged(int)'),
 
149
                self.update_object_on_scroll)
 
150
        panel.addWidget(slider)
 
151
 
 
152
        self._label_hi = QtGui.QLabel()
 
153
        panel.addWidget(self._label_hi)
 
154
        if factory.label_width > 0:
 
155
            self._label_hi.setMinimumWidth(factory.label_width)
 
156
 
 
157
        self.control.text = text = QtGui.QLineEdit(fvalue_text)
 
158
        QtCore.QObject.connect(text, QtCore.SIGNAL('editingFinished()'),
 
159
                self.update_object_on_enter)
 
160
 
 
161
        # The default size is a bit too big and probably doesn't need to grow.
 
162
        sh = text.sizeHint()
 
163
        sh.setWidth(sh.width() / 2)
 
164
        text.setMaximumSize(sh)
 
165
 
 
166
        panel.addWidget(text)
 
167
 
 
168
        low_label = factory.low_label
 
169
        if factory.low_name != '':
 
170
            low_label = self.format % self.low
 
171
 
 
172
        high_label = factory.high_label
 
173
        if factory.high_name != '':
 
174
            high_label = self.format % self.high
 
175
 
 
176
        self._label_lo.setText(low_label)
 
177
        self._label_hi.setText(high_label)
 
178
 
 
179
        self.set_tooltip(slider)
 
180
        self.set_tooltip(self._label_lo)
 
181
        self.set_tooltip(self._label_hi)
 
182
        self.set_tooltip(text)
 
183
 
 
184
    #---------------------------------------------------------------------------
 
185
    #  Handles the user changing the current slider value:
 
186
    #---------------------------------------------------------------------------
 
187
 
 
188
    def update_object_on_scroll(self, pos):
 
189
        """ Handles the user changing the current slider value.
 
190
        """
 
191
        value = self._convert_from_slider(pos)
 
192
        self.control.text.setText(self.format % value)
 
193
        self.value = value
 
194
 
 
195
    #---------------------------------------------------------------------------
 
196
    #  Handle the user pressing the 'Enter' key in the edit control:
 
197
    #---------------------------------------------------------------------------
 
198
 
 
199
    def update_object_on_enter (self):
 
200
        """ Handles the user pressing the Enter key in the text field.
 
201
        """
 
202
        try:
 
203
            try:
 
204
                value = eval(unicode(self.control.text.text()).strip())
 
205
            except Exception, ex:
 
206
                # The entered something that didn't eval as a number, (e.g.,
 
207
                # 'foo') pretend it didn't happen
 
208
                value = self.value
 
209
                self.control.text.setText(unicode(value))
 
210
 
 
211
            if not self.factory.is_float:
 
212
                value = int(value)
 
213
 
 
214
            self.value = value
 
215
            self.control.slider.setValue(self._convert_to_slider(self.value))
 
216
        except TraitError, excp:
 
217
            pass
 
218
 
 
219
    #---------------------------------------------------------------------------
 
220
    #  Updates the editor when the object trait changes external to the editor:
 
221
    #---------------------------------------------------------------------------
 
222
 
 
223
    def update_editor ( self ):
 
224
        """ Updates the editor when the object trait changes externally to the
 
225
            editor.
 
226
        """
 
227
        value = self.value
 
228
        low   = self.low
 
229
        high  = self.high
 
230
        try:
 
231
            text = self.format % value
 
232
            1 / (low <= value <= high)
 
233
        except:
 
234
            text  = ''
 
235
            value = low
 
236
 
 
237
        ivalue = self._convert_to_slider(value)
 
238
 
 
239
        self.control.text.setText(text)
 
240
 
 
241
        blocked = self.control.slider.blockSignals(True)
 
242
        self.control.slider.setValue(ivalue)
 
243
        self.control.slider.blockSignals(blocked)
 
244
 
 
245
    #---------------------------------------------------------------------------
 
246
    #  Returns the editor's control for indicating error status:
 
247
    #---------------------------------------------------------------------------
 
248
 
 
249
    def get_error_control ( self ):
 
250
        """ Returns the editor's control for indicating error status.
 
251
        """
 
252
        return self.control.text
 
253
 
 
254
    #---------------------------------------------------------------------------
 
255
    #  Handles the 'low'/'high' traits being changed:
 
256
    #---------------------------------------------------------------------------
 
257
 
 
258
    def _low_changed ( self, low ):
 
259
        if self.value < low:
 
260
            if self.factory.is_float:
 
261
                self.value = float( low )
 
262
            else:
 
263
                self.value = int( low )
 
264
 
 
265
        if self._label_lo is not None:
 
266
            self._label_lo.setText(self.format % low)
 
267
            self.update_editor()
 
268
 
 
269
    def _high_changed ( self, high ):
 
270
        if self.value > high:
 
271
            if self.factory.is_float:
 
272
                self.value = float( high )
 
273
            else:
 
274
                self.value = int( high )
 
275
 
 
276
        if self._label_hi is not None:
 
277
            self._label_hi.setText(self.format % high)
 
278
            self.update_editor()
 
279
 
 
280
    def _convert_to_slider(self, value):
 
281
        """ Returns the slider setting corresponding to the user-supplied value.
 
282
        """
 
283
        if self.high > self.low:
 
284
            ivalue = int( (float( value - self.low ) /
 
285
                           (self.high - self.low)) * 10000.0 )
 
286
        else:
 
287
            ivalue = self.low
 
288
 
 
289
            if ivalue is None:
 
290
                ivalue = 0
 
291
        return ivalue
 
292
 
 
293
    def _convert_from_slider(self, ivalue):
 
294
        """ Returns the float or integer value corresponding to the slider
 
295
        setting.
 
296
        """
 
297
        value = self.low + ((float( ivalue ) / 10000.0) *
 
298
                            (self.high - self.low))
 
299
        if not self.factory.is_float:
 
300
            value = int(round(value))
 
301
        return value
 
302
 
 
303
 
 
304
#-------------------------------------------------------------------------------
 
305
class LogRangeSliderEditor ( SimpleSliderEditor ):
 
306
#-------------------------------------------------------------------------------
 
307
    """ A slider editor for log-spaced values
 
308
    """
 
309
 
 
310
    def _convert_to_slider(self, value):
 
311
        """ Returns the slider setting corresponding to the user-supplied value.
 
312
        """
 
313
        value = max(value, self.low)
 
314
        ivalue = int( (log10(value) - log10(self.low)) /
 
315
                      (log10(self.high) - log10(self.low)) * 10000.0)
 
316
        return ivalue
 
317
 
 
318
    def _convert_from_slider(self, ivalue):
 
319
        """ Returns the float or integer value corresponding to the slider
 
320
        setting.
 
321
        """
 
322
        value = float( ivalue ) / 10000.0 * (log10(self.high) -log10(self.low))
 
323
        # Do this to handle floating point errors, where fvalue may exceed
 
324
        # self.high.
 
325
        fvalue = min(self.low*10**(value), self.high)
 
326
        if not self.factory.is_float:
 
327
            fvalue = int(round(fvalue))
 
328
        return fvalue
 
329
 
 
330
#-------------------------------------------------------------------------------
 
331
#  'LargeRangeSliderEditor' class:
 
332
#-------------------------------------------------------------------------------
 
333
 
 
334
class LargeRangeSliderEditor ( BaseRangeEditor ):
 
335
    """ A slider editor for large ranges.
 
336
 
 
337
    The editor displays a slider and a text field. A subset of the total range
 
338
    is displayed in the slider; arrow buttons at each end of the slider let
 
339
    the user move the displayed range higher or lower.
 
340
    """
 
341
    #---------------------------------------------------------------------------
 
342
    #  Trait definitions:
 
343
    #---------------------------------------------------------------------------
 
344
 
 
345
    # Low value for the slider range
 
346
    low = Any(0)
 
347
 
 
348
    # High value for the slider range
 
349
    high = Any(1)
 
350
 
 
351
    # Low end of displayed range
 
352
    cur_low = Float
 
353
 
 
354
    # High end of displayed range
 
355
    cur_high = Float
 
356
 
 
357
    # Flag indicating that the UI is in the process of being updated
 
358
    ui_changing = Bool(False)
 
359
 
 
360
    #---------------------------------------------------------------------------
 
361
    #  Finishes initializing the editor by creating the underlying toolkit
 
362
    #  widget:
 
363
    #---------------------------------------------------------------------------
 
364
 
 
365
    def init ( self, parent ):
 
366
        """ Finishes initializing the editor by creating the underlying toolkit
 
367
            widget.
 
368
        """
 
369
        factory = self.factory
 
370
 
 
371
        # Initialize using the factory range defaults:
 
372
        self.low = factory.low
 
373
        self.high = factory.high
 
374
        self.evaluate = factory.evaluate
 
375
 
 
376
        # Hook up the traits to listen to the object.
 
377
        self.sync_value( factory.low_name,  'low',  'from' )
 
378
        self.sync_value( factory.high_name, 'high', 'from' )
 
379
        self.sync_value( factory.evaluate_name, 'evaluate', 'from' )
 
380
 
 
381
        self.init_range()
 
382
        low  = self.cur_low
 
383
        high = self.cur_high
 
384
 
 
385
        self._set_format()
 
386
 
 
387
        self.control = QtGui.QWidget()
 
388
        panel = QtGui.QHBoxLayout(self.control)
 
389
        panel.setContentsMargins(0, 0, 0, 0)
 
390
 
 
391
        fvalue = self.value
 
392
 
 
393
        try:
 
394
            fvalue_text = self._format % fvalue
 
395
            1 / (low <= fvalue <= high)
 
396
        except:
 
397
            fvalue_text = ''
 
398
            fvalue      = low
 
399
 
 
400
        if high > low:
 
401
            ivalue = int( (float( fvalue - low ) / (high - low)) * 10000 )
 
402
        else:
 
403
            ivalue = low
 
404
 
 
405
        # Lower limit label:
 
406
        self.control.label_lo = label_lo = QtGui.QLabel()
 
407
        label_lo.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
 
408
        panel.addWidget(label_lo)
 
409
 
 
410
        # Lower limit button:
 
411
        self.control.button_lo = IconButton(QtGui.QStyle.SP_ArrowLeft,
 
412
                self.reduce_range)
 
413
        panel.addWidget(self.control.button_lo)
 
414
 
 
415
        # Slider:
 
416
        self.control.slider = slider = QtGui.QSlider(QtCore.Qt.Horizontal)
 
417
        slider.setTracking(factory.auto_set)
 
418
        slider.setMinimum(0)
 
419
        slider.setMaximum(10000)
 
420
        slider.setPageStep(1000)
 
421
        slider.setSingleStep(100)
 
422
        slider.setValue(ivalue)
 
423
        QtCore.QObject.connect(slider, QtCore.SIGNAL('valueChanged(int)'),
 
424
                self.update_object_on_scroll)
 
425
        panel.addWidget(slider)
 
426
 
 
427
        # Upper limit button:
 
428
        self.control.button_hi = IconButton(QtGui.QStyle.SP_ArrowRight,
 
429
                self.increase_range)
 
430
        panel.addWidget(self.control.button_hi)
 
431
 
 
432
        # Upper limit label:
 
433
        self.control.label_hi = label_hi = QtGui.QLabel()
 
434
        panel.addWidget(label_hi)
 
435
 
 
436
        # Text entry:
 
437
        self.control.text = text = QtGui.QLineEdit(fvalue_text)
 
438
        QtCore.QObject.connect(text, QtCore.SIGNAL('editingFinished()'),
 
439
                self.update_object_on_enter)
 
440
 
 
441
        # The default size is a bit too big and probably doesn't need to grow.
 
442
        sh = text.sizeHint()
 
443
        sh.setWidth(sh.width() / 2)
 
444
        text.setMaximumSize(sh)
 
445
 
 
446
        panel.addWidget(text)
 
447
 
 
448
        label_lo.setText(str(low))
 
449
        label_hi.setText(str(high))
 
450
        self.set_tooltip(slider)
 
451
        self.set_tooltip(label_lo)
 
452
        self.set_tooltip(label_hi)
 
453
        self.set_tooltip(text)
 
454
 
 
455
        # Update the ranges and button just in case.
 
456
        self.update_range_ui()
 
457
 
 
458
    #---------------------------------------------------------------------------
 
459
    #  Handles the user changing the current slider value:
 
460
    #---------------------------------------------------------------------------
 
461
 
 
462
    def update_object_on_scroll(self, pos):
 
463
        """ Handles the user changing the current slider value.
 
464
        """
 
465
        value = self.cur_low + ((float(pos) / 10000.0) * (self.cur_high - self.cur_low))
 
466
 
 
467
        self.control.text.setText(self._format % value)
 
468
 
 
469
        if self.factory.is_float:
 
470
            self.value = value
 
471
        else:
 
472
            self.value = int(value)
 
473
 
 
474
    #---------------------------------------------------------------------------
 
475
    #  Handle the user pressing the 'Enter' key in the edit control:
 
476
    #---------------------------------------------------------------------------
 
477
 
 
478
    def update_object_on_enter(self):
 
479
        """ Handles the user pressing the Enter key in the text field.
 
480
        """
 
481
        try:
 
482
            self.value = eval(unicode(self.control.text.text()).strip())
 
483
        except TraitError, excp:
 
484
            pass
 
485
 
 
486
    #---------------------------------------------------------------------------
 
487
    #  Updates the editor when the object trait changes external to the editor:
 
488
    #---------------------------------------------------------------------------
 
489
 
 
490
    def update_editor ( self ):
 
491
        """ Updates the editor when the object trait changes externally to the
 
492
            editor.
 
493
        """
 
494
        value = self.value
 
495
        low   = self.low
 
496
        high  = self.high
 
497
        try:
 
498
            text = self._format % value
 
499
            1 / (low <= value <= high)
 
500
        except:
 
501
            value = low
 
502
        self.value = value
 
503
 
 
504
        if not self.ui_changing:
 
505
            self.init_range()
 
506
            self.update_range_ui()
 
507
 
 
508
    def update_range_ui ( self ):
 
509
        """ Updates the slider range controls.
 
510
        """
 
511
        low, high = self.cur_low, self.cur_high
 
512
        value = self.value
 
513
        self._set_format()
 
514
        self.control.label_lo.setText( self._format % low )
 
515
        self.control.label_hi.setText( self._format % high )
 
516
 
 
517
        if high > low:
 
518
            ivalue = int( (float( value - low ) / (high - low)) * 10000.0 )
 
519
        else:
 
520
            ivalue = low
 
521
 
 
522
        blocked = self.control.slider.blockSignals(True)
 
523
        self.control.slider.setValue( ivalue )
 
524
        self.control.slider.blockSignals(blocked)
 
525
 
 
526
        text = self._format % self.value
 
527
        self.control.text.setText( text )
 
528
        self.control.button_lo.setEnabled(low != self.low)
 
529
        self.control.button_hi.setEnabled(high != self.high)
 
530
 
 
531
    def init_range ( self ):
 
532
        """ Initializes the slider range controls.
 
533
        """
 
534
        value     = self.value
 
535
        low, high = self.low, self.high
 
536
        if (high is None) and (low is not None):
 
537
            high = -low
 
538
 
 
539
        mag = abs( value )
 
540
        if mag <= 10.0:
 
541
            cur_low  = max( value - 10, low )
 
542
            cur_high = min( value + 10, high )
 
543
        else:
 
544
            d        = 0.5 * (10**int( log10( mag ) + 1 ))
 
545
            cur_low  = max( low,  value - d )
 
546
            cur_high = min( high, value + d )
 
547
 
 
548
        self.cur_low, self.cur_high = cur_low, cur_high
 
549
 
 
550
    def reduce_range(self):
 
551
        """ Reduces the extent of the displayed range.
 
552
        """
 
553
        low, high = self.low, self.high
 
554
        if abs( self.cur_low ) < 10:
 
555
            self.cur_low  = max( -10, low )
 
556
            self.cur_high = min( 10, high )
 
557
        elif self.cur_low > 0:
 
558
            self.cur_high = self.cur_low
 
559
            self.cur_low  = max( low, self.cur_low / 10 )
 
560
        else:
 
561
            self.cur_high = self.cur_low
 
562
            self.cur_low  = max( low, self.cur_low * 10 )
 
563
 
 
564
        self.ui_changing = True
 
565
        self.value       = min( max( self.value, self.cur_low ), self.cur_high )
 
566
        self.ui_changing = False
 
567
        self.update_range_ui()
 
568
 
 
569
    def increase_range(self):
 
570
        """ Increased the extent of the displayed range.
 
571
        """
 
572
        low, high = self.low, self.high
 
573
        if abs( self.cur_high ) < 10:
 
574
            self.cur_low  = max( -10, low )
 
575
            self.cur_high = min(  10, high )
 
576
        elif self.cur_high > 0:
 
577
            self.cur_low  = self.cur_high
 
578
            self.cur_high = min( high, self.cur_high * 10 )
 
579
        else:
 
580
            self.cur_low  = self.cur_high
 
581
            self.cur_high = min( high, self.cur_high / 10 )
 
582
 
 
583
        self.ui_changing = True
 
584
        self.value       = min( max( self.value, self.cur_low ), self.cur_high )
 
585
        self.ui_changing = False
 
586
        self.update_range_ui()
 
587
 
 
588
    def _set_format ( self ):
 
589
        self._format = '%d'
 
590
        factory      = self.factory
 
591
        low, high    = self.cur_low, self.cur_high
 
592
        diff         = high - low
 
593
        if factory.is_float:
 
594
            if diff > 99999:
 
595
                self._format = '%.2g'
 
596
            elif diff > 1:
 
597
                self._format = '%%.%df' % max( 0, 4 -
 
598
                                                  int( log10( high - low ) ) )
 
599
            else:
 
600
                self._format = '%.3f'
 
601
 
 
602
    #---------------------------------------------------------------------------
 
603
    #  Returns the editor's control for indicating error status:
 
604
    #---------------------------------------------------------------------------
 
605
 
 
606
    def get_error_control ( self ):
 
607
        """ Returns the editor's control for indicating error status.
 
608
        """
 
609
        return self.control.text
 
610
 
 
611
    #---------------------------------------------------------------------------
 
612
    #  Handles the 'low'/'high' traits being changed:
 
613
    #---------------------------------------------------------------------------
 
614
 
 
615
    def _low_changed ( self, low ):
 
616
        if self.control is not None:
 
617
            if self.value < low:
 
618
                if self.factory.is_float:
 
619
                    self.value = float( low )
 
620
                else:
 
621
                    self.value = int( low )
 
622
 
 
623
            self.update_editor()
 
624
 
 
625
    def _high_changed ( self, high ):
 
626
        if self.control is not None:
 
627
            if self.value > high:
 
628
                if self.factory.is_float:
 
629
                    self.value = float( high )
 
630
                else:
 
631
                    self.value = int( high )
 
632
 
 
633
            self.update_editor()
 
634
 
 
635
#-------------------------------------------------------------------------------
 
636
#  'SimpleSpinEditor' class:
 
637
#-------------------------------------------------------------------------------
 
638
 
 
639
class SimpleSpinEditor ( BaseRangeEditor ):
 
640
    """ A simple style of range editor that displays a spin box control.
 
641
    """
 
642
    #---------------------------------------------------------------------------
 
643
    #  Trait definitions:
 
644
    #---------------------------------------------------------------------------
 
645
 
 
646
    # Low value for the slider range
 
647
    low = Any
 
648
 
 
649
    # High value for the slider range
 
650
    high = Any
 
651
 
 
652
    #---------------------------------------------------------------------------
 
653
    #  Finishes initializing the editor by creating the underlying toolkit
 
654
    #  widget:
 
655
    #---------------------------------------------------------------------------
 
656
 
 
657
    def init ( self, parent ):
 
658
        """ Finishes initializing the editor by creating the underlying toolkit
 
659
            widget.
 
660
        """
 
661
        factory = self.factory
 
662
        if not factory.low_name:
 
663
            self.low = factory.low
 
664
 
 
665
        if not factory.high_name:
 
666
            self.high = factory.high
 
667
 
 
668
        self.sync_value( factory.low_name,  'low',  'from' )
 
669
        self.sync_value( factory.high_name, 'high', 'from' )
 
670
        low  = self.low
 
671
        high = self.high
 
672
 
 
673
        self.control = QtGui.QSpinBox()
 
674
        self.control.setMinimum(low)
 
675
        self.control.setMaximum(high)
 
676
        self.control.setValue(self.value)
 
677
        QtCore.QObject.connect(self.control,
 
678
                QtCore.SIGNAL('valueChanged(int)'), self.update_object)
 
679
        self.set_tooltip()
 
680
 
 
681
    #---------------------------------------------------------------------------
 
682
    #  Handle the user selecting a new value from the spin control:
 
683
    #---------------------------------------------------------------------------
 
684
 
 
685
    def update_object(self, value):
 
686
        """ Handles the user selecting a new value in the spin box.
 
687
        """
 
688
        self._locked = True
 
689
        try:
 
690
            self.value = value
 
691
        finally:
 
692
            self._locked = False
 
693
 
 
694
    #---------------------------------------------------------------------------
 
695
    #  Updates the editor when the object trait changes external to the editor:
 
696
    #---------------------------------------------------------------------------
 
697
 
 
698
    def update_editor ( self ):
 
699
        """ Updates the editor when the object trait changes externally to the
 
700
            editor.
 
701
        """
 
702
        if not self._locked:
 
703
            try:
 
704
                self.control.setValue(int(self.value))
 
705
            except:
 
706
                pass
 
707
 
 
708
    #---------------------------------------------------------------------------
 
709
    #  Handles the 'low'/'high' traits being changed:
 
710
    #---------------------------------------------------------------------------
 
711
 
 
712
    def _low_changed ( self, low ):
 
713
        if self.value < low:
 
714
            if self.factory.is_float:
 
715
                self.value = float( low )
 
716
            else:
 
717
                self.value = int( low )
 
718
 
 
719
        if self.control:
 
720
            self.control.setMinimum(low)
 
721
            self.control.setValue(int(self.value))
 
722
 
 
723
    def _high_changed ( self, high ):
 
724
        if self.value > high:
 
725
            if self.factory.is_float:
 
726
                self.value = float( high )
 
727
            else:
 
728
                self.value = int( high )
 
729
 
 
730
        if self.control:
 
731
            self.control.setMaximum(high)
 
732
            self.control.setValue(int(self.value))
 
733
 
 
734
#-------------------------------------------------------------------------------
 
735
#  'RangeTextEditor' class:
 
736
#-------------------------------------------------------------------------------
 
737
 
 
738
class RangeTextEditor ( TextEditor ):
 
739
    """ Editor for ranges that displays a text field. If the user enters a
 
740
    value that is outside the allowed range, the background of the field
 
741
    changes color to indicate an error.
 
742
    """
 
743
 
 
744
    #---------------------------------------------------------------------------
 
745
    #  Trait definitions:
 
746
    #---------------------------------------------------------------------------
 
747
 
 
748
    # Function to evaluate floats/ints
 
749
    evaluate = Any
 
750
 
 
751
    #---------------------------------------------------------------------------
 
752
    #  Finishes initializing the editor by creating the underlying toolkit
 
753
    #  widget:
 
754
    #---------------------------------------------------------------------------
 
755
 
 
756
    def init ( self, parent ):
 
757
        """ Finishes initializing the editor by creating the underlying toolkit
 
758
            widget.
 
759
        """
 
760
        TextEditor.init( self, parent )
 
761
        self.evaluate = self.factory.evaluate
 
762
        self.sync_value( self.factory.evaluate_name, 'evaluate', 'from' )
 
763
 
 
764
    #---------------------------------------------------------------------------
 
765
    #  Handles the user entering input data in the edit control:
 
766
    #---------------------------------------------------------------------------
 
767
 
 
768
    def update_object (self):
 
769
        """ Handles the user entering input data in the edit control.
 
770
        """
 
771
        try:
 
772
            value = eval(unicode(self.control.text()))
 
773
            if self.evaluate is not None:
 
774
                value = self.evaluate(value)
 
775
            self.value = value
 
776
            col = OKColor
 
777
        except:
 
778
            col = ErrorColor
 
779
 
 
780
        pal = QtGui.QPalette(self.control.palette())
 
781
        pal.setColor(QtGui.QPalette.Base, col)
 
782
        self.control.setPalette(pal)
 
783
 
 
784
#-------------------------------------------------------------------------------
 
785
#  'SimpleEnumEditor' factory adaptor:
 
786
#-------------------------------------------------------------------------------
 
787
 
 
788
def SimpleEnumEditor ( parent, factory, ui, object, name, description ):
 
789
    return CustomEnumEditor( parent, factory, ui, object, name, description,
 
790
                             'simple' )
 
791
 
 
792
#-------------------------------------------------------------------------------
 
793
#  'CustomEnumEditor' factory adaptor:
 
794
#-------------------------------------------------------------------------------
 
795
 
 
796
def CustomEnumEditor ( parent, factory, ui, object, name, description,
 
797
                       style = 'custom' ):
 
798
    """ Factory adapter that returns a enumeration editor of the specified
 
799
    style.
 
800
    """
 
801
    if factory._enum is None:
 
802
        import traitsui.editors.enum_editor as enum_editor
 
803
        factory._enum = enum_editor.ToolkitEditorFactory(
 
804
                            values = range( factory.low, factory.high + 1 ),
 
805
                            cols   = factory.cols )
 
806
 
 
807
    if style == 'simple':
 
808
        return factory._enum.simple_editor( ui, object, name, description,
 
809
                                            parent )
 
810
 
 
811
    return factory._enum.custom_editor( ui, object, name, description, parent )
 
812
 
 
813
#-------------------------------------------------------------------------------
 
814
#  Defines the mapping between editor factory 'mode's and Editor classes:
 
815
#-------------------------------------------------------------------------------
 
816
 
 
817
# Mapping between editor factory modes and simple editor classes
 
818
SimpleEditorMap = {
 
819
    'slider':  SimpleSliderEditor,
 
820
    'xslider': LargeRangeSliderEditor,
 
821
    'spinner': SimpleSpinEditor,
 
822
    'enum':    SimpleEnumEditor,
 
823
    'text':    RangeTextEditor,
 
824
    'low':     LogRangeSliderEditor
 
825
}
 
826
# Mapping between editor factory modes and custom editor classes
 
827
CustomEditorMap = {
 
828
    'slider':  SimpleSliderEditor,
 
829
    'xslider': LargeRangeSliderEditor,
 
830
    'spinner': SimpleSpinEditor,
 
831
    'enum':    CustomEnumEditor,
 
832
    'text':    RangeTextEditor,
 
833
    'low':     LogRangeSliderEditor
 
834
}