使用Python创建Flet应用程序
入门
使用Python创建Flet应用程序
要编写 Flet 应用程序,您不需要是前端大师,但建议您具备 Python 和面向对象编程的基础知识。
在本文档中,我们将学习一个Flet应用程序的结构,了解如何使用 Flet 控件输出数据、向用户请求数据以及构建基本页面布局。我们还将介绍一些打包和部署选项,以便为您的用户提供现成的应用程序。
安装flet模块
Flet 需要 Python 3.7 或更高版本。要启动 Flet,您需要先安装flet模块:
pip install flet
💥 注意
需要升级flet模块,请运行:pip install flet --upgrade
要安装 Flet 预发行版(对于高级用户),请运行(初学者不推荐,请忽略):
pip install flet --pre
💥 警告
我们建议将预发布版本安装到虚拟环境中。
Linux
在 Linux 和 WSL 上运行 Flet 应用程序需要安装GStreamer库。您的系统中很可能已经安装了它们,但如果您在运行 Flet 应用程序时遇到该错误error while loading shared libraries: libgstapp-1.0.so.0: cannot open shared object file: No such file or directory
,则需要安装 GStreamer。
要在 Ubuntu/Debian 上安装 GStreamer,请运行以下命令:
sudo apt-get update
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio
有关在其他 Linux 发行版上安装的信息,请参阅本指南。
WSL
Flet 应用程序可以在 WSL2 上运行。如果您遇到cannot open display
错误,请按照本指南进行故障排除。
基础应用程序结构
一个非常简单的 Flet 应用程序具有以下结构:
import flet as ft
def main(page: ft.Page):
# 在页面上增加或者更新控件
pass
ft.app(target=main)
💥 注意
本节特意称为“基础”,因为在本指南的后面部分,我们将学习具有可重用控件的应用程序结构的更真实的方法。
典型的 Flet 程序以调用flet.app()
应用程序开始等待新用户会话的位置结束。函数main()是 Flet 应用程序中的入口点。每个用户会话都会在一个新线程上调用它,并Page传递一个实例。在浏览器中运行 Flet 应用程序时,将为每个打开的选项卡或页面启动新的用户会话。作为桌面应用程序运行时,仅创建一个会话。
Page就像特定于用户的“画布”,用户会话的视觉状态。要构建应用程序 UI,您需要向页面添加和删除控件,并更新它们的属性。上面的代码示例将仅向每个用户显示一个空白页面。
默认情况下,Flet 应用程序在本机操作系统窗口中启动,这对于开发来说非常方便。但是,您可以通过修改调用在新的浏览器窗口中打开它,flet.app如下所示:
ft.app(target=main, view=ft.AppView.WEB_BROWSER)
信息 在内部,每个 Flet 应用程序都是一个 Web 应用程序,即使在本机操作系统窗口中打开,内置的 Web 服务器仍然会在后台启动。Flet Web 服务器称为“Fletd”,默认情况下它侦听随机 TCP 端口。您可以指定自定义 TCP 端口,然后在浏览器中打开应用程序以及桌面视图:
flet.app(port=8550, target=main)
在浏览器中打开http://localhost:<port>
以查看 Flet 应用程序的 Web 版本。
用户界面由控件(也称为小部件)组成。要使控件对用户可见,必须将它们添加到Page其他控件中或内部。页面是最顶层的控件。相互嵌套的控件可以表示为以 Page 为根的树。
控件只是常规的 Python 类。通过构造函数创建控件实例,参数与其属性相匹配,例如:
t = ft.Text(value="Hello, world!", color="green")
要在页面上显示控件,请将其添加到controls页面列表并调用page.update()以将页面更改发送到浏览器或桌面客户端:
import flet as ft
def main(page: ft.Page):
t = ft.Text(value="Hello, world!", color="green")
page.controls.append(t)
page.update()
ft.app(target=main)
笔记 在下面的示例中,我们将仅显示函数的内容main。
您可以修改控件属性,UI 将在下一次更新page.update():
t = ft.Text()
page.add(t) # it's a shortcut for page.controls.append(t) and then page.update()
for i in range(10):
t.value = f"Step {i}"
page.update()
time.sleep(1)
有些控件是“容器”控件(如页面),可以包含其他控件。例如,Rowcontrol 允许将其他控件一一排列在一行中:
page.add(
ft.Row(controls=[
ft.Text("A"),
ft.Text("B"),
ft.Text("C")
])
)
或TextField旁边ElevatedButton:
page.add(
ft.Row(controls=[
ft.TextField(label="Your name"),
ft.ElevatedButton(text="Say my name!")
])
)
page.update()足够聪明,只发送自上次调用以来所做的更改,因此您可以向页面添加几个新控件,删除其中一些控件,更改其他控件的属性,然后调用以进行批量更新,例如page.update():
for i in range(10):
page.controls.append(ft.Text(f"Line {i}"))
if i > 4:
page.controls.pop(0)
page.update()
time.sleep(0.3)
某些控件(例如按钮)可以具有对用户输入做出反应的事件处理程序,例如ElevatedButton.on_click:
def button_clicked(e):
page.add(ft.Text("Clicked!"))
page.add(ft.ElevatedButton(text="Click me", on_click=button_clicked))
以及简单待办事项的更高级示例:
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()
new_task.update()
new_task = ft.TextField(hint_text="Whats needs to be done?", width=300)
page.add(ft.Row([new_task, ft.ElevatedButton("Add", on_click=add_clicked)]))
ft.app(target=main)
信息 Flet 实现命令式UI 模型,您可以在其中“手动”构建具有状态控件的应用程序 UI,然后通过更新控件属性来更改它。Flutter 实现了声明式模型,其中 UI 会根据应用程序数据更改自动重新构建。在现代前端应用程序中管理应用程序状态本质上是一项复杂的任务,而 Flet 的“老式”方法对于没有前端经验的程序员来说可能更有吸引力。
visible 每个控件都有默认visible属性true- 控件在页面上呈现。设置visible为false完全阻止控件(及其所有子控件,如果有)在页面画布上呈现。隐藏控件无法通过键盘或鼠标进行聚焦或选择,并且它们不会发出任何事件。
disabled 每个控件都有默认disabled的属性false- 控件及其所有子控件均已启用。 disabled属性主要与TextField、Dropdown、Checkbox、 按钮等数据输入控件一起使用。但是,disabled可以设置为父控件,其值将递归地传播到所有子控件。
例如,如果您有一个具有多个输入控件的表单,您可以disabled单独为每个控件设置属性:
first_name = ft.TextField()
last_name = ft.TextField()
first_name.disabled = True
last_name.disabled = True
page.add(first_name, last_name)
或者您可以将表单控件放入容器中,例如Column,然后disabled为列设置:
first_name = ft.TextField()
last_name = ft.TextField()
c = ft.Column(controls=[
first_name,
last_name
])
c.disabled = True
page.add(c)
控制 Flet 控件是对象,为了访问它们的属性,我们需要保留对这些对象的引用(变量)。
考虑以下示例:
import flet as ft
def main(page):
first_name = ft.TextField(label="First name", autofocus=True)
last_name = ft.TextField(label="Last name")
greetings = ft.Column()
def btn_click(e):
greetings.controls.append(ft.Text(f"Hello, {first_name.value} {last_name.value}!"))
first_name.value = ""
last_name.value = ""
page.update()
first_name.focus()
page.add(
first_name,
last_name,
ft.ElevatedButton("Say hello!", on_click=btn_click),
greetings,
)
ft.app(target=main)
在方法的一开始main(),我们创建了三个将在按钮的处理程序中使用的控件on_click:两个TextField用于名字和姓氏,一个Column- 用于问候消息的容器。main()我们创建控件并设置其所有属性,并在方法结束时在page.add()调用中使用它们的引用(变量)。
当添加越来越多的控件和事件处理程序时,将所有控件定义保留在一个位置变得很困难,因此它们会分散在整个main()主体中。浏览一下page.add()参数,很难想象(如果没有在 IDE 中不断跳转到变量定义)最终的形式会是什么样子:
page.add(
first_name,
last_name,
ft.ElevatedButton("Say hello!", on_click=btn_click),
greetings,
)
是first_name一个 TextField,它是否设置了自动对焦?问候语是aRow还是a Column?
Flet 提供了Ref实用程序类,它允许定义对控件的引用,在事件处理程序中使用该引用,并稍后在构建树时设置对真实控件的引用。这个想法来自React。
要定义新的类型化控件引用:
first_name = ft.Ref[ft.TextField]()
要访问引用的控件(控件取消引用),请使用Ref.current属性:
# empty first name
first_name.current.value = ""
要将控件分配给引用,请将Control.ref属性设置为引用:
page.add(
ft.TextField(ref=first_name, label="First name", autofocus=True)
)
笔记 所有 Flet 控件都有ref属性。
我们可以重写我们的程序以使用引用:
import flet as ft
def main(page):
first_name = ft.Ref[ft.TextField]()
last_name = ft.Ref[ft.TextField]()
greetings = ft.Ref[ft.Column]()
def btn_click(e):
greetings.current.controls.append(
ft.Text(f"Hello, {first_name.current.value} {last_name.current.value}!")
)
first_name.current.value = ""
last_name.current.value = ""
page.update()
first_name.current.focus()
page.add(
ft.TextField(ref=first_name, label="First name", autofocus=True),
ft.TextField(ref=last_name, label="Last name"),
ft.ElevatedButton("Say hello!", on_click=btn_click),
ft.Column(ref=greetings),
)
ft.app(target=main)
现在我们可以清楚地看到page.add()页面的结构及其构建的所有控件。
是的,逻辑变得有点冗长,因为您需要添加.current.访问引用的控制,但这是个人喜好的问题