Python 带Pyinstaller的ModalView间歇性故障

Python 带Pyinstaller的ModalView间歇性故障,python,kivy,pyinstaller,Python,Kivy,Pyinstaller,我有一个应用程序,它使用时钟从线程打开一个ModalView弹出窗口。schedule_once()并使用队列等待弹出窗口。这在90%的情况下都能正常工作。但偶尔,线程会在不显示弹出窗口的情况下等待解除。此故障仅在运行由Pyinstaller生成的exe时发生,并且仅在第一次尝试(即第一次单击“运行测试”按钮)时发生。如果第一次尝试成功,则所有后续尝试也会成功 我正在使用: Pyinstaller版本3.3.dev0+483c819 视窗10 我正在使用Python3进行开发,但是Pyins

我有一个
应用程序
,它使用
时钟从
线程
打开一个
ModalView
弹出窗口。schedule_once()
并使用
队列
等待弹出窗口。这在90%的情况下都能正常工作。但偶尔,线程会在不显示弹出窗口的情况下等待解除。此故障仅在运行由
Pyinstaller
生成的
exe
时发生,并且仅在第一次尝试(即第一次单击“运行测试”按钮)时发生。如果第一次尝试成功,则所有后续尝试也会成功

我正在使用:

  • Pyinstaller版本3.3.dev0+483c819
  • 视窗10
我正在使用Python3进行开发,但是
Pyinstaller
生成了运行Python2.7.14的代码。调试输出仅显示成功和失败之间的预期差异

这个例子是从一个复杂得多的应用程序中提炼出来的。如果有人发现问题,或者推荐一种更可靠的方法从
线程打开
ModalView
,请告诉我

main.py:

import threading

from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.modalview import ModalView

from kivy.compat import PY2
if PY2:
    from Queue import Queue
else:
    from queue import Queue


class TestLayout(FloatLayout):
    def __init__(self):
        super(TestLayout, self).__init__()

    def do_test(self, *arg):
        self.th = AThread()
        self.th.start()


class MyPopup(object):
    def __init__(self, callback):
        self.buttonCallback = callback
        dismiss_button = Button(text='Dismiss')
        dismiss_button.bind(on_press=self.butt)
        self.popup = ModalView(size_hint=(.5, .5), auto_dismiss=False)
        self.popup.add_widget(dismiss_button)

    def butt(self, *args):
        if self.popup is not None:
            self.popup.dismiss()
        if self.buttonCallback is not None:
            self.buttonCallback()

    def open(self, *args):
        self.popup.open()


class AThread(threading.Thread):
    def __init__(self):
        super(AThread, self).__init__()
        self.daemon = True
        self.pop = None
        self.queue = None

    def run(self):
        print('running')
        self.queue = Queue()
        self.pop = MyPopup(lambda: self.queue.put(None, False))
        Clock.schedule_once(self.pop.open)    # This should open the popup, but occasionally it does not
        print('waiting')
        self.queue.get(True)
        print('done waiting')
        self.queue = None


root = Builder.load_string( '''
TestLayout:
    Button:
        text: 'Run Test'
        on_press: root.do_test()
''')


class testApp(App):
    def build(self):
        return root


testApp().run()
main.spec:

# -*- mode: python -*-

from kivy.deps import sdl2, glew

block_cipher = None


a = Analysis(['main.py'],
             pathex=['C:\\Users\\John\\PyCharmProjects\\popupbug'],
             binaries=[],
             datas=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
          name='Test',
          debug=True,
          strip=False,
          upx=True,
          runtime_tmpdir=None,
          console=True)

问题是主线程挂在Kivy
时钟中(通过进行一些跟踪进行验证),而不是实际执行
self.pop.open
方法。这似乎只发生在Windows上由
Pyinstaller
生成的EXE上。我在Ubuntu上用
Pyinstaller
测试了相同的代码,没有失败。修复/解决方法是通过在“main.py”的开头插入以下代码来使用不同的kivy
时钟:

from kivy.config import Config
Config.set('kivy', 'kivy_clock', 'interrupt')

这使得kivy使用不同的
时钟
并消除了问题

您是否尝试过
时钟。计划一次(self.popup.open,-1)
即在下一帧之前呼叫?是的,我有。我还尝试过使用@mainthread decorator代替Clock.schedule\u一次。所有这些都产生了相同的结果。