将文件拖放到 QTableWidget

Drag and drop files to QTableWidget

我正在尝试将文件拖放到 table-widget 中,但是当我拖放文件时路径不会出现。当我使用 print() 测试时,路径出现,但路径不会出现在 table 中。我不确定为什么会这样,非常感谢您的帮助。

主要代码:

import os
import shutil
import sys

import pandas as pd
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtWidgets import *

from vituix_converter_UI import (Ui_MainWindow)


def no_file_selected():
    msg = QMessageBox()
    msg.setIcon(QMessageBox.Warning)
    msg.setWindowIcon(QtGui.QIcon("D:/Python Projects/HATS SAMS Conversion/Icon/Vituix_Converter_Icon_R1.ico"))
    msg.setText("No File Was Selected!")
    msg.setWindowTitle("Warning!")
    retval = msg.exec_()


def process_completed():
    msg = QMessageBox()
    msg.setIcon(QMessageBox.Information)
    msg.setWindowIcon(QtGui.QIcon("D:/Python Projects/HATS SAMS Conversion/Icon/Vituix_Converter_Icon_R1.ico"))
    msg.setText("Files are converted.")
    msg.setWindowTitle("Completed!")
    retval = msg.exec_()


class MyMainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self.setupUi(self)

        self.pushButton_hats.clicked.connect(self.hats_conversion)
        self.pushButton_sams.clicked.connect(self.sams_conversion)

        self.tableWidget.setAcceptDrops(True)
        self.tableWidget.viewport().installEventFilter(self)
        types = ['text/uri-list']
        types.extend(self.tableWidget.mimeTypes())
        self.tableWidget.mimeTypes = lambda: types
        self.tableWidget.setRowCount(0)

        self.tableWidget.dropEvent(self.eventFilter())

    def hats_conversion(self):

        hats_file = QtWidgets.QFileDialog.getOpenFileName(self, "Select HATS File", "", 'txt (*.txt)')[0]

        if hats_file == '':
            no_file_selected()
        else:

            hats_data = pd.read_csv(hats_file, sep="\t", skiprows=3, header=None, on_bad_lines='skip')

            txt_name = []

            for i in range(1, 148, 2):
                txt_name.append(hats_data.loc[0, i])

            hats_data = hats_data.drop([0, 1])
            hats_data = hats_data.reset_index(drop=True)
            # Select folder to save data output
            save_folder = QtWidgets.QFileDialog.getExistingDirectory(self, "Select HATS Save Folder")
            # iteration variable for lists in following for loop
            i = 1
            # iterate over all text names, allocate corresponding data, and then save as txt
            for j in range(0, len(txt_name)):
                test_df_1 = hats_data[0].values.tolist()
                test_df_2 = hats_data[i].values.tolist()
                test_df_3 = hats_data[i + 1].values.tolist()

                export_df = pd.DataFrame(
                    {'0': test_df_1,
                     '1': test_df_2,
                     '2': test_df_3
                     })

                save_file = save_folder + '/' + txt_name[j] + '.txt'
                export_df.to_csv(save_file, sep='\t', index=False, na_rep='NaN', header=False)
                i = i + 2

            process_completed()

    def sams_conversion(self):

        sams_file = QtWidgets.QFileDialog.getExistingDirectory(self, "Select SAMS Folder")

        if sams_file == '':
            no_file_selected()
        else:
            output_folder = QtWidgets.QFileDialog.getExistingDirectory(self, "Select/Create Save Folder")

            if output_folder == '':
                no_file_selected()

            else:

                file_names = []
                for x in os.listdir(sams_file):
                    if x.endswith(".txt"):
                        file_names.append(x)

                for i in range(len(file_names)):
                    newPath = shutil.copy(os.path.join(sams_file, file_names[i]), output_folder)

                    sams_data = pd.read_csv(newPath, sep="\t", skiprows=5, header=None, on_bad_lines='skip')

                    sams_data.to_csv(newPath, sep='\t', index=False, na_rep='NaN', header=False)

                process_completed()

    def eventFilter(self, source, event):
        if event.mimeData().hasUrls():
            for url in event.mimeData().urls():
                self.addFile(url.toLocalFile())

            return True
        return super().eventFilter(source, event)

    def addFile(self, filepath):
        row = self.tableWidget.rowCount()
        self.tableWidget.insertRow(row)
        item = QtWidgets.QTableWidgetItem(filepath)
        self.tableWidget.setItem(row, 0, item)
        self.tableWidget.resizeColumnToContents(0)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    app.setWindowIcon(QtGui.QIcon("D:/Python Projects/HATS SAMS Conversion/Icon/Vituix_Converter_Icon_R1.ico"))
    win = MyMainWindow()
    win.show()
    sys.exit(app.exec_())

UI代码:

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

# Form implementation generated from reading ui file 'vituix converter.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# 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 PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(400, 500)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
        MainWindow.setSizePolicy(sizePolicy)
        MainWindow.setMinimumSize(QtCore.QSize(400, 500))
        MainWindow.setMaximumSize(QtCore.QSize(400, 500))
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("Icon/Vituix_Converter_Icon_R1.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
        self.tableWidget.setSizePolicy(sizePolicy)
        self.tableWidget.setMinimumSize(QtCore.QSize(0, 350))
        self.tableWidget.setDragEnabled(False)
        self.tableWidget.setDragDropMode(QtWidgets.QAbstractItemView.DropOnly)
        self.tableWidget.setDefaultDropAction(QtCore.Qt.LinkAction)
        self.tableWidget.setAlternatingRowColors(True)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(0)
        self.tableWidget.setRowCount(0)
        self.verticalLayout.addWidget(self.tableWidget, 0, QtCore.Qt.AlignVCenter)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.gridLayout.setVerticalSpacing(6)
        self.gridLayout.setObjectName("gridLayout")
        self.pushButton_hats = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_hats.setMinimumSize(QtCore.QSize(0, 55))
        font = QtGui.QFont()
        font.setPointSize(9)
        font.setBold(True)
        font.setWeight(75)
        self.pushButton_hats.setFont(font)
        self.pushButton_hats.setObjectName("pushButton_hats")
        self.gridLayout.addWidget(self.pushButton_hats, 0, 0, 1, 1)
        self.pushButton_sams = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_sams.setMinimumSize(QtCore.QSize(0, 55))
        font = QtGui.QFont()
        font.setPointSize(9)
        font.setBold(True)
        font.setWeight(75)
        self.pushButton_sams.setFont(font)
        self.pushButton_sams.setObjectName("pushButton_sams")
        self.gridLayout.addWidget(self.pushButton_sams, 0, 1, 1, 1, QtCore.Qt.AlignVCenter)
        self.verticalLayout.addLayout(self.gridLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Vituix Converter"))
        self.pushButton_hats.setText(_translate("MainWindow", "Vituix HATS"))
        self.pushButton_sams.setText(_translate("MainWindow", "Vituix SAMS"))


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

为了使用 event-filter 执行此操作,您需要处理 drag-enter、drag-move 和 drag-drop 事件,并在 mime-data 包含 url。对您的示例进行的以下更改应该会按预期工作:

class MyMainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        ...
        self.tableWidget.setAcceptDrops(True)
        self.tableWidget.viewport().installEventFilter(self)
        self.tableWidget.setRowCount(0)
        self.tableWidget.setColumnCount(1)

    def eventFilter(self, source, event):
        if (source is self.tableWidget.viewport() and
            (event.type() == QtCore.QEvent.DragEnter or
             event.type() == QtCore.QEvent.DragMove or
             event.type() == QtCore.QEvent.Drop) and
            event.mimeData().hasUrls()):
            if event.type() == QtCore.QEvent.Drop:
                for url in event.mimeData().urls():
                    if url.isLocalFile():
                        self.addFile(url.path())
            event.accept()
            return True
        return super().eventFilter(source, event)

table 没有列:如果没有给出有效的行和列,setItemData() 将被忽略:

    def addFile(self, filepath):
        if not self.tableWidget.columnCount():
            self.tableWidget.setColumnCount(1)
        # ...

请注意,在访问事件函数之前,您必须始终检查事件过滤器中的事件类型,否则您将收到错误甚至崩溃:

    def eventFilter(self, source, event):
        if event.type() == event.Drop and event.mimeData().hasUrls():
            for url in event.mimeData().urls():
                self.addFile(url.toLocalFile())
            return True
        return super().eventFilter(source, event)

此外,请删除以下完全错误的行:

        self.tableWidget.dropEvent(self.eventFilter())