Pyglet自定义事件循环因多个窗口而失败

时间:2018-07-03 16:36:10

标签: python python-3.x pyglet

我有一个针对Pyglet的自定义事件循环,可以简化为这段简单的代码

while True:
    pyglet.clock.tick()

    for window in pyglet.app.windows:
        window.switch_to()
        # rendering code
        window.flip()
        window.dispatch_events() # <--- program hangs

只有一个窗口时,事件循环工作正常,但是当我尝试拥有两个窗口时,该循环挂在window.dispatch_events()处。以不同顺序调用函数会产生相同的问题。我也尝试调试:

pyglet.options['debug_win32'] = True

错误消息:

  File "C:\V3\src\customLib\core\gui\window.py", line 20, in __init__
    self.pygletWindow = pyglet.window.Window(width=self.size[0],
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\__init__.py", line 384, in __getattr__
    __import__(import_name)
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\__init__.py", line 1896, in <module>
    gl._create_shadow_window()
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\gl\__init__.py", line 208, in _create_shadow_window
    _shadow_window = Window(width=1, height=1, visible=False)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\win32\__init__.py", line 134, in __init__
    super(Win32Window, self).__init__(*args, **kwargs)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\__init__.py", line 512, in __init__
    config = screen.get_best_config(template_config)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\canvas\base.py", line 159, in get_best_config
    configs = self.get_matching_configs(template)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\canvas\win32.py", line 34, in get_matching_configs
    configs = template.match(canvas)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\gl\win32.py", line 29, in match
    return self._get_pixel_format_descriptor_matching_configs(canvas)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\gl\win32.py", line 73, in _get_pixel_format_descriptor_matching_configs
    pf = _gdi32.ChoosePixelFormat(canvas.hdc, byref(pfd))
b'Too many posts were made to a semaphore.\r\n'
  File "<string>", line 1, in <module>
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\idlelib\run.py", line 144, in main
    ret = method(*args, **kwargs)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\idlelib\run.py", line 474, in runcode
    exec(code, self.locals)
  File "C:\V3\src\main.py", line 8, in <module>
    g.engine.start()
  File "C:\V3\src\engine.py", line 65, in start
    self.application.run()
  File "C:\V3\src\customLib\core\application.py", line 31, in run
    self.guiManager.update()
  File "C:\V3\src\customLib\core\gui\guiManager.py", line 34, in update
    window.update()
  File "C:\V3\src\customLib\core\gui\window.py", line 63, in update
    self.pygletWindow.dispatch_events()
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\win32\__init__.py", line 615, in dispatch_events
    self.dispatch_pending_events()
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\win32\__init__.py", line 631, in dispatch_pending_events
    event[0](*event[1:])
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\win32\__init__.py", line 1035, in _event_killfocus
    self.set_exclusive_keyboard(False)
  File "C:\Users\Judicael\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pyglet\window\win32\__init__.py", line 486, in set_exclusive_keyboard
    _user32.UnregisterHotKey(self._hwnd, 0)
b'Hot key is not registered.\r\n'

经过更多修改之后,我设法使Windows响应时受到了肮脏的黑客攻击,但是事件处理仍然会出现很多问题。修复程序在设置窗口可见后立即调用更新。

样本初始化代码:

if __name__=="__main__":
    engine = Engine()
    engine.run()

class Engine: # test program
    def __init__(self):
        self.application = customLib.Application()

        # define windows, by default they are set as not visible
        self.window1 = self.application.guiManager.createWindow("Window 1",
                                                          (1000, 700),
                                                          cursorPath="../data/cursor.png")

        self.window2 = self.application.guiManager.createWindow("Window 2",
                                                          (1000, 700),
                                                          cursorPath="../data/cursor.png")

        # load up code such as loading textures and .obj files

        self.window1.pygletWindow.set_visible()
        self.window1.update() # calling this after setting self.window2 visible will cause original error
        self.window2.pygletWindow.set_visible()
        self.window2.update()

    def run(self):
        self.application.run()

### CUSTOMLIB ###
class Application:
    ...
    def run(self):
        while True:
            pyglet.clock.tick()
            self.guiManager.update()

class GUIManager: # manages windows
    ...
    def update(self):
        for window in self.windows:
            window.update()

class Window: # pyglet window higher level wrapper
    pygletWindow = None
    ...
    def update(self):
        e = self.pygletWindow.dispatch_events()

        # all events are intercepted by a custom class, it transforms all unhandled events (keyboard, mouse, and close button for now) into a list of events that can then be looped through rather than requiring twenty different functions.
        # the event list is acquired, looped through and then cleared
        for event in self.eventManager.get():
            if event.type == customLib.CLOSE:
                # close application
            elif event.type == customLib.K_W:
                # move player forward

        # rendering
        self.pygletWindow.switch_to()
        # window is cleared
        # window content rendered
        self.pygletWindow.flip()

解决上一个问题的方法似乎有些愚蠢且不可靠。对于当前事件循环,窗口反馈有时不起作用。例如,要关闭窗口,有时必须多次单击它,否则,在窗口周围移动时将不会总是抓住窗口。我dispatch_events()在每个循环中,我的自定义事件处理程序不处理窗口运动事件。事件处理的故障仅在多个窗口中发生。使用单个窗口,事件循环是完美的。可以使用pyglet EventLoop的变体来修复此“错误”。

我得出的结论是,自定义事件循环的弊端大于其优点。将EventLoop子类化(因为它已针对OS进行了优化),并且可以覆盖所有讨厌的例程,这要容易得多。更好的方法是制作一个全新的事件循环,以复制EventLoop和任何后端例程的初始化代码(利用PlatformEventLoop),然后使您可以非常轻松地实现自定义例程。我将保留这个问题,因为关于此问题的文档很少。

0 个答案:

没有答案
相关问题