Pythonでクロスプラットフォームなアプリが作れる「Flet」を試す (original) (raw)

Flet controls

https://flet.dev/docs/getting-started/flet-controls

FletのUIは、controls(通称: ウィジェット)で構成される。controlsはPageまたは他のcontrolsの中に配置する。Pageが最上位のcontrolになり、controlsをネストしていくとPageがルートになるツリー構造として、UIが構成される。

controlsはPythonのクラスになっているので、以下のようにインスタンスを作成することで利用できる。そしてPageにcontrolsを追加、Pageを更新することで反映される。

import flet as ft

def main(page: ft.Page):
    # controlを初期化
    t = ft.Text(value="Hello, world!", color="green", size=30)
    # pageに追加
    page.controls.append(t)
    # pageを更新
    page.update()

ft.app(main)
$ flet run -d -r my_flet_app

page.update()を使うとcontrolのプロパティおよびUIを更新できる。

import flet as ft
import time

def main(page: ft.Page):
  t = ft.Text(size=30)
  page.add(t) # page.controls.append(t) および page.update() のショートカット

  for i in range(100):
      t.value = f"Step {i}"
      page.update()
      time.sleep(1)

ft.app(main)

Pageのように、他のcontrolを含むことができる「コンテナ」としてのcontrolもある。

rowだと横に並べる

import flet as ft

def main(page: ft.Page):

  page.add(
      ft.Row(controls=[
        ft.TextField(label="Your name"),
        ft.ElevatedButton(text="Say my name!")
      ])
  )

ft.app(main)

Columnだと縦に並べる、といった感じ。

import flet as ft

def main(page: ft.Page):

  page.add(
      ft.Column(controls=[
        ft.TextField(label="Your name"),
        ft.ElevatedButton(text="Say my name!")
      ])
  )

ft.app(main)

page.update()は最後に実行されてから変更があった部分だけを更新するようになっているので、controlをまとめて更新してからpage.update()一発で変更できる。

import flet as ft
import time

def main(page: ft.Page):

  for i in range(100):
    page.controls.append(ft.Text(f"Line {i}", size=20))
    if i > 4:
        page.controls.pop(0)

    page.update()
    time.sleep(0.5)


ft.app(main)

ボタンなどのcontrolにはイベントハンドラを設定できる。

import flet as ft
import time

def main(page: ft.Page):

    def button_clicked(e):
        page.add(ft.Text("クリックされました!", size=30))

    page.add(ft.ElevatedButton(text="クリックしてね", on_click=button_clicked))

ft.app(main)

これを使ってシンプルなTo-Doアプリの例が載っている。

import flet as ft

def main(page):
    def add_clicked(e):
        page.add(ft.Checkbox(label=new_task.value))
        new_task.value = ""
        new_task.focus()
        page.update()

    new_task = ft.TextField(hint_text="ToDoを入力", width=300)
    page.add(ft.Row([new_task,ft.ElevatedButton("追加", on_click=add_clicked)]))

ft.app(main)

こんなことが書いてある、なるほど、今の自分にはこのほうがわかりやすいかもしれない。

Fletは、ステートフルコントロールを使用して「手動」でアプリケーションのUIを構築し、コントロールのプロパティを更新することでそれを変更する、命令型UIモデルを実装している。Flutterは、アプリケーションデータの変更時にUIが自動的に再構築される宣言型モデルを実装している。最新のフロントエンドアプリケーションにおけるアプリケーションの状態管理は、本質的に複雑な作業であり、Fletの「旧式」のアプローチは、フロントエンドの経験のないプログラマーにとってはより魅力的である可能性がある。

visible/disabledプロパティ

controlの可視性や操作可否をコントロールすることができるプロパティ。なお、子要素がある場合はそれも含めて制御される。

visibleを使うと表示・非表示を切り替えれる。デフォルトはTrue(表示する)。

import flet as ft

def main(page: ft.Page):

    def toggle_visible(e):
        if t.visible == True:
            t.visible = False
            button.text = "visible!"
        else:
            t.visible = True
            button.text = "invisible!"
        page.update()

    button = ft.ElevatedButton("invisible!", on_click=toggle_visible)
    t = ft.Text(value="Hello, world!", color="green", size=30)

    page.add(ft.Column([button, t]))
    page.update()

ft.app(main)

disabledを使うとデータ入力に関するコントロールの操作、例えば入力・変更等の可否を切り替えれる。デフォルトはFalse(コントロール可能)。

import flet as ft

def main(page: ft.Page):

    def toggle_disabled(e):
        if t.disabled == True:
            t.disabled = False
            button.text = "disable!"
        else:
            t.disabled = True
            button.text = "enable!"
        page.update()

    button = ft.ElevatedButton("disable", on_click=toggle_disabled)
    t = ft.TextField(hint_text="enter something...", width=300)

    page.add(ft.Column([button, t]))
    page.update()

ft.app(main)

あとはボタン・テキストボックス・チェックボックス・ドロップダウンなどを使ったイベントハンドラのサンプルコートが並んでる。

ボタンをクリックしたら増減するカウンター。

import flet as ft

def main(page: ft.Page):
    page.title = "カウンターのサンプル"
    page.vertical_alignment = ft.MainAxisAlignment.CENTER

    txt_number = ft.TextField(value="0", text_align="right", width=100)

    def minus_click(e):
        txt_number.value = str(int(txt_number.value) - 1)
        page.update()

    def plus_click(e):
        txt_number.value = str(int(txt_number.value) + 1)
        page.update()

    page.add(
        ft.Row(
            [
                ft.IconButton(ft.icons.REMOVE, on_click=minus_click),
                txt_number,
                ft.IconButton(ft.icons.ADD, on_click=plus_click),
            ],
            alignment=ft.MainAxisAlignment.CENTER,
        )
    )

ft.app(main)

名前を入力したら挨拶が表示される。

import flet as ft

def main(page):
    def btn_click(e):
        if not txt_name.value:
            txt_name.error_text = "名前を入力してください"
            page.update()
        else:
            name = txt_name.value
            page.clean()
            page.add(ft.Text(f"こんにちは、{name} さん!"))

    txt_name = ft.TextField(label="あなたの名前")

    page.add(txt_name, ft.ElevatedButton("挨拶する", on_click=btn_click))

ft.app(main)

フォーカスが当たるとプレースホルダーの文字がラベルっぽくなるの良いね

何も入力せずにボタンクリックするとこうなる

チェックボックスに連動して、チェックボックスの状態をテキストで出力

import flet as ft


def main(page):
    def checkbox_changed(e):
        output_text.value = (
            f"スキーを習得した? :  {todo_check.value}."
        )
        page.update()

    output_text = ft.Text()
    todo_check = ft.Checkbox(label="ToDo: スキーの習得", value=False, on_change=checkbox_changed)
    page.add(todo_check, output_text)

ft.app(main)

ドロップダウンリストの選択値を取得して表示

import flet as ft


def main(page: ft.Page):
    def button_clicked(e):
        output_text.value = f"リストから選択された色:  {color_dropdown.value}"
        page.update()

    output_text = ft.Text()
    submit_btn = ft.ElevatedButton(text="送信", on_click=button_clicked)
    color_dropdown = ft.Dropdown(
        width=100,
        options=[
            ft.dropdown.Option("赤"),
            ft.dropdown.Option("緑"),
            ft.dropdown.Option("青"),
        ],
    )
    page.add(color_dropdown, submit_btn, output_text)

ft.app(main)