3
3
4
4
from .flipping_aztecdiamond import *
5
5
from sage_widget_adapters .graphs .graph_grid_view_adapter import GraphGridViewAdapter
6
- from sage_combinat_widgets .grid_view_widget import GridViewWidget , ButtonCell , BlankButton , styled_button_cell
6
+ from sage_combinat_widgets .grid_view_widget import GridViewWidget , ButtonCell , BlankButton , StyledButtonCell
7
7
from ipywidgets import Layout
8
8
from traitlets import dlink , HasTraits , Bool , observe , All
9
9
from contextlib import contextmanager
@@ -22,6 +22,7 @@ def set_cell(self, obj, pos, val, dirty={}):
22
22
we prepare a possible flip
23
23
or we try to complete the flip if it has been prepared previously
24
24
"""
25
+ #print("in the adapter for pos =", pos, "val =", val, "dirty =", dirty)
25
26
# Find out the relevant matching for 'pos'
26
27
d1 = obj .domino_for_position (pos )
27
28
if dirty : # if i'm a neighbor, then flip and return a new obj ; else return an error
@@ -34,7 +35,9 @@ def set_cell(self, obj, pos, val, dirty={}):
34
35
continue
35
36
if d2 in d1 .neighbors ():
36
37
# Do the flip
38
+ print ("before flipping :" , d1 , d2 )
37
39
obj .flip (d1 , d2 )
40
+ print ("after flipping :" , d1 , d2 )
38
41
return obj
39
42
return Exception ("Please select a second domino!" )
40
43
else :
@@ -43,7 +46,7 @@ def set_cell(self, obj, pos, val, dirty={}):
43
46
44
47
class ddlink (dlink ):
45
48
"""Double directional link with logic = or/and/none.
46
- Double_source is a tuple of (source, target ) tuples
49
+ `double_source` is a tuple of (source, traitname ) tuples
47
50
Usage: b1 = Button(description = 'B1')
48
51
b2 = Button(description = 'B2')
49
52
b3 = Button(description = 'B3')
@@ -101,6 +104,9 @@ def _busy_updating(self):
101
104
def _update (self , change ):
102
105
#if self.updating or self.target[0].donottrack:
103
106
# return
107
+ #if self.target[0].donottrack:
108
+ # print("on sort ici !!!")
109
+ # return
104
110
with self ._busy_updating ():
105
111
if self .logic == 'and' :
106
112
if self .intermediate_value == False : # aucun bouton pressé avant
@@ -137,38 +143,66 @@ def unlink(self):
137
143
source [0 ].unobserve (self ._update , names = source [1 ])
138
144
self .double_source , self .target = None , None
139
145
146
+
147
+ class MyStyledButtonCell (StyledButtonCell ):
148
+ def _handle_msg (self , msg ):
149
+ r"""
150
+ Override needed to prevent propagation
151
+ when a domino is pressed, in some cases.
152
+ """
153
+ data = msg ['content' ]['data' ]
154
+ if data ['method' ] != 'update' or not 'state' in data or ('buffer_paths' in data and data ['buffer_paths' ]):
155
+ super (FlippinDominosWidget )._handle_msg (msg )
156
+ state = data ['state' ]
157
+ try :
158
+ self .set_state (state )
159
+ except :
160
+ pass
161
+
162
+
163
+ def my_styled_button_cell (disabled = False , style_name = '' , addable = False ):
164
+ class_name = "{}Button" .format (style_name .capitalize ())
165
+ if disabled :
166
+ class_name = "Disabled" + class_name
167
+ elif addable :
168
+ class_name = "Addable" + class_name
169
+ return type (class_name , (MyStyledButtonCell ,), {'disable' : disabled , 'css_class' : style_name , 'addable' : addable })
170
+
171
+
140
172
class Domino (HasTraits ):
141
173
r"""Objet non représenté en lui-même, les 2
142
174
boutons qu'il contient étant, eux, des widgets"""
143
175
value = Bool ()
144
176
145
- def __init__ (self , parent , b1 , b2 ):
177
+ def __init__ (self , parent , b1 , b2 , link = True ):
146
178
"""A domino has a parent widget and is made of 2 buttons"""
179
+ b1 .link = None
180
+ b2 .link = None
181
+ b1 .value = False
182
+ b2 .value = False
147
183
super (Domino , self ).__init__ ()
184
+ self .value = False
148
185
self .geometry = DominoGeometry (b1 .position , b2 .position )
149
186
self .parent = parent
150
187
self .key = None
151
188
self .first = b1
152
189
self .second = b2
153
190
self .buttons = (b1 ,b2 )
154
- b1 .link = dlink ((b1 , 'value' ), (b2 , 'value' ))
155
- b2 .link = dlink ((b2 , 'value' ), (b1 , 'value' ))
156
191
self .link = None
157
192
self .direction = None
158
193
self .orientation = None
159
194
self .compute ()
160
- self .donottrack = False
195
+ if link :
196
+ self .set_links ()
161
197
162
198
def __repr__ (self ):
163
199
if self .value :
164
200
return repr (self .first ) + ' -> ' + repr (self .second ) + " PRESSED"
165
201
return repr (self .first ) + ' -> ' + repr (self .second )
166
202
167
203
def compute (self , css_classes = ['b0' , 'b1' , 'b2' , 'b3' , 'b4' ]):
168
- """Compute buttons relative positions.
169
- Create double directional link from both buttons"""
204
+ """Compute buttons relative positions."""
170
205
self .geometry .compute ()
171
- #print(self.geometry.__dict__)
172
206
if css_classes :
173
207
for cl in css_classes :
174
208
self .first .remove_class (cl )
@@ -196,26 +230,26 @@ def compute(self, css_classes=['b0', 'b1', 'b2', 'b3', 'b4']):
196
230
elif self .geometry .orientation == - 1 :
197
231
self .first .add_class ('bottom' )
198
232
self .second .add_class ('top' )
233
+
234
+ def set_links (self ):
235
+ """Create double directional link from both buttons
236
+ and for domino."""
237
+ self .first .link = dlink ((self .first , 'value' ), (self .second , 'value' ))
238
+ self .second .link = dlink ((self .second , 'value' ), (self .first , 'value' ))
199
239
self .link = ddlink (((self .first , 'value' ), (self .second , 'value' )), (self , 'value' ), logic = 'and' , set_at_init = False ) # Fresh ddlink
200
240
201
241
def is_pressed (self ):
202
242
"""Is the domino pressed?"""
203
243
return self .value
204
244
205
- # def set_value(self, value):
206
- # """Set domino value
207
- # As we have a directional link,
208
- # the domino value will also be set.
209
- # """
210
- # self.link.unlink()
211
- # self.first.value = value
212
- # self.second.value = value
213
- # self.link = ddlink(((self.first, 'value'), (self.second, 'value')), (self, 'value'), logic='and', set_at_init=False) # Fresh ddlink
214
-
215
245
def reset (self ):
216
- """Full domino reset"""
217
- #self.set_value(False)
246
+ """Full domino unlink"""
218
247
self .link .unlink ()
248
+ self .first .link .unlink ()
249
+ self .second .link .unlink ()
250
+ self .value = False
251
+ self .first .value = False
252
+ self .second .value = False
219
253
220
254
def flip (self , other ):
221
255
"""Flip self with some neighboring domino"""
@@ -248,14 +282,15 @@ def __init__(self, g, css_classes=['b0', 'b1', 'b2', 'b3', 'b4']):
248
282
self .css_classes = css_classes
249
283
super (FlippingDominosWidget , self ).__init__ (g , adapter = FlippingDominosAdapter (),
250
284
cell_layout = smallblyt ,
251
- cell_widget_classes = [styled_button_cell (),
252
- styled_button_cell (style_name = 'b1' ),
253
- styled_button_cell (style_name = 'b2' ),
254
- styled_button_cell (style_name = 'b3' ),
255
- styled_button_cell (style_name = 'b4' ),
285
+ cell_widget_classes = [my_styled_button_cell (),
286
+ my_styled_button_cell (style_name = 'b1' ),
287
+ my_styled_button_cell (style_name = 'b2' ),
288
+ my_styled_button_cell (style_name = 'b3' ),
289
+ my_styled_button_cell (style_name = 'b4' ),
256
290
],
257
291
cell_widget_class_index = make_cell_widget_class_index (g ),
258
292
blank_widget_class = BlankButton )
293
+
259
294
def draw (self ):
260
295
self .dominos = {}
261
296
super (FlippingDominosWidget , self ).draw ()
@@ -279,8 +314,10 @@ def apply_matching(self, matching):
279
314
"""Apply a matching"""
280
315
self .dominos = {}
281
316
for d in matching :
282
- self .match (self .children [d .first [0 ]].children [d .first [1 ]],
283
- self .children [d .second [0 ]].children [d .second [1 ]])
317
+ self .match (
318
+ self .children [d .first [0 ]].children [d .first [1 ]],
319
+ self .children [d .second [0 ]].children [d .second [1 ]]
320
+ )
284
321
285
322
def update (self ):
286
323
self .apply_matching (self .value .matching )
@@ -292,54 +329,100 @@ def domino_for_position(self, pos):
292
329
geometry = self .value .domino_for_position (pos )
293
330
for t in (geometry .first , geometry .second ):
294
331
if t in self .dominos :
295
- print ("domino for position" , t , ":" , self .dominos [t ].geometry , self .dominos [t ].value )
296
332
return self .dominos [t ]
297
333
298
- def not_tracking (self , value ):
299
- self .donottrack = value
300
- for d in self .dominos .values ():
301
- d .donottrack = value
302
-
303
334
@observe (All )
304
335
def set_cell (self , change ):
305
336
if self .donottrack :
306
337
return
307
338
if change .name .startswith ('cell_' ):
308
339
print ("set_cell()" , change .name , change .old , change .new )
309
- else :
310
- print ("set_cell()" , change .name )
311
- # Try to reset everything right now to avoid unwanted propagations
312
- domino = self .domino_for_position (extract_coordinates (change .name ))
313
- # First, we want to make the pressed domino visible to the user
314
- self .not_tracking (True )
315
- domino .first .value = True
316
- domino .second .value = True
317
- # Any pressed neighbor?
340
+ click_pos = extract_coordinates (change .name )
341
+ domino = self .domino_for_position (click_pos )
342
+ if not domino : # or domino.donottrack:
343
+ return
344
+ # The domino must be entirely pressed
345
+ if not domino .first .value or not domino .second .value :
346
+ return
347
+ # The domino must have a pressed neighbor
318
348
other = None
319
349
if self .dirty :
320
350
for pos in self .dirty :
351
+ if other and other .geometry != self .domino_for_position (pos ).geometry :
352
+ raise Exception ("on a un double dans les voisins pressés: %s et %s" % (
353
+ other .geometry , self .domino_for_position (pos ).geometry ))
321
354
other = self .domino_for_position (pos )
322
355
if other and not other .geometry in domino .geometry .neighbors ():
323
356
other = None
324
357
continue # we don't have to reset everything, I guess(hope)
325
358
if not other :
359
+ # Feed the 'dirty' dict and return
326
360
self .dirty [domino .geometry .first ] = True
327
361
self .dirty [domino .geometry .second ] = True
328
- self .not_tracking (False )
329
362
return
330
- if domino .link :
331
- domino .link .unlink ()
332
- if other .link :
333
- other .link .unlink ()
334
- self .not_tracking (False )
363
+ # Do the flip
335
364
super (FlippingDominosWidget , self ).set_cell (change )
336
- self .not_tracking (True )
337
- # And now, we want to reset everything before the flip
338
- domino .first .value = False
339
- domino .second .value = False
340
- other .first .value = False
341
- other .second .value = False
342
- # Now, recreate the 2 dominos and compute style
365
+ # Unlink and reset values
366
+ self .donottrack = True
367
+ domino .reset ()
368
+ other .reset ()
369
+ # Build our new dominos
370
+ new_domino , new_other = None , None
371
+ for g1 in self .value .matching :
372
+ if g1 .first == domino .geometry .first or g1 .first == other .geometry .first :
373
+ d1 , d2 = domino .geometry , other .geometry
374
+ if g1 .first == domino .geometry .first :
375
+ new_domino = Domino (
376
+ self ,
377
+ self .children [g1 .first [0 ]].children [g1 .first [1 ]],
378
+ self .children [g1 .second [0 ]].children [g1 .second [1 ]],
379
+ link = False
380
+ )
381
+ self .dominos [domino .key ] = new_domino
382
+ for g2 in g1 .neighbors ():
383
+ if not g2 in self .value .matching :
384
+ continue
385
+ if (other .key in (g2 .first , g2 .second )) or \
386
+ (other .key == g1 .second and domino .geometry .second in (g2 .first , g2 .second )):
387
+ new_other = Domino (
388
+ self ,
389
+ self .children [g2 .first [0 ]].children [g2 .first [1 ]],
390
+ self .children [g2 .second [0 ]].children [g2 .second [1 ]],
391
+ link = False
392
+ )
393
+ self .dominos [other .key ] = new_other
394
+ break
395
+ elif g1 .first == other .geometry .first :
396
+ new_other = Domino (
397
+ self ,
398
+ self .children [g1 .first [0 ]].children [g1 .first [1 ]],
399
+ self .children [g1 .second [0 ]].children [g1 .second [1 ]],
400
+ link = False
401
+ )
402
+ self .dominos [other .key ] = new_other
403
+ for g2 in g1 .neighbors ():
404
+ if not g2 in self .value .matching :
405
+ continue
406
+ if (domino .key in (g2 .first , g2 .second )) or \
407
+ (domino .key == g1 .second and other .geometry .second in (g2 .first , g2 .second )):
408
+ new_domino = Domino (
409
+ self ,
410
+ self .children [g2 .first [0 ]].children [g2 .first [1 ]],
411
+ self .children [g2 .second [0 ]].children [g2 .second [1 ]],
412
+ link = False
413
+ )
414
+ self .dominos [domino .key ] = new_domino
415
+ break
416
+ if new_domino and new_other :
417
+ break
418
+ # Check that new dominos are sound and the flip has actually been performed
419
+ assert (new_domino is not None and new_other is not None )
420
+ assert (new_domino .geometry != domino .geometry and new_other .geometry != other .geometry )
421
+ # Compute the dominos
422
+ new_domino .compute ()
423
+ new_other .compute ()
424
+ new_domino .set_links ()
425
+ new_other .set_links ()
426
+ # Reset
343
427
self .reset_dirty ()
344
- self .update ()
345
- self .not_tracking (False )
428
+ self .donottrack = False
0 commit comments