为什么 pyglet.app.run() 重复调度 on_resize() 会导致最大递归深度错误?
Why is pyglet.app.run() dispatching on_resize() repeatedly leading to max recursion depth error?
我正在尝试制作一个简单的 pyglet 应用程序(使用 Python 3.7),我可以在其中绘制网格、一些瓷砖、为它们着色等。同时还能够平移、缩放,所以我跟着这个问题:
How to pan and zoom properly in 2D?
但是 on_resize()
子类的 on_resize()
事件
似乎在 run()
被调用后被重复调用,即使我没有触及 window,并且脚本以 最大递归深度 崩溃,除非我注释掉了它的 self.width = width
和 self.height = height
行。
但是然后调整 window 的大小会挤压我正在绘制的对象...
我想调整大小以显示更多地图,同时保持绘制对象的纵横比和大小。
我添加了一些 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
等在某些时候 :
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
时,它也会崩溃
run
不调用on_resize()
,但是运行执行事件循环,初始化window后resize事件发生一次。
pyglet.window
提供了属性.width
和.height
,因此不需要分别用self.width = width
设置self.height = height
。给 self.width
或 self.height
赋值可能会触发 on_resize
事件。如果您在 on_resize
中执行此操作,则会导致无休止的递归。
self.width
和 self.height
包含 window 的当前大小,可以通过打印 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
我正在尝试制作一个简单的 pyglet 应用程序(使用 Python 3.7),我可以在其中绘制网格、一些瓷砖、为它们着色等。同时还能够平移、缩放,所以我跟着这个问题:
How to pan and zoom properly in 2D?
但是 on_resize()
子类的 on_resize()
事件
似乎在 run()
被调用后被重复调用,即使我没有触及 window,并且脚本以 最大递归深度 崩溃,除非我注释掉了它的 self.width = width
和 self.height = height
行。
但是然后调整 window 的大小会挤压我正在绘制的对象...
我想调整大小以显示更多地图,同时保持绘制对象的纵横比和大小。
我添加了一些 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
等在某些时候 :
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
时,它也会崩溃
run
不调用on_resize()
,但是运行执行事件循环,初始化window后resize事件发生一次。
pyglet.window
提供了属性.width
和.height
,因此不需要分别用self.width = width
设置self.height = height
。给 self.width
或 self.height
赋值可能会触发 on_resize
事件。如果您在 on_resize
中执行此操作,则会导致无休止的递归。
self.width
和 self.height
包含 window 的当前大小,可以通过打印 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