William Chan
William Chan

Reputation: 43

QGraphicsView unable to drag and drop twice

I'm trying to read an image by drag it to a QGraphicsView. I've done it and here is the code:

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

Everything works fine and the image dropped can be showed. But after an image is dropped, other images can't be dropped. The dragEnterEvent is not called in the second drag. Why?

Upvotes: 0

Views: 217

Answers (1)

William Chan
William Chan

Reputation: 43

Finally, I solved my question by myself.

The problem is that the QGraphicsScene used in DropGraphicsView.show() doesn't support drag and drop. So after an image is shown, the DropGraphicsView area can't receive a drop event.

The solution is to subclass the 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()

And use DropGraphicsScene instead of QGraphicsScene in DropGraphicsView.py:

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

Upvotes: 2

Related Questions