PyQt4 自定义窗体

Author Avatar
Mutse Young 12月 19, 2012

本文主要讲解如何使用 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
段错误

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