Kivy:Popup被主线阻止

时间:2017-01-21 08:08:59

标签: python kivy

我的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()似乎可以解决问题,但我不知道要发送什么事件。

3 个答案:

答案 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