PySide6 GUI 学习笔记——QCursor 光标类图标用法与自定义光标实践
1. 从一次“光标不听话”的调试说起QCursor 到底能做什么如果你正在用 PySide6 写桌面工具大概率遇到过这种场景鼠标移到某个按钮上形状没变用户根本不知道这里能点或者你精心画了一张 32x32 的 PNG 想当光标结果热点偏到了左上角点起来像在“隔空打牛”。这些问题的核心都指向同一个类——QCursor。QCursor是 PySide6也就是 Qt for Python里专门管理鼠标光标形状与位置的类。它能做三件事第一调用系统内置的标准光标比如箭头、手型、十字、等待圈第二用位图或像素图创建完全自定义的光标第三读取和设置光标的全局坐标。适合谁适合所有做桌面 GUI 的开发者尤其是需要精细交互反馈的工具类软件比如截图工具、绘图板、数据标注平台。我试过在一个标注工具里把“画笔”和“橡皮擦”做成两套自定义光标切换时用户一眼就能分辨当前模式误操作率明显下降。这篇笔记就按“内置枚举 → 自定义位图 → 动态切换 → 排错”的顺序把可直接复制的代码和验证步骤给到你。你不需要先学完整个 Qt 体系只要会建一个 QWidget 窗口就能跟着跑起来。先明确一个概念光标分“形状”和“位置”两条线。形状由Qt.CursorShape枚举或自定义图决定位置由QCursor.pos()/setPos()控制。很多人把两者混在一起调结果窗口内光标变了、窗口外又弹回去其实就是没分清“控件级光标”和“应用级光标”。下面从最常用的内置形状开始。2. 内置光标枚举与 QCursor 常用方法速查PySide6 光标形状对照Qt.CursorShape是 PySide6 里预定义光标形状的枚举直接传给QCursor构造函数即可。下面这张表是我在实际项目里整理的高频形状建议收藏对照。形状名称枚举值典型用途箭头Qt.CursorShape.ArrowCursor默认状态手型Qt.CursorShape.PointingHandCursor可点击按钮、链接十字Qt.CursorShape.CrossCursor取点、框选文本I型Qt.CursorShape.IBeamCursor输入框、可编辑区等待Qt.CursorShape.WaitCursor耗时任务进行中禁止Qt.CursorShape.ForbiddenCursor不可放置区域张开手Qt.CursorShape.OpenHandCursor可拖拽画布闭合手Qt.CursorShape.ClosedHandCursor拖拽进行中垂直调整Qt.CursorShape.SizeVerCursor上下拉伸水平调整Qt.CursorShape.SizeHorCursor左右拉伸全向移动Qt.CursorShape.SizeAllCursor整体拖动拖拽移动Qt.CursorShape.DragMoveCursorDnD 移动操作除了构造函数QCursor还有几个静态方法值得记住。QCursor.pos()返回当前主屏幕光标的全局QPoint注意是全局坐标不是控件内坐标。QCursor.setPos(QPoint)能把光标直接挪到指定位置做自动化演示时很有用。实例方法里shape()返回当前形状枚举hotSpot()返回自定义光标的热点坐标pixmap()可以取回像素图标准光标返回空图。这里有个容易踩的坑QCursor(Qt.CursorShape.WaitCursor)创建的是一个“光标对象”而widget.setCursor(cursor)才是把它应用到某个控件。如果你只创建不设置界面上什么都不会变。另外setCursor是控件级作用域——鼠标离开这个控件形状就恢复成父级或默认值。想让整个应用都变得用QApplication.setOverrideCursor()但记得配对调用restoreOverrideCursor()否则光标会“卡”在等待状态。下面这段代码把内置形状和位置读取放在一起你可以直接跑import sys from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout from PySide6.QtGui import QCursor from PySide6.QtCore import Qt, QPoint class BuiltinCursorDemo(QWidget): def __init__(self): super().__init__() self.setWindowTitle(内置光标演示) self.resize(360, 200) btn_hand QPushButton(手型光标按钮) btn_hand.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) btn_cross QPushButton(十字光标按钮) btn_cross.setCursor(QCursor(Qt.CursorShape.CrossCursor)) btn_wait QPushButton(点击读取光标位置) btn_wait.clicked.connect(self.report_pos) layout QVBoxLayout() layout.addWidget(btn_hand) layout.addWidget(btn_cross) layout.addWidget(btn_wait) self.setLayout(layout) def report_pos(self): p QCursor.pos() print(f当前全局光标位置: x{p.x()}, y{p.y()}) if __name__ __main__: app QApplication(sys.argv) w BuiltinCursorDemo() w.show() sys.exit(app.exec())运行后把鼠标分别移到两个按钮上形状会立刻切换点击第三个按钮终端会打印全局坐标。这一步验证了“控件级光标”和“位置读取”两条线都通了。接下来进入自定义光标这才是 QCursor 真正好玩的地方。3. 自定义光标用 QPixmap 和热点坐标打造专属指针附完整配置自定义光标的本质是你给一张图再告诉 Qt“这张图的哪个像素是点击生效点”这个点叫热点hotSpot。热点默认是图片中心但很多时候中心并不合适——比如一个“笔尖”光标热点应该在笔尖而不是图片正中。创建方式有两种。第一种用QPixmap支持透明通道推荐 PNG第二种用QBitmap加掩码属于老式做法现在基本被 QPixmap 取代。尺寸上官方建议 32x32跨平台兼容性最好。Windows 对 32x32 支持稳定macOS 在高分屏下会自动缩放Linux 的 X11 可能对某些尺寸回退到标准光标。下面这段配置我直接给全包括图片生成、热点设置、应用到控件import sys from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout from PySide6.QtGui import QCursor, QPixmap, QPainter, QColor, QPen from PySide6.QtCore import Qt, QPoint def make_pen_cursor_pixmap(): 画一个 32x32 的笔尖光标热点设在笔尖 (4, 28) pix QPixmap(32, 32) pix.fill(Qt.GlobalColor.transparent) painter QPainter(pix) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 笔杆 pen QPen(QColor(30, 30, 30), 3) painter.setPen(pen) painter.drawLine(20, 4, 8, 20) # 笔尖三角 painter.setBrush(QColor(200, 60, 60)) painter.setPen(Qt.PenStyle.NoPen) painter.drawPolygon([ QPoint(8, 20), QPoint(4, 28), QPoint(12, 24) ]) painter.end() return pix class CustomCursorDemo(QWidget): def __init__(self): super().__init__() self.setWindowTitle(自定义光标演示) self.resize(400, 240) pix make_pen_cursor_pixmap() # 热点设在笔尖位置 self.pen_cursor QCursor(pix, hotX4, hotY28) label QLabel(把鼠标移到这里应该显示红色笔尖光标) label.setAlignment(Qt.AlignmentFlag.AlignCenter) label.setCursor(self.pen_cursor) label.setStyleSheet(background:#f0f0f0; font-size:14px;) layout QVBoxLayout() layout.addWidget(label) self.setLayout(layout) # 打印热点验证是否生效 print(自定义光标热点:, self.pen_cursor.hotSpot()) if __name__ __main__: app QApplication(sys.argv) w CustomCursorDemo() w.show() sys.exit(app.exec())运行后把鼠标移到灰色标签区域你会看到一支红色笔尖。重点看QCursor(pix, hotX4, hotY28)这行——hotX 和 hotY 就是热点坐标原点在图片左上角。如果你把热点设成(16, 16)点击生效点就跑到图片中心笔尖会“悬空”手感很怪。这里有个细节QPixmap必须填充透明背景否则光标会带一个白色方块。pix.fill(Qt.GlobalColor.transparent)这行不能省。另外QPainter用完记得end()虽然 Python 有 GC但显式结束更稳。如果你手头已经有 PNG 图标直接QPixmap(pen.png)加载即可但要注意图片尺寸。超过 64x64 在部分平台会被裁剪或缩放建议先用图像工具压到 32x32。热点坐标也要按实际图片重新算不能照抄上面的 4 和 28。4. 动态切换与全局光标让光标跟着交互状态走真实项目里光标很少是静态的。画图工具要在“画笔/橡皮/取色”之间切数据表格要在“普通/可拖拽/禁止”之间切。实现动态切换的核心就是把多个QCursor对象存成成员变量在事件回调里调用setCursor()。下面这个例子模拟一个“模式切换”面板三个按钮分别切换画笔、橡皮、禁止三种光标并且用一个状态标签实时显示当前形状import sys from PySide6.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QLabel) from PySide6.QtGui import QCursor, QPixmap, QPainter, QColor from PySide6.QtCore import Qt, QPoint def make_dot_cursor(color, size32): pix QPixmap(size, size) pix.fill(Qt.GlobalColor.transparent) p QPainter(pix) p.setRenderHint(QPainter.RenderHint.Antialiasing) p.setBrush(color) p.setPen(Qt.PenStyle.NoPen) p.drawEllipse(QPoint(size//2, size//2), size//3, size//3) p.end() return pix class ModeSwitchDemo(QWidget): def __init__(self): super().__init__() self.setWindowTitle(光标动态切换) self.resize(420, 260) self.cursor_pen QCursor(make_dot_cursor(QColor(40, 120, 220)), 16, 16) self.cursor_eraser QCursor(make_dot_cursor(QColor(230, 230, 230)), 16, 16) self.cursor_forbid QCursor(Qt.CursorShape.ForbiddenCursor) self.canvas QLabel(画布区域鼠标移入查看光标变化) self.canvas.setAlignment(Qt.AlignmentFlag.AlignCenter) self.canvas.setStyleSheet(background:#ffffff; border:1px solid #ccc;) self.canvas.setMinimumHeight(140) self.status QLabel(当前模式无) self.status.setAlignment(Qt.AlignmentFlag.AlignCenter) btn_pen QPushButton(画笔模式) btn_pen.clicked.connect(lambda: self.apply_mode(画笔, self.cursor_pen)) btn_eraser QPushButton(橡皮模式) btn_eraser.clicked.connect(lambda: self.apply_mode(橡皮, self.cursor_eraser)) btn_forbid QPushButton(禁止模式) btn_forbid.clicked.connect(lambda: self.apply_mode(禁止, self.cursor_forbid)) row QHBoxLayout() row.addWidget(btn_pen) row.addWidget(btn_eraser) row.addWidget(btn_forbid) layout QVBoxLayout() layout.addWidget(self.canvas) layout.addLayout(row) layout.addWidget(self.status) self.setLayout(layout) def apply_mode(self, name, cursor): self.canvas.setCursor(cursor) self.status.setText(f当前模式{name}) if __name__ __main__: app QApplication(sys.argv) w ModeSwitchDemo() w.show() sys.exit(app.exec())点“画笔模式”鼠标移到白色画布上会变成蓝色圆点点“橡皮模式”变成浅灰圆点点“禁止模式”变成系统禁止符号。状态标签同步更新方便确认当前生效的是哪个对象。这里要提醒一个高频错误不要在paintEvent或鼠标移动事件里反复new QCursor。每次创建都涉及像素图拷贝高频调用会拖慢界面。正确做法是在__init__里一次性建好事件里只做setCursor引用切换。另一个场景是全局光标。比如程序启动时执行一段耗时初始化想把整个应用的光标变成等待圈可以用QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor)) # ... 执行耗时操作 ... QApplication.restoreOverrideCursor()注意setOverrideCursor和restoreOverrideCursor必须成对出现且支持嵌套。如果只设不恢复光标会一直卡在等待状态用户以为程序死了。建议用try/finally包住确保异常时也能恢复。5. 常见报错与排查光标不显示、热点偏移、跨平台回退怎么办这一节按真实报错来。第一个高频问题自定义光标完全不显示还是默认箭头。原因通常有三个。一是QPixmap尺寸为 0比如路径写错导致加载失败此时pix.isNull()返回 TrueQt 会静默回退到默认光标。排查方法在创建后打印pix.width(), pix.height()如果是 0 就是加载问题。二是热点坐标超出图片范围比如 32x32 的图你写了 hotX40Qt 可能拒绝应用。三是控件本身设置了Qt.WA_TransparentForMouseEvents鼠标事件穿透了光标自然不生效。第二个问题热点偏移点击位置和视觉位置对不上。这几乎都是 hotX/hotY 算错。记住原点在左上角x 向右增y 向下增。如果你用绘图软件看坐标注意有些工具原点在左下角直接抄会上下颠倒。验证方法打印cursor.hotSpot()和你在图片上量出的像素对比。第三个问题X11 下部分形状回退。在 Linux 的 X11 环境某些Qt.CursorShape枚举可能没有对应的系统光标主题Qt 会回退到箭头。这不是代码错是平台限制。解决办法是优先用自定义 QPixmap或者换用更通用的形状箭头、手型、十字、IBeam 这几个基本都有。第四个问题QCursor.pos()返回的坐标和控件内坐标不一致。这是概念混淆。QCursor.pos()是全局屏幕坐标控件内坐标要用widget.mapFromGlobal(QCursor.pos())转换。如果你在mouseMoveEvent里想拿控件内位置直接用event.position()更简单不用绕 QCursor。第五个问题多屏幕下 setPos 跑偏。QCursor.setPos(QPoint)默认作用于主屏幕。多屏环境要用重载版本setPos(QScreen, QPoint)先通过QGuiApplication.screens()拿到目标屏幕对象。这个在自动化演示里才用得到普通交互不用管。下面给一个自查清单出问题时按顺序过一遍提示先确认pix.isNull()为 False再确认 hotSpot 在图片范围内最后确认控件没有禁用鼠标事件。三步能解决八成“光标不生效”。如果自定义光标在高分屏上模糊检查是否开启了Qt.AA_EnableHighDpiScaling并准备 2x 图。Qt6 默认开启高分屏缩放32x32 的图在 200% 缩放下会被放大边缘发虚。解决办法是提供 64x64 图并设置pix.setDevicePixelRatio(2.0)。6. 把光标交互落到你的项目里下一步可以做什么光标这件事看起来是小细节但它直接决定用户“知不知道这里能操作”。内置枚举解决 80% 的常规场景自定义 QPixmap 解决品牌化和工具化场景动态切换解决多模式场景。三者组合基本覆盖桌面 GUI 的光标需求。如果你想把这篇的代码直接搬进项目建议先从一个控件开始试比如把主画布的默认光标换成自定义十字跑通后再扩展到模式切换。热点坐标一定要在真实图片上量不要凭感觉写。跨平台发布前至少在 Windows 和一台 Linux 上各跑一次确认没有回退。后续如果要做更复杂的交互比如拖拽时切换ClosedHandCursor、悬停可拖拽区域切换OpenHandCursor思路是一样的在enterEvent/leaveEvent/ 鼠标按下释放事件里调用setCursor。把光标对象提前建好事件里只做引用切换性能就不会有问题。代码都在上面复制到.py文件里就能跑。遇到光标不显示先打印pix.isNull()和hotSpot()这两个信息能帮你快速定位。

相关新闻

2026学生开发者指南:用TaoToken统一Key打通Java+AI的10个技能点

2026学生开发者指南:用TaoToken统一Key打通Java+AI的10个技能点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 19:12:19 阅读更多 →
Claude Code 接入国内大模型:用 cc-switch 把 Base URL 改到 TaoToken 的配置教程

Claude Code 接入国内大模型:用 cc-switch 把 Base URL 改到 TaoToken 的配置教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 19:12:19 阅读更多 →
PLSQL Developer免Oracle客户端实操指南:Instant Client配置与TNS连接

PLSQL Developer免Oracle客户端实操指南:Instant Client配置与TNS连接

简介:PLSQL Developer(免Oracle客户端)是一款面向Oracle数据库开发者的轻量级集成开发环境,专为简化PL/SQL代码编写、调试与数据库对象管理而设计,特别适合不希望安装完整Oracle客户端的中高级开发人员及DBA。资源包共…

2026/10/16 22:36:19 阅读更多 →

最新新闻

谐波平衡法MATLAB实现指南:从残差函数到频响曲线

谐波平衡法MATLAB实现指南:从残差函数到频响曲线

简介:这份MATLAB谐波平衡法实现资源,面向从事非线性电路、电力电子装置及动力系统频域分析的工程师、科研人员和学生,针对手工推导谐波平衡方程组繁琐、难以快速验证的问题,围绕将时域非线性方程经傅立叶级数展开、在频域离散为代…

2026/10/18 19:12:14 阅读更多 →
AGV视觉定位:如何用一张打印纸实现亚毫米级精度

AGV视觉定位:如何用一张打印纸实现亚毫米级精度

前阵子帮朋友调一台接驳AGV,方案评审会上,供应商报价单里光导航型激光雷达一项,就占了整车成本的将近三分之一。会后老师傅不紧不慢地从打印机里抽出一张A4纸,排了一组黑白小方块,贴到工位柱子上,又拿一台几…

2026/10/18 19:12:14 阅读更多 →
Octop:面向Python开发者的PostgreSQL本地开发CLI工具

Octop:面向Python开发者的PostgreSQL本地开发CLI工具

1. 项目概述:Octop 是什么,它解决了哪类开发者的真实痛点? Octop 不是一个广为人知的主流开源项目,也不是 Python 官方生态里的标准库组件。但当你把“Octop”和 Python、Ruff、MIT、PostgreSQL 这四个关键词并列搜索时&#x…

2026/10/18 19:12:14 阅读更多 →
医疗AI落地实录:从肺结节影像检测到临床决策支持系统设计

医疗AI落地实录:从肺结节影像检测到临床决策支持系统设计

医疗AI的落地,影像诊断只是上半场。过去一年半,我们团队在某医院影像科做了一套肺结节辅助检测系统,最初的目标很单纯:把CT影像里的可疑结节用方框标出来。模型精度从0.88一路刷到0.97,内部验证会上大家都觉得技术问题…

2026/10/18 19:12:14 阅读更多 →
Octop:Python代码质量一站式CLI工具

Octop:Python代码质量一站式CLI工具

1. 项目概述:Octop 是什么,它解决的到底是什么问题Octop 这个名字乍一听容易让人联想到章鱼(octopus),但实际它是一个真实存在的、轻量级却极具实用价值的 Python 工具项目——它不是框架,不是库&#xff0…

2026/10/18 19:12:14 阅读更多 →
大数据高级核心技术点全景拆解:存储、计算、调度与数据倾斜调优实战

大数据高级核心技术点全景拆解:存储、计算、调度与数据倾斜调优实战

1. 大数据高级核心技术点全景拆解1.1 从“能跑通”到“跑得稳”的分水岭在哪里刚入行那几年,我对大数据的理解长期停留在“能跑通就行”的阶段——任务提交上去,结果能出来,就认为万事大吉。直到有一次,一个每天凌晨调度的小时级任…

2026/10/18 19:11:14 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →