future is needed for interactive widgets, has to be imported before everything else.

from __future__ import print_function
%load_ext autoreload
%autoreload 2

We can use interactive widgets with Jupyter for our minimal UI.

def fun(x:int):
    print(x)
    
interact(fun, x=10)
<function __main__.fun(x:int)>
games = list(data.games(data.fetch()['Candidates_1953.pgn']))
games_slider = widgets.IntSlider(min=0, max=len(games)-1, step=1, value=0)
widgets.interactive(lambda game_id: print(games[game_id]), game_id=games_slider)

OK, perhaps we can build a simple chess game replay UI?

showGameUi[source]

showGameUi(games:list, games_id:int=0, move:int=0, echo:int=1)

For each UI element that controls input (here: our 3 sliders) we need a function to act as callback for the widget. Because I want those functions to implicitly share some state and yet not be visible as surface API of the ui module, I have chosen to nest those functions instead. Both showGameUi(...) and gameUi(...) act as closures. Since applyMoves(...) takes two arguments it receives input from two sliders (moves_slider, echo_slider). Analogous to applyMoves(...), gameUi(...) takes three arguments. The metric and square filter drop-down list both store/select function pointers.

A rather complex part of the code deals with a typical problem in chess: We have two sides, with alternating moves between them. At move 1, white made its first move and it's black's turn. Once black moves, white is an extra half move away from its initial position, without having moved itself. A move echo larger than 1 makes this issue clearly visible for instance. Our move selection mechanism (here: a Python range) needs to reflect that issue, hence the need for adjusted_{wm, bm} variables. One can discuss whether it might be cleaner to have two separate move stacks, one for each side, simply to avoid mis-steps wrt. move selection when in between moves.

showGameUi(games, games_id=57, move=23, echo=5)

We can override the CSS for IPython's display function so that we can show multiple elements next to each other.

from IPython.display import display, HTML

CSS = """
.output {
    flex-direction: row;
    flex-wrap: wrap;
}
"""

HTML('<style>{}</style>'.format(CSS))