文章目录
一、光标简介
通常我们的鼠标在不同的状态下会显示不同的形状,来提示用户当前的状态,如忙碌,拖拽,禁止,改变窗体大小等:
当然,严格来说,鼠标是硬件,而我们屏幕上看到的随着鼠标的移动而移动的图像称为光标(Cursor
),在pyQt5中,我们可以很方便的设置一个控件的光标样式,也就是当鼠标移动到这个控件上时,显示的图像。
二、光标的设置
1. 程序示例
光标的设置只需要使用setCurror()即可,以下示例了如何设置光标的样式:
from PyQt5.Qt import *
import sys
app = QApplication(sys.argv)
# 创建一个父控件
window = QWidget()
window.resize(500,500)
window.move(300,300)
window.setWindowTitle("Cursor")
# 创建2个子控件
w1 = QWidget(window)
w1.move(148,150)
w1.resize(100,100)
w1.setStyleSheet('background-color:red;')
w2 = QWidget(window)
w2.move(250,150)
w2.resize(100,100)
w2.setStyleSheet('background-color:blue;')
# 定义鼠标的样式
w1.setCursor(Qt.WaitCursor) #设置光标为:等待
w2.setCursor(Qt.SizeAllCursor) #设置光标为:移动
window.show()!
if __name__ == '__main__':
sys.exit(app.exec_())
其程序效果如下:
可以看到,在红蓝两个不同控件上时候,光标的样式是不一样的。
2. 参数查询
上文演示了2个样式的光标,那么如何设置其他的样式呢?这就需要查看Qt的文档,在Qt相关的文档里面,已经给用户列好表格,给出光标的样式,以及一般的用途:
如果我们需要显示SizeFDiagCuror的光标,就在将Qt.SizeFDiagCursor这个参数填入setCursor()中即可,即:setCursor(Qt.SizeFDiagCursor)
。
三、自定义光标
1. 素材准备
在游戏界面,通常不会使用我们系统的鼠标,Qt也可以自定义光标样式,首先需要准备一个或者是系列图标,这里预先准备了一个简约的光标:
2. 程序设计
2.1 一个简单的程序
简单的定义一个光标需要以下三步:
- 创建一个图形对象
- 将图形对象传入鼠标对象
- 使用
setCursor()
方法设置对象
以下是程序示例:
from PyQt5.Qt import *
import sys
app = QApplication(sys.argv)
# 创建一个控件
window = QWidget()
window.resize(500,500)
window.move(300,300)
window.setWindowTitle("Cursor")
#1. 创建光标的图像,参数为光标的相对位置(本文将光标存在工程目录的Cursor_png文件夹下)
pixmap = QPixmap('Cursor_png/01.png')
#2. 将光标对象传入鼠标对象中
cursor = QCursor(pixmap)
#3. 设置控件的光标
window.setCursor(cursor)
window.show()
if __name__ == '__main__':
sys.exit(app.exec_())
其运行效果如下所示:
2.2 调整光标
上图我们导入了一个光标,但是它显然太大了,我们需要通过程序将其设置小一些,另外,非常大的光标让我们注意到一个问题:如果此时有一个按钮,那么这个光标的哪个位置放在按钮上面,才可以单击按钮?是光标的头部还是光标的尾部,还是光标的任意一个地方?这同样可以通过程序来设置,以下是改进后的程序:
from PyQt5.Qt import *
import sys
app = QApplication(sys.argv)
# 创建一个父空间
window = QWidget()
window.resize(500,500)
window.move(300,300)
window.setWindowTitle("Cursor")
#1. 创建光标的图像,参数为光标的相对位置
pixmap = QPixmap('Cursor_png/01.png')
#2. 创建一个新的图像,为原素材缩小到15*20大小
new_pixmap = pixmap.scaled(15,20)
#3. 将新光标对象传入鼠标对象中,并将鼠标热点设为左上角
cursor = QCursor(new_pixmap,0,0)
#4. 设置控件的光标
window.setCursor(cursor)
window.show()
if __name__ == '__main__':
sys.exit(app.exec_())
程序运行如下所示:
程序使用 new_pixmap = pixmap.scaled(15,20)
来生成一个新的对象,这是因为scaled将重新修改大小后的图像作为一个新对象返回,而不是修改原对象本身,这和pygame操作是一样的。所以我们创建一个new_pixmap
对象存储这个返回的对象。其中,第一个参数为宽度,第二个参数为高度。
程序还定义了鼠标的热点,即有效的像素为(0,0),通过 QCursor(new_pixmap,0,0)
的第3,4个参数来设置,如果你不太了解这个概念,可以自行修改这2参数,然后去尝试点击最小化或者其他控件,来感受一下这2个参数的作用。
2.3 其他API
这里在继续介绍光标的其他常用的接口函数:
- 恢复鼠标样式:
unsetCursor()
- 获取鼠标坐标:
cursor.pos()
- 设置鼠标位置:
setPos(200,200)
- 鼠标跟踪(获取鼠标的x.y值):
mouseMoveEvent()
以下程序示例了这三个函数的使用:
from PyQt5.Qt import *
import sys
app = QApplication(sys.argv)
# 创建一个父空间
window = QWidget()
window.resize(500,500)
window.move(300,300)
window.setWindowTitle("Cursor")
pixmap = QPixmap('Cursor_png/01.png')
new_pixmap = pixmap.scaled(15,20)
cursor = QCursor(new_pixmap,0,0)
#1. 设置控件的光标
window.setCursor(cursor)
#2. 恢复鼠标默认值
window.unsetCursor()
#3. 获取鼠标对象
cursor = window.cursor()
#4. 设置鼠标位置
cursor.setPos(200,200)
#5. 打印鼠标的位置
print(cursor.pos())
其运行结果如下:
程序第17虽然设置了光标的样式,但是在18行又将其取消了,所以光标的样式并没有改变,接着程序获取了当前光标对象,然后设置它的坐标,此时运行后你会发现鼠标跑到屏幕(200,200)的位置,最后程序将鼠标的值打印了出来。这里的坐标都是相对于显示器屏幕的。
上文代码演示了鼠标光标位置,获取光标坐标和恢复光标演示API函数的使用,接下来示例鼠标跟踪程序的实现。鼠标跟踪指的是获取当前鼠标的位置,可以是相对屏幕的位置,也可以是相对当前控件的位置,它是通过mouseMoveEvent()
来获取的,当开启鼠标跟踪以后(setMouseTracking(True)
)程序会一直记录鼠标的位置,然后作为事件派送给mouseMoveEvent()
函数。
以下是示例程序:
from PyQt5.Qt import *
import sys
#1. 自定义一个QWidget对象
class MyWindow(QWidget):
#2. 重定义鼠标事件
def mouseMoveEvent(self, QMouseEvent):
print("当前鼠标坐标:", QMouseEvent.pos())
app = QApplication(sys.argv)
#创建一个父空间
window = MyWindow()
window.resize(500,500)
window.move(300,300)
window.setWindowTitle("Cursor")
#3. 启动鼠标跟踪
window.setMouseTracking(True)
window.show()
if __name__ == '__main__':
sys.exit(app.exec_())
其运行效果如下所示:
可以看到,当鼠标在窗体中移动的时候,pyCharm的控制台源源不断的打印出鼠标的坐标。注意到,代码第19行我们还使用了window.setMouseTracking(True)
来使能光标跟踪,如果没有这句代码,那么只有鼠标被按下状态时,才会被跟踪。这里鼠标的坐标获取的是相对于窗体的坐标,如果我们需要获取相对于屏幕的坐标,可以将第8行print
函数中的QMouseEvent.pos()
改为QMouseEvent.globalPos()
,当然了,依然只有当鼠标的光标在窗体上运动时,才会被跟踪到。
四、练习
综合以上的内容,我们来创建一个自定义光标样式的控件(QWidget),并建立一个跟随鼠标的标签(QLabel),并且这个标签实时显示鼠标的坐标。
1. 素材准备
自定义鼠标样式,就需要准备一个素材,这里重新选择一个光标素材:
2. 程序实现
由于此处程序分步实现,每一步只贴出新增或修改部分,其他用分号表示省略,程序运行效果也选择性略过,完整程序最后再统一贴出。
2.1 创建一个基本控件和标签
首先创建一个最基本的控件:
from PyQt5.Qt import *
import sys
#创建一个APP
app = QApplication(sys.argv)
#创建一个基本窗体
window = QWidget()
window.resize(500,500)
window.move(300,300)
window.setWindowTitle("Cursor Trace")
#创建一个label
label = QLabel(window)
label.setText('(x,y)')
label.move(100,100)
label.setStyleSheet('background:green;font-size:25px;')
window.show()
if __name__ == '__main__':
sys.exit(app.exec_())
2.2 标签跟随光标
- 光标的位置的获取:
from PyQt5.Qt import *
import sys
# 创建一个APP
app = QApplication(sys.argv)
# 创建一个新的类
class MyWindow(QWidget):
# 定义鼠标事件
def mouseMoveEvent(self, ev):
print("当前鼠标坐标:", ev.globalPos())
# 创建一个基本窗体,需要修改为新的类
window = MyWindow()
··· ···
# 创建一个label
label = QLabel(window)
··· ···
# 使能光标跟随
window.setMouseTracking(True)
window.show()
if __name__ == '__main__':
sys.exit(app.exec_())
- 设置标签的位置及其显示的值:
这里我们还把上面的代码封装成一个类,这样可以统一创建一个对象,方便管理,完整代码如下所示:
from PyQt5.Qt import *
import sys
#创建一个APP
app = QApplication(sys.argv)
class MyWindow(QWidget):
# 创建一个基本窗体
def __init__(self):
super().__init__()
self.resize(500, 500)
self.move(300, 300)
self.setWindowTitle("Cursor Trace")
self.label = QLabel(self)
self.label.setText('')
self.label.move(100, 100)
self.label.setStyleSheet('background:green;font-size:25px;')
self.setMouseTracking(True)
self.show()
def mouseMoveEvent(self, ev):
x = ev.localPos().x()
y = ev.localPos().y()
self.label.move(x,y)
# 设置标签的显示
self.label.setText('('+str(y)+' '+str(x)+')')
# 自适应大小:因为x的坐标可能是0,有可能是100,y同理,所以label长度需要自适应
self.label.adjustSize()
print("当前鼠标坐标:", x, y)
window = MyWindow()
if __name__ == '__main__':
sys.exit(app.exec_())
2.3 自定义光标
自定义光标需要创建一个类,并导入图片,具体程序如下:
...
self.pixmap = QPixmap('Cursor_png/arrows.png')
self.new_pixmap = self.pixmap.scaled(15, 20)
self.cursor = QCursor(self.new_pixmap, 0, 0)
...
2.3 完整代码
当自定义鼠标光标以后,程序的功能就都完成了,以下是完整的代码:
from PyQt5.Qt import *
import sys
#创建一个APP
app = QApplication(sys.argv)
class MyWindow(QWidget):
# 创建一个基本窗体
def __init__(self):
super().__init__()
self.resize(500, 500)
self.move(300, 300)
self.setWindowTitle("Cursor Trace")
self.label = QLabel(self)
self.label.setText('')
self.label.move(100, 100)
self.label.setStyleSheet('background:green;font-size:25px;')
self.setMouseTracking(True)
self.pixmap = QPixmap('Cursor_png/arrows.png')
self.new_pixmap = self.pixmap.scaled(15, 20)
self.cursor = QCursor(self.new_pixmap, 0, 0)
self.setCursor(self.cursor)
self.show()
def mouseMoveEvent(self, ev):
x = ev.localPos().x()
y = ev.localPos().y()
# 将label显示在右下角
self.label.move((x+15),(y+20))
self.label.setText('('+str(y)+' '+str(x)+')')
self.label.adjustSize()
print("当前鼠标坐标:", x, y)
window = MyWindow()
if __name__ == '__main__':
sys.exit(app.exec_())
程序运行下过如下:
3. 程序的不足与优化
上述的程序实现了基本的功能,但是它有一些明显的不足:
- 光标在靠近边框右侧时候,由于显示坐标的label是子控件,所以导致部分不被显示,如下所示:
- 光标由于在label上不被跟踪,所以导致有时候看起来label没有在跟踪鼠标(这种情不易出现)
- 像素点坐标肯定是整数,这里不需要显示一位小数
以上不足可以通过简单的逻辑处理规避,这里不再赘述。