文章目录
  1. 1. 源码
  2. 2. 解析
    1. 2.1. 定义窗体
    2. 2.2. 创建右键菜单
    3. 2.3. paintEvent 函数
    4. 2.4. mousePressEvent 函数
    5. 2.5. mouseMoveEvent 函数
    6. 2.6. 窗体掩膜
    7. 2.7. 绘制不规则窗体

本文主要讲解如何使用 PyQt4 实现一个自定义的不规则窗体。

custom widget

源码

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
class Widget(QWidget):
def __init__(self, parent = None):
super(Widget, self).__init__(parent)
self.setWindowFlags(Qt.ToolTip)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setMouseTracking(True)
self.startPos = QPoint(0,0)
self.dragPosition = QPoint(0, 0)
self.resize(QApplication.desktop().size())
quit = QAction(self.tr(""), self)
quit.setShortcut(self.tr("Ctrl+Q"))
self.connect(quit, SIGNAL("triggered()"), qApp.quit)
self.addAction(quit)
self.setContextMenuPolicy(Qt.ActionsContextMenu)
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QColor(255,0,0))
painter.setBrush(QColor(255,0,0))
self.drawPainter(painter, QColor(255, 0, 0))
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() & Qt.LeftButton:
self.startPos += (event.pos() - self.dragPosition)
self.dragPosition = event.pos()
self.move(event.globalPos() - self.dragPosition)
if self.size().width() < QApplication.desktop().width():
self.resize(self.size() + QSize(1, 1))
else:
self.resize(self.size() - QSize(1, 1))
event.accept()
def resizeEvent(self, event):
bmp = QBitmap(self.size())
bmp.fill(QColor(255,255,255))
painter = QPainter(bmp)
painter.setPen(QColor(0,0,0))
painter.setBrush(QColor(0,0,0))
self.drawPainter(painter, QColor(0, 0, 0))
self.setMask(bmp)
def drawPainter(self, painter, color):
path = QPainterPath()
path.addEllipse(self.startPos.x(), self.startPos.y(), 100, 100)
painter.fillPath(path, color)
painter.end()
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = Widget()
widget.show()
sys.exit(app.exec_())

解析

定义窗体

首先定义一个 Widget,并继承 QWidget,在构造函数 __init__ 中调用基类 QWidget 的构造函数 super(Widget, self).__init__(parent)。

1
2
3
class Widget(QWidget):
def __init__(self, parent = None):
super(Widget, self).__init__(parent)

并设置窗体属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 设置窗体置顶显示
self.setWindowFlags(Qt.ToolTip)
# 设置窗体背景透明
self.setAttribute(Qt.WA_TranslucentBackground)
self.setMouseTracking(True)
# 初始化成员变量
self.startPos = QPoint(0,0)
self.dragPosition = QPoint(0, 0)
# 设置窗体大小
self.resize(QApplication.desktop().size())

创建右键菜单

创建右键菜单,并连接相应的槽。

1
2
3
4
5
6
7
8
9
10
11
12
# 创建quit菜单项
quit = QAction(self.tr(""), self)
quit.setShortcut(self.tr("Ctrl+Q"))
# 连接qApp.quit槽函数
self.connect(quit, SIGNAL("triggered()"), qApp.quit)
# 将菜单项添加至窗体上
self.addAction(quit)
# 设置上下文菜单
self.setContextMenuPolicy(Qt.ActionsContextMenu)

paintEvent 函数

创建一个不规则的窗体,需要重写 QWidget 类的 paintEvent 函数:

1
2
3
4
5
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QColor(255,0,0)) # 设置画笔颜色
painter.setBrush(QColor(255,0,0)) # 设置画刷颜色
self.drawPainter(painter, QColor(255, 0, 0)) # 不规则窗体绘制

mousePressEvent 函数

由于隐藏了不规则窗体的边框,想拖动它,还需实现 mousePressEvent 和 mouseMoveEvent 函数:

1
2
3
4
5
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
event.accept()

mouseMoveEvent 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton:
self.startPos += (event.pos() - self.dragPosition)
self.dragPosition = event.pos()
self.move(event.globalPos() - self.dragPosition)
# 用来解决窗体无法拖动的现象
if self.size().width() < QApplication.desktop().width():
self.resize(self.size() + QSize(1, 1))
else:
self.resize(self.size() - QSize(1, 1))
event.accept()

窗体掩膜

1
2
3
4
5
6
7
8
9
10
def resizeEvent(self, event):
bmp = QBitmap(self.size())
bmp.fill(QColor(255,255,255))
painter = QPainter(bmp)
painter.setPen(QColor(0,0,0))
painter.setBrush(QColor(0,0,0))
self.drawPainter(painter, QColor(0, 0, 0))
self.setMask(bmp) # 设置窗体掩膜

绘制不规则窗体

1
2
3
4
5
6
def drawPainter(self, painter, color):
path = QPainterPath()
# 绘制圆形窗体
path.addEllipse(self.startPos.x(), self.startPos.y(), 100, 100)
painter.fillPath(path, color)
painter.end() # 解决运行时的段错误

若无 painter.end() 一行代码,运行时将产生段错误:

QPaintDevice: Cannot destroy paint device that is being painted
QPaintDevice: Cannot destroy paint device that is being painted
段错误

注: 用鼠标拖动圆形窗体时,窗体移动较缓慢,下次再分析其原因。

文章目录
  1. 1. 源码
  2. 2. 解析
    1. 2.1. 定义窗体
    2. 2.2. 创建右键菜单
    3. 2.3. paintEvent 函数
    4. 2.4. mousePressEvent 函数
    5. 2.5. mouseMoveEvent 函数
    6. 2.6. 窗体掩膜
    7. 2.7. 绘制不规则窗体