Frage

Trying to migrate from PyQt with Kivy and I cant even imagine a solution for this.

I have thousands of lines of code that use Qt's dialogues for text input. That is, when their line of code is reached, they 'stop' the script until the "ok" button is pressed, so they can return the text input.

Kivy doesnt have that functionality, so ideally, when the program needs user input, the "ok" button would call for the next function to run.

Therefore I must replace all the current calls to a PyQt function with a function that stops the running script, launches a working responsive dialogue, then resumes the original when it has the text input it requested. So the question is:

Is there a way to stop a running script until a function finishes, without hanging the GUI?

I have already tried:

  • Threading:

Even if I start the text input in a new thread:

t = threading.Thread(target=TextInput.waiter)

the function that calls such thread will return just after calling the text input. If I use this code:

t.start()
t.join()

The main script will stop, but also hangs the text input GUI.

  • While/Sleep: Waiting for the text input variable to contain a valid result. But this blocks the ongoing textinput GUI in Kivy

  • Hacking raw_input: Currently thinking into try some hack with that, that would allow me to stop the script, then feed back the input found by the kivy text input popup.

Any pointers would be really welcome, thanks for reading.

War es hilfreich?

Lösung

You can't just pause the running script. Instead, you'll need to refactor your program to be event-driven (as Kivy is an event-driven GUI).

Here's a simple example function:

def myfunc():
    # do some stuff here
    # now we need some input...
    val = qt_input_dialogue()
    # do some more stuff here

Refactored:

class MyPopup(Popup):
    value = StringProperty() # bind this to a TextInput or something

def myfunc1():
    # do some stuff here
    p = MyPopupClass()
    p.bind(on_dismiss=lambda *_: myfunc2(p.value))
    p.open()

def myfunc2(val):
    # do some more stuff here

If you're willing to use Twisted, you can make this even easier using Deferreds and inlineCallbacks.

from kivy.support import install_twisted_reactor
install_twisted_reactor()

from twisted.internet import defer

Builder.load_string('''
<MyPopup>:
    BoxLayout:
        orientation: 'vertical'
        TextInput:
            id: text_input
        BoxLayout:
            orientation: 'horizontal'
            Button:
                text: 'OK'
                on_press: root.okfn(text_input.text)
''')

class MyPopup(Popup):
    def show(self, *args):
        d = defer.Deferred()
        self.okfn = d.callback
        self.open(*args)
        return d

@defer.inlineCallbacks
def myfunc():
    # do some stuff here
    val = yield MyPopup().show()
    # do some more stuff here

This way, you can just replace the calls to QT input dialogues with yield MyPopup().show().

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top