PyQt5 - QWidgets部件进阶教程之塑形时钟
2016-03-12 22:48
363 查看
废话
定义ShapedClock类
最终代码
最终效果
视窗模板注意事项
问题说明
当然该案例的视窗不提供文件菜单或关闭按钮,我们提供一个包含退出口的环境菜单,这样就能使案例关闭,在窗口上点击鼠标右键就可以打开这个菜单。
paintEvent()的重写与AnalogClock类相同,实现sizeHint(),这样就不用明确的调整部件大小,同样为调整尺寸事件提供一个事件处理程序,如果时钟调整大小,这允许我们更新模板。
视窗不包含标题栏,我们重写mouseMoveEvent()和mousePressEvent(),这可以实现时钟在屏幕内拖动,dragPosition变量让我们保持追踪用户最后在部件上点击的位置。
设置一个timer,并连接到部件的update()槽
我们告知视窗管理器,部件不再用视窗框架装饰,这通过在部件上设置FramelessWindowHint标志实现。然后我们需要提供一个用户在屏幕内移动时钟的方法。鼠标按钮事件交由mousePressEvent()操作。
如果鼠标左键点击在部件上,我们记录‘部件框架的顶-左位置’和‘点鼠标点击时的点’之间的全局坐标位移。如果用户点住鼠标左键时移动,则会使用位移。当我们作用在事件上后,会通过accept()函数接受它的值。
如果鼠标在部件上移动,会调用mouseMoveEvent()处理程序。如果点住左键移动,部件的顶-左角则移动到一点,该点是从‘全局坐标中的当前光标位置’减去‘拖动位置’获得。如果我们拖动部件,同样接受事件。
当时在部件中心后绘制圆形钟面后,我们将这个作为模板原型。
缺少视窗框架可能导致用户在一些平台上难以调整部件大小,但这不一定是不可能的。当部件尺寸调整,resizeEvent()函数总是可以确保部件模板更新,另外可以保证当部件首次显示时可以正确设置。
最终我们为部件实现sizeHint(),这样当首次显示时塔克已给出一个合理的默认尺寸。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/14/5f807de2b1ed38e0ce8d5131be0b4a9a)
定义ShapedClock类
最终代码
最终效果
视窗模板注意事项
问题说明
废话
部件模板通过限制绘制的可用区域,来自定义顶层部件的形状。在一些视窗系统中,设置一些窗口标志可以使窗口装饰关闭(如标题栏、窗口矿建、按钮等),这样就能实现创建指定形状的视窗。该案例中,我们使用这一特性来创建一个模拟时钟的圆形视窗。当然该案例的视窗不提供文件菜单或关闭按钮,我们提供一个包含退出口的环境菜单,这样就能使案例关闭,在窗口上点击鼠标右键就可以打开这个菜单。
定义ShapedClock类
ShapedClock类基于AnalogClock类案例,本例中不再重新展示相同代码说明,如有需要可参考链接。class ShapedClock(QWidget): def __init__(self): super(ShapedClock, self).__init__() self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowSystemMenuHint) self.timer = QTimer(self) self.timer.timeout.connect(self.update) self.timer.start(1000) self.quitAction = QAction('E&xit', self) self.quitAction.setShortcut('Ctrl+Q') self.quitAction.triggered.connect(qApp.quit) self.addAction(self.quitAction) self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setToolTip('Drag the clock with the left mouse button.\n ' 'Use the right mouse button to open a context menu.') self.setWindowTitle('Shaped Analog Clock')
paintEvent()的重写与AnalogClock类相同,实现sizeHint(),这样就不用明确的调整部件大小,同样为调整尺寸事件提供一个事件处理程序,如果时钟调整大小,这允许我们更新模板。
视窗不包含标题栏,我们重写mouseMoveEvent()和mousePressEvent(),这可以实现时钟在屏幕内拖动,dragPosition变量让我们保持追踪用户最后在部件上点击的位置。
设置一个timer,并连接到部件的update()槽
我们告知视窗管理器,部件不再用视窗框架装饰,这通过在部件上设置FramelessWindowHint标志实现。然后我们需要提供一个用户在屏幕内移动时钟的方法。鼠标按钮事件交由mousePressEvent()操作。
def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.dragPosition = event.globalPos() - self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self, event): if event.buttons() and Qt.LeftButton: self.move(event.globalPos() - self.dragPosition) event.accept()
如果鼠标左键点击在部件上,我们记录‘部件框架的顶-左位置’和‘点鼠标点击时的点’之间的全局坐标位移。如果用户点住鼠标左键时移动,则会使用位移。当我们作用在事件上后,会通过accept()函数接受它的值。
如果鼠标在部件上移动,会调用mouseMoveEvent()处理程序。如果点住左键移动,部件的顶-左角则移动到一点,该点是从‘全局坐标中的当前光标位置’减去‘拖动位置’获得。如果我们拖动部件,同样接受事件。
def resizeEvent(self, event): side = max(self.width(), self.height()) maskedRegion = QRegion(self.width() / 2 - side / 2, self.height() / 2 - side / 2, side, side, QRegion.Ellipse) self.setMask(maskedRegion) def sizeHint(self): return QSize(100, 100)
当时在部件中心后绘制圆形钟面后,我们将这个作为模板原型。
缺少视窗框架可能导致用户在一些平台上难以调整部件大小,但这不一定是不可能的。当部件尺寸调整,resizeEvent()函数总是可以确保部件模板更新,另外可以保证当部件首次显示时可以正确设置。
最终我们为部件实现sizeHint(),这样当首次显示时塔克已给出一个合理的默认尺寸。
最终代码
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class ShapedClock(QWidget): def __init__(self): super(ShapedClock, self).__init__() self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowSystemMenuHint) self.timer = QTimer(self) self.timer.timeout.connect(self.update) self.timer.start(1000) self.quitAction = QAction('E&xit', self) self.quitAction.setShortcut('Ctrl+Q') self.quitAction.triggered.connect(qApp.quit) self.addAction(self.quitAction) self.setContextMenuPolicy(Qt.ActionsContextMenu) self.setToolTip('Drag the clock with the left mouse button.\n ' 'Use the right mouse button to open a context menu.') self.setWindowTitle('Shaped Analog Clock')
def mousePressEvent(self, event):
if event.button() =
4000
= Qt.LeftButton:
self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
event.accept()
def mouseMoveEvent(self, event):
if event.buttons() and Qt.LeftButton:
self.move(event.globalPos() - self.dragPosition)
event.accept()
def paintEvent(self, QPaintEvent):
hourHand = [
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -40)
]
minuteHand = [
QPoint(7, 8),
QPoint(-7, 8),
QPoint(0, -70)
]
hourColor = QColor(127, 0, 127)
minuteColor = QColor(0, 127, 127, 191)
side = min(self.width(), self.height())
time = QTime.currentTime()
painter = QPainter()
painter.begin(self)
painter.setRenderHint(QPainter.Antialiasing)
painter.translate(self.width() / 2, self.height() / 2)
painter.scale(side / 200.0, side / 200.0)
painter.setPen(Qt.NoPen)
painter.setBrush(hourColor)
painter.save()
painter.rotate(30.0 * (time.hour() + time.minute() / 60))
painter.drawConvexPolygon(QPolygon(hourHand))
painter.restore()
painter.setPen(hourColor)
for i in range(12):
painter.drawLine(88, 0, 96, 0)
painter.rotate(30.0)
painter.setPen(Qt.NoPen)
painter.setBrush(minuteColor)
painter.save()
painter.rotate(6.0 * (time.minute() + time.second() / 60))
painter.drawConvexPolygon(QPolygon(minuteHand))
painter.restore()
painter.setPen(minuteColor)
for i in range(60):
if i % 5 != 0:
painter.drawLine(92, 0, 96, 0)
painter.rotate(6.0)
painter.end()
def resizeEvent(self, event):
side = max(self.width(), self.height())
maskedRegion = QRegion(self.width() / 2 - side / 2, self.height() / 2 - side / 2, side, side, QRegion.Ellipse)
self.setMask(maskedRegion)
def sizeHint(self):
return QSize(100, 100)
app = QApplication(sys.argv)
clock = ShapedClock()
clock.show()
app.exec_()
最终效果
视窗模板注意事项
当QRegion允许创建任意符合区域后,部件模板可以创建成适配大部分异型视窗,甚至允许部件中间显示一个洞!部件模板同样可以使用位图的内容进行构造,来定义部件的不透明的部分。位图有一个alpha通道,可以通过QPixmap.mask()获取。问题说明
画出来的时钟面我突然发现不圆,不知道是算法的问题,还是视觉差的问题,这里我作为一个Bug记录下来,后续会跟进处理。相关文章推荐
- Python动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- Python中zip()函数用法举例
- Python中map()函数浅析
- Python将excel导入到mysql中
- Python在CAM软件Genesis2000中的应用
- QT学习 第一章:基本对话框
- 使用Shiboken为C++和Qt库创建Python绑定
- FREEBASIC 编译可被python调用的dll函数示例