本文主要讲解如何使用 PyQt4 实现一个自定义的不规则窗体。
![custom widget](/images/pyqt-widget.png)
源码
代码如下:
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
|
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 = QAction(self.tr(""), self) quit.setShortcut(self.tr("Ctrl+Q"))
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
段错误
注: 用鼠标拖动圆形窗体时,窗体移动较缓慢,下次再分析其原因。