简体   繁体   中英

PyQt5 update/replace a QTableWidget inside a QScrollArea

I have a problem with a python GUI based on Qt5. I have an element to select a path and then a QScrollArea where a QTable is displayed with the files of the directory. This works fine when the directory is selected the first time, but when a new directory is selected the table is not updated, I get the following error: "QLayout: Attempting to add QLayout "" to QScrollArea "", which already has a layout"

I understand so far that I have to delete the parent, but I do not find a solution (sorry I am new to Qt). Any help is appreciated.

Thanks, Alex

Here is the working example:

import sys
import tempfile
import time
import ntpath
import os
from os import listdir
from os.path import isfile, join
import platform
import subprocess
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QPushButton, QScrollArea, QVBoxLayout, QCheckBox, QInputDialog, QLineEdit, QComboBox, QMessageBox, QWidget, QDialog
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import QTableWidget,QTableWidgetItem, QLabel
from PyQt5.QtGui import QPixmap, QIcon


LastStateRole = QtCore.Qt.UserRole

class Ui_brows(object):
    def createFileTable(self, fileList):
        self.fileTable = QTableWidget()
        self.fileTable.setRowCount(len(fileList))
        self.fileTable.setColumnCount(4)
        self.fileTable.setHorizontalHeaderLabels(['Include', 'Target','Name', 'Path'])
        for l in range (len(fileList)):
            item = QtWidgets.QTableWidgetItem()
            item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
            item.setCheckState(QtCore.Qt.Checked)
            item.setData(LastStateRole, item.checkState())
            self.fileTable.setItem(l,0,item)
            self.fileTable.setItem(l,1,QTableWidgetItem(""))
            nameString = ntpath.basename(fileList[l])
            self.fileTable.setItem(l,2,QTableWidgetItem(nameString))
            self.fileTable.setItem(l,3,QTableWidgetItem(fileList[l]))
        self.fileTable.setColumnWidth(0,40)
        self.fileTable.setColumnWidth(0,80)    
        self.layout = QVBoxLayout(self.scrollArea)
        self.layout.addWidget(self.fileTable)

    def _open_file_dialog(self):
        directory = str(QtWidgets.QFileDialog.getExistingDirectory())
        fileList = []
        for f in listdir(directory):
            fileList.append(f)
        self.createFileTable(fileList)

    def setupUi(self, brows):
        brows.setObjectName("MyApp")
        brows.resize(900, 600)
        
        #### line 1
        self.toolButtonOpenDialog = QtWidgets.QToolButton(brows)
        self.toolButtonOpenDialog.setGeometry(QtCore.QRect(310, 10, 35, 19))
        self.toolButtonOpenDialog.setObjectName("toolButtonOpenDialog")
        self.toolButtonOpenDialog.clicked.connect(self._open_file_dialog)

        self.importPath = QtWidgets.QLineEdit(brows)
        self.importPath.setEnabled(False)
        self.importPath.setGeometry(QtCore.QRect(110, 10, 191, 20))
        self.importPath.setObjectName("importPath")
        
        self.label1 = QtWidgets.QLabel(brows)
        self.label1.setText('Folder to scan')
        self.label1.setGeometry(QtCore.QRect(10,10,90,20))
        self.label1.setObjectName("Label1")
        
        self.label11 = QtWidgets.QLabel(brows)
        self.label11.setText('Scan Depth')
        self.label11.setGeometry(QtCore.QRect(370,10,90,20))
        self.label11.setObjectName("Label11")
        
        self.folderDepth = QComboBox(brows)
        self.folderDepth.addItems(['1','2','3','>3'])
        self.folderDepth.setGeometry(QtCore.QRect(450,8,50,25))
        self.folderDepth.setObjectName("FolderDepth")
        
        #### line 2
        self.label2 = QtWidgets.QLabel(brows)
        self.label2.setText('Local mount')
        self.label2.setGeometry(QtCore.QRect(10,50,90,20))
        self.label2.setObjectName("Label2")
        
        self.localMount = QtWidgets.QLineEdit(brows)
        self.localMount.setEnabled(True)
        self.localMount.setGeometry(QtCore.QRect(100, 50, 90, 20))
        self.localMount.setObjectName("localMount")
        
        self.label3 = QtWidgets.QLabel(brows)
        self.label3.setText('Remote mount')
        self.label3.setGeometry(QtCore.QRect(230,50,90,20))
        self.label3.setObjectName("Label3")
        
        self.remoteMount = QtWidgets.QLineEdit(brows)
        self.remoteMount.setEnabled(True)
        self.remoteMount.setGeometry(QtCore.QRect(330, 50, 80, 20))
        self.remoteMount.setObjectName("remoteMount")
        
        #### line 3
        self.scrollArea = QScrollArea(brows)
        self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setGeometry(QtCore.QRect(10, 100, 880, 400))

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

    def retranslateUi(self, brows):
        _translate = QtCore.QCoreApplication.translate
        brows.setWindowTitle(_translate("myApp", "MyApp"))
        self.toolButtonOpenDialog.setText(_translate("brows", "..."))

    
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    brows = QtWidgets.QDialog()
    ui = Ui_brows()
    ui.setupUi(brows)
    brows.show()

    sys.exit(app.exec_())

QTableWidget already has a scroll area inside, you don't need to create another one. See code below.

In case you still want to place additional scroll area outside of the table. You may check example how to use one correctly in structure:

Top_Level_Widget->Top_Level_Layout
Second_Level_QScrollArea
Third_Level_Widget_For_QScrollArea->Third_Level_Layout_of_ScrollArea_Widget
Fourth_Level_Contents_Inside.

https://github.com/baoboa/pyqt5/blob/master/examples/draganddrop/delayedencoding/delayedencoding.py

import sys
import tempfile
import time
import ntpath
import os
from os import listdir
from os.path import isfile, join
import platform
import subprocess
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QPushButton, QScrollArea, QVBoxLayout, QCheckBox, QInputDialog, QLineEdit, QComboBox, QMessageBox, QWidget, QDialog
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtWidgets import QTableWidget,QTableWidgetItem, QLabel
from PyQt5.QtGui import QPixmap, QIcon


LastStateRole = QtCore.Qt.UserRole

class Ui_brows(object):
    def createFileTable(self, fileList):
        self.fileTable = QTableWidget()
        self.fileTable.setRowCount(len(fileList))
        self.fileTable.setColumnCount(4)
        self.fileTable.setHorizontalHeaderLabels(['Include', 'Target','Name', 'Path'])
        for l in range (len(fileList)):
            item = QtWidgets.QTableWidgetItem()
            item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
            item.setCheckState(QtCore.Qt.Checked)
            item.setData(LastStateRole, item.checkState())
            self.fileTable.setItem(l,0,item)
            self.fileTable.setItem(l,1,QTableWidgetItem(""))
            nameString = ntpath.basename(fileList[l])
            self.fileTable.setItem(l,2,QTableWidgetItem(nameString))
            self.fileTable.setItem(l,3,QTableWidgetItem(fileList[l]))
        self.fileTable.setColumnWidth(0,40)
        self.fileTable.setColumnWidth(0,80)
        if self.table_place_holder_layout.count():
            self.table_place_holder_layout.takeAt(0).widget().deleteLater()
        self.table_place_holder_layout.addWidget(self.fileTable)

    def _open_file_dialog(self):
        directory = str(QtWidgets.QFileDialog.getExistingDirectory())
        if not directory:
            return
        fileList = []
        for f in listdir(directory):
            fileList.append(f)
        self.createFileTable(fileList)

    def setupUi(self, brows):
        brows.setObjectName("MyApp")
        brows.resize(900, 600)

        #### line 1
        self.toolButtonOpenDialog = QtWidgets.QToolButton(brows)
        self.toolButtonOpenDialog.setGeometry(QtCore.QRect(310, 10, 35, 19))
        self.toolButtonOpenDialog.setObjectName("toolButtonOpenDialog")
        self.toolButtonOpenDialog.clicked.connect(self._open_file_dialog)

        self.importPath = QtWidgets.QLineEdit(brows)
        self.importPath.setEnabled(False)
        self.importPath.setGeometry(QtCore.QRect(110, 10, 191, 20))
        self.importPath.setObjectName("importPath")

        self.label1 = QtWidgets.QLabel(brows)
        self.label1.setText('Folder to scan')
        self.label1.setGeometry(QtCore.QRect(10,10,90,20))
        self.label1.setObjectName("Label1")

        self.label11 = QtWidgets.QLabel(brows)
        self.label11.setText('Scan Depth')
        self.label11.setGeometry(QtCore.QRect(370,10,90,20))
        self.label11.setObjectName("Label11")

        self.folderDepth = QComboBox(brows)
        self.folderDepth.addItems(['1','2','3','>3'])
        self.folderDepth.setGeometry(QtCore.QRect(450,8,50,25))
        self.folderDepth.setObjectName("FolderDepth")

        #### line 2
        self.label2 = QtWidgets.QLabel(brows)
        self.label2.setText('Local mount')
        self.label2.setGeometry(QtCore.QRect(10,50,90,20))
        self.label2.setObjectName("Label2")

        self.localMount = QtWidgets.QLineEdit(brows)
        self.localMount.setEnabled(True)
        self.localMount.setGeometry(QtCore.QRect(100, 50, 90, 20))
        self.localMount.setObjectName("localMount")

        self.label3 = QtWidgets.QLabel(brows)
        self.label3.setText('Remote mount')
        self.label3.setGeometry(QtCore.QRect(230,50,90,20))
        self.label3.setObjectName("Label3")

        self.remoteMount = QtWidgets.QLineEdit(brows)
        self.remoteMount.setEnabled(True)
        self.remoteMount.setGeometry(QtCore.QRect(330, 50, 80, 20))
        self.remoteMount.setObjectName("remoteMount")

        #### line 3
        self.table_place_holder = QtWidgets.QWidget(brows)
        self.table_place_holder_layout = QVBoxLayout(self.table_place_holder)
        # self.scrollArea = QScrollArea(brows)
        # self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        # self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        # self.scrollArea.setWidgetResizable(True)
        # self.scrollArea.setGeometry(QtCore.QRect(10, 100, 880, 400))
        self.table_place_holder.setGeometry(QtCore.QRect(10, 100, 880, 400))

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

    def retranslateUi(self, brows):
        _translate = QtCore.QCoreApplication.translate
        brows.setWindowTitle(_translate("myApp", "MyApp"))
        self.toolButtonOpenDialog.setText(_translate("brows", "..."))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    brows = QtWidgets.QDialog()
    ui = Ui_brows()
    ui.setupUi(brows)
    brows.show()

    sys.exit(app.exec_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM