GUI」タグアーカイブ

Eelにて新規ウィンドウをアプリケーションモードで開きたい

Eelで新規ウィンドウを開く際に、単純にhtmlやjavascriptから開くとアプリケーションモードが解除されて、タブやらメニューバーやら表示されてしまう。
別に問題があるわけじゃないんだけど、何となく格好が悪い。

ということで、Pythonから新規ウィンドウを開けないか試してみた。
単純にeel.start()をもう一度呼べばいいのではないかと試してみる。

フォルダ構成main.py
web
 └─ main.html

main.pyimport eel

def main():
    eel.init("web")
    eel.start("main.html")

"""新しいウィンドウを開く"""
@eel.expose
def open_window():
    eel.start("main.html")

if __name__ == '__main__':
     main()

main.html<html>
<head>
    <meta charset="UTF-8">
    <title>eel_exsample</title>
    <script type="text/javascript" src="/eel.js"></script>
    <script type="text/javascript">
        // 新しいWindowを開くPythonスクリプトを実行する
        function openWindow() {
            eel.open_window();
        }
    </script>
</head>

<body>
    <a href="#" onclick="openWindow()">openWindow</a>
</body>
</html>

・実行結果


openWindowのリンクを押下すると新しいウィンドウが開いた。
問題なく動作しているようだが、コンソールに以下のエラーが出ている。

OSError: [WinError 10048] 通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。: ('localhost', 8000)
2018-10-04T01:58:57Z  failed with OSError

同じポートでもう一つWebサーバーを立てようとして怒られてるようだ。
1回目のeel.start()ですでにサーバーは立ってるから問題ないんだけどちょっと気持ちが悪い。

以下のように立てるwebサーバーのポートに0を指定してやって、空いている任意のポートを取得するようにしてやればエラーは発生しなくなる。

main.pyimport eel

def main():
    eel.init("web")
    eel.start("main.html", options={"port" : 0,})

"""新しいウィンドウを開く"""
@eel.expose
def open_window():
    eel.start("main.html", options={"port" : 0,})

if __name__ == '__main__':
     main()


ただ、新しいウィンドウを開くたびにサーバーが立つのでこれは無いかな。

Eelのソースを読んだところ、eel.browsers.open()にてよしなにchromiumのウィンドウを開いてくれているようだ。。。たぶん。
なので、以下のように直接eel.browsers.open()を呼び出してやれば、エラーメッセージも出てこず、サーバプロセスは一つのみで動作させることができる。
注意点として、1回目にeel.start()に渡したオプションを使いまわすようにすること (eel.start()にて必要なオプションを詰めてくれるので、それを使いまわす)。

main.pyimport eel

web_app_options = {
	'port': 0,
}

def main():
    eel.init("web")
    # eel.start()では、引数に渡したオプションに未指定のものがあった場合、デフォルトの値を詰めてくれる。
    eel.start("main.html", options=web_app_options)

"""新しいウィンドウを開く"""
@eel.expose
def open_window():
    # 第一引数は開きたいURLのリスト、第二引数はeel.start()に渡したオプション
    eel.browsers.open(["main.html",], web_app_options)

if __name__ == '__main__':
     main()

ちょっと無理やり感があるかな。

その他の注意点として、開いたばかりのウィンドウのjavascriptの関数をPythonから呼び出せないことに注意する。
eel.sleep()などで開き終わるまで待ってやれば、新規ウィンドウのjavascriptの関数を呼び出せるようになる。

また、同じhtmlを複数ウィンドウ開いている場合、そのhtmlに含まれるjavascriptの関数をPythonから呼び出したら、すべてのウィンドウでその関数が実行される。
一つのウィンドウのみ対象にしたい場合は、javascriptに変数として任意のIDを持たせるなどの工夫が必要になりそう。

と色々はまりどころがあること考えると、Eelで複数ウィンドウ開くのは悪手くさいな。
少なくとも複数ウィンドウに同じ名称のjavascriptの関数が含まれる状況は避けた方がいい(Pythonから呼ばれないなら問題ない)。
複数ウィンドウが必要な場合は、javascriptのモーダルダイアログ扱うようなライブラリ使って、あんまりポカスカ新規ウィンドウが作られないようにした方がいいかな。
大体の機能はモーダルダイアログで済みそうだし、それならアプリケーションモードの見た目との相性も悪くないはず。

PythonのちょっとしたGUIにEelが便利

自分用にちょっとしたGUIをPythonで作りたいときのライブラリにしっくりくるものが無かったんだけど、最近Eelというライブラリを試してみたらこれが中々良い感じだった。
Electronを軽くした感じのライブラリで、GUIをhtmlで作成できる。
内部的にはbottleでサーバーを立てて、それにChromiumからリクエストを送るような作りになっているようだ。

使い方は公式のgitや、Qiitaの記事を参照。

例えば、リンクをクリックした際にPythonのスクリプトを起動したい場合は以下のようにする。

フォルダ構成main.py
web
 └─ main.html

main.pyimport eel

def main():
    eel.init("web")
    eel.start("main.html")


"""link1が押下された際に呼び出すスクリプト"""
@eel.expose
def link1_click():
    print("link1_clicked")

"""link2が押下された際に呼び出すスクリプト"""
@eel.expose
def link2_click(args):
    print(args)
    return "link2_clicked"

if __name__ == '__main__':
     main()

main.html<html>
<head>
    <meta charset="UTF-8">
    <title>eel_exsample</title>
    <script type="text/javascript" src="/eel.js"></script>
    <script type="text/javascript">
        // 非同期でPythonスクリプトを実行する
        function link1Click() {
            eel.link1_click();
        }
        
        // Pythonスクリプトに引数を渡し、結果の帰り値を待つ例
        async function link2Click() {
            let val = await eel.link2_click("args-test")();
            alert(val);
        }
    </script>
</head>

<body>
    <a href="#" onclick="link1Click()">Link1</a>
    <br>
    <a href="#" onclick="link2Click()">Link2</a>
</body>
</html>

・実行結果


Link1は単純にPythonスクリプトを実行する例、Link2はPythonスクリプトに引数を渡し、その帰り値を待つ実行例。

・Link2押下結果


非常にシンプルである。
自分用に作ったスクリプトにちょっとしたGUIを付けたいときなんかは非常に重宝しそう。
作ったソフト公開するには作りが素朴すぎるかな。少し使い込んでみてここら辺の感覚はつかみたい。

これとPygletぐらいあれば、自分のやりたいことは一通り簡単に実装できそうだ。