教程 2 - 使之更有趣¶
在 [教程 1
生成的内容¶
在 src/helloworld 目录中,你应该看到 3 个文件:__init__.py、__main__.py 和 app.py。
__init__.py 将 helloworld 目录标记为可导入的 Python 模块。这是一个空文件;它的存在告诉 Python 解释器
helloworld 目录定义了一个模块。
__main__.py 将 helloworld 模块标记为一种特殊的模块 - 可执行模块。如果使用 python -m helloworld
试图运行 helloworld 模块,__main__.py 文件就是 Python 开始执行的地方。__main__.py 的内容相对简单:
from helloworld.app import main
if __name__ == "__main__":
main().main_loop()
此文件做以下两件事:
- 它从
helloworld应用程序导入了main方法。 - 然后,它会开始应用程序的主循环,以便侦听用户输入(例如鼠标点击或键盘按键)。
更有趣的文件是 app.py - 它包含创建我们应用程序窗口的逻辑:
import toga
from toga.style.pack import COLUMN, ROW
class HelloWorld(toga.App):
def startup(self):
main_box = toga.Box()
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
def main():
return HelloWorld()
让我们逐行查看:
import toga
from toga.style.pack import COLUMN, ROW
首先,我们导入 toga 控件工具包,以及一些与样式相关的实用常量。目前我们的代码还没有使用这些——但我们很快就会用到它们。
然后,我们定义了一个类 :
class HelloWorld(toga.App):
每个 Toga 应用程序都有一个 toga.App
实例,代表应用程序的运行实体。应用程序最终可能会管理多个窗口;但是对于简单的应用程序来说,可能只有一个主窗口。
接下来,我们定义一个 startup() 方法 (startup意为启动):
def startup(self):
main_box = toga.Box()
startup 方法的第一件事是定义一个主盒子 (main box)。Toga 的布局方案类似于 HTML。你通过构造一系列盒子 (box) 来构建应用程序,每个盒子包含其他盒子或实际的控件 (widgets)。然后,你对这些盒子应用样式 (styles),以定义它们将如何消耗可用的窗口空间 (window space)。
在这个应用程序中,我们定义了一个单独的空盒子 (我们没有放任何东西进去)。
接下来,我们定义一个可以将这个空盒子放入其中的窗口:
self.main_window = toga.MainWindow(title=self.formal_name)
这将创建一个 toga.MainWindow 的实例,它的标题 (title) 将与应用程序的名称 (self.formal_name) 匹配。主窗口是
Toga 中的一种特殊窗口——它与应用程序的生命周期 (life cycle) 密切绑定。主窗口关闭,应用程序就退出了。主窗口也具有应用程序菜单(例如类似
Windows 这样的平台上,菜单栏是窗口的一部分)。
我的窗户在哪里?
如果你写的代码中有错误,app的主窗口可能不会显示。要是真这样了,你可以找到先前启动app的终端窗口,并在其中按下\ Ctrl + C,以终止app运行。之后,你就可以修改代码里的错误,再重启app。
然后,我们将空盒子作为主窗口的内容,并指示应用程序显示我们的窗口:
self.main_window.content = main_box
self.main_window.show()
最后,我们定义一个 main() 方法。它将创建应用程序的实例:
def main():
return HelloWorld()
这个 main() 方法由 __main__.py 导入并调用。它创建并返回我们的 HelloWorld 应用程序的实例。
这是最简单可能的 Toga 应用程序。接下来,让我们在应用程序中加入一些我们自己的内容,使应用程序做一些有趣的事情。
添加一些我们自己的内容¶
对我们的 HelloWorld 应用程序,做些更有意思的事吧。
备注
在做这些更改时,确保不要删除文件顶部的导入 (imports),也不要删除底部的 main()。您只需更新 HelloWorld 类。
修改 src/helloworld/app.py 中的 HelloWorld 类,使其看起来像这样:
class HelloWorld(toga.App):
def startup(self):
main_box = toga.Box(direction=COLUMN)
name_label = toga.Label(
"Your name: ",
margin=(0, 5),
)
self.name_input = toga.TextInput(flex=1)
name_box = toga.Box(direction=ROW, margin=5)
name_box.add(name_label)
name_box.add(self.name_input)
button = toga.Button(
"Say Hello!",
on_press=self.say_hello,
margin=5,
)
main_box.add(name_box)
main_box.add(button)
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
def say_hello(self, widget):
print(f"Hello, {self.name_input.value}")
让我们详细看看有哪些变化。
我们仍然在创建一个主盒子;然而,现在我们正在应用一个样式:
main_box = toga.Box(direction=COLUMN)
Toga 的内置布局系统称为 "Pack" (包)。它的行为很像 CSS (Cascading Style Sheets
层叠样式表)。你可以在一个层次结构中定义对象–在 HTML 中,对象是 <div> (division 块级容器)、<span> (inline
span 内联容器) 和其他 DOM (Document Object Model 文档对象模型) 元素;在 Toga 中,对象是控件 (widgets )
和盒子 (boxes)。然后,您可以为各个元素指定样式。在本例中,我们表示这是一个 COLUMN (垂直) 框,也就是说,它是一个将占用所有可用宽度
(width) 的框,并会随着内容的添加而扩大高度 (height),但会尽量使高度更短。
备注
对于更高级的使用,Toga 也支持可以被这样使用的单独的样式对象:
from toga.style import Pack
main_box = toga.Box(style=Pack(direction=COLUMN))
接下来,我们定义了一些控件:
name_label = toga.Label(
"Your name: ",
margin=(0, 5),
)
self.name_input = toga.TextInput(flex=1)
在这里,我们定义了一个标签 (Label) 和一个文本输入框 (TextInput)。这两个控件都有与其相关的样式;标签左右会各有 5px 的填充,上下没有填充。文本输入框是被标记为灵活的——也就是说,它将吸收其布局方向上所有可用的空间。
文本输入框 (TextInput) 被分配为类的实例变量。这使我们能够轻松访问控件 (widget) 实例 - 这是我们马上就会使用的东西。
接下来,我们定义了一个盒子来容纳这两个控件:
name_box = toga.Box(direction=ROW, margin=5)
name_box.add(name_label)
name_box.add(self.name_input)
name_box 就像主盒子一样;然而,这次它是一个 ROW (水平)
盒子。这意味着内容将会被水平添加,并且它会尽量使其宽度尽可能窄。盒子也有一些空白填充——四周各为 5px。
现在,我们定义一个按钮:
button = toga.Button(
"Say Hello!",
on_press=self.say_hello,
margin=5,
)
按钮四边的边距也是 5px。我们还定义了一个 handler - 按钮按下时调用的方法。
然后,我们将 name box 和按钮添加到主盒子中:
main_box.add(name_box)
main_box.add(button)
这完成了我们的布局;其余的 startup 方法与以前一样 - 定义一个 MainWindow ,并将主盒子指定为窗口的内容:
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
我们需要做的最后一件事,就是定义按钮的处理器(handler)——它可以是任何方法、生成器或异步协程,且接受生成事件的控件 (widget) 作为参数。只要按下按钮,就会调用这些处理器:
def say_hello(self, widget):
print(f"Hello, {self.name_input.value}")
方法的主体是一个简单的打印语句——然而,它会使用名称输入的当前值,并使用该内容作为打印的文本。
现在我们已经做了这些更改,我们可以通过再次启动应用程序来看看它们的样子。和以前一样,我们将使用开发者模式:
(beeware-venv) $ briefcase dev
[helloworld] Starting in dev mode...
===========================================================================
(beeware-venv) $ briefcase dev
[helloworld] Starting in dev mode...
===========================================================================
(beeware-venv) C:\...>briefcase dev
[helloworld] Starting in dev mode...
===========================================================================
你会注意到,这次\ 没有\ 安装依赖项。Briefcase 可以检测到已经被运行过的应用程序 –
为了节省时间,它只会运行应用程序,而不会安装依赖项。如果你在应用程序中添加了新的依赖项,你可以在运行 briefcase dev 时通过 -r
选项来确保它们被安装。
这将打开一个图形用户界面 (GUI) 窗口:
Hello World 教程 2 窗口,在 macOS 上](../images/macOS/tutorial-2.png)。
!
!
如果在文本框中输入名称,并按下图形用户界面中的按钮,就会在启动程序的控制台中看到输出结果。
在继续之前,关闭应用程序。和教程 1 一样,您可以使用应用程序窗口上的关闭按钮、从应用程序的菜单栏选择关闭/退出、或在您运行 briefcase dev
的命令行窗口中按 Ctrl+C 以关闭应用程序。
下一步¶
我们现在有了一个应用程序,它能做一些更有趣的事情。但它只能在我们自己的电脑上运行。让我们将应用程序打包,以便发布。在 教程 3中,我们将把应用程序打包成一个独立的安装程序,发送给朋友、客户或上传到应用程序商店。