Search code examples
pythonpyqtpyqt5qt-designerqcombobox

How to enable QDropEvent when using a QT Designer (.ui) file in Python?


Using the answer from this I have been able to get the QDropEvent to fire when not using a QT Designer (.ui) File; but I can't seem to get it working otherwise.

According to this documentation and this example it seems like I should only need to set .setAcceptDrops() as True for the QComboBox I want to accept the event, and implement the dragEnterEvent and dropEvent with all the necessary logic. I'm not really fluent in C++ so I only understand some of the example, but for the most part it seems straight forward.

The example I created for this doesn't throw any exception, and nothing seems to happen when I try to drop a text file from Windows Explorer onto the cb1 QComboBox. I feel like I'm missing something simple, but I'm not sure what. Any ideas?

Using Python 3.7 and PyQt5

main.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>269</width>
    <height>116</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="3" column="1">
     <widget class="QComboBox" name="cb4">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
     </widget>
    </item>
    <item row="1" column="1">
     <widget class="QComboBox" name="cb2">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
     </widget>
    </item>
    <item row="2" column="1">
     <widget class="QComboBox" name="cb3">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
     </widget>
    </item>
    <item row="0" column="1">
     <widget class="QComboBox" name="cb1">
      <property name="sizePolicy">
       <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
      </property>
     </widget>
    </item>
    <item row="0" column="0">
     <widget class="QLabel" name="l1">
      <property name="text">
       <string>cb1</string>
      </property>
     </widget>
    </item>
    <item row="1" column="0">
     <widget class="QLabel" name="l2">
      <property name="text">
       <string>cb2</string>
      </property>
     </widget>
    </item>
    <item row="2" column="0">
     <widget class="QLabel" name="l3">
      <property name="text">
       <string>cb3</string>
      </property>
     </widget>
    </item>
    <item row="3" column="0">
     <widget class="QLabel" name="l4">
      <property name="text">
       <string>cb4</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

QComboBoxDrop.py

from PyQt5 import uic
from PyQt5.Qt import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

Ui_main = uic.loadUiType(r'main.ui')[0]


class MinExample(QMainWindow, Ui_main):
    def __init__(self, parent = None, flags = Qt.Window):
        QMainWindow.__init__(self, parent, flags)
        self.setupUi(self)

        self.cb1.addItems(["item {}".format(i) for i in range(5)])
        self.cb2.addItems(["item {}".format(i) for i in range(5)])
        self.cb3.addItems(["item {}".format(i) for i in range(5)])
        self.cb4.addItems(["item {}".format(i) for i in range(5)])

        self.cb1.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        print("dragEnterEvent Occurred")
        event.acceptProposedAction()

    def dropEvent(self, event):
        print("dropEvent Occurred")

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

Solution

  • In the case that you already have a class that implements the logic that you want and want to use it in Qt Designer the correct option is to use the promotion, for it follow the steps that are in this answer and in step 4.1 change to the following:

    enter image description here

    On the other hand my solution of the old post does not work if the widget is a child of the window, in that case you must overwrite the dragMoveEvent method.

    Considering the above, the solution is:

    ├── main.py
    ├── main.ui
    └── qcomboboxdrop.py
    

    main.py

    from PyQt5 import uic, QtCore, QtWidgets
    
    Ui_main = uic.loadUiType(r'main.ui')[0]
    
    
    class MinExample(QtWidgets.QMainWindow, Ui_main):
        def __init__(self, parent = None, flags = QtCore.Qt.Window):
            super(MinExample, self).__init__(parent, flags)
            self.setupUi(self)
    
            self.cb1.addItems(["item {}".format(i) for i in range(5)])
            self.cb2.addItems(["item {}".format(i) for i in range(5)])
            self.cb3.addItems(["item {}".format(i) for i in range(5)])
            self.cb4.addItems(["item {}".format(i) for i in range(5)])
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = MinExample()
        w.show()
        sys.exit(app.exec_())
    

    main.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>269</width>
        <height>148</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QGridLayout" name="gridLayout">
        <item row="3" column="1">
         <widget class="QComboBoxDrop" name="cb4">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
         </widget>
        </item>
        <item row="1" column="1">
         <widget class="QComboBoxDrop" name="cb2">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
         </widget>
        </item>
        <item row="2" column="1">
         <widget class="QComboBoxDrop" name="cb3">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
         </widget>
        </item>
        <item row="0" column="1">
         <widget class="QComboBoxDrop" name="cb1">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
         </widget>
        </item>
        <item row="0" column="0">
         <widget class="QLabel" name="l1">
          <property name="text">
           <string>cb1</string>
          </property>
         </widget>
        </item>
        <item row="1" column="0">
         <widget class="QLabel" name="l2">
          <property name="text">
           <string>cb2</string>
          </property>
         </widget>
        </item>
        <item row="2" column="0">
         <widget class="QLabel" name="l3">
          <property name="text">
           <string>cb3</string>
          </property>
         </widget>
        </item>
        <item row="3" column="0">
         <widget class="QLabel" name="l4">
          <property name="text">
           <string>cb4</string>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
     </widget>
     <customwidgets>
      <customwidget>
       <class>QComboBoxDrop</class>
       <extends>QComboBox</extends>
       <header>qcomboboxdrop.h</header>
      </customwidget>
     </customwidgets>
     <resources/>
     <connections/>
    </ui>
    

    qcomboboxdrop.py

    from PyQt5 import QtCore, QtWidgets
    
    class QComboBoxDrop(QtWidgets.QComboBox):
        def __init__(self, *args, **kwargs):
            super(QComboBoxDrop, self).__init__(*args, **kwargs)
            self.setAcceptDrops(True)
    
        def dragEnterEvent(self, event):
             # print("formats: ", event.mimeData().formats())
            if event.mimeData().hasFormat("text/plain"):
                event.acceptProposedAction()
    
        def dragMoveEvent(self, event):
            if event.mimeData().hasFormat("text/plain"):
                event.acceptProposedAction()        
    
        def dropEvent(self, event):
            url =QtCore.QUrl(event.mimeData().text().strip())
            if url.isLocalFile():
                file = QtCore.QFile(url.toLocalFile())
                if file.open(QtCore.QFile.ReadOnly|QtCore.QFile.Text):
                    ts = QtCore.QTextStream(file)
                    while not ts.atEnd():
                        print(ts.readLine())
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = QComboBoxDrop()
        w.addItems(["item {}".format(i) for i in range(10)])
        w.show()
        sys.exit(app.exec_())