2017-01-21 13 views
2

Meine Kivy App hat eine Taste, deren Rückruf eine UrlRequest beinhaltet. Ich möchte ein Popup bereitstellen, das den Benutzer auffordert, zu warten, während die Anfrage abgeschlossen wird. Das Problem besteht darin, dass das Ausführen der Anforderung selbst das Popup blockiert. Ich habe versucht, die open() Methode des Popup an verschiedenen Orten ohne Glück zu platzieren. Im folgende Beispiel wird das Popup im on_progress() Rückruf der UrlRequest geöffnet:Kivy: Popup ist vom Haupt-Thread blockiert

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 = ''' 
<[email protected]>: 
    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() 

Lauf dies bewirkt, dass das Popup nach erscheint die Anforderung abgeschlossen ist, die den Zweck besiegt.

Beachten Sie die kommentierte on_press: app.p.open() Bindung in der Schaltfläche unter <[email protected]>. Dies funktioniert perfekt, außer dass es eine Workaround ist, die alles andere als ideal ist. Ich möchte, dass das Popup geöffnet wird, wenn die UrlRequest gesendet wird; Die obige Problemumgehung muss auf jede Schaltfläche angewendet werden.

Irgendwelche Ideen, wie man den Knopf vom Hauptfaden öffnet, während der Rückruf ausgeführt wird, würde geschätzt werden. Ich habe versucht, auch Clock.schedule_once() zu verwenden. Von der docs scheint es EventDispatcher.dispatch() könnte den Trick, aber ich weiß nicht, welches Ereignis zu versenden.

Antwort

0

denke ich diese Zeile:

req.wait() 

Blöcke, die Sie auf dem Haupt-Thread. entfernen Sie es. Sie können auch das Popup früher öffnen - statt der req.wait()

Dass gesagt wird, können Sie dieses Verhalten mit verzögerbare (von kivyoav) replizieren

@delayable 
def callback1(self): 
    ... 
    while not reg.is_finished: 
     yield 0.02 # sleep for 20 ms... 

    ... #req is ready now... 

Haftungsausschluss: Ich m der autor von kivyoav

+0

Die 'wait()' in der Tat blockiert den Thread. Ich migriere jedoch vorhandenen Code aus der synchronen Anforderungsbibliothek in das asynchrone UrlRequest. Ich hoffte, dass ich in der Zwischenzeit das 'wait()' benutzen konnte, um die Dinge nicht zu unterbrechen. Ich denke, das ist aufgrund [dieses bekannte Problem] (https://groups.google.com/forum/#!topic/kivy-users/ERcZDjr3znE) einfach nicht möglich. – ScissorHill

+0

@ScissorHill - Ich habe meine Antwort aktualisiert, so dass Sie Ihr altes Verhalten replizieren können ... –

0

Ich hatte das gleiche Problem wie Sie. Die UrlRequest findet im Hauptthread statt. Dies bedeutet, dass die Benutzeroberfläche nur dann aktualisiert wird, wenn der Hauptthread ausgeführt werden kann, da er innerhalb der callback1-Funktion blockiert ist. Was Sie tun möchten, ist ein neuer Thread für die UrlRequest Versuchen Sie mit Threading

Verwandte Themen