KivyMD//Python 如何创建一个显示纺车的加载对话框(弹出),同时代码在后台运行另一个函数
KivyMD//Python How to create a loading dialog box (pop-up) that displays a spinning wheel while the code runs another function in the background
我正在使用 concurrent.futures,目前我的代码似乎可以按需要运行这两个函数,但是第一个弹出窗口不会显示,直到两个函数都完成(问题是第二个函数以先关闭第一个对话框然后打开第二个对话框结束。
这是我的完整 .py 代码:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd.uix.dialog import MDDialog
import concurrent.futures
KV = '''
<Content>
orientation: "vertical"
spacing: -40
MDSpinner:
size_hint: None, None
size: dp(46), dp(46)
pos_hint: {'center_x': 0.1}
active: True
MDLabel:
text: "Processing..."
pos_hint: {'center_x': .7}
FloatLayout:
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.start()
'''
class Content(BoxLayout):
pass
class Example(MDApp):
dialog = None
def build(self):
return Builder.load_string(KV)
def start(self):
with concurrent.futures.ThreadPoolExecutor() as executor:
f1 = executor.submit(self.pop_up1())
f2 = executor.submit(self.test())
def pop_up1(self):
'''Displays a pop_up with a spinning wheel'''
if not self.dialog:
self.dialog = MDDialog(
size_hint=(.45, None),
auto_dismiss=True,
type="custom",
content_cls=Content(),
)
self.dialog.open()
def test(self):
'''Counts to 1000000 and then it closes pop_up1 and opens pop_up2'''
for number in range(1000000):
print(number)
self.dismiss()
self.pop_up2()
def pop_up2(self):
if not self.dialog:
self.dialog = MDDialog(
title="Done",
size_hint=(.6, None),
text="Done",
)
self.dialog.open()
def dismiss(self, *args):
self.dialog.dismiss()
Example().run()
你的代码有几个问题。首先,当您使用构造时:
with concurrent.futures.ThreadPoolExecutor() as executor:
f1 = executor.submit(self.pop_up1())
f2 = executor.submit(self.test())
有一个隐含的 executor.sutdown()
调用,等待提交的任务完成。参见documentation。由于您在主线程上使用此构造,所有 GUI 更新都将被阻止,直到所有提交的任务完成。所以你通常不应该在 kivy 应用程序的主线程上使用该构造。
第二个问题是 MDDialog
的创建和显示应该在主线程上完成,因此将 pop_up1()
之类的方法提交给 executor
可能会有问题。
第三个问题是您尝试将 MDDialog
与 if not self.dialog:
条件一起使用。无论如何,您不能重复使用 MDDialog
。
因此,考虑到所有这些因素,这里是您的代码的修改版本,可以满足您的需求:
from kivy.clock import Clock, mainthread
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd.uix.dialog import MDDialog
import concurrent.futures
KV = '''
<Content>
orientation: "vertical"
spacing: -40
MDSpinner:
size_hint: None, None
size: dp(46), dp(46)
pos_hint: {'center_x': 0.1}
active: True
MDLabel:
text: "Processing..."
pos_hint: {'center_x': .7}
FloatLayout:
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.start()
'''
class Content(BoxLayout):
pass
class Example(MDApp):
dialog = None
def build(self):
return Builder.load_string(KV)
def start(self):
# this must be done on the main thread
self.pop_up1()
executor = concurrent.futures.ThreadPoolExecutor()
# f1 = executor.submit(self.pop_up1) # this must be done on the main thread
f2 = executor.submit(self.test)
def pop_up1(self):
'''Displays a pop_up with a spinning wheel'''
self.dialog = MDDialog(
size_hint=(.45, None),
auto_dismiss=True,
type="custom",
content_cls=Content(),
)
self.dialog.open()
def test(self):
'''Counts to 1000000 and then it closes pop_up1 and opens pop_up2'''
for number in range(100000):
print(number)
self.dismiss()
Clock.schedule_once( self.pop_up2) # pop_up2() must be done on the main thread
def pop_up2(self,*args):
self.dialog = MDDialog(
title="Done",
size_hint=(.6, None),
text="Done",
)
self.dialog.open()
@mainthread
def dismiss(self, *args):
self.dialog.dismiss()
Example().run()
我正在使用 concurrent.futures,目前我的代码似乎可以按需要运行这两个函数,但是第一个弹出窗口不会显示,直到两个函数都完成(问题是第二个函数以先关闭第一个对话框然后打开第二个对话框结束。
这是我的完整 .py 代码:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd.uix.dialog import MDDialog
import concurrent.futures
KV = '''
<Content>
orientation: "vertical"
spacing: -40
MDSpinner:
size_hint: None, None
size: dp(46), dp(46)
pos_hint: {'center_x': 0.1}
active: True
MDLabel:
text: "Processing..."
pos_hint: {'center_x': .7}
FloatLayout:
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.start()
'''
class Content(BoxLayout):
pass
class Example(MDApp):
dialog = None
def build(self):
return Builder.load_string(KV)
def start(self):
with concurrent.futures.ThreadPoolExecutor() as executor:
f1 = executor.submit(self.pop_up1())
f2 = executor.submit(self.test())
def pop_up1(self):
'''Displays a pop_up with a spinning wheel'''
if not self.dialog:
self.dialog = MDDialog(
size_hint=(.45, None),
auto_dismiss=True,
type="custom",
content_cls=Content(),
)
self.dialog.open()
def test(self):
'''Counts to 1000000 and then it closes pop_up1 and opens pop_up2'''
for number in range(1000000):
print(number)
self.dismiss()
self.pop_up2()
def pop_up2(self):
if not self.dialog:
self.dialog = MDDialog(
title="Done",
size_hint=(.6, None),
text="Done",
)
self.dialog.open()
def dismiss(self, *args):
self.dialog.dismiss()
Example().run()
你的代码有几个问题。首先,当您使用构造时:
with concurrent.futures.ThreadPoolExecutor() as executor:
f1 = executor.submit(self.pop_up1())
f2 = executor.submit(self.test())
有一个隐含的 executor.sutdown()
调用,等待提交的任务完成。参见documentation。由于您在主线程上使用此构造,所有 GUI 更新都将被阻止,直到所有提交的任务完成。所以你通常不应该在 kivy 应用程序的主线程上使用该构造。
第二个问题是 MDDialog
的创建和显示应该在主线程上完成,因此将 pop_up1()
之类的方法提交给 executor
可能会有问题。
第三个问题是您尝试将 MDDialog
与 if not self.dialog:
条件一起使用。无论如何,您不能重复使用 MDDialog
。
因此,考虑到所有这些因素,这里是您的代码的修改版本,可以满足您的需求:
from kivy.clock import Clock, mainthread
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivymd.uix.dialog import MDDialog
import concurrent.futures
KV = '''
<Content>
orientation: "vertical"
spacing: -40
MDSpinner:
size_hint: None, None
size: dp(46), dp(46)
pos_hint: {'center_x': 0.1}
active: True
MDLabel:
text: "Processing..."
pos_hint: {'center_x': .7}
FloatLayout:
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.start()
'''
class Content(BoxLayout):
pass
class Example(MDApp):
dialog = None
def build(self):
return Builder.load_string(KV)
def start(self):
# this must be done on the main thread
self.pop_up1()
executor = concurrent.futures.ThreadPoolExecutor()
# f1 = executor.submit(self.pop_up1) # this must be done on the main thread
f2 = executor.submit(self.test)
def pop_up1(self):
'''Displays a pop_up with a spinning wheel'''
self.dialog = MDDialog(
size_hint=(.45, None),
auto_dismiss=True,
type="custom",
content_cls=Content(),
)
self.dialog.open()
def test(self):
'''Counts to 1000000 and then it closes pop_up1 and opens pop_up2'''
for number in range(100000):
print(number)
self.dismiss()
Clock.schedule_once( self.pop_up2) # pop_up2() must be done on the main thread
def pop_up2(self,*args):
self.dialog = MDDialog(
title="Done",
size_hint=(.6, None),
text="Done",
)
self.dialog.open()
@mainthread
def dismiss(self, *args):
self.dialog.dismiss()
Example().run()