简体   繁体   中英

How to open image when corresponding QGraphicsPixmapItem is clicked?

I'm constructing a GUI for showing photographs (from a folder) in a grid using QGraphicsScene and QGraphicsPixmapItem (code below). Now I want to open the corresponding original image when clicking on one of the QGraphicsPixmapItems . I overrode MousePressEvent and made the program do "something" if I click inside the QGraphicsScene , but now I wonder how to retrieve the information which item was clicked in order to open the corresponding image.

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

First of all, when using loops it's completely useless to continuously set the same instance attribute: each time the loop is cycling, self.u_pixmap , self.pixmap and self.item will be overwritten.

Then, mouse events are always captured from the currently focused object (or widget ); since your mousePressEvent override is for the QMainWindow instance and the current focused object is the QGraphicsView, you'll never get any mouse event, because the graphics view will capture it.

Finally, in order to achieve what you want you need to implement the mousePressEvent for the graphics item, but since QGraphicsItem is not a QObject subclass there's no direct way to create a new signal, if not by using a custom "signal proxy".

As a side note, using self.resize() is completely useless too, since you're using self.showMaximized() .

This is a possible implementation of what you're trying to achieve:

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

Tip: avoid to mix import "styles". You either import the module ( from PyQt5 import QtWidgets, QtCore ) or the related classes ( from PyQt5.QtWidgets import (...) ).

To find the original pixmap corresponding to an item in the graphics view, you could store it as data in the item via QGraphicsItem.setData() , eg

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)

    ....

This would allow you to retrieve the original pixmap from the item via item.data(0) .

To find the item under the mouse cursor, you could use QGraphicsView.itemAt in MainWindow.mousePressEvent . The position of the mouse cursor is given by event.pos() . However, this position is relative to the local coordinate system of the main window. To match these coordinates to the coordinate system of the graphics view you need to map them to a coordinate system that is common to both the graphics view and the main window such as the global coordinate system. With this in mind, MainWindow.mousePressEvent would become something like

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM