使用Pyinstaller的ModalView的间歇性失败

时间:2018-04-25 18:15:31

标签: python kivy pyinstaller

我有App使用ModalViewThread打开Clock.schedule_once()弹出窗口,并使用Queue等待弹出窗口关闭。大约90%的时间都可以正常工作。但偶尔,线程正在等待解除而不显示弹出窗口。此故障仅在运行exe生成的Pyinstaller时发生,并且仅在第一次尝试时发生(即,第一次单击&​​#34;运行测试"按钮)。如果第一次尝试成功,则所有后续尝试也会成功。

我正在使用:

  • Pyinstaller版本3.3.dev0 + 483c819
  • Windows 10

我正在使用Python 3开发,但Pyinstaller生成运行Python 2.7.14的代码。调试输出仅显示成功与失败之间的预期差异。

这个例子是从一个更复杂的应用程序中提炼出来的。如果有人可以看到问题,或建议从ModalView打开Thread更可靠的方式,请告知我们。

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)

1 个答案:

答案 0 :(得分:0)

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

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

这使kivy使用不同的Clock并消除了问题