我的Kivy应用程序有一个按钮,其回调涉及open()
。我想提供一个弹出窗口,要求用户在请求完成时等待。问题是执行请求本身会阻止弹出窗口出现。我尝试将弹出窗口的on_progress()
方法放在不同的地方但没有运气。在以下示例中,弹出窗口在UrlRequest
的{{1}}回调中打开:
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.garden.navigationdrawer import NavigationDrawer
from kivy.network.urlrequest import UrlRequest
from kivy.uix.popup import Popup
from kivy.uix.label import Label
import urllib
kv = '''
<ScreenTemplate@SignUpScreen>:
canvas:
Color:
rgb: (0.09,0.65,0.8)
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint: .3, .2
pos_hint: {'center_x': .5,'center_y': .25}
text: 'Call Request'
# on_press: app.p.open()
on_release: app.callback1()
Label:
text: 'This is ' + root.name
font_size: '50sp'
<MyNavDrawer>:
BoxLayout:
orientation: 'vertical'
size_hint_y: .25
pos_hint: {'center_y':.5}
Button:
text: 'Screen 1'
on_press: app.callback2( 'screen1')
Button:
text: 'Screen 2'
on_press: app.callback2('screen2')
SMRoot:
<SMRoot>:
ScreenTemplate:
name: 'screen1'
ScreenTemplate:
name: 'screen2'
'''
Builder.load_string(kv)
class SMRoot(ScreenManager):
pass
class SignUpScreen(Screen):
pass
class myNavDrawer(NavigationDrawer):
pass
class myApp(App):
popup_opened = False
p=Popup(title="Posting request...",
content=Label(text="... Please wait"),
size=(100, 100),
size_hint=(0.5, 0.5),
auto_dismiss = False)
def build(self):
self.mynavdrawer = myNavDrawer()
return self.mynavdrawer
def on_success(self, req, results):
print 'In on_success: '+ results
self.p.title = 'Success'
self.p.content = Label(text=results)
self.p.auto_dismiss = True
popup_opened = False
def on_failure(self, req, results):
self.p.title = 'Failure'
self.p.content = Label(text=results)
print 'In on_failure: '+ results
self.p.auto_dismiss = True
def on_error(self, req, results):
self.p.title = 'Error'
self.p.content = Label(text=results.strerror)
print 'In on_error: '+ results.strerror
self.p.auto_dismiss = True
def on_progress (self, req, results, chunk):
if not self.popup_opened:
print 'In on_progress: '+ str(results)
self.p.open()
self.popup_opened = True
def callback1(self):
params={'show_env':'1'}
params = urllib.urlencode(params)
headers = {'Content-type': 'application/x-www-form-urlencoded',
'Accept': 'text/plain'}
url = 'https://httpbin.org/get'
req = UrlRequest(url,
self.on_success,
req_body = params,
req_headers = headers,
on_failure=self.on_failure,
on_error=self.on_error,
on_progress=self.on_progress,
timeout=4)
req.wait()
print 'After UrlRequest'
if __name__ == '__main__':
myApp().run()
运行此操作会导致弹出窗口在请求完成后显示,这会破坏目的。
注意on_press: app.p.open()
下按钮中注释的<ScreenTemplate@SignUpScreen>
绑定。这非常有效,除了它是一个远非理想的解决方法。我希望在UrlRequest
发送的任何时候打开弹出窗口;必须将上述解决方法应用于每个按钮。
有关如何在回调运行时从主线程打开按钮的任何想法都将不胜感激。我也试过使用Clock.schedule_once()
。从docs开始,EventDispatcher.dispatch()
似乎可以解决问题,但我不知道要发送什么事件。
答案 0 :(得分:0)
我认为这一行:
req.wait()
阻止你在主线程上。 去掉它。你也可以早点打开弹出窗口 - 而不是 req.wait()
话虽如此,您可以使用可延迟(来自kivyoav)
复制此行为@delayable
def callback1(self):
...
while not reg.is_finished:
yield 0.02 # sleep for 20 ms...
... #req is ready now...
免责声明:我是 kivyoav
的作者答案 1 :(得分:0)
我和你有同样的问题。 UrlRequest
发生在主线程中。这意味着除非允许主线程运行,否则不会更新UI,因为它在callback1
函数内被阻止。
你想要做的是为UrlRequest
创建一个新线程
尝试使用Threading
答案 2 :(得分:0)
我使用线程解决了类似的问题。任何其他尝试都会使GIL阻塞弹出窗口以在操作方法之后显示或显示该弹出窗口。当我要显示一个弹出窗口直到另一个线程中的方法完成时,我使用def showTheMessage(self):
。
# main screen
class MainScreen(Screen):
def __init__(self, **kwargs):
self.name="MAIN SCREEN"
super(Screen, self).__init__(**kwargs)
# popup class
class MsgBox(Popup):
def __init__(self, obj, **kwargs):
super(MsgBox, self).__init__(**kwargs)
self.obj = obj
# app class
class MyApp(App):
def step1(self):
# start the method in a thread
t1 = threading.Thread(target = self.yourBelowedMethod)
t1.start()
t1.join()
# dismiss the message popup once finished
self.popupMsg.dismiss()
def step2(self, *args):
# set the popup structure
self.popupMsg = MsgBox(self)
# run the activity popup in a thread
t3 = threading.Thread(target = self.popupMsg.open)
t3.start()
t3.join()
def showTheMessage(self):
# call step2 and step1
self.step2() # call the message popup
self.step1() # call the method
def build(self):
sm = Builder.load_string("""
ScreenManager
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
size: self.size
MainScreen:
size_hint: 1, 1
auto_dismiss: False
title: "MainScreenTitle"
title_align: "center"
BoxLayout:
orientation: "vertical"
spacing: 10
Label:
text: "MainScreenLabel"
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .8
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .2
Button:
font_size: 50
text: "MessageButtonExit" # exit app
on_press:
self.background_color = 0,255,0,1
app.exit()
<MsgBox>:
size_hint: 1, .7
auto_dismiss: False
title: "yourTitle"
title_align: "center"
title_size: 30
BoxLayout:
orientation: "vertical"
Label:
font_size: '30sp'
text: "yourText"
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
""")
return sm