暂停python脚本直到事件发生而不挂起/阻止GUI

时间:2014-05-15 18:27:11

标签: python multithreading qt modal-dialog kivy

尝试使用Kivy从PyQt迁移,我甚至无法想象一个解决方案。

我有数千行使用Qt对话框进行文本输入的代码。也就是说,当达到他们的代码行时,他们会“停止”脚本,直到按下“确定”按钮,这样他们就可以返回文本输入。

Kivy没有这个功能,所以理想情况下,当程序需要用户输入时,“ok”按钮会调用下一个函数来运行。

因此,我必须用一个停止正在运行的脚本的函数替换对PyQt函数的所有当前调用,启动一个有效的响应对话,然后在它有所请求的文本输入时恢复原始。所以问题是:

有没有办法在函数完成之前停止正在运行的脚本,没有挂起GUI?

我已经尝试过了:

  • 线程

即使我在新线程中启动文本输入:

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

调用此类线程的函数将在调用文本输入后立即返回。 如果我使用此代码:

t.start()
t.join()

主脚本将停止,但也会挂起文本输入GUI。

  • While / Sleep :等待文本输入变量包含有效结果。但这阻止了Kivy

  • 中正在进行的textinput GUI
  • 黑客raw_input : 目前正在考虑尝试一些黑客,这将允许我停止脚本,然后反馈由kivy文本输入弹出窗口找到的输入。

任何指示都会非常受欢迎,感谢阅读。

1 个答案:

答案 0 :(得分:2)

您不能只是暂停正在运行的脚本。相反,您需要重构您的程序以使其成为事件驱动的(因为Kivy是一个事件驱动的GUI)。

这是一个简单的示例函数:

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

重构:

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

如果您愿意使用Twisted,则可以使用DeferredinlineCallbacks更轻松地完成此操作。

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

这样,您只需用yield MyPopup().show()替换对QT输入对话的调用。