用 Python 编写安卓 APK ,简单几步搞定beeware详解(一): 搭建环境beeware教程: 二 静态部件类(上) + 写一个登录页面
如果想使用Python语言编写图形界面程序,那么有不少的框架可以提供支持,比如Tkinter、Qt for Python、WxPython等。不过这些框架都是只能创建桌面图形界面程序,比如Windows、Linux和macOS。如果我们想要创建IOS或Android等平台的移动应用APP,它们就无能为力了。
那么Python能不能写移动应用APP呢?实际上是可以的。据了解,Kivy和BeeWare都宣称"一次编写,处处部署",基于这些个框架编写的图形界面程序,都能够打包为全平台的应用程序,比如:Windows、Linux、macOS、Android、IOS。
1 BeeWare
BeeWare官方文档 BeeWare是一个基于Python构建的跨平台应用开发框架。
1.1 安装BeeWare
根据BeeWare的文档说明,在Windows上使用,我们首先需要安装Git和WiX Toolset,根据给出的网址,下载安装即可。
一、安装依赖
(1)Windows电脑
(2)3.5至3.8版本的python
(3)git
(4)WIX工具集
(5)Pycharm(可选)
(1)windows电脑
(2)conda安装的python
CMD>conda env list
CMD>conda create -n python38 python=3.8
CMD>conda activate python38
CMD>conda deactivate
(3)git
git官网下载地址 安装位置D:\Program Files\Git。
(4)WIX工具集
WIX工具集官网地址WIX工具集Github下载地址 安装位置C:\Program Files (x86)\WiX Toolset v3.11。
二、安装BeeWare
使用pip工具安装BeeWare
CMD>conda activate python38
CMD>pip install briefcase
CMD>pip install beeware
beeware主要的三部分:
(1)briefcase beeware的命令行工具-公文包
(2)toga beeware的GUI开发工具
(3)cricket beeware调试工具
1.2 应用BeeWare
BeeWare安装完成之后,我们就可以通过briefcase命令在命令行终端进行 BeeWare应用的管理,比如新建、运行、构建、打包等。
1.2.1 创建应用briefcase new
命令输入之后,会让我们输入「应用的正式名称」、「应用程序名称」、「域名」、「项目名称」等信息。
一、Formal Name [Hello World]: demo-01正式名称
二、App Name [demo01]:应用名称
三、Bundle Identifier [com.example]:域
四、Project Name [demo-01]:项目名称
五、Description [My first application]:描述
六、Author [Jane Developer]:作者
七、Author Email [jane@example.com]:作者邮件地址
八、Application URL [https://example.com/demo01]:应用程序登陆页面的URL
九、What license do you want to use for this project code?许可证
Select one of the following:
[1] BSD license
[2] MIT license
[3] Apache Software License
[4] GNU General Public License v2 (GPLv2)
[5] GNU General Public License v2 or later (GPLv2+)
[6] GNU General Public License v3 (GPLv3)
[7] GNU General Public License v3 or later (GPLv3+)
[8] Proprietary
[9] Other
Project License [1]: 2
十、What GUI toolkit do you want to use for this project?GUI框架
Select one of the following:
[1] Toga
[2] PySide2 (does not support iOS/Android deployment)
[3] PySide6 (does not support iOS/Android deployment)
[4] PursuedPyBear (does not support iOS/Android deployment)
[5] None
GUI Framework [1]:
输入完成之后,BeeWare会开始创建应用,创建完成之后,会有如下提示:
Generating a new application demo-01
Using existing template (sha 5ec6b6fbcef0686e22e0a13be0e57dbc303a36ec,
updated Mon Feb 28 11:36:44 2022)
Application demo-01 has been generated.
To run your application, type:
cd demo01
briefcase dev
1.2.2 运行应用briefcase dev
以开发者模式运行应用程序。
同时目录下多出了一个与应用程序名称同名的目录:
我们的程序的主要代码都将在app.py里面编写,默认app.py文件内已经有一个demo代码,我们可以直接运行项目:
cd demo01
briefcase dev
在命令行输入上述命令,会生成一个如下图所示的窗口:
1.2.3 打包为Windows程序
briefcase create windows
briefcase build windows
briefcase run windows
briefcase package windows
(1)创建应用的脚手架
cd demo01
briefcase create
运行命令,将会生成一些预配置文件,然后下载依赖的包。
完成之后,项目目录下会生成一个Windows的目录,如下图所示:
(2)构建应用
briefcase build
(3)运行构建的应用
briefcase run
(4)打包应用
briefcase package
打包完成后,./Windows目录下会生成一个.msi的二进制安装文件:
双击安装即可。
1.2.4 打包为安卓APP
briefcase create android
briefcase build android
briefcase run android
briefcase package android
(1)创建应用的安卓脚手架
cd demo01
briefcase create android
Downloading OpenJDK8U-jdk_x64_windows_hotspot_8u242b08.zip
Downloading sdk-tools-windows-4333796.zip
Downloading Python-3.8-Android-support.b3.zip
(2)构建安卓应用
briefcase build android
Downloading https://services.gradle.org/distributions/gradle-7.2-all.zip
(3)运行构建好的安卓应用
briefcase run android
Downloading the Android emulator and system image
在这里会让我们选择设备,
可以选择BeeWare提供的安卓虚拟机或者是在电脑上连接自己的手机,
在这里我们选择安卓虚拟机:
输入1
Emulator name [beePhone]:
(4)打包安卓应用
briefcase package android
打包完成之后,
可以在.\android\gradle\demo-01\app\build\outputs
找到打包好的文件:
BeeWare提供了两种打包好的文件,一种是用于上架Google Play的.aab格式文件,一种是用于调试.apk文件。
1.3 程序说明BeeWare
在src/demo2目录中,看到 3 个文件:
(1)__init__.py
将目录demo02标记为可导入的Python模块。
它是一个空文件,用于告诉Python解释器该demo2目录定义了一个模块。
(2)__main__.py
将模块demo02标记为一种特殊的模块(可执行模块)。
如果您尝试运行demo02模块,则该文件是Python将开始执行的位置。
from demo02.app import main
if __name__ == '__main__':
main().main_loop()
也就是说__main__.py从demo02应用程序中导入方法;
如果它作为入口点执行,则调用main()方法,并启动应用程序的主循环。主循环是GUI应用程序监听用户输入(如鼠标点击和键盘按下)的方式。
(3)app.py
包含创建我们的应用程序窗口的逻辑
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
class demo02(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 demo02()
1.3.1 init.py
(1)__init__.py
将目录demo02标记为可导入的Python模块。
它是一个空文件,用于告诉Python解释器该demo2目录定义了一个模块。
1.3.2 main.py
将模块demo02标记为一种特殊的模块(可执行模块)。
如果您尝试运行demo02模块,则该文件是Python将开始执行的位置。
from demo02.app import main
if __name__ == '__main__':
main().main_loop()
也就是说__main__.py从demo02应用程序中导入方法;
如果它作为入口点执行,则调用main()方法,并启动应用程序的主循环。主循环是GUI应用程序监听用户输入(如鼠标点击和键盘按下)的方式。
1.3.3 app.py
一、首先导入toga小部件工具包
一些与样式相关的实用程序类和常量。
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
二、然后定义一个类
每个Toga应用程序都有一个toga.App实例,代表应用程序的运行实体。该应用程序最终可能会管理多个窗口;但对于简单的应用程序,将只有一个主窗口。
class demo02(toga.App):
三、接下来定义一个startup()方法
(1)启动方法做的第一件事是定义一个主框。
Toga的布局方案的行为类似于HTML,可以通过构建一组盒子来构建应用程序,每个盒子都包含其他盒子或实际的小部件。然后将样式应用于这些框以定义它们将如何使用可用的窗口空间。
在这个应用程序中,定义了一个盒子,但没有往里面放任何东西。
def startup(self):
main_box = toga.Box()
(2)定义一个主窗口
该实例toga.MainWindow将具有与应用程序名称匹配的标题。主窗口是Toga中一种特殊的窗口,它是一个与应用程序的生命周期密切相关的窗口。当主窗口关闭时,应用程序退出。主窗口也是具有应用程序菜单的窗口(如果您在像Windows这样的平台上,其中菜单栏是窗口的一部分)。
self.main_window = toga.MainWindow(title=self.formal_name)
(3)添加空框作为主窗口的内容,并指示应用程序显示窗口
self.main_window.content = main_box
self.main_window.show()
四、最后定义一个main()方法
这就是创建应用程序实例的原因:
def main():
return demo02()
此main()方法是由__main__.py导入和调用的方法。
1.4 应用示例
修改demo02类src/demo02/app.py,使它看起来像这样:
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
class demo02(toga.App):
def startup(self):
main_box = toga.Box(style=Pack(direction=COLUMN))
name_label = toga.Label(
'Your name: ',
style=Pack(padding=(0, 5))
)
self.name_input = toga.TextInput(style=Pack(flex=1))
name_box = toga.Box(style=Pack(direction=ROW, padding=5))
name_box.add(name_label)
name_box.add(self.name_input)
button = toga.Button(
'Say Hello!',
on_press=self.say_hello,
style=Pack(padding=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("Hello", self.name_input.value)
def main():
return demo02()
一、应用一种样式创建一个主盒子
Toga的内置布局系统称为Pack,它的行为很像CSS。在HTML中,对象是div、span和其他DOM元素;在Toga中,它们是小部件和盒子。可以将样式分配给各个元素。
在当前情况下,表示这是一个COLUMN盒子,也就是说,它是一个会消耗所有可用宽度的盒子,并且会随着内容的添加而扩大其高度,但它会尽量短。
main_box = toga.Box(style=Pack(direction=COLUMN))
二、定义几个小部件
name_label = toga.Label(
'Your name: ',
style=Pack(padding=(0, 5))
)
self.name_input = toga.TextInput(style=Pack(flex=1))
在这里定义了一个Label和一个TextInput。两个小部件都有与之关联的样式;标签将在其左右两侧有5px的填充,并且在顶部和底部没有填充。TextInput被标记为灵活的,也就是说,它将吸收其布局轴上的所有可用空间。
TextInput被分配为类的实例变量,这使我们可以轻松访问小部件实例。
三、接下来定义一个盒子来容纳这两个小部件
name_box = toga.Box(style=Pack(direction=ROW, padding=5))
name_box.add(name_label)
name_box.add(self.name_input)
和主盒子一样是一个盒子;然而,这一次,它是一个 ROW盒子。这意味着内容将水平添加,并且会尝试使其宽度尽可能窄。盒子也有一些内边距,四面都是5px。
四、定义一个按钮
button = toga.Button(
'Say Hello!',
on_press=self.say_hello,
style=Pack(padding=5)
)
该按钮的所有边也有5px的填充,还定义了一个处理程序,按下按钮时调用的方法。
五、将名称框和按钮添加到主框
main_box.add(name_box)
main_box.add(button)
六、定义一个MainWindow,并将主框指定为窗口的内容
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("Hello", self.name_input.value)
该方法的主体是一个简单的打印语句,但是,它将询问名称输入的当前值,并将该内容用作打印的文本。
2 Toga
软件首页toga软件文档toga
Toga是一个Python原生的GUI工具包。
Toga是一项正在进行中的工作,可能无法在所有平台上保持一致。
2.1 窗口小部件Widgets
# Widgets
'ActivityIndicator',显示一个圆形的loading提示符号
'Box',盒子
'Button',按钮
'Canvas',画布
'DetailedList',明细表
'Divider',
'Window',
'Widget',
'ImageView',
'Label',
'DatePicker',
'TimePicker',
'MultilineTextInput',
'NumberInput',
'OptionContainer',
'PasswordInput',
'ProgressBar',
'ScrollContainer',
'Selection',
'Slider',
'SplitContainer',
'Switch',
'Table',
'TextInput',
'Tree',
'WebView'
2.1.1 按钮toga.Button
toga.Button(label,
id=None,
style=None,
on_press=None,
enabled=True,
factory=None)
label: 按钮上的文字
id: 按钮ID标识
style: 按钮风格
on_press: 按钮回调
enabled: 是否激活
factory: 通常不用。
Button可以被视作一个沙箱,支持add方法。
例子
import toga
def my_callback(button):
# handle event
pass
button = toga.Button('Click me', on_press=my_callback)
import toga
def button_handler(widget):
print("hello")
def build(app):
box = toga.Box()
button = toga.Button('Hello world',
on_press=button_handler)
button.style.padding = 50
button.style.flex = 1
box.add(button)
return box
def main():
return toga.App('First App',
'org.beeware.helloworld',
startup=build)
if __name__ == '__main__':
main().main_loop()
一、设置了一个处理程序,它是一个包装行为,我们希望在按下按钮时激活它。处理程序只是一个函数。该函数将激活的小部件作为第一个参数;根据正在处理的事件类型,还可以提供其他参数。但是,在简单按下按钮的情况下,没有额外的参数。
def button_handler(widget):
print("hello")
二、当app被实例化时(在main()中,如下所述),Toga将创建一个带有菜单的窗口。我们需要提供一种方法,告诉Toga在窗口中显示什么内容。该方法可以命名为任何名称,它只需要接受一个app实例:
def build(app):
box = toga.Box()
button = toga.Button('Hello world',
on_press=button_handler)
button.style.padding = 50
button.style.flex = 1
box.add(button)
return box
(1)我们想在窗口上挂一个按钮。然而,除非我们想让按钮填满整个app窗口,否则我们不能只将按钮放入app窗口。相反,我们需要创建一个box,并将按钮放入box中。
box是一个对象,可以用来容纳多个小部件,并定义小部件周围的填充。因此,我们定义了一个box:
box = toga.Box()
(2)然后我们可以定义一个按钮。当我们创建按钮时,我们可以设置按钮文本,我们还可以设置按下按钮时要调用的行为,引用我们之前定义的处理程序:
button = toga.Button('Hello world',
on_press=button_handler)
(3)现在我们必须定义按钮在窗口中的显示方式。默认情况下,Toga使用名为Pack的样式算法,有点像“CSS-lite”。我们可以设置按钮的样式属性:
button.style.padding = 50
button.style.flex = 1
我们在这里所做的是说,按钮的所有侧面都有50个像素的填充。如果我们想在按钮顶部定义20像素的填充,我们可以定义padding_top=20,或者指定padding=(20,50,50,50)。
现在,我们将使按钮占据所有可用宽度:flex属性指定一个元素相对于其方向上的其他元素的大小。默认方向是行(水平),因为按钮是这里唯一的元素,它将占据整个宽度。
(4)下一步是将按钮添加到框中:
box.add(button)
按钮具有默认高度,由底层平台绘制按钮的方式定义。因此,这意味着我们将在app窗口中看到一个按钮,它可以延伸到屏幕的宽度,但周围有50个像素的空间。
现在我们已经设置好这个box,我们返回包含所有UI内容的外部框。此box将是应用程序主窗口的内容:
return box
三、最后,我们实例化应用程序本身。应用程序是表示可执行文件的高级容器。该应用程序有一个名称和唯一标识符。在注册任何特定于应用程序的系统资源时使用该标识符。按照惯例,标识符是一个“反向域名”。该应用程序还接受我们定义主窗口内容的方法。我们将这个创建过程包装到一个名为main()的方法中,该方法返回应用程序的一个新实例:
def main():
return toga.App('First App',
'org.beeware.helloworld',
startup=build)
四、然后,项目的入口点需要实例化该入口点并启动主应用程序循环。对main_loop()的调用是一个阻塞调用;在你退出主应用程序之前,它不会返回:
if __name__ == '__main__':
main().main_loop()
2.1.2 输入框toga.TextInput
TextInput(id=None,
style=None,
factory=None,
initial=None,
placeholder=None,
readonly=False,
on_change=None,
on_gain_focus=None,
on_lose_focus=None,
validators=None)
id: 输入框ID标识
style: 输入框风格
factory: 通常不用
initial: 默认文字
placeholder: 提示文字
readonly: 是否采用只读
on_change: 回调,输入框中的文本更改时调用的方法
on_gain_focus: 回调,获得焦点时执行的函数
on_lose_focus: 回调,失去焦点时执行的函数
validators: 文本验证器,通常不用
大多数应用程序需要的不仅仅是页面上的一个按钮。让我们构建一个稍微复杂一点的例子——华氏温度到摄氏温度的转换器:
import toga
from toga.style.pack import COLUMN, LEFT, RIGHT, ROW, Pack
def build(app):
c_box = toga.Box()
f_box = toga.Box()
box = toga.Box()
c_input = toga.TextInput(readonly=True)
f_input = toga.TextInput()
c_label = toga.Label('Celsius', style=Pack(text_align=LEFT))
f_label = toga.Label('Fahrenheit', style=Pack(text_align=LEFT))
join_label = toga.Label('is equivalent to',
style=Pack(text_align=RIGHT))
def calculate(widget):
try:
c_input.value = (float(f_input.value) - 32.0) * 5.0 / 9.0
except ValueError:
c_input.value = '???'
button = toga.Button('Calculate', on_press=calculate)
f_box.add(f_input)
f_box.add(f_label)
c_box.add(join_label)
c_box.add(c_input)
c_box.add(c_label)
box.add(f_box)
box.add(c_box)
box.add(button)
box.style.update(direction=COLUMN, padding_top=10)
f_box.style.update(direction=ROW, padding=5)
c_box.style.update(direction=ROW, padding=5)
c_input.style.update(flex=1)
f_input.style.update(flex=1, padding_left=160)
c_label.style.update(width=100, padding_left=10)
f_label.style.update(width=100, padding_left=10)
join_label.style.update(width=150, padding_right=10)
button.style.update(padding=15, flex=1)
return box
def main():
return toga.App('Temperature Converter',
'org.beeware.f_to_c',
startup=build)
if __name__ == '__main__':
main().main_loop()
这个例子展示了Toga的Pack风格引擎的更多功能。在这个示例应用程序中,我们设置了一个垂直堆叠的外盒;在那个盒子里,我们放了两个水平的盒子和一个按钮。
由于水平框上没有宽度样式,因此它们会尝试将包含的小部件放入可用空间。TextInput小部件的样式为flex=1,但标签小部件的宽度是固定的;因此,TextInput小部件将被拉伸以适应可用的水平空间。然后,边距和填充确保小部件垂直和水平对齐。
2.1.3 网页toga.WebView
2.1.4 沙箱toga.Box
toga.Box(id=None,
style=None,
children=None,
factory=None)
id: Box的唯一标识,str类型。
style: Box的风格。
children: Box中的组件。
factory: 通常不用。
Box.add支持添加多个组件。
例子
import toga
box = toga.Box('box1')
button = toga.Button('Hello world', on_press=button_handler)
box.add(button)
2.1.5 文本toga.Label
toga.Label(text,
id=None,
style=None,
factory=None)
text: 文本
id: 文本ID标识
style: 文本风格
factory: 通常不用