slalomchip
slalomchip

Reputation: 909

PyQt - Drag and Drop into Different QLabel Widgets

I am using Windows, Python 3.6, OpenCV3, and PyQt5. I have a main window that has two QLabel widgets (label1 and label2). I want to drag and drop different video files into both QLabel widgets. My script displays the first frame of each of the two video files.

I want to have the video display in label2 regardless if I drag the file around or through label1. Suggestions?

import sys, cv2
from PyQt5.QtWidgets import QApplication, QLabel, QFrame, QMainWindow
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import Qt

class Example(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.setGeometry(200, 300, 800, 600)
        self.setAcceptDrops(True)
        self.setMouseTracking(True)

        self.label1 = QLabel(self)
        self.label1.move(10, 10)
        self.label1.resize(780, 280)
        self.label1.setFrameShape(QFrame.Box)
        self.label1.setAcceptDrops(True)

        self.label2 = QLabel(self)
        self.label2.move(10, 310)
        self.label2.resize(780, 280)
        self.label2.setFrameShape(QFrame.Box)
        self.label2.setAcceptDrops(True)

        self.label1.setText("Label 1")
        self.label2.setText("Label 2")
        self.show()

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

    def dropEvent(self, e):
        if e.mimeData().hasUrls:
            e.accept()
            for url in e.mimeData().urls():
                if self.label1.underMouse():
                    fname = str(url.toLocalFile())
                    self.openFile1(fname)
                elif self.label2.underMouse():
                    fname = str(url.toLocalFile())
                    self.openFile2(fname)
        else:
            e.ignore()

    def openFile1(self, filename):
        self.cap1 = cv2.VideoCapture(str(filename))
        self.cap1.set(cv2.CAP_PROP_POS_FRAMES, 0)
        width = self.cap1.get(cv2.CAP_PROP_FRAME_WIDTH)
        height = self.cap1.get(cv2.CAP_PROP_FRAME_HEIGHT)
        ret, frame = self.cap1.read()
        if ret == True:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
            pix = QPixmap.fromImage(img)
            pix = self.scalePix(self.label1, pix, width, height)
            self.label1.setPixmap(pix)

    def openFile2(self, filename):
        self.cap2 = cv2.VideoCapture(str(filename))
        self.cap2.set(cv2.CAP_PROP_POS_FRAMES, 0)
        width = self.cap2.get(cv2.CAP_PROP_FRAME_WIDTH)
        height = self.cap2.get(cv2.CAP_PROP_FRAME_HEIGHT)
        ret, frame = self.cap2.read()
        if ret == True:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
            pix = QPixmap.fromImage(img)
            pix = self.scalePix(self.label1, pix, width, height)
            self.label2.setPixmap(pix)

    def scalePix(self, label, p, width, height):
        window_width = label.width()
        ratio = height / width
        window_height = int(ratio * window_width)
        window_height = label.height() 
        window_width = int(1 / ratio * window_height)
        p = p.scaledToWidth(window_width, Qt.SmoothTransformation)
        p = p.scaledToHeight(window_height, Qt.SmoothTransformation)
        return p

if __name__ == '__main__':    
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())

Upvotes: 1

Views: 1997

Answers (1)

eyllanesc
eyllanesc

Reputation: 244132

Documentation indicates that underMouse() may fail in the drag and drop process:

bool QWidget::underMouse() const

Returns true if the widget is under the mouse cursor; otherwise returns false.

This value is not updated properly during drag and drop operations.

So it's better not to use, instead we can create a custom QLabel that implements the drag and drop:

import sys, cv2
from PyQt5 import QtCore, QtGui, QtWidgets

class OpenCVLabel(QtWidgets.QLabel):
    def __init__(self, *args, **kwargs):
        super(OpenCVLabel, self).__init__(*args, **kwargs)
        self.setFrameShape(QtWidgets.QFrame.Box)
        self.setAcceptDrops(True)

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

    def dropEvent(self, e):
        if e.mimeData().hasUrls():
            e.accept()
            for url in e.mimeData().urls():
                self.openFile(url.toLocalFile())
        else:
            e.ignore()

    def openFile(self, filename):
        cap = cv2.VideoCapture(str(filename))
        cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
        width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
        height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
        ret, frame = cap.read()
        if ret:
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
            pix = QtGui.QPixmap.fromImage(img)
            pix = pix.scaled(self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
            self.setPixmap(pix)

class Example(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        label1 = OpenCVLabel("label1")
        label2 = OpenCVLabel("label2")
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QVBoxLayout(central_widget)
        lay.addWidget(label1)
        lay.addWidget(label2)
        self.resize(780, 560)

if __name__ == '__main__':    
    app = QtWidgets.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions