Skip to main content

PyQt5 QGraphicsView 完全指南

目录

QGraphicsView概述

QGraphicsView是PyQt5中强大的2D图形渲染框架,提供高性能的场景渲染和交互功能。它采用模型-视图架构,将图形内容与渲染分离,支持复杂的图形操作和高效渲染。

核心概念

  • QGraphicsScene(场景):管理所有图形项目的容器
  • QGraphicsView(视图):负责渲染场景内容的窗口组件
  • QGraphicsItem(图形项目):场景中可渲染和交互的对象

应用场景

  • 数据可视化(图表、图形界面)
  • 游戏开发(2D游戏场景)
  • CAD软件(工程绘图)
  • 多媒体播放器(视频预览、进度条)
  • 图像编辑器(滤镜、变换)

基本框架

QGraphicsView的基本使用非常简单,遵循以下步骤:

  1. 创建QApplication实例
  2. 创建QGraphicsScene对象
  3. 向场景添加图形项目
  4. 创建QGraphicsView并关联场景
  5. 显示视图并启动事件循环

完整示例

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()

最佳实践

1. 性能优化

  • 合理设置场景边界:使用setSceneRect()明确指定场景大小
  • 批量操作:使用addItems()一次性添加多个项目
  • 缓存机制:对静态内容启用缓存
  • 视口优化:只渲染可见区域
# 性能优化示例
def optimize_scene():
scene = QGraphicsScene()

# 1. 设置场景边界
scene.setSceneRect(0, 0, 1000, 1000)

# 2. 启用缓存
scene.setItemIndexMethod(QGraphicsScene.BspTreeIndex)

# 3. 批量添加项目
items = []
for i in range(1000):
item = scene.addEllipse(0, 0, 10, 10)
items.append(item)

# 4. 使用批量操作
scene.addItems(items)

return scene

2. 内存管理

  • 及时清理:删除不再需要的项目
  • 复用对象:避免频繁创建销毁
  • 合理生命周期:确保对象在正确时机释放
def memory_management():
scene = QGraphicsScene()

# 添加临时项目
temp_item = scene.addText("Temporary")

# 稍后删除
scene.removeItem(temp_item)
del temp_item

# 清理所有项目
scene.clear()

3. 事件处理

  • 合理使用事件过滤器
  • 避免在事件处理中进行耗时操作
  • 使用防抖技术处理高频事件
from PyQt5.QtCore import QObject, QEvent

class EventFilter(QObject):
def eventFilter(self, obj, event):
if event.type() == QEvent.MouseMove:
# 处理鼠标移动事件
return True
return super().eventFilter(obj, event)

4. 代码组织

  • 模块化设计:将不同功能的代码分离
  • 自定义项目类:封装复杂逻辑
  • 配置管理:使用配置文件管理参数
# 自定义图形项目基类
class BaseGraphicsItem(QGraphicsRectItem):
def __init__(self, x, y, width, height, style=None):
super().__init__(0, 0, width, height)
self.setPos(x, y)
self.setup_style(style)
self.setup_interactions()

def setup_style(self, style):
"""设置样式"""
pass

def setup_interactions(self):
"""设置交互"""
self.setFlag(QGraphicsRectItem.ItemIsSelectable, True)
self.setFlag(QGraphicsRectItem.ItemIsMovable, True)

5. 调试技巧

  • 启用调试模式:显示项目边界框
  • 日志记录:记录关键操作
  • 性能监控:监控渲染性能
def debug_mode():
view = QGraphicsView()

# 显示项目边界
view.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)

# 启用抗锯齿(调试时关闭)
view.setRenderHint(Qt.Antialiasing, False)

# 显示网格(自定义实现)
view.setBackgroundBrush(Qt.lightGray)

参考资料

总结

QGraphicsView是PyQt5中功能强大的2D图形渲染框架,通过合理的架构设计和最佳实践,可以构建出高性能、交互性强的图形应用程序。掌握其核心概念和使用技巧,能够满足大部分GUI图形编程需求。

在实际项目中,建议:

  1. 根据具体需求选择合适的图形项目类型
  2. 注重性能优化,避免不必要的重绘
  3. 合理处理用户交互,提供流畅的用户体验
  4. 遵循模块化设计原则,保持代码可维护性

通过持续的实践和探索,可以充分发挥QGraphicsView的强大功能,创造出优秀的图形应用程序。