Kivy+Python
Python文件控制逻辑
定义类;
MainApp(APP)控制 Rootwidget(ScreenManager)控制每一个(Screen);
#转为执行文件时需要命名为main.py
from kivy.app import App
from kivy.lang import Builder #连接kv
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file("design.kv") #引入kvfile
class LoginScreen(Screen):
def sign_up(self):
self.manager.current="sign_up_screen" #manager为当前页面
#使用sign_up函数会跳转到signup页面
class SignUpScreen(Screen):
def back(self):
self.manager.current="login_screen"
class Rootwidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return Rootwidget() #返回一个对象
if __name__=="__main__":
MainApp().run()
kv文件用于编辑app的显示图像
每一个screen都为Python文件中的一类;
布置每一个screen的布局;
注意点
对每个screen命名,以便于在python文件中调用;
<LoginScreen>:
GridLayout:
cols:1
GridLayout:
cols:1
Label:
text:"User Login"
TextInput:
hint_text:"User name"
TextInput:
hint_text:"User password"
Button:
text:"Login"
GridLayout:
cols: 2
Button:
text:"Forgot password"
Button:
text:"Sign Up"
on_press: root.sign_up()
<SignUpScreen>:
GridLayout:
cols:1
Label:
text:"Sign up for a space journey!"
TextInput:
hint_text:"Username"
TextInput:
hint_text:"Password"
Button:
text:"Submit"
on_press:root.back()
<Rootwidget>:
LoginScreen:
name:"login_screen"
SignUpScreen:
name:"sign_up_screen"
调整signup screen,使输出signup的名字和密码
main.py
class SignUpScreen(Screen):
def add_user(self,uname,pword):
print(uname,pword)
design.ky
<SignUpScreen>:
GridLayout:
cols:1
Label:
text:"Sign up for a space journey!"
TextInput:
id:username
hint_text:"Username"
TextInput:
id:password
hint_text:"Password"
Button:
text:"Submit"
on_press:root.add_user(root.ids.username.text,root.ids.password.text)
#.text,从object中提出text
#root 代表signupscreen , adduser 来自signupscreen类
# ids是Screen 中的properties,username为上面创建的id,text为strvalue
改变add_user函数,使得输入的info可以传入jsonfile
load得到jsonfile中的字典数据;
dump对jsonfile进行添加关键词:值;
import json
from datetime import datetime
class SignUpScreen(Screen):
def add_user(self,uname,pword):
with open("user.json") as file:
users = json.load(file)
users[uname]={"username":uname,"password":pword,
"created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
#覆盖原本jsonfile
with open("user.json",'w') as file:
json.dump(users,file)
添加注册成功页面
按钮从右往左返回login页面
class SignUpScreenSuccess(Screen):
def go_to_login(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
显示——success,下方一个返回按钮;
<SignUpScreenSuccess>:
GridLayout:
cols:1
Label:
text:"Sign up Successful!"
Button:
text:"Login page"
on_press:root.go_to_login()
添加账号密码的判断——login按钮
main.py
密码正确则进入successpage;
失败则在label显示wrongname;
class LoginScreen(Screen):
def sign_up(self):
self.manager.current="sign_up_screen"
def login(self,uname,pword):
with open("user.json") as file:
users = json.load(file)
if uname in users and users[uname]['password'] == pword:
self.manager.transition.direction="left"
self.manager.current="login_screen_success"
else:
self.ids.login_wrong.text="Wrong username or password!"
design.ky
为loginpage增加一个label,用于显示账号密码是否正确;
<LoginScreen>:
GridLayout:
cols:1
GridLayout:
cols:1
Label:
text:"User Login"
TextInput:
id:username
hint_text:"User name"
TextInput:
id:password
hint_text:"User password"
Button:
text:"Login"
on_press:root.login(root.ids.username.text,root.ids.password.text)
Label:
id:login_wrong
text:""
GridLayout:
cols: 2
Button:
text:"Forgot password"
Button:
text:"Sign Up"
on_press: root.sign_up()
设置登入后界面,实现随机输出txt文本的内容
main.py
glob得到给定目录下的所有txt的相对路径;
path.stem得到路径下的文件名;
打开文件,readlines()得到文件中的行列表;
random.choice随机选择文件中的一行;
import glob
from pathlib import Path
import random
class LoginScreenSuccess(Screen):
def log_out(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
def get_quotes(self,feel):
feel=feel.lower() #转为小写
available_feelings=glob.glob("quotes/*txt")
#glob得到目录下每一个txt
#path.stem得到文件名
#name得到文件名+后缀
available_feelings=[Path(filename).stem for filename in available_feelings]
#去掉后缀
if feel in available_feelings:
with open(f"quotes/{feel}.txt",'r',encoding='UTF-8') as file:
quotes=file.readlines()
#得到list
self.ids.quote.text=random.choice(quotes)
#print(feel)
else:
self.ids.quote.text="Try another feeling"
design.kv
<LoginScreenSuccess>:
GridLayout:
cols:1
Button:
text:"Logout"
on_press:root.log_out()
Label:
text:"How do you feel!"
TextInput:
id:feeling
hint_text:"Things to try: happy, sad, unloved..."
Button:
text:"Enlighten me"
on_press:root.get_quotes(root.ids.feeling.text)
Label:
id:quote
text:""
上述代码可实现app所有功能
main.py
#转为执行文件时需要命名为main.py
from kivy.app import App
from kivy.lang import Builder #连接kv
from kivy.uix.screenmanager import ScreenManager, Screen
import json
from datetime import datetime
import glob
from pathlib import Path
import random
Builder.load_file("design.kv") #引入kvfile
class LoginScreen(Screen):
def sign_up(self):
self.manager.current="sign_up_screen"
def login(self,uname,pword):
with open("user.json") as file:
users = json.load(file)
if uname in users and users[uname]['password'] == pword:
self.manager.transition.direction="left"
self.manager.current="login_screen_success"
else:
self.ids.login_wrong.text="Wrong username or password!"
class SignUpScreen(Screen):
def add_user(self,uname,pword):
with open("user.json") as file:
users = json.load(file)
users[uname]={"username":uname,"password":pword,
"created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
#覆盖原本jsonfile
with open("user.json",'w') as file:
json.dump(users,file)
self.manager.current="sign_up_screen_success"
class SignUpScreenSuccess(Screen):
def go_to_login(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
class LoginScreenSuccess(Screen):
def log_out(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
def get_quotes(self,feel):
feel=feel.lower() #转为小写
available_feelings=glob.glob("quotes/*txt")
#glob得到目录下每一个txt
#path.stem得到文件名
#name得到文件名+后缀
available_feelings=[Path(filename).stem for filename in available_feelings]
#去掉后缀
if feel in available_feelings:
with open(f"quotes/{feel}.txt",'r',encoding='UTF-8') as file:
quotes=file.readlines()
#得到list
self.ids.quote.text=random.choice(quotes)
#print(feel)
else:
self.ids.quote.text="Try another feeling"
class Rootwidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return Rootwidget() #返回一个对象
if __name__=="__main__":
MainApp().run()
design.kv
<LoginScreen>:
GridLayout:
cols:1
GridLayout:
cols:1
Label:
text:"User Login"
TextInput:
id:username
hint_text:"User name"
TextInput:
id:password
hint_text:"User password"
Button:
text:"Login"
on_press:root.login(root.ids.username.text,root.ids.password.text)
Label:
id:login_wrong
text:""
GridLayout:
cols: 2
Button:
text:"Forgot password"
Button:
text:"Sign Up"
on_press: root.sign_up()
<SignUpScreen>:
GridLayout:
cols:1
Label:
text:"Sign up for a space journey!"
TextInput:
id:username
hint_text:"Username"
TextInput:
id:password
hint_text:"Password"
Button:
text:"Submit"
on_press:root.add_user(root.ids.username.text,root.ids.password.text)
#.text,从object中提出text
#root 代表signupscreen , adduser 来自signupscreen类
# ids是Screen 中的properties,username为上面创建的id,text为strvalue
<SignUpScreenSuccess>:
GridLayout:
cols:1
Label:
text:"Sign up Successful!"
Button:
text:"Login page"
on_press:root.go_to_login()
<LoginScreenSuccess>:
GridLayout:
cols:1
Button:
text:"Logout"
on_press:root.log_out()
Label:
text:"How do you feel!"
TextInput:
id:feeling
hint_text:"Things to try: happy, sad, unloved..."
Button:
text:"Enlighten me"
on_press:root.get_quotes(root.ids.feeling.text)
Label:
id:quote
text:""
<Rootwidget>:
LoginScreen:
name:"login_screen"
SignUpScreen:
name:"sign_up_screen"
SignUpScreenSuccess:
name:"sign_up_screen_success"
LoginScreenSuccess:
name:"login_screen_success"
下面将优化app显示界面和兼容性
布置login界面
degin.kv
<LoginScreen>:
GridLayout:
cols:1
GridLayout: #等分子layout
cols:1
padding:15,15 #此grid相对外面的layout设置padding
#左右 上下
spacing:20,20
#grid内部每个widget的距离
Label:
text:"User Login"
font_size:'20sp'
#sp=space-independent pixels
TextInput:
id:username
hint_text:"User name"
TextInput:
id:password
password: True
hint_text:"User password"
RelativeLayout:#设定相对layout
Button:
text:"Login"
on_press:root.login(root.ids.username.text,root.ids.password.text)
#需要设定size
size_hint:0.3,0.5
# 百分比缩短 wide height
pos_hint:{"center_x":0.5,"center_y":0.6}
#0.5 0.6比较好
Label:
id:login_wrong
text:""
GridLayout:
cols: 2
size_hint:0.2,0.2
#该layout占20%的比例,相对的另外的平分80%
padding:10,10
spacing:10,0
Button:
text:"Forgot password"
background_color:1,1,1,0
#RGB+背景透明度
opacity:1 if self.state == 'normal' else 0.5
#normal时透明度1,其他时候透明度0.5
color:0.1,0.7,1,1
Button:
text:"Sign Up"
on_press: root.sign_up()
background_color:1,1,1,0
#RGB+背景透明度
opacity:1 if self.state == 'normal' else 0.5
#normal时透明度1,其他时候透明度0.5
color:0.1,0.7,1,1
显示效果:
sign-up page的布置
<SignUpScreen>:
GridLayout:
cols:1
padding:20,20
spacing:20,20
Label:
text:"Sign up for a space journey!"
TextInput:
size_hint:0.4,0.4
id:username
hint_text:"Username"
TextInput:
size_hint:0.4,0.4
id:password
hint_text:"Password"
Button:
size_hint:0.4,0.4
text:"Submit"
on_press:root.add_user(root.ids.username.text,root.ids.password.text)
#.text,从object中提出text
#root 代表signupscreen , adduser 来自signupscreen类
# ids是Screen 中的properties,username为上面创建的id,text为strvalue
设置图像按钮——执行hover监测
main.py
定义imagebutton类,继承自三个类
from hoverable import HoverBehavior
#引入hover
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior
class ImageButton(ButtonBehavior,HoverBehavior,Image):
#ButtonBehavior要放在最前面!
#image的顺序会影响behavior
pass
#imagebutton 可以使用上述三种类的功能
design.kv
使用两个图像,在hover中切换;
调用的button为imagebutton类,继承三个类的功能;
<LoginScreenSuccess>:
GridLayout:
cols:1
padding:30,30
spacing:30,30
RelativeLayout:
ImageButton:
#text:"Logout" #无用
size_hint:0.35,0.35
#pos_hint需要在relateivelayout中使用
pos_hint:{'center_x':0.93,'center_y':0.8}
on_press:root.log_out()
source:"logout_hover.png" if self.hovered else "logout_nothover.png"
Label:
text:"How do you feel!"
TextInput:
id:feeling
hint_text:"Things to try: happy, sad, unloved..."
Button:
text:"Enlighten me"
on_press:root.get_quotes(root.ids.feeling.text)
Label:
id:quote
text:""
为enlighten页面添加滚轮+动态text
text_size:可设置label中text的大小;
其中None表示text不受label高度的限制;
size_hint_y:None表示label的高度不受hiny的限制(可高于分配的20%);
设置height参数可设置fixed height,即固定label 的高度;
动态高度:height:self.texture_size[1]; 高度为1,宽度为0;
则当前label高度受text变化而变化;
将整个label放入ScrollView中,即可使text动态缩进,且label高度为scrollview高度,通过滚轮访问;
ScrollView: #添加滚轮
#滚轮作为label中text大小的限制
Label:
id:quote
text:""
#text_size:self.width, self.height
#self指label,则width和height会随label变化
text_size:self.width, None
#height不受label限制
size_hint_y:None
#labelheight不受hinty限制(可超过20%) 默认100px
#height:200
#设置label 的高度
#设置label动态高度
height:self.texture_size[1]
#0 - width 1-height 动态高度
上面已完成app的所有界面
添加forget password
main.py
#转为执行文件时需要命名为main.py
from kivy.app import App
from kivy.lang import Builder #连接kv
from kivy.uix.screenmanager import ScreenManager, Screen
import json
from datetime import datetime
import glob
from pathlib import Path
import random
from hoverable import HoverBehavior
#引入hover
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior
Builder.load_file("design.kv") #引入kvfile
class LoginScreen(Screen):
def sign_up(self):
self.manager.transition.direction="left"
self.manager.current="sign_up_screen"
def login(self,uname,pword):
with open("user.json") as file:
users = json.load(file)
if uname in users and users[uname]['password'] == pword:
self.manager.transition.direction="left"
self.manager.current="login_screen_success"
else:
self.ids.login_wrong.text="Wrong username or password!"
def forget(self):
self.manager.transition.direction="left"
self.manager.current="forget_password"
class SignUpScreen(Screen):
def add_user(self,uname,pword):
with open("user.json") as file:
users = json.load(file)
users[uname]={"username":uname,"password":pword,
"created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
#覆盖原本jsonfile
with open("user.json",'w') as file:
json.dump(users,file)
self.manager.transition.direction="left"
self.manager.current="sign_up_screen_success"
def back(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
class SignUpScreenSuccess(Screen):
def go_to_login(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
class LoginScreenSuccess(Screen):
def log_out(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
def get_quotes(self,feel):
feel=feel.lower() #转为小写
available_feelings=glob.glob("quotes/*txt")
#glob得到目录下每一个txt
#path.stem得到文件名
#name得到文件名+后缀
available_feelings=[Path(filename).stem for filename in available_feelings]
#去掉后缀
if feel in available_feelings:
with open(f"quotes/{feel}.txt",'r',encoding='UTF-8') as file:
quotes=file.readlines()
#得到list
self.ids.quote.text=random.choice(quotes)
#print(feel)
else:
self.ids.quote.text="Try another feeling"
class ImageButton(ButtonBehavior,HoverBehavior,Image):
#ButtonBehavior要放在最前面!
#image的顺序会影响behavior
pass
#imagebutton 可以使用上述三种类的功能
class FindPassword(Screen):
def change(self,uname,pword):
with open("user.json") as file:
users=json.load(file)
if uname not in users.keys():
self.ids.board.text="This username is not exist! Please try again!"
else:
users[uname]={"username":uname,"password":pword,
"created":datetime.now().strftime("%Y-%m-%d %H-%M-%S")}
with open("user.json",'w') as file:
json.dump(users,file)
self.manager.transition.direction="right"
self.manager.current="login_screen"
def back(self):
self.manager.transition.direction="right"
self.manager.current="login_screen"
class Rootwidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return Rootwidget() #返回一个对象
if __name__=="__main__":
MainApp().run()
design.kv
<LoginScreen>:
GridLayout:
cols:1
GridLayout: #等分子layout
cols:1
padding:15,15 #此grid相对外面的layout设置padding
#左右 上下
spacing:20,20
#grid内部每个widget的距离
Label:
text:"User Login"
font_size:'20sp'
#sp=space-independent pixels
TextInput:
id:username
hint_text:"User name"
TextInput:
id:password
password: True
hint_text:"User password"
RelativeLayout:#设定相对layout
Button:
text:"Login"
on_release:root.login(root.ids.username.text,root.ids.password.text)
#需要设定size
size_hint:0.3,0.5
# 百分比缩短 wide height
pos_hint:{"center_x":0.5,"center_y":0.6}
#0.5 0.6比较好
Label:
id:login_wrong
text:""
GridLayout:
cols: 2
size_hint:0.2,0.2
#该layout占20%的比例,相对的另外的平分80%
padding:10,10
spacing:10,0
Button:
text:"Forgot password"
on_release:root.forget()
background_color:1,1,1,0
#RGB+背景透明度
opacity:1 if self.state == 'normal' else 0.5
#normal时透明度1,其他时候透明度0.5
color:0.1,0.7,1,1
Button:
text:"Sign Up"
on_release: root.sign_up()
background_color:1,1,1,0
#RGB+背景透明度
opacity:1 if self.state == 'normal' else 0.5
#normal时透明度1,其他时候透明度0.5
color:0.1,0.7,1,1
<SignUpScreen>:
GridLayout:
cols:1
padding:20,20
spacing:20,20
RelativeLayout:
size_hint:0,0.35
Button:
size_hint:0.3,0.3
pos_hint:{'center_x':0.17,'center_y':0.8}
text:"BACK"
on_release:root.back()
Label:
#font_size:'40sp'
text:"Sign up for a space journey!"
TextInput:
#size_hint:0.4,0.4
id:username
hint_text:"Username"
TextInput:
#size_hint:0.4,0.4
id:password
hint_text:"Password"
Button:
#size_hint:0.4,0.4
text:"Submit"
on_release:root.add_user(root.ids.username.text,root.ids.password.text)
#.text,从object中提出text
#root 代表signupscreen , adduser 来自signupscreen类
# ids是Screen 中的properties,username为上面创建的id,text为strvalue
<SignUpScreenSuccess>:
GridLayout:
cols:1
padding:20,20
spacing:20,20
Label:
text:"Sign up Successful!"
RelativeLayout:
Button:
size_hint:0.6,0.35
pos_hint:{'center_x':0.5,'center_y':0.6}
text:"Login page"
on_release:root.go_to_login()
<FindPassword>:
GridLayout:
cols:1
padding:20,20
spacing:20,20
RelativeLayout:
size_hint:0,0.35
Button:
size_hint:0.3,0.3
pos_hint:{'center_x':0.17,'center_y':0.8}
text:"BACK"
on_release:root.back()
Label:
id:board
text:"Change your password"
TextInput:
hint_text:"Enter your username"
id:username
TextInput:
hint_text:"Enter your new password"
id:password
Button:
text:"Change password"
on_release:root.change(root.ids.username.text,root.ids.password.text)
<LoginScreenSuccess>:
GridLayout:
cols:1
padding:30,30
spacing:30,30
RelativeLayout:
ImageButton:
#text:"Logout" #无用
size_hint:0.35,0.35
#pos_hint需要在relateivelayout中使用
pos_hint:{'center_x':0.93,'center_y':0.8}
on_press:root.log_out()
source:"logout_hover.png" if self.hovered else "logout_nothover.png"
Label:
text:"How do you feel!"
TextInput:
id:feeling
hint_text:"Things to try: happy, sad, unloved..."
Button:
text:"Enlighten me"
on_release:root.get_quotes(root.ids.feeling.text)
ScrollView: #添加滚轮
#滚轮作为label中text大小的限制
Label:
id:quote
text:""
#text_size:self.width, self.height
#self指label,则width和height会随label变化
text_size:self.width, None
#height不受label限制
size_hint_y:None
#labelheight不受hinty限制(可超过20%) 默认100px
#height:200
#设置label 的高度
#设置label动态高度
height:self.texture_size[1]
#0 - width 1-height 动态高度
<Rootwidget>:
LoginScreen:
name:"login_screen"
SignUpScreen:
name:"sign_up_screen"
SignUpScreenSuccess:
name:"sign_up_screen_success"
LoginScreenSuccess:
name:"login_screen_success"
FindPassword:
name:"forget_password"
下面将files包装为APK file
进入linux——ubuntu系统;
按如下顺序在ubuntu中下载;
buildozer init
Just in case: Ubuntu version 20.04 LTS: checked by lsb_release -a
- This one helped me to run kivy with Python3 on Ubuntu at least
sudo apt-get install python3-kivy
- Another line to install buildozer
pip3 install -U buildozer
- After running buildozer android debug
that very new Ubuntu installation messaged about missing “git” and “java-jdk”
So, installed “git” and “jdk”
sudo apt install git
sudo apt install default-jdk
- …and run “buildozer” again with:
buildozer appclean
buildozer android debug
the process will install lots of missing things as python-for-android and Android SDK, NDK, etc
still an error but different from what Ardit had with "android.arch = armeabi-v7a"
in buildozer.spec file.
the only good thing the command:
bash kivy-buildozer-installer.sh
works after all, but cannot go further yet (
- Thanks to David and some Internet articles: need to install a bit more to avoid of missing autoconf, automake,ctypes errors:
sudo apt install cython3
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install g++
sudo apt-get install libtool m4 automake
sudo apt-get install lld
sudo apt install libssl-dev
sudo apt-get install libffi-devel
the last line cleared _ctypes file missing problem
buildozer appclean
buildozer android debug
after work bin/ folder will have an *.apk file
结束并得到bin,里面放着apk文件;
传入github;