在 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的主要内容,如果未能解决你的问题,请参考以下文章