我对 kivy 完全陌生,我正在将其作为一个实践项目。我搜索了很多但找不到解决方案。基本上问题是当我通过互联网发送请求时我的kivy屏幕冻结一段时间。因此,我的旋转器(MDSpinner)无法工作。请任何人帮助我。
我的main.py代码:
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.datatables import MDDataTable
from kivy.uix.screenmanager import ScreenManager, Screen
import requests
import shutil
from bs4 import BeautifulSoup as Soup
import re
import os
#Not Real Url for some security reason
Login_URL = "http://some_url.com/login"
Back_URL = "http://some_url.com/info"
Profile_URL = "http://some_url.com/profile"
SESSION = None
sm = ScreenManager()
stu_name = None
stu_id = None
def login(uname_passw):
uname,passw = uname_passw.split(":::")
s = requests.Session()
data = {'email': uname, 'password': passw}
try:
r = s.post(Login_URL, data=data)
text = r.text
except:
text = "<div>Net Problem > Login > </div>"
soup = Soup(text, 'html.parser')
error = soup.find(class_="error_msg")
if error is not None:
error_msg = error.get_text()
error_msg = error_msg.split(".")[0] + "."
return error_msg
else:
global SESSION
SESSION = s
return True
def get_info(session):
s = SESSION
url = Back_URL
try:
r = s.get(url)
text = r.text
except:
text = "<div>Net Problem > Get_Info > </div>"
soup = Soup(text, "html.parser")
tables = soup.find_all(class_="table table-striped table-responsive table-bordered table-hover")
stu_basic_info = tables[0]
stu_drop_change = tables[1]
basic_tds = stu_basic_info.find_all("td")
basic_finfo = []
for td in basic_tds:
t = td.get_text()
if "Admission Fee" in str(t):
t = t + soup.find(id="sum_admission_fee").get("value")
basic_finfo.append(t)
drop_tds = stu_drop_change.find_all("td")
drop_change_finfo = []
for td in drop_tds:
t = td.get_text()
drop_change_finfo.append(t)
basic_info = {}
for i in basic_finfo:
if "Attempted credit" in i:
break
txt = i.split(":")
points = txt[0]
answer = txt[1]
pattern = '[A-z0-9.]+'
s1 = re.findall(pattern, points)
s1 = " ".join(s1)
s2 = re.findall(pattern, answer)
s2 = " ".join(s2)
basic_info[s1] = s2
total_fee = basic_info['Total Fee']
discount = basic_info['Waiver Discount']
payable = str(int(total_fee) - int(discount))
basic_info['Payable Amount'] = payable
d = []
for i in drop_change_finfo:
c = " ".join(re.findall(pattern, i))
d.append(c)
drop_change_finfo = d
numbers = []
names = []
for i in drop_change_finfo:
r = " ".join(re.findall('[0-9]+', i))
if r != '':
numbers.append(r)
r2 = " ".join(re.findall('[A-z]+', i))
if r2 != '':
names.append(r2)
extra_info = {}
for name, num in zip(names, numbers):
extra_info[name] = num
return basic_info, extra_info
class LoginScreen(Screen):
def on_pre_enter(self, *args):
dir_list = os.listdir(os.getcwd())
for file in dir_list:
if "img" in file:
os.remove(file)
def user_login(self):
self.ids.spinner1.active = True
uname = self.ids.uname.text
passw = self.ids.passw.text
uname_passw = f"{uname}:::{passw}"
self.user_login_var = login(uname_passw)
if self.user_login_var is not True:
msg = self.ids.msg
msg.text = str(self.user_login_var)
msg.theme_text_color = "Error"
msg.font_style = "Subtitle1"
msg.pos_hint = {'center_x': 0.5, 'center_y': 0.6}
else:
sm.switch_to(InfoScreen(name='info'))
class InfoScreen(Screen):
def on_pre_enter(self, *args):
self.info_dict, self.extra_info = get_info(SESSION)
def on_enter(self, *args):
global stu_name
global stu_id
stu_name, stu_id = self.info_dict['Student Name'], self.info_dict['Student ID']
nickname = stu_name.split(" ")
nickname = nickname[-1]
self.ids.tool.title = f"Hi, {nickname}"
a = []
for item in self.info_dict.items():
a.append(item)
for item in self.extra_info.items():
r, t = item
if "SL NO" in r:
pass
else:
a.append(item)
table = MDDataTable(
pos_hint={'center_x': 0.5, 'center_y': 0.5},
size_hint=(1, 0.7),
rows_num=21,
column_data=[
("Name", dp(30)),
("Details", dp(30))
],
row_data=a
)
self.ids.spinner.active = False
self.add_widget(table)
def change_screen(self, name):
if name == 'profile':
sm.switch_to(ProfileScreen(name='profile'))
else:
sm.switch_to(LoginScreen(name='login'))
class ProfileScreen(Screen):
def on_pre_enter(self, *args):
self.ids.welcome.text = f"Welcome {stu_name}"
s = SESSION
url = Profile_URL + stu_id
try:
r = s.get(url)
self.text = r.text
except:
self.text = "<div>Net Problem > Profile > </div>"
def on_enter(self, *args):
soup = Soup(self.text, 'html.parser')
tables = soup.find_all("table")
stu_data = tables[1]
img_data = tables[2]
academic_data = tables[3]
img_url = img_data.find("img").get("src")
response = requests.get(img_url, stream=True)
img_file = f"img_{str(stu_id)}.png"
with open(img_file, 'wb') as out_file:
shutil.copyfileobj(response.raw, out_file)
self.ids.bg_image.source = img_file
def change_screen(self):
sm.switch_to(InfoScreen(name='info'))
class SuLoginApp(MDApp):
def build(self):
global sm
sm.add_widget(LoginScreen(name='login'))
sm.add_widget(InfoScreen(name='info'))
sm.add_widget(ProfileScreen(name='profile'))
return sm
SuLoginApp().run()
sulogin.kv代码:
<LoginScreen>:
MDSpinner:
id:spinner1
size_hint: None, None
size: dp(30), dp(30)
pos_hint: {'center_x': .5, 'center_y': .5}
active: False
MDLabel:
id:msg
text: "Student Login"
halign: "center"
pos_hint:{'center_x':0.5,'center_y':0.8}
font_style:'H2'
MDTextField:
id:uname
text: "CSE2201025001"
hint_text: "Username"
helper_text_mode: "on_focus"
pos_hint:{'center_x':0.5,'center_y':0.5}
size_hint:None,0.1
width:root.width*0.8
mode: "rectangle"
helper_text_mode: "on_error"
required: True
MDTextField:
id:passw
text:"123456"
hint_text: "Password"
helper_text_mode: "on_focus"
pos_hint:{'center_x':0.5,'center_y':0.38}
size_hint:None,0.1
width:root.width*0.8
mode: "rectangle"
helper_text_mode: "on_error"
required: True
MDRectangleFlatButton:
text: "Login"
pos_hint:{'center_x':0.5,'center_y':0.2}
size_hint:None,0.1
width:root.width*0.5
on_press: root.user_login()
<InfoScreen>:
BoxLayout:
orientation: 'vertical'
MDToolbar:
id:tool
elevation: 10
right_action_items: [['account-circle', lambda x:root.change_screen('profile') ]]
left_action_items: [['logout', lambda x:root.change_screen('login') ]]
Widget:
MDSpinner:
id:spinner
size_hint: None, None
size: dp(30), dp(30)
pos_hint: {'center_x': .5, 'center_y': .5}
active: True
<ProfileScreen>:
MDCard:
elevation: 10
radius: [36, ]
FitImage:
id: bg_image
size_hint_y: .35
pos_hint: {"top": 1}
radius: 36, 36, 0, 0
MDLabel:
id:welcome
halign:'center'
font_style:'H6'
MDRectangleFlatButton:
text:"Back"
on_press:root.change_screen()
pos_hint: {'center_x': .5, 'center_y': .2}
为了在获取/发送数据时保持 UI 活动,您可以使用线程。
首先创建一个新方法,如
init_login
中的.py
,用于创建新线程,
def init_login(self):
"""Function to start a new thread each time."""
self.new_thread = threading.Thread(target = self.user_login) # Now call that function from this a new thread.
self.new_thread.start() # You can use this thread instance later if you want.
...
def user_login(self, *args):
self.ids.spinner1.active = True
uname = self.ids.uname.text
passw = self.ids.passw.text
...
然后调用该方法
init_login
,而不是直接在user_login
中调用.kv
,
...
MDRectangleFlatButton:
text: "Login"
pos_hint:{'center_x':0.5,'center_y':0.2}
size_hint:None,0.1
width:root.width*0.5
on_press: root.init_login()
...
我这样做只是为了保持方法
user_login
原样(几乎),你可以尝试其他方法。
您不应该在主线程中执行与 UI 相关的任务,因为 openGL 操作应该在主线程中完成。否则,使用@mainthread装饰器。
查看此文档 - https://github.com/kivy/kivy/wiki/Working-with-Python-threads-inside-a-Kivy-application
进口猕猴桃 从 kivy.app 导入 App 从 kivy.uix.button 导入按钮 进口螺纹
MyApp类(应用程序): def 构建(自身): self.button = Button(text="发送请求") self.button.bind(on_press=self.send_request) 返回自我按钮
def send_request(self, instance):
# Create a new thread to send the request
request_thread = threading.Thread(target=self.send_request_in_thread)
request_thread.start()
def send_request_in_thread(self):
# This function will run in a separate thread
# Place your network request code here
# For example, you can use the requests library
import requests
try:
response = requests.get('https://example.com')
# Process the response here
except Exception as e:
# Handle exceptions here
print(f"Error: {e}")
if name == 'main': MyApp().run()