Выбор файла или папки QTWidgets

Как я могу поставить на кнопку действие выбора либо файла, либо папки. Есть варианты: QtWidgets.QFileDialog.getExistingDirectory и QtWidgets.QFileDialog.getOpenFileName() А как сделать, чтобы можно было выбрать или папку, или файл? И еще хотелось бы узнать, можно ли при использовании QtWidgets.QFileDialog.getOpenFileName() сделать, чтобы показывало только файлы определенного расширения front.py

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

# Form implementation generated from reading ui file 'front.ui'
#
# Created by: PyQt5 UI code generator 5.14.2
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(300, 400)
        MainWindow.setMinimumSize(QtCore.QSize(300, 400))
        MainWindow.setMaximumSize(QtCore.QSize(300, 400))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.btnBrowse = QtWidgets.QPushButton(self.centralwidget)
        self.btnBrowse.setGeometry(QtCore.QRect(0, 0, 131, 23))
        self.btnBrowse.setObjectName("btnBrowse")
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(0, 20, 131, 151))
        self.listWidget.setObjectName("listWidget")
        self.btnArch = QtWidgets.QPushButton(self.centralwidget)
        self.btnArch.setGeometry(QtCore.QRect(110, 180, 81, 23))
        self.btnArch.setObjectName("btnArch")
        self.extractButton = QtWidgets.QPushButton(self.centralwidget)
        self.extractButton.setGeometry(QtCore.QRect(180, 0, 81, 23))
        self.extractButton.setObjectName("extractButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "MainWindow"))
        self.btnBrowse.setText(_translate("MainWindow", "Выбрать папку"))
        self.btnArch.setText(_translate("MainWindow", "Архивировать"))
        self.extractButton.setText(_translate("MainWindow", "Извлечь"))

main.py

import front
import sys, os, zipfile, traceback
from PyQt5 import QtWidgets


class ExampleApp(QtWidgets.QMainWindow, front.Ui_MainWindow):
    directory = ''

    def __init__(self):
        # Это здесь нужно для доступа к переменным, методам
        # и т.д. в файле design.py
        super().__init__()
        self.setupUi(self)  # Это нужно для инициализации дизайна
        self.btnBrowse.clicked.connect(self.browse_folder)
        self.btnArch.clicked.connect(self.arch_folder)
        self.extractButton.clicked.connect(self.extract)

    def browse_folder(self):
        self.listWidget.clear()  # На случай, если в списке уже есть элементы
        self.directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Выберите папку")


        if self.directory:  # не продолжать выполнение, если пользователь не выбрал директорию
            for file_name in os.listdir(self.directory):  # для каждого файла в директории
                self.listWidget.addItem(file_name)  # добавить файл в listWidget
        return self.directory

    def arch_folder(self):
        archive = zipfile.ZipFile('test.zip', 'w')
        dirfiles = os.listdir(self.directory)
        for file in dirfiles:
            try:
                archive.write(self.directory+"/"+file, compress_type=zipfile.ZIP_DEFLATED)
            except:
                print(traceback.format_exc())
        archive.close()

    def extract(self):
        self.archive = QtWidgets.QFileDialog.getOpenFileName(self, "Выберите архив")
        zip_arch = zipfile.ZipFile(self.archive[0], 'r')
        for files in zip_arch.namelist():
            zip_arch.extract(files)
        zip_arch.close()



def main():
    app = QtWidgets.QApplication(sys.argv)  # Новый экземпляр QApplication
    window = ExampleApp()  # Создаём объект класса ExampleApp
    window.show()  # Показываем окно
    app.exec_()  # и запускаем приложение


if __name__ == '__main__':  # Если запускаем файл напрямую, а не импортируем
    main()  # то запускаем функцию main()


Ответы (3 шт):

Автор решения: S. Nick

Попробуйте так:

import sys 
import os 
import zipfile
import traceback
from PyQt5 import QtCore, QtGui, QtWidgets

#import front
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(300, 400)
        MainWindow.setMinimumSize(QtCore.QSize(500, 400))
#        MainWindow.setMaximumSize(QtCore.QSize(300, 400))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.btnBrowse = QtWidgets.QPushButton(self.centralwidget)
        self.btnBrowse.setGeometry(QtCore.QRect(0, 0, 131, 23))
        self.btnBrowse.setObjectName("btnBrowse")
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(0, 20, 331, 151))
        self.listWidget.setObjectName("listWidget")
        self.btnArch = QtWidgets.QPushButton(self.centralwidget)
        self.btnArch.setGeometry(QtCore.QRect(110, 180, 81, 23))
        self.btnArch.setObjectName("btnArch")
        self.extractButton = QtWidgets.QPushButton(self.centralwidget)
        self.extractButton.setGeometry(QtCore.QRect(380, 0, 81, 23))
        self.extractButton.setObjectName("extractButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "MainWindow"))
        self.btnBrowse.setText(_translate("MainWindow", "Выбрать ..."))
        self.btnArch.setText(_translate("MainWindow", "Архивировать"))
        self.extractButton.setText(_translate("MainWindow", "Извлечь"))


class Dialog(QtWidgets.QDialog):                                                   # +++
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)

        label = QtWidgets.QLabel("Сделайте выбор:")
        self.rbDir = QtWidgets.QRadioButton('directory', self)
        self.rbPath = QtWidgets.QRadioButton('pathFile', self)
        btnOk = QtWidgets.QPushButton("Ok", clicked=self.hide)

        layout = QtWidgets.QGridLayout(self)
        layout.addWidget(label, 0, 0, 1, 3)        
        layout.addWidget(self.rbDir, 1, 1, 1, 1) 
        layout.addWidget(self.rbPath, 2, 1, 1, 1) 
        layout.addWidget(btnOk, 3, 2, 1, 1) 


#class ExampleApp(QtWidgets.QMainWindow, front.Ui_MainWindow):
class ExampleApp(QtWidgets.QMainWindow, Ui_MainWindow):
    directory = ''
    pathFile = ''                                                                  # +++

    def __init__(self):
        # Это здесь нужно для доступа к переменным, методам
        # и т.д. в файле design.py
        super().__init__()
        self.setupUi(self)  # Это нужно для инициализации дизайна
        self.btnBrowse.clicked.connect(self.browse_folder)
        self.btnArch.clicked.connect(self.arch_folder)
        self.extractButton.clicked.connect(self.extract)

        self.dialog = Dialog()                                                     # +++

# ++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    def browse_folder(self):
        self.directory, self.pathFile = '', ''
        self.dialog.exec_()
        if self.dialog.rbDir.isChecked():
            print(f'dialog')
            self.listWidget.clear()  # На случай, если в списке уже есть элементы
            self.directory = QtWidgets.QFileDialog.getExistingDirectory(self, "Выберите папку")


            if self.directory:  # не продолжать выполнение, если пользователь не выбрал директорию
                for file_name in os.listdir(self.directory):  # для каждого файла в директории
                    self.listWidget.addItem(file_name)        # добавить файл в listWidget
        elif self.dialog.rbPath.isChecked():
            print(f'rbPath')
            self.pathFile, _ = QtWidgets.QFileDialog.getOpenFileName(
                                                self, 
                                                'Open File', './', 
                                                'Files (*.py *.txt *.log)')
            if self.pathFile:
                self.listWidget.addItem(self.pathFile)        # добавить файл в listWidget

        else:
            print(f'no')
            msg = QtWidgets.QMessageBox.information(self, 'Message', 'Вы ничего не выбрали.')

# ++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^            


    def arch_folder(self):
        print(f'def arch_folder(self): directory = `{self.directory}`')
        print(f'def arch_folder(self): pathFile = `{self.pathFile}`')
        '''
        archive = zipfile.ZipFile('test.zip', 'w')
        dirfiles = os.listdir(self.directory)
        for file in dirfiles:
            try:
                archive.write(self.directory+"/"+file, compress_type=zipfile.ZIP_DEFLATED)
            except:
                print(traceback.format_exc())
        archive.close()
        '''

    def extract(self):
        print(f'def extract(self): directory = `{self.directory}`')
        print(f'def extract(self): pathFile = `{self.pathFile}`')

        '''
        self.archive = QtWidgets.QFileDialog.getOpenFileName(self, "Выберите архив")
        zip_arch = zipfile.ZipFile(self.archive[0], 'r')
        for files in zip_arch.namelist():
            zip_arch.extract(files)
        zip_arch.close()
        '''

def main():
    app = QtWidgets.QApplication(sys.argv)  # Новый экземпляр QApplication
    window = ExampleApp()  # Создаём объект класса ExampleApp
    window.show()  # Показываем окно
    app.exec_()  # и запускаем приложение

if __name__ == '__main__':  # Если запускаем файл напрямую, а не импортируем
    main()  # то запускаем функцию main()

введите сюда описание изображения

→ Ссылка
Автор решения: S. Nick

Чтобы я нажимал на условное "выбрать" открывалось сразу окно выбора файла, но в нем я мог выбрать, как файл, так и папку. Это возможно ?

QFileDialog не позволяет этого изначально. Единственное решение - создать свой собственный getOpenFilesAndDirs.

Обратите внимание, что для достижения этого вы не можете использовать собственные диалоги вашей ОС, так как Qt почти не контролирует их,
это причина флага dialog.DontUseNativeDialog, который является обязательным.

Следующий код работает так же, как и статические методы, и возвращает выбранные элементы (или ни одного, если диалог отменен).

import sys 
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(300, 400)
        MainWindow.setMinimumSize(QtCore.QSize(500, 400))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.btnBrowse = QtWidgets.QPushButton(self.centralwidget)
        self.btnBrowse.setGeometry(QtCore.QRect(0, 0, 131, 23))
        self.btnBrowse.setObjectName("btnBrowse")
        self.listWidget = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget.setGeometry(QtCore.QRect(0, 20, 331, 151))
        self.listWidget.setObjectName("listWidget")
        self.btnArch = QtWidgets.QPushButton(self.centralwidget)
        self.btnArch.setGeometry(QtCore.QRect(110, 180, 81, 23))
        self.btnArch.setObjectName("btnArch")
        self.extractButton = QtWidgets.QPushButton(self.centralwidget)
        self.extractButton.setGeometry(QtCore.QRect(380, 0, 81, 23))
        self.extractButton.setObjectName("extractButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "MainWindow"))
        self.btnBrowse.setText(_translate("MainWindow", "Выбрать ..."))
        self.btnArch.setText(_translate("MainWindow", "Архивировать"))
        self.extractButton.setText(_translate("MainWindow", "Извлечь"))


def getOpenFilesAndDirs(parent=None, caption='', directory='', 
                        filter='', initialFilter='', options=None):
    def updateText():
        # обновить содержимое виджета редактирования строки выбранными файлами
        selected = []
        for index in view.selectionModel().selectedRows():
            selected.append('"{}"'.format(index.data()))
        lineEdit.setText(' '.join(selected))

    dialog = QtWidgets.QFileDialog(parent, windowTitle=caption)
    dialog.setFileMode(dialog.ExistingFiles)
    if options:
        dialog.setOptions(options)
    dialog.setOption(dialog.DontUseNativeDialog, True)            # !!!
    if directory:
        dialog.setDirectory(directory)
    if filter:
        dialog.setNameFilter(filter)
        if initialFilter:
            dialog.selectNameFilter(initialFilter)

    # по умолчанию, если каталог открыт в режиме списка файлов, 
    # QFileDialog.accept() показывает содержимое этого каталога, 
    # но нам нужно иметь возможность "открывать" и каталоги, как мы можем делать с файлами, 
    # поэтому мы просто переопределяем `accept()` с реализацией QDialog по умолчанию, 
    # которая просто вернет `dialog.selectedFiles()`

    dialog.accept = lambda: QtWidgets.QDialog.accept(dialog)

    # в неродном диалоге есть много представлений элементов, 
    # но те, которые отображают фактическое содержимое, создаются внутри QStackedWidget; 
    # это QTreeView и QListView, и дерево используется только тогда, 
    # когда viewMode установлен на QFileDialog.Details, что не в этом случае.
    
    stackedWidget = dialog.findChild(QtWidgets.QStackedWidget)
    view = stackedWidget.findChild(QtWidgets.QListView)
    view.selectionModel().selectionChanged.connect(updateText)

    lineEdit = dialog.findChild(QtWidgets.QLineEdit)
    # очищаем содержимое строки редактирования всякий раз, когда изменяется текущий каталог
    dialog.directoryEntered.connect(lambda: lineEdit.setText(''))

    dialog.exec_()
    return dialog.selectedFiles()
    

class ExampleApp(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)  
        
        self.btnBrowse.clicked.connect(self.browse_folder)
        self.btnArch.clicked.connect(self.arch_folder)
        self.extractButton.clicked.connect(self.extract)
       
    def browse_folder(self):
        selected_files = getOpenFilesAndDirs()
        print(selected_files) 
        if selected_files:
            self.listWidget.addItems(selected_files)
        else:
            msg = QtWidgets.QMessageBox.information(self, 'Message', 'Вы ничего не выбрали.')

    def arch_folder(self):
        print(f'Выполнить Архивирование: ...')

    def extract(self):
        print(f'Что-то Извлечь:  ...')


if __name__ == '__main__': 
    app = QtWidgets.QApplication(sys.argv)  
    window = ExampleApp()  
    window.show()  
    sys.exit(app.exec_())

введите сюда описание изображения

введите сюда описание изображения

→ Ссылка
Автор решения: Дудич Михаил

Отвечу только на часть вопроса. У метода QtWidgets.QFileDialog.getOpenFileName() есть именованный параметр filter, который отвечает за то, чтобы пользователю показывали файлы только нужных расширений. Например, в этом диалоге:

QFileDialog.getOpenFileName(self, 'Выбрать картинку', '', filter='Картинка 
(*.jpg);;Картинка (*.png);;Все файлы (*)')[0]

Пользователь сможет увидеть:

  1. Или только файлы с расширением .jpg
  2. Или только файлы с расширением .png
  3. Или файлы всех расширений

Выбрать любой из этих вариантов пользователь сможет в выпадающем списке, расположенном в правом нижнем углу (По крайней мере в ОС Windows)

выпадающий список

Если нужно, чтобы сразу были видны файлы не одного, а нескольких расширений, то при описании фильтра необходимо в скобках через пробел указывать несколько расширений в следующем формате: (*.(расширение))

Пример:

QFileDialog.getOpenFileName(self, 'Выбрать картинку', '', filter='Картинка 
(*.jpg *.png *.jpeg))[0]
→ Ссылка