33
44from .flipping_aztecdiamond import *
55from 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
77from ipywidgets import Layout
88from traitlets import dlink , HasTraits , Bool , observe , All
99from contextlib import contextmanager
@@ -22,6 +22,7 @@ def set_cell(self, obj, pos, val, dirty={}):
2222 we prepare a possible flip
2323 or we try to complete the flip if it has been prepared previously
2424 """
25+ #print("in the adapter for pos =", pos, "val =", val, "dirty =", dirty)
2526 # Find out the relevant matching for 'pos'
2627 d1 = obj .domino_for_position (pos )
2728 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={}):
3435 continue
3536 if d2 in d1 .neighbors ():
3637 # Do the flip
38+ print ("before flipping :" , d1 , d2 )
3739 obj .flip (d1 , d2 )
40+ print ("after flipping :" , d1 , d2 )
3841 return obj
3942 return Exception ("Please select a second domino!" )
4043 else :
@@ -43,7 +46,7 @@ def set_cell(self, obj, pos, val, dirty={}):
4346
4447class ddlink (dlink ):
4548 """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
4750 Usage: b1 = Button(description = 'B1')
4851 b2 = Button(description = 'B2')
4952 b3 = Button(description = 'B3')
@@ -101,6 +104,9 @@ def _busy_updating(self):
101104 def _update (self , change ):
102105 #if self.updating or self.target[0].donottrack:
103106 # return
107+ #if self.target[0].donottrack:
108+ # print("on sort ici !!!")
109+ # return
104110 with self ._busy_updating ():
105111 if self .logic == 'and' :
106112 if self .intermediate_value == False : # aucun bouton pressé avant
@@ -137,38 +143,66 @@ def unlink(self):
137143 source [0 ].unobserve (self ._update , names = source [1 ])
138144 self .double_source , self .target = None , None
139145
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+
140172class Domino (HasTraits ):
141173 r"""Objet non représenté en lui-même, les 2
142174 boutons qu'il contient étant, eux, des widgets"""
143175 value = Bool ()
144176
145- def __init__ (self , parent , b1 , b2 ):
177+ def __init__ (self , parent , b1 , b2 , link = True ):
146178 """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
147183 super (Domino , self ).__init__ ()
184+ self .value = False
148185 self .geometry = DominoGeometry (b1 .position , b2 .position )
149186 self .parent = parent
150187 self .key = None
151188 self .first = b1
152189 self .second = b2
153190 self .buttons = (b1 ,b2 )
154- b1 .link = dlink ((b1 , 'value' ), (b2 , 'value' ))
155- b2 .link = dlink ((b2 , 'value' ), (b1 , 'value' ))
156191 self .link = None
157192 self .direction = None
158193 self .orientation = None
159194 self .compute ()
160- self .donottrack = False
195+ if link :
196+ self .set_links ()
161197
162198 def __repr__ (self ):
163199 if self .value :
164200 return repr (self .first ) + ' -> ' + repr (self .second ) + " PRESSED"
165201 return repr (self .first ) + ' -> ' + repr (self .second )
166202
167203 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."""
170205 self .geometry .compute ()
171- #print(self.geometry.__dict__)
172206 if css_classes :
173207 for cl in css_classes :
174208 self .first .remove_class (cl )
@@ -196,26 +230,26 @@ def compute(self, css_classes=['b0', 'b1', 'b2', 'b3', 'b4']):
196230 elif self .geometry .orientation == - 1 :
197231 self .first .add_class ('bottom' )
198232 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' ))
199239 self .link = ddlink (((self .first , 'value' ), (self .second , 'value' )), (self , 'value' ), logic = 'and' , set_at_init = False ) # Fresh ddlink
200240
201241 def is_pressed (self ):
202242 """Is the domino pressed?"""
203243 return self .value
204244
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-
215245 def reset (self ):
216- """Full domino reset"""
217- #self.set_value(False)
246+ """Full domino unlink"""
218247 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
219253
220254 def flip (self , other ):
221255 """Flip self with some neighboring domino"""
@@ -248,14 +282,15 @@ def __init__(self, g, css_classes=['b0', 'b1', 'b2', 'b3', 'b4']):
248282 self .css_classes = css_classes
249283 super (FlippingDominosWidget , self ).__init__ (g , adapter = FlippingDominosAdapter (),
250284 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' ),
256290 ],
257291 cell_widget_class_index = make_cell_widget_class_index (g ),
258292 blank_widget_class = BlankButton )
293+
259294 def draw (self ):
260295 self .dominos = {}
261296 super (FlippingDominosWidget , self ).draw ()
@@ -279,8 +314,10 @@ def apply_matching(self, matching):
279314 """Apply a matching"""
280315 self .dominos = {}
281316 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+ )
284321
285322 def update (self ):
286323 self .apply_matching (self .value .matching )
@@ -292,54 +329,100 @@ def domino_for_position(self, pos):
292329 geometry = self .value .domino_for_position (pos )
293330 for t in (geometry .first , geometry .second ):
294331 if t in self .dominos :
295- print ("domino for position" , t , ":" , self .dominos [t ].geometry , self .dominos [t ].value )
296332 return self .dominos [t ]
297333
298- def not_tracking (self , value ):
299- self .donottrack = value
300- for d in self .dominos .values ():
301- d .donottrack = value
302-
303334 @observe (All )
304335 def set_cell (self , change ):
305336 if self .donottrack :
306337 return
307338 if change .name .startswith ('cell_' ):
308339 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
318348 other = None
319349 if self .dirty :
320350 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 ))
321354 other = self .domino_for_position (pos )
322355 if other and not other .geometry in domino .geometry .neighbors ():
323356 other = None
324357 continue # we don't have to reset everything, I guess(hope)
325358 if not other :
359+ # Feed the 'dirty' dict and return
326360 self .dirty [domino .geometry .first ] = True
327361 self .dirty [domino .geometry .second ] = True
328- self .not_tracking (False )
329362 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
335364 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
343427 self .reset_dirty ()
344- self .update ()
345- self .not_tracking (False )
428+ self .donottrack = False
0 commit comments