在 PyQt5 中使用浏览按钮添加适当的 FileDialog

Posted

技术标签:

【中文标题】在 PyQt5 中使用浏览按钮添加适当的 FileDialog【英文标题】:Add a proper FileDialog with Browse PushButton in PyQt5 【发布时间】:2020-03-05 15:06:48 【问题描述】:

为了进行一些计算,我编写了以下代码。我想添加一个像小图片一样的小部件,这样当我单击按钮时,我可以浏览 CSV 文件,并且该路径显示在 LineEdite 中。我需要在复选框下方的 Experiment Info QGroupBox 中有这个 Widger。但我不知道如何在这个组框中将所有这 3 个 Widget 添加到一行中。

import sys
from PyQt5.QtWidgets import QMainWindow, QAction, QMenu, QApplication
from PyQt5 import QtGui, QtCore
from PyQt5 import QtWidgets

class Application(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('File')
        run = menubar.addMenu('Run')
        exit = menubar.addMenu('Exit')

        impMenu = QMenu('Import', self)
        impAct = QAction('Import mail', self)
        impMenu.addAction(impAct)

        newAct = QAction('New', self)

        fileMenu.addAction(newAct)
        fileMenu.addMenu(impMenu)

        # ------------- Layout ---------------------------
        wid =QtWidgets.QWidget(self)
        self.setCentralWidget(wid)
        mainLayout = QtWidgets.QVBoxLayout()

        config_box = QtWidgets.QGroupBox("Configuration")
        info_box = QtWidgets.QGroupBox("Info Box")

        # ------------- Config Box Layut -----------------
        Config_Box_Layout = QtWidgets.QGridLayout()


        case_box = QtWidgets.QGroupBox("case")

        operations_box = QtWidgets.QGroupBox("Operations")

        Config_Box_Layout.addWidget(case_box, 0, 0)
        Config_Box_Layout.addWidget(operations_box, 0, 1)

        # ----------- Operations-----------------------
        operation_layout = QtWidgets.QVBoxLayout()

        op1 = QtWidgets.QCheckBox('Add')
        op2= QtWidgets.QCheckBox('Multiplication')

        operation_layout.addWidget(op1)
        operation_layout.addWidget(op2)
        # ----------- cases -----------------------
        case_layout = QtWidgets.QVBoxLayout()
        _1 = QtWidgets.QRadioButton("1")
        _2 = QtWidgets.QRadioButton("2")
        _3 = QtWidgets.QRadioButton("3")

        case_layout.addWidget(_1)
        case_layout.addWidget(_2)
        case_layout.addWidget(_3)


        # --------- Info Box LayOut -----------------------
        Info_box_layout = QtWidgets.QVBoxLayout()

        exp_input_grou = QtWidgets.QGroupBox('Experiment Info')
        exp_input_layout = QtWidgets.QVBoxLayout()

        version_input = QtWidgets.QComboBox()
        version_input.addItem("Algo Version")
        version_input.addItem("V53")
        version_input.addItem("V52")
        version_input.addItem("V4")
        version_input.addItem("V3")

        nested_folder = QtWidgets.QCheckBox('Nested Data Path')

        dialog_file = QtWidgets.QFileDialog()

        pushB_ = QtWidgets.QPushButton('Browse')

        exp_input_layout.addWidget(version_input)
        exp_input_layout.addWidget(nested_folder)
        exp_input_layout.addWidget(pushB_)
        exp_input_layout.addWidget(dialog_file)


        exp_report = QtWidgets.QTextEdit()
        progres_bar = QtWidgets.QProgressBar()


        Info_box_layout.addWidget(exp_input_grou)
        Info_box_layout.addWidget(exp_report)
        Info_box_layout.addWidget(progres_bar)

        gt_path = QtWidgets.QLineEdit()
        gt_path.setFixedWidth(100)
        # -------- Adding Layouts ------------------------
        mainLayout.addWidget(config_box)
        config_box.setLayout(Config_Box_Layout)
        case_box.setLayout(case_layout)
        operations_box.setLayout(operation_layout)
        info_box.setLayout(Info_box_layout)
        exp_input_grou.setLayout(exp_input_layout)

        mainLayout.addWidget(info_box)
        wid.setLayout(mainLayout)

        self.setGeometry(300, 300, 300, 700)
        self.setWindowTitle('Example')
        app_icon = QtGui.QIcon()
        app_icon.addFile('pic.jpg', QtCore.QSize(256, 256))
        self.setWindowIcon(app_icon)
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Application()
    sys.exit(app.exec_())

【问题讨论】:

我没有看到任何“浏览”按钮。另外,您为什么要尝试将 dialog 添加到布局中? 我刚刚添加了。我必须从路径目录中导入一些数据,因此我需要浏览它。 抱歉,您的请求仍然有点混乱。您刚刚添加的图像与您的代码有何关系?是否要为组合框下的按钮和浏览按钮添加行编辑?文件对话框是否用于选择路径?请尽量说得更清楚。 抱歉我的解释不好。我改变了上面的问题。是的,我需要在复选框下方的 Experiment Info Group Box 中使用同一行 Widget。 【参考方案1】:

如果要在垂直布局中水平添加小部件,则必须为其添加水平布局。这种技术通常被称为“嵌套布局”。

然后,您不必在 init 中创建文件对话框实例(否则它可能会与主窗口一起显示),并且您不必使用 QFileDialog() 构造函数来满足您的需要,因为 QDialog 已经提供了static methods,它可以让您轻松打开标准对话框并获取选定的文件或目录。


class Application(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        # ...

        # --------- Info Box LayOut -----------------------
        Info_box_layout = QtWidgets.QVBoxLayout()

        exp_input_grou = QtWidgets.QGroupBox('Experiment Info')
        exp_input_layout = QtWidgets.QVBoxLayout()

        version_input = QtWidgets.QComboBox()
        version_input.addItem("Algo Version")
        version_input.addItem("V53")
        version_input.addItem("V52")
        version_input.addItem("V4")
        version_input.addItem("V3")

        nested_folder = QtWidgets.QCheckBox('Nested Data Path')

        # This should not be here!!!
        # dialog_file = QtWidgets.QFileDialog()

        exp_input_layout.addWidget(version_input)
        exp_input_layout.addWidget(nested_folder)

        browse_layout = QtWidgets.QHBoxLayout()
        exp_input_layout.addLayout(browse_layout)

        self.gt_path = QtWidgets.QLineEdit()

        pushB_ = QtWidgets.QPushButton('Browse')
        pushB_.clicked.connect(self.browse_path)

        browse_layout.addWidget(self.gt_path)
        browse_layout.addWidget(pushB_)

        # ...

    def browse_path(self):
        path, filter = QtWidgets.QFileDialog.getOpenFileName(self, 'Select file', 
            '', 'CSV files (*.csv);;All files (*)')
        if path:
            self.gt_path.setText(path)

QGridLayout 也是一个有效的替代方案,但它并不适用于所有内容,因为它的大小始终取决于每行或每列包含的小部件的大小。你的情况很简单,所以可以毫无问题地使用:

    def initUI(self):
        # ...

        # --------- Info Box LayOut -----------------------
        Info_box_layout = QtWidgets.QVBoxLayout()

        exp_input_grou = QtWidgets.QGroupBox('Experiment Info')
        exp_input_layout = QtWidgets.QGridLayout()

        version_input = QtWidgets.QComboBox()
        version_input.addItem("Algo Version")
        version_input.addItem("V53")
        version_input.addItem("V52")
        version_input.addItem("V4")
        version_input.addItem("V3")

        nested_folder = QtWidgets.QCheckBox('Nested Data Path')

        self.gt_path = QtWidgets.QLineEdit()
        pushB_ = QtWidgets.QPushButton('Browse')
        pushB_.clicked.connect(self.browse_path)

        # add the combobox to the first row, first column, but set its column
        # span to 2, meaning that it occupies two "columns"
        exp_input_layout.addWidget(version_input, 0, 0, 1, 2)
        # the same for the checkbox, but on the second row
        exp_input_layout.addWidget(nested_folder, 1, 0, 1, 2)
        # add the line edit, third row, first column; if you don't specify the
        # span, the widget will only occupy one "cell" of the grid layout
        exp_input_layout.addWidget(self.gt_path, 2, 0)
        # and the button, same row, second column
        exp_input_layout.addWidget(pushB_, 2, 1)

        # ...

【讨论】:

【参考方案2】:

下面的代码会做你想做的事,你只需要在你的代码中实现它:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(387, 224)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")

        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit.setReadOnly(True)

        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setObjectName("label")

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.pushButton.clicked.connect(self.open_file)

        self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.gridLayout.addWidget(self.pushButton, 0, 2, 1, 1)

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 387, 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.label.setText(_translate("MainWindow", "Select output file"))
        self.pushButton.setText(_translate("MainWindow", "..."))

    def open_file(self):
        self.file_name = QtWidgets.QFileDialog.getOpenFileName(None, "Open", "", "CSV Files (*.csv)")
        if self.file_name[0] != '':
            self.lineEdit.setText(self.file_name[0])


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

输出:

【讨论】:

编辑 pyuic 生成的代码不是一个好习惯:这是一直不鼓励的事情,拿它来举例也不是一件好事。跨度> 好吧,事实是它看起来比传统的面向对象方法更复杂,但它帮助我生成了快速简单的 gui。我已经使用这种方法在 PyQt5 中学习了编程,所以切换对我来说有点困难:p 我理解你的观点,但是对于 PyQt 的工作流程,pyuic 文件应该永远被编辑,而只能用作导入的模块(参见using Designer)。最重要的原因是程序员在创建 gui 后经常需要多次编辑它,如果在部分逻辑已经完成的情况下完成,这可能会成为一个痛苦的过程。此外,在 SO 中,我们正在尝试尽可能地“打击”这种方法,因为它是一个非常常见的问题来源,否则可以避免。 感谢您向我解释为什么 pyuic 不是一个好习惯,我很感激。我想我必须学会把我的 guis 构建成一个普通的类。 使用 pyuic 文件还不错,您只需将这些文件保持原样,永远不要编辑它们,只需按照之前链接的文档中的说明导入它们。或者您可以使用PyQt5.uic 模块的loadUi('file.ui', self) 功能,这将允许您直接使用在Designer 中创建的ui 文件,从而可以完全避免pyuic 步骤。我通常使用这种方法,因为我相信它更容易并且更不容易出错(有时您编辑 ui 但忘记重建相关的 py 文件)。不客气,顺便说一句:-)

以上是关于在 PyQt5 中使用浏览按钮添加适当的 FileDialog的主要内容,如果未能解决你的问题,请参考以下文章

如何在每个 Qlabel PyQt5 中显示两个图像

在pyqt5中单击按钮之前触发浏览按钮方法[重复]

PyQt5 - 如何在“按钮”小部件中添加图像? | (不是图标)

PyQt5 在每个选项卡旁边添加添加和删除小部件按钮

PyQt5 设计师。使按钮事件起作用

pyqt5坚持进口