Почему pyglet.app.run () отправляет on_resize (), что постоянно приводит к ошибке максимальной глубины рекурсии? - PullRequest
1 голос
/ 12 апреля 2020

Я пытаюсь создать простое приложение pyglet (с Python 3.7), где я могу нарисовать сетку, несколько плиток, раскрасить их и т. Д. c. Также имея возможность панорамирования, увеличивать масштаб, я следовал за этим вопросом:

Как правильно панорамировать и масштабировать в 2D?

Однако событие on_resize() подкласс MyWindow, кажется, вызывается повторно после вызова run(), даже если я не касаюсь окна, и сценарий падает с максимальной глубиной рекурсии , за исключением случаев, когда я закомментирую его self.width = width и self.height = height строки. Но затем изменение размера окна приводит к сжатию объектов, которые я рисую ...

Я бы хотел изменить размеры, чтобы просто показать больше карты, сохраняя соотношение сторон и размер нарисованных объектов.

I Я добавил print() вызовов, чтобы проверить, что происходит, но я действительно в растерянности, что происходит здесь.

import pyglet
from pyglet.gl import *

from algorithm.priorityqueue import PriorityQueue
from algorithm.squaregrid import heuristic, SquareGrid

ZOOM_IN_FACTOR = 1.2
ZOOM_OUT_FACTOR = 1/ZOOM_IN_FACTOR

class Triangle:
    def __init__(self, color):
        self.color = color
        self.vertices = pyglet.graphics.vertex_list(3, ('v3f', [0,0,0, 1000,0,0, 1000,1000,0]),
                                                       ('c4B', self.color*3))

    def draw(self, x, y, z):
        glTranslatef(x, y, z)
        self.vertices.draw(GL_TRIANGLES)
        glTranslatef(-x, -y, -z)

class Tile:

    def __init__(self, size, color):
        self.size = size
        self.color = color
        self.vertices = pyglet.graphics.vertex_list(
                                        4, ('v3f', [0,0,0, 0+self.size,0,0, 
                                        0+self.size,0+self.size,0, 0,0+self.size,0]),
                                        ('c4B', self.color*4))

    def draw(self, x, y, z):
        glTranslatef(x, y, z)
        self.vertices.draw(GL_QUADS)
        glTranslatef(-x, -y, -z)

class Line:

    def __init__(self, start, end, color, stroke):
        self.color = color
        self.stroke = stroke
        self.sx, self.sy = start
        self.ex, self.ey = end

        self.vertices = pyglet.graphics.vertex_list(
                                        2, ('v3f', [self.sx,self.sy,0, self.ex,self.ey,0]),
                                        ('c4B', self.color*2))

    def draw(self):
        glLineWidth(self.stroke)
        self.vertices.draw(GL_LINES)


class MyWindow(pyglet.window.Window):
    def __init__(self, width, height, *args, **kwargs):
        conf = Config(sample_buffers=1,
                      samples=4,
                      depth_size=16,
                      double_buffer=True)
        super().__init__(width, height, config=conf, *args, **kwargs)

        #self.set_minimum_size(960, 540)
        #glClearColor(0.8, 0.8, 0.8, 1.0)

        #glOrtho(0, self.width, 0, self.height, -10, 10) # setup orthogonal projection

        self.left = 0
        self.right = width
        self.bottom = 0
        self.top = height
        self.zoom_level = 1
        self.zoomed_width = width
        self.zoomed_height = height
        print("init finished")

    def init_gl(self, width, height):
        print("init_gl started")
        # Clear color
        glClearColor(255/255, 255/255, 255/255, 255/255)

        # Antialiasing
        glEnable(GL_LINE_SMOOTH)
        glEnable(GL_POLYGON_SMOOTH)
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)

        # Alpha Blending
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        # Viewport
        glViewport(0, 0, width, height)


    def drawstuff(self):
        print("drawstuff called")
        self.triangle = Triangle((255, 0, 0, 255))
        self.square = Tile(10, (255,0,255,255))

        self.gridlines = []
        for j in range(0,25):
            self.gridlines.append(Line((0, 40*j),(1000, 40*j),(50,50,50,255), 2))
        for i in range(0,25):
            self.gridlines.append(Line((40*i, 0),(40*i, 1000),(50,50,50,255), 2))       

    def on_resize(self, width, height):

        print("on_resize called")

        # It crashes here!
        self.width = width
        print(width)
        self.height = height
        print(height)

        self.init_gl(width, height)

    def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
        # Move camera
        self.left -= dx*self.zoom_level
        self.right -= dx*self.zoom_level
        self.bottom -= dy*self.zoom_level
        self.top -= dy*self.zoom_level       

    def on_mouse_scroll(self, x, y, dx, dy):
        # Scale factor
        f = ZOOM_IN_FACTOR if dy<0 else ZOOM_OUT_FACTOR if dy>0 else 1
        # If in proper range
        if .2 < self.zoom_level*f < 5:

            self.zoom_level *= f

            print(self.width, self.height)

            mouse_x = x/self.width
            mouse_y = y/self.height

            mouse_x_in_world = self.left + mouse_x * self.zoomed_width
            mouse_y_in_world = self.bottom + mouse_y * self.zoomed_height

            self.zoomed_width *= f
            self.zoomed_height *= f

            self.left = mouse_x_in_world - mouse_x * self.zoomed_width
            self.right = mouse_x_in_world + (1 - mouse_x) * self.zoomed_width
            self.bottom = mouse_y_in_world - mouse_y * self.zoomed_height
            self.top = mouse_y_in_world + (1 - mouse_y) * self.zoomed_height

    def on_draw(self):
        print("draw called")
        # Init projection matrix
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()

        # Init Modelview matrix
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        # Save default modelview matrix
        glPushMatrix()

        # Clear window with ClearColor
        glClear(GL_COLOR_BUFFER_BIT)

        # Set Orthographic projection matrix
        glOrtho(self.left, self.right, self.bottom, self.top, 1, -1)

        #self.draw_background()
        self.triangle.draw(0, 0, 0)
        self.square.draw(-100, -100, 0)
        for line in self.gridlines:
            line.draw()

        # Remove default modelview matrix
        glPopMatrix()

    def run(self):
        print("run called")
        pyglet.app.run()
        print("run finished")



if __name__ == "__main__":
    App = MyWindow(800, 500, resizable=True)
    App.drawstuff()
    App.run()

И консоль показывает:

$ python3 environments/testapp.py
init finished
drawstuff called
run called
on_resize called
on_resize called
on_resize called
on_resize called
on_resize called

et c. с каким-то моментом:

on_resize called
on_resize called
Traceback (most recent call last):
  File "environments/testapp.py", line 185, in <module>
    App.run()
  File "environments/testapp.py", line 177, in run
    pyglet.app.run()
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/app/__init__.py", line 107, in run
    event_loop.run()
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/app/base.py", line 159, in run
    self._legacy_setup()
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/app/base.py", line 182, in _legacy_setup
    window.dispatch_pending_events()
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 914, in dispatch_pending_events
    EventDispatcher.dispatch_event(self, *self._event_queue.pop(0))
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/event.py", line 415, in dispatch_event
    if getattr(self, event_type)(*args):
  File "environments/testapp.py", line 110, in on_resize
    self.width = width
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 964, in width
    self.set_size(new_width, self.height)
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 574, in set_size
    self.dispatch_event('on_resize', width, height)
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 1323, in dispatch_event
    if EventDispatcher.dispatch_event(self, *args) != False:
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/event.py", line 415, in dispatch_event
    if getattr(self, event_type)(*args):
  File "environments/testapp.py", line 110, in on_resize
    self.width = width
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 964, in width
    self.set_size(new_width, self.height)
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/xlib/__init__.py", line 574, in set_size
    self.dispatch_event('on_resize', width, height)
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/window/__init__.py", line 1323, in dispatch_event
    if EventDispatcher.dispatch_event(self, *args) != False:

С последними 10 строками, повторяющимися некоторое время, и в конце:

if EventDispatcher.dispatch_event(self, *args) != False:
  File "/home/admin/.local/lib/python3.7/site-packages/pyglet/event.py", line 415, in dispatch_event
    if getattr(self, event_type)(*args):
  File "environments/testapp.py", line 108, in on_resize
    print("on_resize called")
RecursionError: maximum recursion depth exceeded while calling a Python object

Я нахожу странным, что run() вызывает on_resize() сам по себе, и я действительно понятия не имею, почему переназначение атрибутов self.width и self.height входит в рекурсию.

Также происходит сбой, когда назначенное значение не ширина или высота, а любое постоянное число, а также передается ли экземпляр Window resizable = True или = False в качестве аргумента

1 Ответ

0 голосов
/ 12 апреля 2020

run не вызывает on_resize(), но команда run выполняет событие l oop, и событие изменения размера происходит один раз после инициализации окна.
pyglet.window предоставляет свойства .width и .height, поэтому нет необходимости устанавливать их на self.width = width соответственно self.height = height. Вероятно, присвоение значения self.width или self.height вызывает событие on_resize. Если вы сделаете это в on_resize, это приведет к бесконечной рекурсии.

То, что self.width и self.height содержат текущий размер окна, можно легко проверить, напечатав значения в on_resize:

class MyWindow(pyglet.window.Window):
    # [...]

    def on_resize(self, width, height):
        print("on_resize called")
        print(self.width, width)
        print(self.height, height)

Не назначать значение self.width или self.height, если новое значение равно текущему значению:

class MyWindow(pyglet.window.Window):
    # [...]

    def on_resize(self, width, height):

        new_width = ...
        new_height = ...

        if new_width != width:
            self.width = new_width
        if new_height != height:
            self.height = new_height
...