2

I'm trying to drag and drop files into a table-widget, but the path will not appear when I drop the files. When I test by using print() the path appears, but the path will not appear in the table. I am not sure why this is happening, I would greatly appreciate some help.

Main code:

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 code:

# -*- 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_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336

2 Answers2

2

In order to do this using an event-filter, you need to handle the drag-enter, drag-move and drag-drop events, and accept the events when the mime-data contains urls. The following changes to your example should work as expected:

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)
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
0

The table has no columns: setItemData() will be ignored if no valid row and column are given:

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

Note that you must always check the event type in an event filter before accessing the event functions, otherwise you'll get an error or even a crash:

    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)

Also, remove the following line, which is completely wrong:

        self.tableWidget.dropEvent(self.eventFilter())
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • I tried this our and it didn't work, @ekhumoro 's answer did work. Thank you though! – cwdesignsvs Feb 14 '22 at 03:05
  • @cwdesignsvs sorry but "didn't work" alone means nothing. Have you properly edited the code as suggested? Did you run the code in a terminal or prompt to see for any useful output and debug? ekhumoro's answer is correct and complete, but mine should be valid as much as long as it's based on your code. It is possible that it won't work, but knowing **why** is not only useful to you but also so me and to anybody reading this answer. "It doesn't work" is meaningless without further explanation, so, please add further details. – musicamante Feb 14 '22 at 04:18
  • I edited the code as you suggested and received the following error: Traceback (most recent call last): File "D:\Python Projects\HATS SAMS Conversion\Vituix Converter.py", line 113, in eventFilter if event.mimeData().hasUrls(): AttributeError: 'QEvent' object has no attribute 'mimeData' – cwdesignsvs Feb 14 '22 at 05:28
  • @cwdesignsvs that's *not* my code, that's still yours. Read more carefully what I wrote: the `if` in `eventFilter()` starts with a check of the event type, and I specifically explained that you **must** do that to avoid exactly what you just wrote. – musicamante Feb 14 '22 at 14:39