有没有办法 select PyQt 中的目录并显示其中的文件?
Is there a way to select a directory in PyQt and show the files within?
我希望用户 select 一个包含用于分析的文件的目录。我只想让用户 select 一个目录,而不是其中的文件。我正在使用:
QFileDialog.getExistingDirectory
这仅显示目录(未列出任何文件,即使它们存在)。这可能是正确的行为,但查看其中的文件可能对用户有帮助。理想情况下,其中的文件可见但 selectable
我可以用
QFileDialog.getOpenFileName
和select他们选择的文件的父目录。这种方法并不完美,因为它们 select 是文件而不是文件目录。
有什么想法或建议吗?
QFileDialog 静态方法不允许这种方法,最重要的是,Qt 总是尝试使用底层操作系统提供的 native 文件对话框,其中有没有可用的 access/control.
解决方案是使用自定义的 QFileDialog 实例,确保它不使用本机对话框,并设置相关选项。
由于您还想将任何 non-directory 项显示为已禁用,我们还需要将自定义 代理模型 应用到对话框。请注意,这一步并不是真正需要的,因为使用 setFileMode(dialog.Directory)
已经确保所选项目实际上是一个目录,否则不允许打开当前所选项目。
from PyQt5 import QtCore, QtWidgets
class DirProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, fsModel):
super().__init__()
self.fsModel = fsModel
self.setSourceModel(fsModel)
def lessThan(self, left, right):
# QFileSystemModel populates its entries with some delay, which results
# in the proxy model not able to do the proper sorting (usually showing
# directories first) since the proxy does not always "catch up" with the
# source sorting; so, this has to be manually overridden by
# force-checking the entry type of the index.
leftIsDir = self.fsModel.fileInfo(left).isDir()
if leftIsDir != self.fsModel.fileInfo(right).isDir():
return leftIsDir
return super().lessThan(left, right)
def flags(self, index):
flags = super().flags(index)
# map the index to the source and check if it's a directory or not
if not self.fsModel.fileInfo(self.mapToSource(index)).isDir():
# if it is a directory, remove the enabled flag
flags &= ~QtCore.Qt.ItemIsEnabled
return flags
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QHBoxLayout(self)
self.lineEdit = QtWidgets.QLineEdit()
layout.addWidget(self.lineEdit)
self.selectBtn = QtWidgets.QToolButton(text='...')
layout.addWidget(self.selectBtn)
self.selectBtn.clicked.connect(self.selectDirectory)
def selectDirectory(self):
dialog = QtWidgets.QFileDialog(self, windowTitle='Select directory')
dialog.setDirectory(self.lineEdit.text() or __file__)
dialog.setFileMode(dialog.Directory)
dialog.setOptions(dialog.DontUseNativeDialog)
# find the underlying model and set our own proxy model for it
for view in self.findChildren(QtWidgets.QAbstractItemView):
if isinstance(view.model(), QtWidgets.QFileSystemModel):
proxyModel = DirProxyModel(view.model())
dialog.setProxyModel(proxyModel)
break
# try to hide the file filter combo
fileTypeCombo = dialog.findChild(QtWidgets.QComboBox, 'fileTypeCombo')
if fileTypeCombo:
fileTypeCombo.setVisible(False)
dialog.setLabelText(dialog.FileType, '')
if dialog.exec_():
self.lineEdit.setText(dialog.selectedFiles()[0])
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
我希望用户 select 一个包含用于分析的文件的目录。我只想让用户 select 一个目录,而不是其中的文件。我正在使用:
QFileDialog.getExistingDirectory
这仅显示目录(未列出任何文件,即使它们存在)。这可能是正确的行为,但查看其中的文件可能对用户有帮助。理想情况下,其中的文件可见但 selectable
我可以用
QFileDialog.getOpenFileName
和select他们选择的文件的父目录。这种方法并不完美,因为它们 select 是文件而不是文件目录。
有什么想法或建议吗?
QFileDialog 静态方法不允许这种方法,最重要的是,Qt 总是尝试使用底层操作系统提供的 native 文件对话框,其中有没有可用的 access/control.
解决方案是使用自定义的 QFileDialog 实例,确保它不使用本机对话框,并设置相关选项。
由于您还想将任何 non-directory 项显示为已禁用,我们还需要将自定义 代理模型 应用到对话框。请注意,这一步并不是真正需要的,因为使用 setFileMode(dialog.Directory)
已经确保所选项目实际上是一个目录,否则不允许打开当前所选项目。
from PyQt5 import QtCore, QtWidgets
class DirProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, fsModel):
super().__init__()
self.fsModel = fsModel
self.setSourceModel(fsModel)
def lessThan(self, left, right):
# QFileSystemModel populates its entries with some delay, which results
# in the proxy model not able to do the proper sorting (usually showing
# directories first) since the proxy does not always "catch up" with the
# source sorting; so, this has to be manually overridden by
# force-checking the entry type of the index.
leftIsDir = self.fsModel.fileInfo(left).isDir()
if leftIsDir != self.fsModel.fileInfo(right).isDir():
return leftIsDir
return super().lessThan(left, right)
def flags(self, index):
flags = super().flags(index)
# map the index to the source and check if it's a directory or not
if not self.fsModel.fileInfo(self.mapToSource(index)).isDir():
# if it is a directory, remove the enabled flag
flags &= ~QtCore.Qt.ItemIsEnabled
return flags
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QHBoxLayout(self)
self.lineEdit = QtWidgets.QLineEdit()
layout.addWidget(self.lineEdit)
self.selectBtn = QtWidgets.QToolButton(text='...')
layout.addWidget(self.selectBtn)
self.selectBtn.clicked.connect(self.selectDirectory)
def selectDirectory(self):
dialog = QtWidgets.QFileDialog(self, windowTitle='Select directory')
dialog.setDirectory(self.lineEdit.text() or __file__)
dialog.setFileMode(dialog.Directory)
dialog.setOptions(dialog.DontUseNativeDialog)
# find the underlying model and set our own proxy model for it
for view in self.findChildren(QtWidgets.QAbstractItemView):
if isinstance(view.model(), QtWidgets.QFileSystemModel):
proxyModel = DirProxyModel(view.model())
dialog.setProxyModel(proxyModel)
break
# try to hide the file filter combo
fileTypeCombo = dialog.findChild(QtWidgets.QComboBox, 'fileTypeCombo')
if fileTypeCombo:
fileTypeCombo.setVisible(False)
dialog.setLabelText(dialog.FileType, '')
if dialog.exec_():
self.lineEdit.setText(dialog.selectedFiles()[0])
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())