Python绘图应用使用压感笔触发Stack Overflow Error(0xC0000409)求助
问题描述
我正在开发一款Python绘图应用,使用鼠标输入时一切正常,但使用压感笔时,应用会在短时间后崩溃,报错:Process finished with exit code -1073740791 (0xC0000409)。已知该错误由栈溢出导致,但反复检查代码仍未找到内存泄漏点,已将问题范围缩小到处理鼠标和手写笔输入的几个事件函数。
相关事件处理代码:
def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.mousedown = True x, y = event.pos().x(), event.pos().y() if y >= self.height() - 80: for rect, colour in self.palette_rects: if rect.collidepoint(x, y): self.selected_colour = colour print(f"Selected colour: {self.selected_colour}") break else: self.shade = self.selected_colour elif event.button() == Qt.RightButton: self.mousedown = True self.shade = self.backdrop def mouseMoveEvent(self, event): if self.mousedown: spot = (event.pos().x(), event.pos().y()) adjusted_radius = int(self.radius * self.pen_pressure) pygame.draw.circle(self.transparent_surface, self.shade, spot, adjusted_radius) self.update() print(f"Spot: {spot}, Radius: {adjusted_radius}") def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton or event.button() == Qt.RightButton: self.mousedown = False def keyPressEvent(self, event): print(f"Key pressed: {event.key()}") if event.key() == Qt.Key_Q: self.close()
主循环代码:
if __name__ == '__main__': app = QApplication(sys.argv) if not check_license(): sys.exit() # Exit the app if the license is not valid window = SketchWidget() window.resize(800, 800) window.show() try: while True: window.update() window.clock.tick(60) # Limit the frame rate to 60 FPS if not app.exec_(): break except Exception as e: print(f"An error occurred: {e}") finally: pygame.quit() # Clean up Pygame before exiting sys.exit()
重点关注mouseMoveEvent函数,因为它涉及压感处理,可能随时间影响内存。请问如何避免压感笔输入导致的栈溢出,或排查代码中的内存问题?
解决方案
1. 修复主循环的嵌套事件循环问题
你的主循环存在核心错误:app.exec_()是Qt的阻塞式事件循环,会持续运行直到应用退出。但你把它放在while True循环里,每次迭代都会重新启动一个新的事件循环,导致事件栈被反复压入,压感笔频繁触发事件时,栈会快速耗尽,直接引发栈溢出。
修改后的主循环:
if __name__ == '__main__': app = QApplication(sys.argv) if not check_license(): sys.exit() window = SketchWidget() window.resize(800, 800) window.show() try: # Qt事件循环本身会处理刷新和事件,无需手动外层循环 exit_code = app.exec_() except Exception as e: print(f"An error occurred: {e}") exit_code = 1 finally: pygame.quit() sys.exit(exit_code)
2. 优化mouseMoveEvent的高频操作
压感笔移动时会触发大量mouseMoveEvent,当前代码的频繁操作会加剧资源压力:
- 移除
mouseMoveEvent里的print(f"Spot: {spot}, Radius: {adjusted_radius}")语句,仅在调试时保留。 - 给
adjusted_radius加取值限制,避免压感值异常导致半径过大,引发pygame绘图内存问题:adjusted_radius = max(1, min(int(self.radius * self.pen_pressure), 50)) # 限制半径在1-50区间
3. 正确处理压感笔专属事件
部分压感笔设备会发送Qt的TabletEvent而非普通鼠标事件,用mouseMoveEvent处理压感可能存在兼容性问题,甚至导致事件堆积。建议重写tabletEvent专门处理压感输入:
def tabletEvent(self, event): if event.type() == QEvent.TabletPress: self.mousedown = True self.pen_pressure = event.pressure() # 获取标准压感值(0.0-1.0) # 复用原mousePressEvent的颜色选择/初始化逻辑 x, y = event.pos().x(), event.pos().y() if y >= self.height() - 80: for rect, colour in self.palette_rects: if rect.collidepoint(x, y): self.selected_colour = colour break else: self.shade = self.selected_colour else: self.shade = self.selected_colour elif event.type() == QEvent.TabletMove: if self.mousedown: self.pen_pressure = event.pressure() spot = (event.pos().x(), event.pos().y()) adjusted_radius = max(1, min(int(self.radius * self.pen_pressure), 50)) pygame.draw.circle(self.transparent_surface, self.shade, spot, adjusted_radius) self.update() elif event.type() == QEvent.TabletRelease: self.mousedown = False
4. 排查内存泄漏问题
- 检查
transparent_surface的创建逻辑:确保它只初始化一次,而非每次绘图都创建新的Surface对象,否则会导致内存持续增长。 - 使用Python内置的
tracemalloc工具跟踪内存变化,定位泄漏点:import tracemalloc if __name__ == '__main__': tracemalloc.start() app = QApplication(sys.argv) # ... 其他初始化代码 ... exit_code = app.exec_() # 打印内存快照,定位TOP10内存消耗点 snapshot = tracemalloc.take_snapshot() top_stats = snapshot.statistics('lineno') print("[Top 10内存消耗]") for stat in top_stats[:10]: print(stat) tracemalloc.stop() sys.exit(exit_code)
内容的提问来源于stack exchange,提问作者Kazber
相关产品推荐
相关产品推荐

