Пользовательский цикл событий Pyglet завершается неудачно с несколькими окнами - PullRequest
0 голосов
/ 03 июля 2018

У меня есть пользовательский цикл событий для 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

Сообщение об ошибке:

...
_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 (так как он оптимизирован для ОС) и переопределить любые надоедливые подпрограммы. Еще лучше сделать новый цикл обработки событий, копирующий код инициализации EventLoop и любые подпрограммы бэкэнда (используя PlatformEventLoop), который затем позволяет очень легко реализовать пользовательские подпрограммы. Я оставлю этот вопрос, потому что по этому вопросу очень мало документации.

...