QPainter简介
QPainter类在窗口部件或者其他绘图设备上执行底层绘图。QPainter提供了高度优化的绘图功能,可以完成大多数GUI应用所需要的绘图功能。它可以绘制从简单的线条到复杂的形状。可以绘制带对齐方式的文本,也可以绘制像素图。它还可以进行视图和图象的空间坐标变换。QPainter可以在继承自QPaintDevice类的对象上进行绘图操作。
QPainter是一个功能丰富的框架,允许开发人员执行各种图形操作,例如渐变,合成模式和矢量图形。QPainter可以在各种不同的硬件和软件堆栈中执行此操作。
QPainter最常见的用法是在部件的paintEvent()中进行绘图,同时也提供一些和绘图相关的一些功能,比如背景,字符字体,画笔,画刷设置,绘制质量控制以及剪裁操作等。
一些这些函数可以对QPainter绘制要求进行设置:
- font(): 是用于绘制文本的字体。
- brush(): 定义用于填充形状的颜色或图案。
- pen(): 定义用于绘制线条或边界的颜色或点画。
- backgroundMode(): 定义背景模式为透明的还是不透明的(Qt.OpaqueMode:不透明, Qt.TransparentMode:透明)。
- background(): 仅在background()为Qt.OpaqueMode并且pen()为画点时适用。在这种情况下,它描述点画中背景像素的颜色。
- brushOrign(): 定义画刷在平铺模式的原点,通常为部件背景的原点。
- viewport(), window(), worldTransform(): 组成绘图用的坐标转化系统。
- hasClipping(): 是否进行剪辑。
- layoutDirectio(): 定义绘制图中绘制文本时所使用的布局方向。
QPainter绘图设置采用了状态机制的模式,就当前设置,对后续的绘图都起作用。可以随时调用save()函数来保存QPainter的状态,该函数将所有的可用设置保存在内部堆栈中,restore()函数可恢复保存的状态。
QPainter的核心功能是绘图,常用绘图函数有:
- drawPoint(): 使用当前笔的颜色在给定位置绘制一个点。
- drawPoints(): 使用当前笔的颜色绘制多个点。
- drawLine(): 绘制一条线。
- drawRect(): 绘制一个矩形。
- drawRoundedRect(): 绘制一个圆角矩形。
- drawEllipse(): 绘制一个椭圆。
- drawArc(): 绘画一段弧形。
- darwPie(): 绘制一个饼图。
- drawChord(): 绘制一段弧形和它的弦做成的闭合区域。
- drawPolyline(): 绘制一条多边形折线。
- drawPolygon(): 绘制可填充一个多边形。
- drawConvexPolygon(): 绘制一个凸多边形。
- drawText(): 绘制一段文本。
- drawPath(): 绘制一个给定的路径。
- drawPixmap():将给定像素图的指定区域部分绘制到指定的矩形区域。
- drawImage(): 将给定图像的指定区域部分绘制到指定的矩形区域。
QPainter常用绘图函数
渲染质量控制
为了使用QPainter获得最佳的渲染效果,应使用独立于平台的QImage作为绘制设备; 意思就是使用QImage可以确保绘图结果在任何平台上都具有相同的像素表示。
QPainter提供了一种通过其RenderHint枚举和对浮点精度的支持来控制渲染质量的方法,使用setRenderHint()函数来设置或者清除当前所设置的RenderHints(),其参数有:
- QPainter.Antialiasing: 指示绘图引擎尽可能对图元的边缘进行抗锯齿处理。
- QPainter.TextAntialiasing: 指示绘图引擎尽可能对文本进行抗锯齿。
- QPainter.SmoothPixmapTransform: 指示绘图引擎应使用平滑的像素映射转换算法。
坐标转换
通常情况下,QPainter在设备自身的坐标系(通常为像素)上运行,但是QPainter对坐标转换有很好的支持。
最常用的转换是缩放,旋转,平移和扭曲:
- scale(): 将坐标缩放给定的倍数。
- rotate(): 将坐标顺时针旋转给定的度数。
- translate(): 将坐标进行平移。
- shear(): 围绕原点扭曲坐标。
测试QPainter
示例代码演示了各种设置下使用QPainter绘制基本形状。完整代码如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QPoint, QSize, QRect, pyqtSignal
from PyQt5.QtGui import (QPolygon, QPen, QBrush, QPainter, QPixmap, QConicalGradient,
QLinearGradient, QRadialGradient, QPainterPath, QPalette,
QColor, QIcon)
from PyQt5.QtWidgets import (QApplication, QWidget, QCheckBox, QComboBox, QLabel, QSpinBox,
QFormLayout, QHBoxLayout, QSizePolicy, QColorDialog, QFrame)
import resource_rc
#颜色选择按钮
class ColorButton(QFrame):
colorChanged = pyqtSignal()
clicked = pyqtSignal()
def __init__(self, clr = Qt.blue, parent= None):
super(ColorButton, self).__init__(parent)
self.setAutoFillBackground(True)
self.setFrameShape(QFrame.Box)
self.setFrameShadow(QFrame.Plain)
self.setLineWidth(1)
self.setMinimumHeight(20)
self.__color = clr
self.clicked.connect(self.getColor)
self.setColor(clr)
def setColor(self, newColor):
pal = self.palette()
pal.setColor(QPalette.Window, newColor)
self.setPalette(pal)
self.__color = newColor
self.colorChanged.emit()
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.clicked.emit()
else:
super(ColorButton, self).mousePressEvent(event)
def getColor(self):
color = QColorDialog.getColor(self.__color)
if color != self.__color:
self.setColor(color)
def color(self):
return self.__color
#渲染区域
class RenderArea(QWidget):
Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, Arc, \
Chord, Pie, Path, Text, Pixmap = range(13)
def __init__(self, parent=None):
super(RenderArea, self).__init__(parent)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.pen = QPen()
self.brush = QBrush()
self.pixmap = QPixmap()
#抗锯齿控制
self.antialiased = False
#加载用于填充的位图
self.pixmap.load(':/images/python-logo.png')
#缺省形状
self.shape = RenderArea.Polygon
#背景填充
self.setBackgroundRole(QPalette.Base)
self.setAutoFillBackground(True)
#绘图区域最小尺寸
def minimumSizeHint(self):
return QSize(400,400)
#绘图区域缺省尺寸
def sizeHint(self):
return QSize(400, 400)
def setShape(self, shape):
self.shape = shape
self.update()
def setPen(self, pen):
self.pen = pen
self.update()
def setBrush(self, brush):
self.brush = brush
self.update()
def setAntialiased(self, antialiased):
self.antialiased = antialiased
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(self.pen)
painter.setBrush(self.brush)
if(self.antialiased):
painter.setRenderHint(QPainter.Antialiasing)
rect = QRect(50,100,300,200)
points = QPolygon([QPoint(150,100),QPoint(300,150),QPoint(350,250),QPoint(100,300)])
startAngle = 30 * 16
spanAngle = 120 * 16
path = QPainterPath()
#path.addRect(150,150,100,100)
path.moveTo(100,100)
path.cubicTo(300,100,200,200,300,300)
path.cubicTo(100,300,200,200,100,100)
if self.shape == RenderArea.Line:
painter.drawLine(rect.topLeft(), rect.bottomRight())
elif self.shape == RenderArea.Points:
painter.drawPoints(points)
elif self.shape == RenderArea.Polyline:
painter.drawPolyline(points)
elif self.shape == RenderArea.Polygon:
painter.drawPolygon(points)
elif self.shape == RenderArea.Rect:
painter.drawRect(rect)
elif self.shape == RenderArea.RoundedRect:
painter.drawRoundedRect(rect, 25, 25, Qt.RelativeSize)
elif self.shape == RenderArea.Ellipse:
painter.drawEllipse(rect)
elif self.shape == RenderArea.Arc:
painter.drawArc(rect, startAngle, spanAngle)
elif self.shape == RenderArea.Chord:
painter.drawChord(rect, startAngle, spanAngle)
elif self.shape == RenderArea.Pie:
painter.drawPie(rect, startAngle, spanAngle)
elif self.shape == RenderArea.Path:
painter.drawPath(path)
elif self.shape == RenderArea.Text:
painter.drawText(self.rect(), Qt.AlignCenter, 'Qt for python')
elif self.shape == RenderArea.Pixmap:
painter.drawPixmap(150, 150, self.pixmap)
#边框
painter.setPen(self.palette().dark().color())
painter.setBrush(Qt.NoBrush)
painter.drawRect(QRect(0,0, self.width()-1, self.height()-1))
class DemoPainter(QWidget):
def __init__(self, parent=None):
super(DemoPainter, self).__init__(parent)
# 设置窗口标题
self.setWindowTitle('实战 Qt for Python: QPainter演示')
# 设置窗口大小
self.resize(600, 400)
self.initUi()
def initUi(self):
self.renderArea = RenderArea()
self.brushColor = Qt.green
formLayout = QFormLayout()
#形状控制
self.shapeComboBox = QComboBox()
self.shapeComboBox.addItem('Polygon', RenderArea.Polygon)
self.shapeComboBox.addItem('Rectangle', RenderArea.Rect)
self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect)
self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse)
self.shapeComboBox.addItem("Pie", RenderArea.Pie)
self.shapeComboBox.addItem("Chord", RenderArea.Chord)
self.shapeComboBox.addItem("Path", RenderArea.Path)
self.shapeComboBox.addItem("Line", RenderArea.Line)
self.shapeComboBox.addItem("Polyline", RenderArea.Polyline)
self.shapeComboBox.addItem("Arc", RenderArea.Arc)
self.shapeComboBox.addItem("Points", RenderArea.Points)
self.shapeComboBox.addItem("Text", RenderArea.Text)
self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap)
self.shapeComboBox.activated.connect(self.shapeChanged)
shapeLabel = QLabel('形状(&S):')
shapeLabel.setBuddy(self.shapeComboBox)
#线宽控制
self.penWidthSpinBox = QSpinBox()
self.penWidthSpinBox.setRange(0, 20)
self.penWidthSpinBox.setSpecialValueText('0 (cosmetic pen')
self.penWidthSpinBox.valueChanged.connect(self.penChanged)
penWidthLabel = QLabel('画笔线宽(&W):')
penWidthLabel.setBuddy(self.penWidthSpinBox)
#画笔颜色控制
self.penColorButton = ColorButton()
self.penColorButton.colorChanged.connect(self.penChanged)
penColorLabel = QLabel('画笔颜色(&C):')
penColorLabel.setBuddy(self.penColorButton)
#画笔线型控制
self.penStyleComboBox = QComboBox()
self.penStyleComboBox.addItem('Solid', Qt.SolidLine)
self.penStyleComboBox.addItem('Dash', Qt.DashLine)
self.penStyleComboBox.addItem('Dot', Qt.DotLine)
self.penStyleComboBox.addItem('Dash Dot', Qt.DashDotLine)
self.penStyleComboBox.addItem('Dash Dot Dot', Qt.DashDotDotLine)
self.penStyleComboBox.addItem('None', Qt.NoPen)
self.penStyleComboBox.activated.connect(self.penChanged)
penStyleLabel = QLabel("画笔线型(&P):")
penStyleLabel.setBuddy(self.penStyleComboBox)
#笔尖样式
self.penCapComboBox = QComboBox()
self.penCapComboBox.addItem("Flat", Qt.FlatCap)
self.penCapComboBox.addItem("Square", Qt.SquareCap)
self.penCapComboBox.addItem("Round", Qt.RoundCap)
self.penCapComboBox.activated.connect(self.penChanged)
penCapLabel = QLabel("笔尖样式(&C):")
penCapLabel.setBuddy(self.penCapComboBox)
#连接方式
self.penJoinComboBox = QComboBox()
self.penJoinComboBox.addItem("Miter", Qt.MiterJoin)
self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin)
self.penJoinComboBox.addItem("Round", Qt.RoundJoin)
self.penJoinComboBox.activated.connect(self.penChanged)
penJoinLabel = QLabel("连接方式(&J):")
penJoinLabel.setBuddy(self.penJoinComboBox)
#画刷填充模式
self.brushStyleComboBox = QComboBox()
self.brushStyleComboBox.addItem("Linear Gradient",
Qt.LinearGradientPattern)
self.brushStyleComboBox.addItem("Radial Gradient",
Qt.RadialGradientPattern)
self.brushStyleComboBox.addItem("Conical Gradient",
Qt.ConicalGradientPattern)
self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern)
self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern)
self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern)
self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern)
self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern)
self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern)
self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern)
self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern)
self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern)
self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern)
self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern)
self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern)
self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern)
self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern)
self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern)
self.brushStyleComboBox.addItem("None", Qt.NoBrush)
self.brushStyleComboBox.activated.connect(self.brushChanged)
brushStyleLabel = QLabel("填充模式(&):")
brushStyleLabel.setBuddy(self.brushStyleComboBox)
#画刷颜色控制
self.brushColorButton = ColorButton(self.brushColor)
self.brushColorButton.colorChanged.connect(self.brushColorChanged)
brushColorLabel = QLabel('画刷颜色(&C):')
brushColorLabel.setBuddy(self.brushColorButton)
#抗锯齿
self.antialiasingCheckBox = QCheckBox('抗锯齿(&A)')
self.antialiasingCheckBox.toggled.connect(self.renderArea.setAntialiased)
formLayout.addRow(shapeLabel, self.shapeComboBox)
formLayout.addRow(penWidthLabel, self.penWidthSpinBox)
formLayout.addRow(penColorLabel, self.penColorButton)
formLayout.addRow(penStyleLabel, self.penStyleComboBox)
formLayout.addRow(penCapLabel, self.penCapComboBox)
formLayout.addRow(penJoinLabel, self.penJoinComboBox)
formLayout.addRow(brushStyleLabel, self.brushStyleComboBox)
formLayout.addRow(brushColorLabel, self.brushColorButton)
formLayout.addRow('', self.antialiasingCheckBox)
mainLayout = QHBoxLayout()
mainLayout.setSpacing(20)
mainLayout.addWidget(self.renderArea)
mainLayout.addLayout(formLayout)
self.setLayout(mainLayout)
self.shapeChanged()
self.penChanged()
self.brushChanged()
self.antialiasingCheckBox.setChecked(True)
def shapeChanged(self):
shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(), Qt.UserRole)
self.renderArea.setShape(shape)
def penChanged(self):
width = self.penWidthSpinBox.value()
color = self.penColorButton.color()
style = Qt.PenStyle(self.penStyleComboBox.itemData(
self.penStyleComboBox.currentIndex(), Qt.UserRole))
cap = Qt.PenCapStyle(self.penCapComboBox.itemData(
self.penCapComboBox.currentIndex(), Qt.UserRole))
join = Qt.PenJoinStyle(self.penJoinComboBox.itemData(
self.penJoinComboBox.currentIndex(), Qt.UserRole))
self.renderArea.setPen(QPen(color, width, style, cap, join))
def brushChanged(self):
style = Qt.BrushStyle(self.brushStyleComboBox.itemData(
self.brushStyleComboBox.currentIndex(), Qt.UserRole))
#style = Qt.HorPattern
if style == Qt.LinearGradientPattern:
linearGradient = QLinearGradient(0, 0, 400, 400)
linearGradient.setColorAt(0.0, Qt.white)
linearGradient.setColorAt(0.2, self.brushColor)
linearGradient.setColorAt(1.0, Qt.black)
self.renderArea.setBrush(QBrush(linearGradient))
elif style == Qt.RadialGradientPattern:
radialGradient = QRadialGradient(200, 200, 80, 70, 70)
radialGradient.setColorAt(0.0, Qt.white)
radialGradient.setColorAt(0.2, self.brushColor)
radialGradient.setColorAt(1.0, Qt.black)
self.renderArea.setBrush(QBrush(radialGradient))
elif style == Qt.ConicalGradientPattern:
conicalGradient = QConicalGradient(200, 200, 30)
conicalGradient.setColorAt(0.0, Qt.white)
conicalGradient.setColorAt(0.2, self.brushColor)
conicalGradient.setColorAt(1.0, Qt.black)
self.renderArea.setBrush(QBrush(conicalGradient))
elif style == Qt.TexturePattern:
self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png')))
else:
self.renderArea.setBrush(QBrush(self.brushColor, style))
def brushColorChanged(self):
color = self.brushColorButton.color()
if color != self.brushColor:
self.brushColor = color
self.brushChanged()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DemoPainter()
window.show()
sys.exit(app.exec())
Qpainter绘图演示
本文知识点
- QPainter画笔,画刷设置。
- QPainter基本绘图函数。
- QPainter抗锯齿功能。
- 自定义一个颜色选择按钮。