我有一个针对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),然后使您可以非常轻松地实现自定义例程。我将保留这个问题,因为关于此问题的文档很少。