拖放小部件实现错误 - RuntimeWarning:MetaObjectBuilder::addMethod:为 "dropped" 提供的方法签名无效

Drag & Drop Widget Implementation Error- RuntimeWarning: MetaObjectBuilder::addMethod: Invalid method signature provided for "dropped"

这是我为此创建的自定义小部件:

from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
import os

class DragDropWidget(QWidget):
    def __init__(self, parent=None):
        super(DragDropWidget, self).__init__(parent)
        self.setAcceptDrops(True)

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

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            if len(event.mimeData().urls()) != 1:
                event.ignore()
            else:
                event.setDropAction(Qt.CopyAction)
                event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(Qt.CopyAction)
            event.accept()
            if len(event.mimeData().urls()) != 1:
                event.ignore()
            else:
                url = event.mimeData().urls()[
                    0].toLocalFile()
                if os.path.exists(url):
                    self.emit(SIGNAL("dropped"), url)

        else:
            event.ignore()

我可以从 dropEvent 函数打印出文件的位置,但我无法使用连接从我的主函数访问它。 我的主要功能为此具有以下几行:

self.connect(self.ui.DragDropEncode, SIGNAL("dropped"), self.add_file)
def add_file(self, file):
    print(file)

我使用以下方法从另一个文件导入了我的 UI:

from Main_UI import Ui_MainWindow

我有此小部件的以下代码:

   self.DragDropEncode = DragDropWidget(self.AddFileEncode)
   self.DragDropEncode.setAcceptDrops(True)

当我 运行 我的主文件时,出现以下错误:

main.py:55: RuntimeWarning: MetaObjectBuilder::addMethod: Invalid method signature provided for "dropped"
  self.connect(self.ui.DragDropEncode, SIGNAL("dropped"), self.add_file)

此外,删除文件绝对没有任何作用。

我还是不明白为什么会出现这个错误。任何帮助将非常感激。 谢谢!

这是一个最小的可重现示例:

main.py

# Importing The Required Modules 
from PySide2 import QtCore, QtWidgets, QtGui
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
import sys


# Importing the GUI file
from Problematic import Ui_MainWindow


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        # Connects to the "DragDrop" Widget defined in the GUI file
        self.connect(self.ui.DragDrop, SIGNAL("dropped"), self.add_file)

        self.show()

    def add_file(self, file):
        # Just some debugging. Not working from this end.
        print("Signal Recieved:", file)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())

Problematic.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'Problematic.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PySide2 import QtCore, QtGui, QtWidgets
from DragDropWidget import DragDropWidget


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.Frame = QtWidgets.QFrame(self.centralwidget)
        self.Frame.setStyleSheet("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(158, 7, 23, 255), stop:1 rgba(255, 130, 20, 255));")
        self.Frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.Frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.Frame.setObjectName("Frame")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.Frame)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.DragDrop = DragDropWidget(self.Frame)
        self.DragDrop.setAcceptDrops(True)
        self.DragDrop.setStyleSheet("background-color: qlineargradient(spread:pad, x1:0.248473, y1:0.483, x2:1, y2:0, stop:0.208955 rgba(131, 62, 40, 22), stop:1 rgba(163, 13, 23, 0));\n"
"border: 2px dashed rgba(85, 85, 85, 95);")
        self.DragDrop.setObjectName("DragDrop")
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.DragDrop)
        self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout_3.setSpacing(0)
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.Text = QtWidgets.QLabel(self.DragDrop)
        font = QtGui.QFont()
        font.setFamily("Microsoft Sans Serif")
        font.setPointSize(80)
        font.setItalic(True)
        font.setStyleStrategy(QtGui.QFont.PreferAntialias)
        self.Text.setFont(font)
        self.Text.setAlignment(QtCore.Qt.AlignCenter)
        self.Text.setObjectName("Text")
        self.horizontalLayout_3.addWidget(self.Text)
        self.horizontalLayout_2.addWidget(self.DragDrop)
        self.horizontalLayout.addWidget(self.Frame)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Text.setText(_translate("MainWindow", "Drag & Drop\n"
"Files Here"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

DragDropWidget.py

from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
import os

class DragDropWidget(QWidget):
    def __init__(self, type, parent=None):
        super(DragDropWidget, self).__init__(parent)
        self.setAcceptDrops(True)

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

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls:
            if len(event.mimeData().urls()) != 1:
                event.ignore()
            else:
                event.setDropAction(Qt.CopyAction)
                event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls:
            event.setDropAction(Qt.CopyAction)
            event.accept()
            if len(event.mimeData().urls()) != 1:
                event.ignore()
            else:
                url = event.mimeData().urls()[
                    0].toLocalFile()
                if os.path.exists(url):
                    self.emit(SIGNAL("dropped"), url)
                    # Just Some Debugging, Working from this end
                    print("Signal Emitted:", url)

        else:
            event.ignore()

使用 SIGNAL() 宏多年来一直被认为已经过时,“新式”信号和槽语法必须始终用于新代码。

另外,SIGNAL 语法应该总是有一个(可能是空的)参数类型列表作为它的签名。 PyQt 过去支持所谓的 "short-circuit signals",它允许将没有适当签名的自定义信号连接到 python 可调用对象,并有可能发出具有任何数量和类型参数的信号。这可以通过使用不带 括号的信号 来实现。

如前所述,此语法已过时,pyside 还删除了对那些 short-circuit 信号的支持,如页面 Differrences between PySide and PyQt:

中所述

Since this is an old and deprecated feature, and the effort to fix this is not worth it, we decided to not implement it. In PySide code you need to use something like:

self.emit(SIGNAL ('text_changed_cb(QString)'), text)

在你的例子中,因为你没有使用参数,所以它应该是这样的:

self.connect(self.ui.DragDropEncode, SIGNAL("dropped()"), self.add_file)

但是,如前所述,这是一个过时且已弃用的功能(而且过于冗长而且不是很 pythonic)。

解决方案是为 class 创建信号并直接发射它们:

class DragDropWidget(QWidget):
    dropped = Signal(str)
    # ...

    def dropEvent(self, event):
        # ...
        self.dropped.emit(url)

然后将实例的信号连接到插槽:

self.ui.DragDropEncode.dropped.connect(self.add_file)

请注意,在发出信号时必须遵守参数签名。在上述情况下,根据您的代码,我假设您正在将 mimeData 的 QUrl 转换为字符串。如果您需要发出 QUrl,信号必须反映:

class DragDropWidget(QWidget):
    dropped = Signal(QUrl)

或者,有两种可能性:您可以使用 generic object 签名,它允许发出 any参数 (dropped = Signal(object)),或使用信号重载。在这种情况下,您可以使用能够发出各种参数长度和类型的单个信号。在这种情况下,emit 将默认使用第一个重载,而其他必须用方括号选择:

class DragDropWidget(QWidget):
    dropped = Signal([str], [QUrl])

    # ...

    def dropEvent(self, event):
        # ...
        url = event.mimeData().urls()[0]
        self.dropped.emit(url.toLocalFile())
        self.dropped[QUrl].emit(url)

如果您需要根据信号的签名连接到不同的插槽,这会很有用:

    self.ui.DragDropEncode.dropped.connect(self.function_that_uses_strings)
    self.ui.DragDropEncode.dropped[QUrl].connect(self.function_that_uses_urls)