最近学习了视图(QGraphicsView)的知识,总结一下,做一个demo以备忘。在demo中演示了常用的设置方法和信号槽传递机制。
QT的视图(QGraphicsView)体系是建立在场景(QGraphicsScene)基础上的,场景(QGraphicsScene)是图形项(QGraphicsItem)的容器,图形项(QGraphicsItem)最终呈现的显示元素。
基本的框架演示:
# 设置样本图片的QGraphicsView模型
from PySide6.QtCore import Qt, QRectF
from PySide6.QtGui import QPainter, QPen, QColor, QAction, QMouseEvent
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsRectItem, QMenu, QWidget, \
QVBoxLayout, QFrame, QSizePolicy
# 设置矩形方框(QGraphicsRectItem)模型
class MyRectItem(QGraphicsRectItem):
def __init__(self, temp_pen, normal_pen, *args):
super().__init__(*args)
self.temp_pen = temp_pen # 临时画笔
self.normal_pen = normal_pen # 正常画笔
# 启用鼠标跟踪,以便在没有按下鼠标按钮时也能接收鼠标移动事件
self.setAcceptHoverEvents(True)
def hoverEnterEvent(self, event: QMouseEvent):
"""鼠标进入项的事件处理"""
print("鼠标进入项")
super().hoverEnterEvent(event)
self.setPen(self.temp_pen)
def hoverLeaveEvent(self, event: QMouseEvent):
"""鼠标离开项的事件处理"""
print("鼠标离开项")
super().hoverLeaveEvent(event)
self.setPen(self.normal_pen)
# 设置QGraphicsView模型
class MyView(QGraphicsView):
def __init__(self, parent=None, autoScale=True):
super().__init__(parent)
self.autoScale = autoScale # 自动缩放标志,是否全画幅显示
self.set_scene() # 设置场景
self.set_mouse() # 设置鼠标
self.set_flags() # 设置标志
self.set_menu() # 设置菜单
def set_scene(self):
"""设置场景"""
self.scene = QGraphicsScene() # 创建窗口场景
# 设置渲染提示
self.setRenderHint(QPainter.Antialiasing) # 开启抗锯齿
self.setRenderHint(QPainter.SmoothPixmapTransform) # 开启平滑缩放
# 设置缩放锚点
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) # 转换时以鼠标为中心
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) # 缩放时以鼠标为中心
self.setScene(self.scene) # 将场景应用到视窗中
def set_mouse(self):
"""设置鼠标"""
self.setMouseTracking(True) # 设置鼠标跟踪
# 鼠标的实时位置(视窗坐标)
self.view_x = 0
self.view_y = 0
# 鼠标的实时位置(场景坐标)
self.scene_x = 0
self.scene_y = 0
# 鼠标按下时的位置(场景坐标)
self.press_x = None
self.press_y = None
# 鼠标释放时的位置(场景坐标)
self.release_x = 0
self.release_y = 0
# 处理滚轮事件以实现缩放
def wheelEvent(self, event):
"""处理滚轮事件以实现缩放"""
factor = 1.001 ** event.angleDelta().y() # 滚轮每滚动一格,缩放比例变化
self.scale(factor, factor)
def set_flags(self):
"""设置标志"""
pass
def set_menu(self):
"""设置菜单"""
pass
# 设置图像文件
def set_image(self, pixmap):
self.scene.clear() # 清空场景
self.scene_rect = QRectF(0, 0, pixmap.width(), pixmap.height()) # 设置场景范围
self.image_item = QGraphicsPixmapItem(pixmap) # 创建图片对象
self.scene.addItem(self.image_item) # 将图片对象添加到场景中
self.setAlignment(Qt.AlignmentFlag.AlignCenter) # 设置视图对齐方式
if self.autoScale:
self.fitInView(self.image_item, Qt.AspectRatioMode.KeepAspectRatio) # 设置视图适应图片,全幅显示
if __name__ == "__main__":
import sys
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QPixmap
app = QApplication(sys.argv)
widget = QWidget() # 创建窗口对象
widget.resize(800, 600) # 设置窗口大小
widget.layout = QVBoxLayout(widget) # 创建垂直布局对象
widget.view = MyView(widget) # 创建MyView对象
widget.layout.addWidget(widget.view) # 将视图添加到布局中
pixmap = QPixmap("your/image/path") # 创建QPixmap对象
widget.show() # 显示窗口
widget.view.set_image(pixmap) # 设置图像文件
sys.exit(app.exec()) # 进入程序的主循环,并通过exit()函数确保主循环安全结束
基本框架具备了图像显示和鼠标滚轮缩放的功能:
注意:
widget.show() # 显示窗口
widget.view.set_image(pixmap) # 设置图像文件
这两行代码的顺序一定不能反,如果将 widget.show() 放在widget.view.set_image(pixmap)后面,将会是下面的显示效果,初始化后不能全幅显示。
这是因为 fitInView 方法的行为依赖于视图的实际大小,而在窗口尚未显示时,视图的大小可能是未确定的或者是默认值,从而导致 fitInView 无法正确计算和应用缩放,而show()函数执行之后就确定了视图的大小。