@@ -180,13 +180,31 @@ def to_cell(self, val):
180
180
"""
181
181
return val
182
182
183
- def validate (self , obj , value = None , obj_class = None ):
183
+ def validate (self , new_obj , obj_class = None ):
184
184
r"""
185
185
Validate object type.
186
+
187
+ TESTS ::
188
+
189
+ sage: from sage_combinat_widgets import GridViewEditor
190
+ sage: obj = Tableau([[1, 2, 5, 6], [3], [4]])
191
+ sage: e = GridViewEditor(obj)
192
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
193
+ sage: new_valid_obj = Tableau([[1, 2, 3, 6], [4], [5]])
194
+ sage: e.validate(new_valid_obj, obj.__class__)
195
+ True
196
+ sage: new_invalid_obj = Partition([3,3,1])
197
+ sage: issubclass(new_invalid_obj.__class__, obj.__class__)
198
+ False
199
+ sage: e.validate(new_invalid_obj, obj.__class__)
200
+ False
201
+ sage: new_invalid_obj = 42
202
+ sage: e.validate(new_invalid_obj)
203
+ False
186
204
"""
187
205
if obj_class :
188
- return issubclass (obj .__class__ , obj_class )
189
- return issubclass (obj .__class__ , SageObject )
206
+ return issubclass (new_obj .__class__ , obj_class )
207
+ return issubclass (new_obj .__class__ , SageObject ) and hasattr ( new_obj , 'cells' )
190
208
191
209
def modified_add_traits (self , ** traits ):
192
210
r"""
@@ -305,33 +323,83 @@ def get_value(self):
305
323
sage: from sage.combinat.tableau import Tableau
306
324
sage: from sage_combinat_widgets import GridViewEditor
307
325
sage: e = GridViewEditor(Tableau([[1, 2, 5, 6], [3], [4]]))
308
- sage: e.value
326
+ sage: e.get_value()
309
327
[[1, 2, 5, 6], [3], [4]]
310
- sage: type(GridViewEditor.value)
311
- <class 'traitlets.traitlets.Any'>
312
- sage: e.set_value(Tableau([[1, 2], [3], [4]]))
313
- sage: e.value
314
- [[1, 2], [3], [4]]
315
328
"""
316
329
return self .value
317
330
318
331
def set_value (self , obj ):
319
332
r"""
320
333
Check compatibility, then set editor value.
334
+
335
+ TESTS ::
336
+
337
+ sage: from sage_combinat_widgets import GridViewEditor
338
+ sage: t = Tableau([[1, 2, 5, 6], [3], [4]])
339
+ sage: e = GridViewEditor(t)
340
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
341
+ sage: new_valid_obj = Tableau([[1, 2, 7, 6], [3], [4]])
342
+ sage: e.set_value(new_valid_obj)
343
+ sage: e.value
344
+ [[1, 2, 7, 6], [3], [4]]
345
+ sage: new_invalid_obj = 42
346
+ sage: e.set_value(new_invalid_obj)
347
+ Traceback (most recent call last):
348
+ ...
349
+ ValueError: Object 42 is not compatible.
321
350
"""
322
351
if not self .validate (obj , self .value .__class__ ):
323
352
raise ValueError ("Object %s is not compatible." % str (obj ))
324
353
self .value = obj
325
354
326
355
def push_history (self , obj ):
327
356
r"""
357
+ Push an object to editor history.
358
+ Ensure that history does not become too long.
359
+
360
+ INPUT:
361
+
362
+ - ``obj`` -- an object (the old one)
363
+
364
+ TESTS::
365
+
366
+ sage: from sage_combinat_widgets import GridViewEditor
367
+ sage: t = Tableau([[1, 2, 5, 6], [3], [4]])
368
+ sage: e = GridViewEditor(t)
369
+ sage: e._history
370
+ []
371
+ sage: e.push_history(t)
372
+ sage: e._history
373
+ [[[1, 2, 5, 6], [3], [4]]]
374
+
328
375
"""
329
376
self ._history .append (obj )
330
377
if len (self ._history ) > MAX_LEN_HISTORY :
331
378
self ._history = self ._history [1 :]
332
379
333
380
@traitlets .observe ('value' )
334
381
def value_changed (self , change ):
382
+ r"""
383
+ What to do when the value has been changed.
384
+
385
+ INPUT:
386
+
387
+ - ``change`` -- a change Bunch
388
+
389
+ TESTS ::
390
+
391
+ sage: from sage_combinat_widgets import GridViewEditor
392
+ sage: t = Tableau([[1, 2, 5, 6], [3], [4]])
393
+ sage: new_t = Tableau([[1, 2, 7, 6], [3], [4]])
394
+ sage: e = GridViewEditor(t)
395
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
396
+ sage: e._history
397
+ []
398
+ sage: from traitlets import Bunch
399
+ sage: e.value_changed(Bunch({'name': 'value', 'old': t, 'new': new_t, 'owner': e, 'type': 'change'}))
400
+ sage: e._history
401
+ [[[1, 2, 5, 6], [3], [4]]]
402
+ """
335
403
self .reset_dirty ()
336
404
if self .donottrack :
337
405
return
@@ -389,35 +457,125 @@ def set_value_from_cells(self, obj_class=None, cells={}):
389
457
print ("These cells cannot be turned into a %s" % cl )
390
458
else :
391
459
raise TypeError ("Unable to cast the given cells into a grid-like object." )
392
- if not self .validate (obj , None , obj_class ):
460
+ if not self .validate (obj , obj_class ):
393
461
raise ValueError ("Could not make a compatible ('%s') object from given cells" % str (obj_class ))
394
462
self .donottrack = True
395
463
self .set_value (obj )
396
464
self .donottrack = False
397
465
398
- def set_dirty (self , pos , val , e = None ):
466
+ def set_dirty (self , pos , val , err = None ):
467
+ r"""
468
+ Set a cell 'dirty'.
469
+
470
+ INPUT:
471
+
472
+ - ``pos`` -- a tuple
473
+ - ``val`` -- a(n incorrect) value for `pos`
474
+ - ``err`` -- an exception
475
+
476
+ TESTS ::
477
+
478
+ sage: from sage_combinat_widgets import GridViewEditor
479
+ sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
480
+ sage: e = GridViewEditor(t)
481
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
482
+ sage: from traitlets import Bunch
483
+ sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
484
+ sage: e.set_dirty((0,2), 7, err)
485
+ sage: e.dirty
486
+ {(0, 2): 7}
487
+ sage: e.dirty_errors[(0,2)]
488
+ ValueError('the entries in each row of a semistandard tableau must be weakly increasing',)
489
+ """
399
490
self .dirty [pos ] = val
400
- if e :
401
- self .dirty_errors [pos ] = e
491
+ if err :
492
+ self .dirty_errors [pos ] = err
402
493
403
494
def unset_dirty (self , pos ):
495
+ r"""
496
+ Set a cell no more 'dirty'.
497
+
498
+ INPUT:
499
+
500
+ - ``pos`` -- a tuple
501
+
502
+ TESTS ::
503
+
504
+ sage: from sage_combinat_widgets import GridViewEditor
505
+ sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
506
+ sage: e = GridViewEditor(t)
507
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
508
+ sage: from traitlets import Bunch
509
+ sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
510
+ sage: e.set_dirty((0,2), 7, err)
511
+ sage: err = e.set_cell(Bunch({'name': 'cell_2_0', 'old': 4, 'new': 9, 'owner': e, 'type': 'change'}))
512
+ sage: e.set_dirty((2,0), 9, err)
513
+ sage: e.dirty
514
+ {(0, 2): 7, (2, 0): 9}
515
+ sage: e.unset_dirty((0,2))
516
+ sage: e.dirty
517
+ {(2, 0): 9}
518
+ """
404
519
del self .dirty [pos ]
405
520
del self .dirty_errors [pos ]
406
521
407
522
def reset_dirty (self ):
523
+ r"""
524
+ Reset all previously 'dirty' cells.
525
+
526
+ TESTS ::
527
+
528
+ sage: from sage_combinat_widgets import GridViewEditor
529
+ sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
530
+ sage: e = GridViewEditor(t)
531
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
532
+ sage: from traitlets import Bunch
533
+ sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
534
+ sage: e.set_dirty((0,2), 7, err)
535
+ sage: err = e.set_cell(Bunch({'name': 'cell_2_0', 'old': 4, 'new': 9, 'owner': e, 'type': 'change'}))
536
+ sage: e.set_dirty((2,0), 9, err)
537
+ sage: e.dirty
538
+ {(0, 2): 7, (2, 0): 9}
539
+ sage: e.reset_dirty()
540
+ sage: e.dirty
541
+ {}
542
+ """
408
543
if not self .dirty : # Prevent any interactive loops
409
544
return
410
- for pos in self .dirty . keys ():
411
- self .unset_dirty ( pos )
545
+ self .dirty = {}
546
+ self .dirty_errors = {}
412
547
413
548
def dirty_info (self , pos ):
549
+ r"""
550
+ Get error details from a 'dirty' cell.
551
+
552
+ INPUT:
553
+
554
+ - ``pos`` -- a tuple
555
+
556
+ TESTS ::
557
+
558
+ sage: from sage_combinat_widgets import GridViewEditor
559
+ sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
560
+ sage: e = GridViewEditor(t)
561
+ sage: e.donottrack = False # This class is not meant to work by itself without a widget.
562
+ sage: from traitlets import Bunch
563
+ sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
564
+ sage: e.set_dirty((0,2), 7, err)
565
+ sage: err = e.set_cell(Bunch({'name': 'cell_2_0', 'old': 4, 'new': 9, 'owner': e, 'type': 'change'}))
566
+ sage: e.set_dirty((2,0), 9, err)
567
+ sage: e.dirty_info((0, 2))
568
+ 'the entries in each row of a semistandard tableau must be weakly increasing'
569
+ """
414
570
if pos in self .dirty_errors :
415
571
return str (self .dirty_errors [pos ])
416
572
return ''
417
573
418
574
@traitlets .observe (traitlets .All )
419
575
def set_cell (self , change ):
420
576
r"""
577
+ What to do when a cell value has been changed.
578
+
421
579
TESTS ::
422
580
423
581
sage: from sage_combinat_widgets import GridViewEditor
@@ -533,6 +691,8 @@ def add_cell(self, change):
533
691
@traitlets .observe (traitlets .All )
534
692
def remove_cell (self , change ):
535
693
r"""
694
+ What to do when a cell has been removed.
695
+
536
696
TESTS ::
537
697
538
698
sage: from sage_combinat_widgets import GridViewEditor
0 commit comments