LoginSignup
3
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-08-17

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

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

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を使って無理やり書いてみます。

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では必要な非同期処理のコード記述をしなくても良いのじゃないかと思っています。

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が出ません。

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のドキュメントは

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0