暂停 python 脚本,直到事件发生而不挂起/阻塞 GUI
问题描述
尝试使用 Kivy 从 PyQt 迁移,我什至无法想象解决方案.
Trying to migrate from PyQt with Kivy and I cant even imagine a solution for this.
我有数千行代码使用 Qt 的对话进行文本输入.也就是说,当到达他们的代码行时,他们会停止"脚本,直到按下确定"按钮,这样他们就可以返回文本输入.
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 没有该功能,因此理想情况下,当程序需要用户输入时,确定"按钮会调用下一个功能来运行.
Kivy doesnt have that functionality, so ideally, when the program needs user input, the "ok" button would call for the next function to run.
因此,我必须将所有当前对 PyQt 函数的调用替换为一个函数,该函数会停止正在运行的脚本,启动一个有效的响应式对话,然后在它有请求的文本输入时恢复原始对话.所以问题是:
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:
有没有办法在函数完成之前停止正在运行的脚本,不挂起 GUI?
Is there a way to stop a running script until a function finishes, without hanging the GUI?
我已经试过了:
- 线程:
即使我在新线程中开始文本输入:
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()
主脚本将停止,但也会挂起文本输入 GUI.
The main script will stop, but also hangs the text input GUI.
While/Sleep:等待文本输入变量包含有效结果.但这会阻止 Kivy 中正在进行的文本输入 GUI
While/Sleep: Waiting for the text input variable to contain a valid result. But this blocks the ongoing textinput GUI in Kivy
破解 raw_input:目前正在考虑尝试一些破解,这将允许我停止脚本,然后反馈由 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.
解决方案
你不能只是暂停正在运行的脚本.相反,您需要将程序重构为事件驱动(因为 Kivy 是事件驱动的 GUI).
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
重构:
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,你可以使用 Deferred
s 和 inlineCallbacks
使这更容易.
If you're willing to use Twisted, you can make this even easier using Deferred
s 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
这样,您只需将 QT 输入对话的调用替换为 yield MyPopup().show()
.
This way, you can just replace the calls to QT input dialogues with yield MyPopup().show()
.
相关文章