JupyterLabで動的にTableを生成する

# あらかじめTableを生成するのではなく、動的に生成してみた
それっぽいものが見つけられなかったから作ってみた。
簡単に作ろうと思ったらだいぶ凝ってしまった。
それにしても、無駄な隙間が多い。それと、更新は未対応.

(追記)
HTMLへのExportは反映されるけど、閉じて再ロードした際には消える。。。
使えん。。。

```python
t = HTML_Table()
t.clear()
t.appendTh(["a", "b", "c"])
t.append([1,2,3])
t.append(range(4,7))

t = HTML_Table()
t.clear()
t.appendTh(["", "a", "b", "c"])
t.appendTh("a")
t[-1].append(1)
t[-1].append(2)
t[-1].append(3)
t.appendTh("b")
t[-1].append(range(4,7))
None
```

```
a	b	c
1	2	3
4	5	6

	a	b	c
a	1	2	3
b	4	5	6
```


```python
def load_jQuery():
    from IPython.display import Javascript, display
    display(Javascript("""
    if(typeof jQuery === "undefined") {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = '//code.jquery.com/jquery-3.5.1.min.js';
        document.head.appendChild(script);
    }
    """))

def gen_audio(data, rate):
    from IPython.display import Audio
    tmpl = '<audio controls><source src="%s" type="audio/wav"></audio>'
    html = tmpl % Audio(data, rate=rate).src_attr()
    return html

def tag_em(txt):
    return "<em>%s</em>" % txt
def tag_s(txt):
    return "
%s
" % txt
def tag_strong(txt):
    return "<strong>%s</strong>" % txt
def tag_p(txt):
    return "<p>%s</p>" % txt

class HTML_tag(object):
    def __init__(self, tag, name=None):
        if name is None:
            name = self._randomname(32)
        self.name = name
        self.tag  = tag
    def _run(self, script):
        #print(script)
        from IPython.display import Javascript, display
        display(Javascript(script))
    def _append(self, html):
        script = '''$("#{name}").append('{html}')'''.format(name=self.name, html=html)
        self._run(script)
    def _update(self, html):
        script = '''$("#{name}").html('{html}')'''.format(name=self.name, html=html)
        self._run(script)
    def _gen_tag(self, html=""):
        if html is None:
            return '''<{tag} id="{name}" />'''.format(tag=self.tag, name=self.name)
        else:
            return '''<{tag} id="{name}">{html}</{tag}>'''.format(tag=self.tag, name=self.name, html=html)
    def _randomname(self, n):
        import random, string
        return ''.join(random.choices(string.ascii_letters + string.digits, k=n))

class HTML_Td(HTML_tag):
    def __init__(self, name=None, html=""):
        super().__init__('td', name)
        self.html = html
    def update(self, html):
        self.html = html
        self._update(html)
    def clear(self):
        self.update("")
    def append(self, html):
        self.html += html
        self._append(html)
    def to_html(self):
        return self._gen_tag(self.html)
    def __repr__(self):
        return "%s('%s', '%s')" % (self.__class__.__name__, self.html, self.name)

    
class HTML_Th(HTML_Td):
    def __init__(self, name=None, html=""):
        super().__init__(name, html)
        self.tag = "th"
    
class HTML_Tr(HTML_tag):
    def __init__(self, name=None, columns=[], tag=HTML_Td):
        super().__init__('tr', name)
        self.columns = []
        for c in columns:
            if not isinstance(c, HTML_Td):
                c = tag(html=str(c))
            self.columns.append(c)
    def appendTh(self, td):
        return self.append(td, HTML_Th)
    def append(self, td, tag=HTML_Td):
        from collections.abc import Iterable
        if isinstance(td, str):
            td = tag(html=td)
        elif isinstance(td, list) or isinstance(td, Iterable):
            tds = []
            for itr in td:
                tds.append(self.append(itr))
            return tds
        elif not isinstance(td, HTML_Td):
            td = tag(html=str(td))
        self.columns.append(td)
        self._append(td.to_html())
        return td
    def clear(self):
        self.columns = []
        self._update("")
    def to_html(self):
        html = ""
        for c in self.columns:
            html += c.to_html()
        return self._gen_tag(html)
    def __getitem__(self, item):
        if isinstance(item, int) or isinstance(item, slice):
            return self.columns[item]
        else:
            raise ValueError("Not Support type: " + type(item) + ", " + str(item))
    def __repr__(self):
        return "%s(%s, '%s')" % (self.__class__.__name__, self.columns, self.name)

class HTML_Table(HTML_tag):
    def __init__(self, rows=[], name=None):
        super().__init__('table', name)
        self.rows = []
        from IPython.display import HTML, display
        display(HTML(self._gen_tag()))
        for tr in rows:
            self.append(tr)
    def clear(self):
        self.rows = []
        self._update("")
    def appendTh(self, tr):
        return self.append(tr, HTML_Th)
    def append(self, tr, tag=HTML_Td):
        from collections.abc import Iterable
        if isinstance(tr, HTML_Tr):
            pass
        elif isinstance(tr, str) or (not isinstance(tr, list) and not isinstance(tr, Iterable)):
            td = tag(html=tr)
            tr = HTML_Tr(columns=[td], tag=tag)
        else:
            tr = HTML_Tr(columns=tr, tag=tag)
        self.rows.append(tr)
        self._append(tr.to_html())
        return tr
    def __getitem__(self, item):
        if isinstance(item, int) or isinstance(item, slice):
            return self.rows[item]
        if not isinstance(item, tuple):
            raise ValueError("Not Support type: " + type(item) + ", " + str(item))
        if len(item) == 1:
            return self.__getitem__(item[0])
        if len(item) != 2:
            raise ValueError("Not Support len: " + len(item) + ", " + str(item))
        if isinstance(item[0], int):
            return self.__getitem__(item[0])[item[1]]
        if not isinstance(item[0], slice):
            raise ValueError("Not Support type: " + type(item[0]) + ", " + str(item[0]))
        return [e[item[1]] for e in self.__getitem__(item[0])]
    def __repr__(self):
        return "%s(%s, '%s')" % (self.__class__.__name__, self.rows, self.name)
        
load_jQuery()
```