Pyglet中事件无法中和及窗口异常问题咨询
Pyglet Widget事件处理问题修复
问题1:叠加Widget重复接收事件
当两个PushButton叠加时,点击上层按钮会触发两个按钮的on_press和on_release回调。虽然文档说明返回True可终止事件传递,但这仅对窗口事件处理器链生效,PushButton内部的事件分发逻辑不会因回调返回True而停止传递事件给其他Widget。
解决思路
重写PushButton的鼠标事件处理方法,让按钮在完成自身事件处理后返回True,直接终止事件在窗口处理器链中的传递,避免下层Widget接收到事件。
问题2:按钮移出窗口松开触发异常
点击按钮不松开,移出窗口后再松开时,鼠标坐标超出窗口范围,PushButton内部的区域判断逻辑会触发异常,同时_pressed状态无法正确重置。
解决思路
重写PushButton的on_mouse_release方法,优先重置按钮的按压状态,再处理后续逻辑,避免坐标异常导致的错误;同时完善鼠标移动事件的处理,确保hover状态更新逻辑稳定。
修改后的完整代码
from __future__ import annotations import pyglet from pyglet.graphics import Batch from pyglet.window import Window from pyglet.gui.widgets import PushButton from pyglet.shapes import Rectangle from PIL import Image, ImageDraw # 重写PushButton,修复事件传递和移出窗口异常问题 class FixedPushButton(PushButton): def on_mouse_press(self, x, y, buttons, modifiers): if self._check_hit(x, y): self._pressed = True self._update_image() if self.on_press: self.on_press(self) # 返回True阻止事件传递给其他Widget return True return False def on_mouse_release(self, x, y, buttons, modifiers): # 先重置按压状态,避免坐标异常导致逻辑出错 if self._pressed: self._pressed = False self._update_image() if self.on_release: self.on_release(self) # 返回True阻止事件传递 return True return False def on_mouse_motion(self, x, y, dx, dy): # 修复hover状态的异常处理 hit = self._check_hit(x, y) if hit != self._hover: self._hover = hit self._update_image() if self.on_hover: self.on_hover(self, hit) return self._hover class TestsEventsKlass(): def __init__(self, width, height): self.init_display(width, height) self.setup_ressources() self.setup_widgets() self.setup_handlers() def init_display(self, width, height): self.width = width self.height = height self.batch = Batch() self.display = pyglet.display.get_display() self.screen = self.display.get_default_screen() template = pyglet.gl.Config(double_buffer=True, alpha_size=8, depth_size=0, stencil_size=0) try: self.config = self.screen.get_best_config(template) except pyglet.window.NoSuchConfigException as Err: print(f"Configuration désirée non supportée...[{Err}]") raise context = self.config.create_context(None) self.main_window = Window(width, height, config=self.config, style=Window.WINDOW_STYLE_OVERLAY) self.main_window.set_caption("B & Ev") def setup_ressources(self): self.back_color = (0, 0, 0, 0) w = round(self.main_window.width * 0.4) h = round(self.main_window.height * 0.3) # UNPRESSED temp_image = Image.new("RGBA", (w, h), self.back_color) draw = ImageDraw.Draw(temp_image) draw.rounded_rectangle(xy=(0, 0, w, h), radius=27.0, fill=(64, 64, 64, 255), outline=(32, 32, 32, 255), width=7, corners=(True, True, True, True)) raw_image = temp_image.tobytes() self.unpressed_img = pyglet.image.ImageData(temp_image.width, temp_image.height, 'RGBA', raw_image, pitch=-temp_image.width * 4) # PRESSED temp_image = Image.new("RGBA", (w, h), self.back_color) draw = ImageDraw.Draw(temp_image) draw.rounded_rectangle(xy=(0, 0, w, h), radius=27.0, fill=(32, 32, 32, 255), outline=(64, 64, 64, 255), width=4, corners=(True, True, True, True)) raw_image = temp_image.tobytes() self.pressed_img = pyglet.image.ImageData(temp_image.width, temp_image.height, 'RGBA', raw_image, pitch=-temp_image.width * 4) # HOVER temp_image = Image.new("RGBA", (w, h), self.back_color) draw = ImageDraw.Draw(temp_image) draw.rounded_rectangle(xy=(0, 0, w, h), radius=27.0, fill=(32, 64, 32, 255), outline=(32, 64, 32, 255), width=2, corners=(True, True, True, True)) raw_image = temp_image.tobytes() self.hover_img = pyglet.image.ImageData(temp_image.width, temp_image.height, 'RGBA', raw_image, pitch=-temp_image.width * 4) def setup_widgets(self): self.background = Rectangle(0, 0, self.main_window.width, self.main_window.height, (0, 0, 255, 32), batch=self.batch) w = self.unpressed_img.width h = self.unpressed_img.height x = round((self.main_window.width - w) / 2) y = round((self.main_window.height - (h * 2)) / 3) # 使用修复后的FixedPushButton self.button1 = FixedPushButton(x, self.main_window.height - (y * 4), self.unpressed_img, self.pressed_img, self.hover_img, self.batch) self.button2 = FixedPushButton(x, y * 2, self.unpressed_img, self.pressed_img, self.hover_img, self.batch) def setup_handlers(self): self.main_window.on_draw = self.window_on_draw self.button1_handlers = Button1_Events() self.button1.push_handlers(self.button1_handlers) self.button2_handlers = Button2_Events() self.button2.push_handlers(self.button2_handlers) self.main_window.push_handlers(self.button2) self.main_window.push_handlers(self.button1) def window_on_draw(self): self.main_window.clear() self.batch.draw() def run(self): pyglet.app.run() class Button1_Events(): def on_press(self, widget: PushButton): print(f"{self.__class__.__name__}.on_press") widget._sprite.opacity = 27 return True def on_release(self, widget: PushButton): print(f"{self.__class__.__name__}.on_release") widget._sprite.opacity = 255 return True class Button2_Events(): def on_press(self, widget: PushButton): print(f"{self.__class__.__name__}.on_press") widget._sprite.opacity = 27 return True def on_release(self, widget: PushButton): print(f"{self.__class__.__name__}.on_release") widget._sprite.opacity = 255 return True if __name__ == "__main__": try: ww = wh = 500 app = TestsEventsKlass(ww, wh) app.run() except Exception as Err: print(Err) raise
修复说明
- 问题1解决:通过
FixedPushButton重写鼠标事件方法,处理完自身事件后返回True,直接终止事件传递,下层Widget不会再收到同一事件。 - 问题2解决:在
on_mouse_release中优先重置按压状态,避免坐标异常导致的逻辑错误;同时优化on_mouse_motion逻辑,确保hover状态更新稳定。
内容的提问来源于stack exchange,提问作者izd
相关产品推荐
相关产品推荐

