单击相应的QGraphicsPixmapItem 如何打开图像?

How to open image when corresponding QGraphicsPixmapItem is clicked?

我正在构建一个 GUI,用于使用 QGraphicsSceneQGraphicsPixmapItem(下面的代码)在网格中显示照片(来自文件夹)。现在我想在点击其中一个 QGraphicsPixmapItems 时打开相应的原始图像。如果我在 QGraphicsScene 内部单击,我覆盖了 MousePressEvent 并使程序执行“某事”,但现在我想知道如何检索单击哪个项目以打开相应图像的信息。

import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QGraphicsPixmapItem
from PyQt5.QtCore import Qt, QRect
from PyQt5.QtGui import QPixmap

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.showMaximized()
        self.central()

    def central(self):
        self.scene = QtWidgets.QGraphicsScene(QtCore.QRectF(0, 0, 1080, 1000), self)
        self.graphicsview = QtWidgets.QGraphicsView(self.scene)
        self.setCentralWidget(self.graphicsview)
        self.resize(1000, 1000)

        list = ['./images/1.JPG', './images/2.JPG', './images/3.JPG', './images/4.JPG',
                './images/5.JPG', './images/6.JPG', './images/7.JPG', './images/8.JPG']

        self.t_list = []

        for n in range(len(list)):
            self.label = QLabel()
            self.u_pixmap = QPixmap(list[n])

            imgsize = min(self.u_pixmap.width(), self.u_pixmap.height())
            rect = QRect(
                int((self.u_pixmap.width() - imgsize) / 2),
                int((self.u_pixmap.height() - imgsize) / 2), imgsize, imgsize)
            self.v_pixmap = self.u_pixmap.copy(rect)

            self.pixmap = self.v_pixmap.scaled(200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation)

            self.item = QGraphicsPixmapItem()
            self.item.setPixmap(self.pixmap)
            self.scene.addItem(self.item)

            g = 210
            a = 5
            y = int(n/a)
            x = n - (y * a)
            self.item.setOffset(x * g, y * g)

   def mousePressEvent(self, event):
        if event.button() == Qt.RightButton:
            print("item clicked")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

首先,在使用循环时,连续设置相同的实例属性是完全没有用的:每次循环循环,self.u_pixmapself.pixmapself.item都会被覆盖.

然后,鼠标事件总是从当前聚焦的object(或widget)捕获;由于您的 mousePressEvent 覆盖是针对 QMainWindow 实例的,而当前聚焦的对象是 QGraphicsView,因此您将 永远不会 获得任何鼠标事件,因为图形视图将捕获它。

最后,为了实现您想要的效果,您需要为图形项实现 mousePressEvent,但是由于 QGraphicsItem 不是 QObject 的子类,所以没有直接的创建新信号的方法,如果不是使用自定义“信号代理”的话。

附带说明一下,使用 self.resize() 也完全没用,因为您使用的是 self.showMaximized().

这是您要实现的目标的可能实现方式:

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QGraphicsPixmapItem, 
    QGraphicsView, QGraphicsScene, QLabel)
from PyQt5.QtCore import Qt, QObject, pyqtSignal, QRectF
from PyQt5.QtGui import QPixmap


class SignalProxy(QObject):
    clicked = pyqtSignal(object)


class ClickablePixmapItem(QGraphicsPixmapItem):
    def __init__(self, source):
        super().__init__()
        self.source = source
        self.setPixmap(source.scaled(200, 200, 
            Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self._signalProxy = SignalProxy()
        self.clicked = self._signalProxy.clicked

    def mousePressEvent(self, event):
        self.clicked.emit(self)


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.showMaximized()
        self.central()
        self.windows = {}

    def central(self):
        self.scene = QGraphicsScene(QRectF(0, 0, 1080, 1000), self)
        self.graphicsview = QGraphicsView(self.scene)
        self.setCentralWidget(self.graphicsview)
        self.resize(1000, 1000)

        list = ['./images/1.JPG', './images/2.JPG', './images/3.JPG', './images/4.JPG',
                './images/5.JPG', './images/6.JPG', './images/7.JPG', './images/8.JPG']

        self.t_list = []

        for n in range(len(list)):
            item = ClickablePixmapItem(QPixmap(list[n]))
            item.clicked.connect(self.imageClicked)
            self.scene.addItem(item)

            g = 210
            a = 5
            y = int(n/a)
            x = n - (y * a)
            item.setOffset(x * g, y * g)

    def imageClicked(self, item):
        window = self.windows.get(item)
        if not window:
            window = self.windows[item] = QLabel()
            window.setPixmap(item.source)
        window.show()
        window.activateWindow()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    sys.exit(app.exec_())

提示:避免混合导入“样式”。您可以导入模块 (from PyQt5 import QtWidgets, QtCore) 或相关的 类 (from PyQt5.QtWidgets import (...)).

要在图形视图中找到对应于项目的原始像素图,您可以通过QGraphicsItem.setData()将其作为数据存储在项目中,例如

def central(self):
    ....
    self.v_pixmap = self.u_pixmap.copy(rect)
    self.pixmap = self.v_pixmap.scaled(200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation)

    self.item = QGraphicsPixmapItem()
    self.item.setPixmap(self.pixmap)
    self.item.setData(0, self.v_pixmap)

    ....

这将允许您通过 item.data(0) 从项目中检索原始像素图。

要查找鼠标光标下的项目,您可以在 MainWindow.mousePressEvent 中使用 QGraphicsView.itemAt。鼠标光标的位置由 event.pos() 给出。但是,这个位置是相对于主体window的局部坐标系而言的。要将这些坐标与图形视图的坐标系匹配,您需要将它们映射到图形视图和主要 window 通用的坐标系,例如全局坐标系。考虑到这一点,MainWindow.mousePressEvent 会变成

def mousePressEvent(self, event):
    if event.button() == Qt.RightButton:
        print("item clicked")
        pos = self.mapToGlobal(event.pos())
        pos1 = self.graphicsview.mapFromGlobal(pos)
        item = self.graphicsview.itemAt(pos1)
        if item:
            u_pixmap = item.data(0)
            if u_pixmap:
                self.label.setPixmap(u_pixmap)
                self.label.show()