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