You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:31:02