pythonのGUIのFlexxのつまづきポイント

Flexxはpythonで書くので裏にブラウザがいるのを忘れてしまう。Flexxはpythonをjavascriptに変換してブラウザ上でGUIを実現してる。なのでjavascriptに存在しないライブラリは動かない。numpyなどのpython特有のモノを使うとエラー出してしまう。そこを何とかする方法を書いてみます。  

`display`という関数を使うGUIを書いてみます。これは文字の大きさを返す関数です。  

```python
from flexx import flx

def display(s):
    return str(len(s)) + " Chars"

class View(flx.Widget):
    def init(self):
        self.e1 = flx.LineEdit(text='flexx')
        self.b1 = flx.Button(text='文字数カウント')
        self.t1 = flx.Label(text="",  style='border:1px solid red')
    
    def setLabelText(self, s):
        self.t1.set_text(s)
    
    @flx.reaction('b1.pointer_click')
    def _foo(self):
        self.setLabelText(display(self.e1.text))

if __name__ == '__main__':
    m = flx.launch(View)
    flx.run()

```

次に`display`をnumpyを使って無理やり書いてみます。

```python
from flexx import flx
import numpy

def display(s):
    return "{0} Chars".format(numpy.array((len(s),))[0])

class View(flx.Widget):
    def init(self):
        self.e1 = flx.LineEdit(text='flexx')
        self.b1 = flx.Button(text='文字数カウント')
        self.t1 = flx.Label(text="",  style='border:1px solid red')
    
    def setLabelText(self, s):
        self.t1.set_text(s)
    
    @flx.reaction('b1.pointer_click')
    def _foo(self):
        self.setLabelText(display(self.e1.text))

if __name__ == '__main__':
    m = flx.launch(View)
    flx.run()
```  

これだと

```
ValueError: JS in "example_2nd.py" uses 'numpy.array' but cannot convert <class 'builtin_function_or_method'> to JS.
```  
というエラーを出して止まってしまいます。どうやらnumpyがjavascriptに変換できないようです。で、どうするかですが、flx.PyComponentを使えば良さそうです。`emit`を使ってイベントのキューを作るみたい。それをflx.PyComponentの方でイベントとして受け取って処理する手順のようです。この仕組みでeelやpywebviewでは必要な非同期処理のコード記述をしなくても良いのじゃないかと思っています。


```python
from flexx import flx
import numpy

def display(s):
    return "{0} Chars".format(numpy.array((len(s),))[0])

class View(flx.Widget):
    def init(self):
        self.e1 = flx.LineEdit(text='flexx')
        self.b1 = flx.Button(text='文字数カウント')
        self.t1 = flx.Label(text="",  style='border:1px solid red')
        
    @flx.action
    def setLabelText(self, s):
        self.t1.set_text(s)
    
    @flx.reaction('b1.pointer_click')
    def _foo(self):
        self.emit('foo', {"text":self.e1.text})
    
class App(flx.PyComponent):
    def init(self):
        self.widget = View()
    
    @flx.reaction('!widget.foo')
    def _foo(self, *events):
        for ev in events:
            text = ev["text"]
            self.widget.setLabelText(display(text))

if __name__ == '__main__':
    m = flx.launch(App)
    flx.run()
```  
これで動くようになりました。`self.emit('foo', {"text":self.e1.text})`ここで使う`foo`とか`text`とかは任意に決められます。  
`App`クラスにある `@flx.reaction('!widget.foo')` で受け取る`type`を選ぶようです。ちなみに`ev`には
`Dict(text='flexx', type='foo', source=<JsComponent 'View_2' at 0x1896ff950c8>)`
が入っていました。  
### 追記
`self.emit`の部分が`@flx.emitter`で書き直せました。なので`@flx.reaction('widget.foo')`の`widget.foo`部分に納得がいきます。`self.emit`を使う場合は`!`を省くとwarningが出ますが`@flx.emitter`を使う場合はwarningが出ません。

```python
from flexx import flx
import numpy

def display(s):
    return "{0} Chars".format(numpy.array((len(s),))[0])

class View(flx.Widget):
    def init(self):
        self.e1 = flx.LineEdit(text='flexx')
        self.b1 = flx.Button(text='文字数カウント')
        self.t1 = flx.Label(text="",  style='border:1px solid red')
        
    @flx.action
    def setLabelText(self, s):
        self.t1.set_text(s)
        
    @flx.emitter
    def foo(self):
        return {"text":self.e1.text}
    
    @flx.reaction('b1.pointer_click')
    def _foo(self):
        self.foo()
    
class App(flx.PyComponent):
    def init(self):
        self.widget = View()
    
    @flx.reaction('widget.foo')
    def _foo(self, *events):
        for ev in events:
            print(ev)
            text = ev["text"]
            self.widget.setLabelText(display(text))

if __name__ == '__main__':
    m = flx.launch(App)
    flx.run()
```
ここだけ理解していれば、後はドキュメントを見ながら何とかなると思います。  

Flexxのドキュメントは

https://flexx.readthedocs.io/en/stable/index.html