panofish
panofish

Reputation: 7879

PyQt Drop Event without subclassing?

This python pyqt code works how I intended. But, I don't like having to subclass QLineEdit so that I can detect file drop onto my QLineEdit field. I like the more elegant and simpler "connect" technique (new style signal/slot handling) that I used to detect text changes to the edit field.

My question: Is there a signal/slot connect solution for handling drops on the edit field without having to subclass QLineEdit?

Also, it is annoying that I must implement both methods in the subclass... dragEnterEvent & dropEvent to make the drop work!

import sys
from PyQt4 import QtGui, QtCore

class dropedit(QtGui.QLineEdit):   # subclass 
    def __init__(self, parent=None):
        super(dropedit, self).__init__(parent)
        self.setDragEnabled(True)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        print "dragEnterEvent:"

        if event.mimeData().hasUrls():
            event.accept()   # must accept the dragEnterEvent or else the dropEvent can't occur !!!
        else:
            event.ignore()

    def dropEvent(self, event):

        if event.mimeData().hasUrls():   # if file or link is dropped

            urlcount = len(event.mimeData().urls())  # count number of drops

            url = event.mimeData().urls()[0]   # get first url

            self.setText(url.toString())   # assign first url to editline

            #event.accept()  # doesnt appear to be needed

class testDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super(testDialog, self).__init__(parent)


        form = QtGui.QFormLayout()
        form.setHorizontalSpacing(0)

        myedit = dropedit()
        form.addWidget(myedit)

        self.setLayout(form)
        self.setGeometry(300, 300, 400, 0)
        self.setWindowTitle('drop test')

        myedit.textChanged.connect(self.editchange)   # new style signal slot connections

    @QtCore.pyqtSlot(str)   # int represent the column value
    def editchange(self,data):
        print "editchange:", data

if __name__ == "__main__":

    app = QtGui.QApplication([])
    dl = testDialog()
    dl.exec_()
    sys.exit(app.closeAllWindows())

Upvotes: 1

Views: 5034

Answers (2)

Lucas Wolfgang
Lucas Wolfgang

Reputation: 11

I had the same issue today and has able to get it solved in a different (and maybe less orthodoxy) way. So, what can also be done is to remap the dropEvent function from the tree to another name, either internal to the instance or to the class that owns the instance, and assign a new custom function the dropEvent. This new function can call or not the original one, depending if you want to keep some of the functionallities, or just rewrite everything. Here is a snippet:

import sys
from PyQt5.QtWidgets import QTreeView
from PyQt5.QtGui import QDropEvent

class Demo: 

    def __init__(self):
        self.tree = QTreeView()
        self.tree.setDragEnabled(True)
        self.tree.setAcceptDrops(True)
        self.tree.original_dropEvent = self.dataView.dropEvent
        self.tree.dropEvent = self._dataview_dropevent

    def _tree_dropevent(self, event: QDropEvent):
        # do stuff ...
        self.tree.original_dropEvent(event)
        # maybe more stuff ..

.

Upvotes: 0

Frodon
Frodon

Reputation: 3775

No need to subclass: you can use an event filter:

import sys
from PyQt4 import QtGui, QtCore

class testDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super(testDialog, self).__init__(parent)

        form = QtGui.QFormLayout()
        form.setHorizontalSpacing(0)

        self.myedit = QtGui.QLineEdit()
        self.myedit.setDragEnabled(True)
        self.myedit.setAcceptDrops(True)
        self.myedit.installEventFilter(self)

        form.addWidget(self.myedit)

        self.setLayout(form)
        self.setGeometry(300, 300, 400, 0)
        self.setWindowTitle('drop test')

        self.myedit.textChanged.connect(self.editchange)   # new style signal slot connections

    @QtCore.pyqtSlot(str)   # int represent the column value
    def editchange(self,data):
        print "editchange:", data.toLatin1()

    def eventFilter(self, object, event):
        if (object is self.myedit):
            if (event.type() == QtCore.QEvent.DragEnter):
                if event.mimeData().hasUrls():
                    event.accept()   # must accept the dragEnterEvent or else the dropEvent can't occur !!!
                    print "accept"
                else:
                    event.ignore()
                    print "ignore"
            if (event.type() == QtCore.QEvent.Drop):
                if event.mimeData().hasUrls():   # if file or link is dropped
                    urlcount = len(event.mimeData().urls())  # count number of drops
                    url = event.mimeData().urls()[0]   # get first url
                    object.setText(url.toString())   # assign first url to editline
                    #event.accept()  # doesnt appear to be needed
            return False # lets the event continue to the edit
        return False

if __name__ == "__main__":

    app = QtGui.QApplication([])
    dl = testDialog()
    dl.exec_()
    sys.exit(app.closeAllWindows())

Upvotes: 3

Related Questions