PyQt5 QGraphicsView 完全指南
目录
QGraphicsView概述
QGraphicsView是PyQt5中强大的2D图形渲染框架,提供高性能的场景渲染和交互功能。它采用模型-视图架构,将图形内容与渲染分离,支持复杂的图形操作和高效渲染。
核心概念
- QGraphicsScene(场景):管理所有图形项 目的容器
- QGraphicsView(视图):负责渲染场景内容的窗口组件
- QGraphicsItem(图形项目):场景中可渲染和交互的对象
应用场景
- 数据可视化(图表、图形界面)
- 游戏开发(2D游戏场景)
- CAD软件(工程绘图)
- 多媒体播放器(视频预览、进度条)
- 图像编辑器(滤镜、变换)
基本框架
QGraphicsView的基本使用非常简单,遵循以下步骤:
- 创建QApplication实例
- 创建QGraphicsScene对象
- 向场景添加图形项目
- 创建QGraphicsView并关联场景
- 显示视图并启动事件循环
完整示例
import sys
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsView
from PyQt5.QtCore import Qt
def main():
# 1. 创建应用程序实例
app = QApplication(sys.argv)
# 2. 创建图形场景
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 800, 600) # 设置场景大小
# 3. 向场景添加图形项目
text_item = scene.addText("Hello, QGraphicsView!",
font_size=20,
color=Qt.blue)
text_item.setPos(100, 100) # 设置位置
# 4. 创建视图并关联场景
view = QGraphicsView(scene)
view.setWindowTitle("PyQt5 QGraphicsView 示例")
view.resize(900, 700)
view.setRenderHint(Qt.Antialiasing, True) # 启用抗锯齿
# 5. 显示视图
view.show()
# 6. 启动事件循环
sys.exit(app.exec_())
if __name__ == "__main__":
main()
框架要点
- 场景设置:使用
setSceneRect()明确指定场景边界 - 抗锯齿渲染:使用
setRenderHint()提升视觉效果 - 项目定位:使用
setPos()精确控制项目位置
显示图片
图 片显示是QGraphicsView的常见应用,通过QGraphicsPixmapItem实现高效渲染。
基础图片显示
from PyQt5.QtGui import QImage, QPixmap, QTransform
from PyQt5.QtWidgets import QGraphicsPixmapItem, QGraphicsScene, QGraphicsView, QApplication
from PyQt5.QtCore import Qt
import sys
def display_image():
app = QApplication(sys.argv)
# 加载图片文件
image_path = "example_image.jpg"
frame = QImage(image_path)
if frame.isNull():
print("图片加载失败")
sys.exit(1)
# 转换为像素图
pix = QPixmap.fromImage(frame)
# 创建图形项目
item = QGraphicsPixmapItem(pix)
# 创建场景
scene = QGraphicsScene()
scene.setSceneRect(0, 0, pix.width(), pix.height())
scene.addItem(item)
# 创建视图
view = QGraphicsView(scene)
view.setWindowTitle('PyQt5 图片显示示例')
view.resize(800, 600)
view.setRenderHint(Qt.Antialiasing, True)
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
display_image()
高级图片操作
from PyQt5.QtGui import QPixmap, QTransform
from PyQt5.QtWidgets import QGraphicsPixmapItem, QGraphicsScene, QGraphicsView
from PyQt5.QtCore import Qt
def advanced_image_operations():
app = QApplication(sys.argv)
scene = QGraphicsScene()
# 加载原始图片
original_pixmap = QPixmap("original.jpg")
# 创建多个变换版本
items = []
# 1. 原始图片
item1 = QGraphicsPixmapItem(original_pixmap)
item1.setPos(50, 50)
scene.addItem(item1)
items.append(item1)
# 2. 缩放图片(50%)
scaled_pixmap = original_pixmap.scaled(
original_pixmap.width() // 2,
original_pixmap.height() // 2,
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
item2 = QGraphicsPixmapItem(scaled_pixmap)
item2.setPos(400, 50)
scene.addItem(item2)
items.append(item2)
# 3. 旋转图片(45度)
transform = QTransform()
transform.rotate(45)
rotated_pixmap = original_pixmap.transformed(transform, Qt.SmoothTransformation)
item3 = QGraphicsPixmapItem(rotated_pixmap)
item3.setPos(50, 300)
scene.addItem(item3)
items.append(item3)
# 4. 镜像图片
mirrored_pixmap = original_pixmap.mirrored(True, False)
item4 = QGraphicsPixmapItem(mirrored_pixmap)
item4.setPos(400, 300)
scene.addItem(item4)
items.append(item4)
# 设置交互(可选)
for item in items:
item.setFlag(QGraphicsPixmapItem.ItemIsSelectable, True)
item.setFlag(QGraphicsPixmapItem.ItemIsMovable, True)
view = QGraphicsView(scene)
view.setWindowTitle('图片变换示例')
view.resize(900, 700)
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
advanced_image_operations()
图片操作要点
- 文件验证:使用
isNull()检查图片加载是否成功 - 缩放质量:使用
Qt.SmoothTransformation保证缩放质量 - 性能优化:大图片建议预先缩放,避免实时处理
- 变换矩阵:QTransform提供精确的2D 变换控制
绘制线条
线条绘制是图形编程的基础,QGraphicsView提供多种线条绘制方法。
基础线条绘制
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPen, QColor, QBrush
from PyQt5.QtWidgets import (
QApplication, QGraphicsScene, QGraphicsView,
QGraphicsRectItem, QGraphicsEllipseItem
)
def draw_lines():
app = QApplication(sys.argv)
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 800, 600)
# 1. 基础直线
line1 = scene.addLine(20, 20, 200, 200,
pen=QPen(Qt.red, 3, Qt.SolidLine))
line1.setFlag(QGraphicsRectItem.ItemIsSelectable, True)
# 2. 虚线
line2 = scene.addLine(20, 200, 200, 20,
pen=QPen(Qt.blue, 2, Qt.DashLine))
# 3. 点线
line3 = scene.addLine(250, 20, 450, 200,
pen=QPen(Qt.green, 3, Qt.DotLine))
# 4. 自定义颜色和宽度
custom_pen = QPen(QColor(255, 165, 0), 5, Qt.DashDotLine) # 橙色
line4 = scene.addLine(250, 200, 450, 20, pen=custom_pen)
# 5. 添加文本
text = scene.addText('线条绘制示例', font_size=16, color=Qt.darkBlue)
text.setPos(500, 100)
# 6. 几何形状(边框线条)
# 矩形边框
rect = QGraphicsRectItem(0, 0, 320, 240)
rect.setPen(QPen(Qt.black, 2))
rect.setBrush(QBrush(Qt.NoBrush)) # 无填充
rect.setPos(500, 250)
scene.addItem(rect)
# 椭圆边框
ellipse = QGraphicsEllipseItem(100, 100, 100, 100)
ellipse.setPen(QPen(Qt.magenta, 3, Qt.DashDotDotLine))
ellipse.setBrush(QBrush(Qt.NoBrush))
ellipse.setPos(500, 250)
scene.addItem(ellipse)
# 7. 创建可交互的线条
interactive_line = QGraphicsRectItem(99, 99, 102, 102)
interactive_line.setPen(QPen(Qt.red, 3))
interactive_line.setBrush(QBrush(Qt.NoBrush))
interactive_line.setPos(600, 400)
interactive_line.setFlag(QGraphicsRectItem.ItemIsSelectable, True)
interactive_line.setFlag(QGraphicsRectItem.ItemIsMovable, True)
scene.addItem(interactive_line)
view = QGraphicsView(scene)
view.setWindowTitle('PyQt5 线条绘制示例')
view.resize(900, 700)
view.setRenderHint(Qt.Antialiasing, True)
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
draw_lines()
线条类型详解
QGraphicsView支持多种线条样式:
- SolidLine:实线(默认)
- DashLine:虚线(— — —)
- DotLine:点线(· · ·)
- DashDotLine:点划线(— · — ·)
- DashDotDotLine:双点划线(— · · — · ·)
交互式线条示例
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QPen, QColor
from PyQt5.QtWidgets import QGraphicsLineItem, QGraphicsScene
class InteractiveLine(QGraphicsLineItem):
"""可交互的线条类"""
def __init__(self, x1, y1, x2, y2, color=Qt.red):
super().__init__(x1, y1, x2, y2)
self.setPen(QPen(color, 3, Qt.SolidLine))
self.setFlag(QGraphicsLineItem.ItemIsSelectable, True)
self.setFlag(QGraphicsLineItem.ItemIsMovable, True)
self.setFlag(QGraphicsLineItem.ItemIsSelectable, True)
def create_interactive_scene():
app = QApplication(sys.argv)
scene = QGraphicsScene()
scene.setSceneRect(0, 0, 800, 600)
# 添加多条可交互线条
lines = [
InteractiveLine(100, 100, 300, 100), # 水平线
InteractiveLine(100, 100, 100, 300), # 垂直线
InteractiveLine(100, 100, 300, 300), # 对角线
InteractiveLine(100, 300, 300, 100), # 反对角线
]
for line in lines:
scene.addItem(line)
view = QGraphicsView(scene)
view.setWindowTitle('交互式线条示例')
view.resize(900, 700)
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
create_interactive_scene()
绘制图案
图案绘制涉及矩形、椭圆等基本几何形状的创建和样式设置。
基础图案绘制
import sys
from PyQt5.QtWidgets import (
QGraphicsScene, QGraphicsView, QGraphicsRectItem,
QGraphicsEllipseItem, QGraphicsPolygonItem, QApplication
)
from PyQt5.QtGui import QBrush, QPen, QPolygonF
from PyQt5.QtCore import Qt, QPointF
def draw_shapes():
app = QApplication(sys.argv)
# 定义场景大小为400x200,原点在(0,0)
scene = QGraphicsScene(0, 0, 400, 200)
# 1. 绘制矩形项目
rect = QGraphicsRectItem(0, 0, 200, 50)
rect.setPos(50, 20)
# 设置填充画笔(填充颜色)
rect_brush = QBrush(Qt.red)
rect.setBrush(rect_brush)
# 设置边界线条
rect_pen = QPen(Qt.cyan)
rect_pen.setWidth(10)
rect.setPen(rect_pen)
# 2. 绘制椭圆
ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)
ellipse_brush = QBrush(Qt.blue)
ellipse.setBrush(ellipse_brush)
ellipse_pen = QPen(Qt.green)
ellipse_pen.setWidth(5)
ellipse.setPen(ellipse_pen)
# 3. 绘制多边形(三角形)
triangle_points = QPolygonF([
QPointF(0, 100),
QPointF(50, 0),
QPointF(100, 100)
])
triangle = QGraphicsPolygonItem(triangle_points)
triangle.setPos(250, 20)
triangle.setBrush(QBrush(Qt.yellow))
triangle.setPen(QPen(Qt.darkGreen, 3))
# 4. 绘制圆形(正方形边界内的正圆)
circle = QGraphicsEllipseItem(0, 0, 80, 80)
circle.setPos(280, 100)
circle.setBrush(QBrush(Qt.magenta))
circle.setPen(QPen(Qt.white, 2))
# 将项目添加到场景中(按添加顺序堆叠)
scene.addItem(ellipse)
scene.addItem(rect)
scene.addItem(triangle)
scene.addItem(circle)
view = QGraphicsView(scene)
view.setWindowTitle('PyQt5 图案绘制示例')
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
draw_shapes()
高级图案操作
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush, QPen, QColor, QLinearGradient, QRadialGradient
from PyQt5.QtWidgets import QGraphicsRectItem, QGraphicsEllipseItem
def advanced_shape_styling():
app = QApplication(sys.argv)
scene = QGraphicsScene(0, 0, 600, 400)
# 1. 渐变填充
# 线性渐变
linear_grad = QLinearGradient(0, 0, 200, 100)
linear_grad.setColorAt(0, Qt.red)
linear_grad.setColorAt(0.5, Qt.yellow)
linear_grad.setColorAt(1, Qt.blue)
rect1 = QGraphicsRectItem(0, 0, 200, 100)
rect1.setPos(50, 50)
rect1.setBrush(QBrush(linear_grad))
rect1.setPen(QPen(Qt.black, 2))
scene.addItem(rect1)
# 径向渐变
radial_grad = QRadialGradient(50, 50, 50)
radial_grad.setColorAt(0, Qt.green)
radial_grad.setColorAt(1, Qt.transparent)
rect2 = QGraphicsRectItem(0, 0, 100, 100)
rect2.setPos(300, 50)
rect2.setBrush(QBrush(radial_grad))
rect2.setPen(QPen(Qt.darkGreen, 3))
scene.addItem(rect2)
# 2. 透明度效果
rect3 = QGraphicsRectItem(0, 0, 150, 80)
rect3.setPos(50, 200)
rect3.setBrush(QBrush(QColor(255, 0, 0, 128))) # 半透明红色
rect3.setPen(QPen(Qt.red, 3))
scene.addItem(rect3)
# 3. 复杂形状(圆角矩形)
rect4 = QGraphicsRectItem(0, 0, 150, 80)
rect4.setPos(250, 200)
rect4.setBrush(QBrush(Qt.blue))
rect4.setPen(QPen(Qt.darkBlue, 4))
rect4.setTransformOriginPoint(75, 40) # 设置变换原点
rect4.setRotation(15) # 旋转15度
scene.addItem(rect4)
# 4. 描边样式变化
for i in range(5):
ellipse = QGraphicsEllipseItem(0, 0, 60, 60)
ellipse.setPos(450, 50 + i * 65)
ellipse.setBrush(QBrush(Qt.NoBrush)) # 无填充
ellipse.setPen(QPen(Qt.red, i + 1)) # 线条宽度递增
scene.addItem(ellipse)
view = QGraphicsView(scene)
view.setRenderHint(Qt.Antialiasing, True)
view.setWindowTitle('高级图案样式示例')
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
advanced_shape_styling()
图案属性详解
- Brush(画笔):控制填充颜色和样式
- Pen(钢笔):控制边界线条样式
- Transform(变换):位置、旋转、缩放等操作
- Z-Value(Z值):控制项目堆叠顺序
- Opacity(透明度):控制项目透明度
高级应用技巧
场景交互和事件处理
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QGraphicsEllipseItem, QGraphicsScene
class DraggableCircle(QGraphicsEllipseItem):
"""可拖拽的圆形类"""
def __init__(self, x, y, radius, color=Qt.blue):
super().__init__(0, 0, radius * 2, radius * 2)
self.setPos(x, y)
self.setBrush(QBrush(color))
self.setFlag(QGraphicsEllipseItem.ItemIsMovable, True)
self.setFlag(QGraphicsEllipseItem.ItemIsSelectable, True)
# 添加悬停效果
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event):
"""鼠标悬停进入事件"""
self.setBrush(QBrush(Qt.yellow))
super().hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
"""鼠标悬停离开事件"""
self.setBrush(QBrush(Qt.blue))
super().hoverLeaveEvent(event)
def interactive_scene():
app = QApplication(sys.argv)
scene = QGraphicsScene(0, 0, 800, 600)
# 添加多个可交互的圆形
colors = [Qt.red, Qt.green, Qt.blue, Qt.yellow, Qt.magenta, Qt.cyan]
for i in range(6):
circle = DraggableCircle(
100 + i * 100, 200, 30, colors[i]
)
scene.addItem(circle)
view = QGraphicsView(scene)
view.setWindowTitle('交互式场景示例')
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
interactive_scene()
视图控制技巧
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene
from PyQt5.QtCore import Qt, QRectF
def view_control_demo():
app = QApplication(sys.argv)
scene = QGraphicsScene()
# 添加一些测试内容
for i in range(10):
for j in range(10):
circle = scene.addEllipse(
i * 80, j * 80, 60, 60,
brush=Qt.red if (i + j) % 2 == 0 else Qt.blue
)
view = QGraphicsView(scene)
# 设置视图属性
view.setRenderHint(Qt.Antialiasing, True) # 抗锯齿
view.setDragMode(QGraphicsView.RubberBandDrag) # 橡皮筋选择
# 缩放控制
view.setZoom(2.0) # 2倍缩放(自定义方法)
# 居中显示
view.centerOn(400, 400)
# 滚动条控制
view.verticalScrollBar().setValue(200)
view.horizontalScrollBar().setValue(200)
view.setWindowTitle('视图控制示例')
view.show()
sys.exit(app.exec_())
# 扩展QGraphicsView类添加缩放方法
class ZoomableView(QGraphicsView):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.zoom_factor = 1.0
def setZoom(self, factor):
"""设置缩放因子"""
self.zoom_factor = factor
self.resetTransform()
self.scale(factor, factor)
def zoomIn(self):
"""放大"""
self.setZoom(self.zoom_factor * 1.2)
def zoomOut(self):
"""缩小"""
self.setZoom(self.zoom_factor / 1.2)
def resetZoom(self):
"""重置缩放"""
self.setZoom(1.0)
if __name__ == "__main__":
view_control_demo()
实际应用场景
1. 数据可视化图表
import random
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPen, QBrush
def create_bar_chart():
app = QApplication(sys.argv)
scene = QGraphicsScene(0, 0, 800, 400)
# 生成随机数据
data = [random.randint(50, 300) for _ in range(10)]
# 绘制柱状图
bar_width = 60
spacing = 20
for i, value in enumerate(data):
x = 50 + i * (bar_width + spacing)
bar = scene.addRect(
x, 350 - value, bar_width, value,
brush=Qt.blue, pen=Qt.black
)
# 添加数值标签
text = scene.addText(str(value), font_size=10)
text.setPos(x + bar_width // 2 - 5, 350 - value - 20)
# 绘制坐标轴
scene.addLine(30, 350, 750, 350) # X轴
scene.addLine(30, 350, 30, 50) # Y轴
view = QGraphicsView(scene)
view.setWindowTitle('柱状图示例')
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
create_bar_chart()
2. 简单的图像编辑器
from PyQt5.QtGui import QPixmap, QTransform
from PyQt5.QtWidgets import QGraphicsPixmapItem, QGraphicsScene, QGraphicsView
def simple_image_editor():
app = QApplication(sys.argv)
scene = QGraphicsScene()
# 加载图片
pixmap = QPixmap("image.jpg")
item = QGraphicsPixmapItem(pixmap)
scene.addItem(item)
# 添加滤镜效果示例
# 1. 原图
scene.addText("Original", font_size=14).setPos(10, 10)
# 2. 缩放版本
scaled = pixmap.scaled(200, 150, Qt.KeepAspectRatio)
item2 = QGraphicsPixmapItem(scaled)
item2.setPos(10, 180)
scene.addItem(item2)
scene.addText("Scaled", font_size=14).setPos(10, 170)
# 3. 旋转版本
transform = QTransform()
transform.rotate(90)
rotated = pixmap.transformed(transform)
item3 = QGraphicsPixmapItem(rotated)
item3.setPos(250, 10)
scene.addItem(item3)
scene.addText("Rotated", font_size=14).setPos(250, 200)
view = QGraphicsView(scene)
view.setWindowTitle('简单图像编辑器')
view.show()
sys.exit(app.exec_())
if __name__ == "__main__":
simple_image_editor()