QGraphicsView无法拖放两次

QGraphicsView unable to drag and drop twice

我试图通过将图像拖到 QGraphicsView 来读取图像。我已经完成了,这是代码:

ui.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>772</width>
    <height>671</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <widget class="DropGraphicsView" name="graphicsView"/>
    </item>
    <item>
     <widget class="Line" name="line">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>Run</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>DropGraphicsView</class>
   <extends>QGraphicsView</extends>
   <header>DropGraphicsView</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

DropGraphicsView.py

import warnings
import numpy as np
from PyQt5.QtWidgets import QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
from PyQt5.QtGui import QPixmap, QImage

class DropGraphicsView(QGraphicsView):
    def __init__(self, parent):
        super(DropGraphicsView, self).__init__(parent)
        self.setAcceptDrops(True)
        self.image_path = None  # to save the path of the image

    def dragEnterEvent(self, event):
        print("dragEnterEvent:")  # called in the first drag, not in the second, why?
        if event.mimeData().hasUrls():
            event.accept()
            print("accepted")
        else:
            event.ignore()
            print("ignored")

    def dropEvent(self, event):
        self._get_image_path(event)  # get the path of image
        self.show_image(self.image_path)  # show the image
        print(self.acceptDrops())  # still True when printed

    def _get_image_path(self, event):
        urls = event.mimeData().urls()
        image_path = urls[0].toString().replace('file:///', '')
        print(image_path)
        self.image_path = image_path

    def show_image(self, image_path):
        # read the image
        if isinstance(image_path, str):
            frame = QImage(image_path)
        elif isinstance(image_path, np.ndarray):
            frame = QImage(image_path, image_path.shape[1], image_path.shape[0],
                           QImage.Format_RGB888)
        else:
            warnings.warn("wrong type")
            return

        if frame is not None:
            # scale the image
            size = self.size()
            size.setWidth(size.width() - 2)
            size.setHeight(size.height() - 2)
            frame = frame.scaled(size)
            # show the image
            pix = QPixmap.fromImage(frame)
            self.item = QGraphicsPixmapItem(pix)  # 创建像素图元
            self.scene = QGraphicsScene()  # 创建场景
            self.scene.addItem(self.item)
            self.setScene(self.scene)
            self.show()

main.py

from PyQt5 import uic
from PyQt5.QtWidgets import QApplication

UI_path = "ui.ui"

class Main:

    def __init__(self):
        self.ui = uic.loadUi(UI_path)
        self.ui.pushButton.clicked.connect(self._run)

    def _run(self):
        # do something


if __name__ == '__main__':
    app = QApplication([])
    main = Main()
    main.ui.show()
    app.exec_()

一切正常,可以显示掉落的图像。但是一个镜像被drop之后,其他的image就不能再被drop了。第二次拖动时未调用 dragEnterEvent。为什么?

最后我自己解决了我的问题

问题是 DropGraphicsView.show() 中使用的 QGraphicsScene 不支持拖放。所以显示图片后,DropGraphicsView 区域无法接收到拖放事件。

解决方案是继承 QGraphicsScene: DropGraphicsScene.py

from PyQt5.QtWidgets import QGraphicsScene

class DropGraphicsScene(QGraphicsScene):
    def __init__(self):
        super(QGraphicsScene, self).__init__()

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        event.accept()

    def dragMoveEvent(self, e):
        e.acceptProposedAction()

并在 DropGraphicsView.py 中使用 DropGraphicsScene 而不是 QGraphicsScene:

import warnings
import numpy as np
from PyQt5.QtWidgets import QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
from PyQt5.QtGui import QPixmap, QImage
import DropGraphicsScene

class DropGraphicsView(QGraphicsView):
    def __init__(self, parent):
        super(DropGraphicsView, self).__init__(parent)
        self.setAcceptDrops(True)
        self.image_path = None  # to save the path of the image

    def dragEnterEvent(self, event):
        print("dragEnterEvent:")  # called in the first drag, not in the second, why?
        if event.mimeData().hasUrls():
            event.accept()
            print("accepted")
        else:
            event.ignore()
            print("ignored")

    def dropEvent(self, event):
        self._get_image_path(event)  # get the path of image
        self.show_image(self.image_path)  # show the image
        print(self.acceptDrops())  # still True when printed

    def _get_image_path(self, event):
        urls = event.mimeData().urls()
        image_path = urls[0].toString().replace('file:///', '')
        print(image_path)
        self.image_path = image_path

    def show_image(self, image_path):
        # read the image
        if isinstance(image_path, str):
            frame = QImage(image_path)
        elif isinstance(image_path, np.ndarray):
            frame = QImage(image_path, image_path.shape[1], image_path.shape[0],
                           QImage.Format_RGB888)
        else:
            warnings.warn("wrong type")
            return

        if frame is not None:
            # scale the image
            size = self.size()
            size.setWidth(size.width() - 2)
            size.setHeight(size.height() - 2)
            frame = frame.scaled(size)
            # show the image
            pix = QPixmap.fromImage(frame)
            self.item = QGraphicsPixmapItem(pix)
            self.scene = DropGraphicsScene.DropGraphicsScene()
            self.scene.addItem(self.item)
            self.setScene(self.scene)
            self.show()