我想要一个简单的小部件,其中包含一个 QLineEdit、一个 QPushButton 和一个用于输入目录和文件名的第二个 QLineEdit。我首先在同一个 QWidget 中定义所有内容,名为
CentralWidget
,我想将其添加为 QMainWindow
的中央小部件。但是将所有内容放在同一个对象中会快速影响代码的可读性,因此这一次,我想将我的 GUI 分成多个更简单的小部件,然后将它们排列在一起。
但是当我这样做时,
QFormLayout
中的对齐方式就会丢失。
MWE:首先,版本“工作”,但所有内容都在同一个对象中。
from PyQt5.QtCore import QRegExp
from PyQt5.QtWidgets import (
QApplication,
QLineEdit,
QFormLayout,
QWidget,
QPushButton,
QGridLayout,
QStyle,
)
from PyQt5.QtGui import QRegExpValidator
class CentralWidget(QWidget):
def __init__(self):
super().__init__()
self.setObjectName("central_widget")
# create central widget layout
layout = QFormLayout()
layout_dir = QGridLayout()
line = QLineEdit(objectName="QLineEdit_dir")
button = QPushButton("", objectName="QPushButton_dir")
button.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
layout_dir.addWidget(line, 0, 0, 1, 5)
layout_dir.addWidget(button, 0, 6, 1, 1)
layout.addRow("Directory:", layout_dir)
validator = QRegExpValidator(QRegExp(r"^[a-zA-Z0-9]{1,8}$"))
line = QLineEdit(maxLength=8, objectName="QLineEdit_fname")
line.setValidator(validator)
layout.addRow("File name:", line)
self.setLayout(layout)
app = QApplication([])
window = CentralWidget()
window.show()
app.exec()
现在目录部分拆分在第二个小部件中的版本:
from pathlib import Path
from PyQt5.QtCore import QRegExp
from PyQt5.QtWidgets import (
QApplication,
QLineEdit,
QFormLayout,
QWidget,
QPushButton,
QGridLayout,
QStyle,
QFileDialog,
)
from PyQt5.QtGui import QRegExpValidator
class CentralWidget(QWidget):
def __init__(self):
super().__init__()
self.setObjectName("central_widget")
# create central widget layout
layout = QFormLayout()
layout.addRow("Directory:", DirectoryDialog())
validator = QRegExpValidator(QRegExp(r"^[a-zA-Z0-9]{1,8}$"))
line = QLineEdit(maxLength=8, objectName="QLineEdit_fname")
line.setValidator(validator)
layout.addRow("File name:", line)
self.setLayout(layout)
class DirectoryDialog(QWidget):
def __init__(self):
super().__init__()
self.line = QLineEdit()
layout = QGridLayout()
self.button = QPushButton("")
self.button.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
self.button.clicked.connect(self.browse_path)
layout.addWidget(self.line, 0, 0, 1, 5)
layout.addWidget(self.button, 0, 6, 1, 1)
self.setLayout(layout)
def browse_path(self):
path = QFileDialog.getExistingDirectory(
self, "Select directory", str(Path.home()), QFileDialog.ShowDirsOnly
)
if len(path) != 0:
self.line.setText(path)
app = QApplication([])
window = CentralWidget()
window.show()
app.exec()
那么,我该如何修复对齐,更重要的是,为什么它会搞乱对齐?
Qt 布局在创建时始终具有默认的“未定义”边距。
当设置为小部件的管理器时,这些边距将按照当前的 QStyle 进行设置(所有边大约 10 像素,确切的值取决于操作系统或自定义样式)。
当直接作为 nested 布局添加到父布局时,这些边距保持未定义状态并默认为 0。
在第二种情况下您看到的是内部小部件的further边距,如果您决定直接使用布局而不是容器小部件(由于上述原因),则该边距不存在。
请注意,虽然您可以为此目的使用布局子类,但通常不鼓励这样做,并且在任何情况下都最好使用实际的小部件。
对于此类自定义组件,通常最好显式将布局边距设置为 0,以便它们可以作为“唯一小部件”与父布局正确对齐。
请注意,如果您只需要一行对象,则使用网格布局是没有意义的,因为该布局的目的是沿行和列显示小部件。
为未使用的“单元格”设置跨度也是毫无意义的:网格布局的行和列并不代表布局上实际使用的“空间”,而只是代表它们的逻辑索引;如果这些行和列实际上并未被在不同单元格中具有起始点和跨度的其他小部件占用,则使用 5 的列跨度并在第六列添加小部件与将它们添加到第 0 列和第 1 列基本上相同。如果您需要确保某个小部件比其他小部件占用更多空间,则必须正确使用
拉伸因子,或最终为该小部件设置Expanding
大小策略。这是代码的改进版本,它还提供了更好的接口来访问或设置路径,包括更改时的专用信号。
class CentralWidget(QWidget):
def __init__(self):
super().__init__()
self.setObjectName("central_widget")
# create central widget layout
layout = QFormLayout(self)
layout.addRow("Directory:", DirectoryDialog())
validator = QRegExpValidator(QRegExp(r"^[a-zA-Z0-9]{1,8}$"))
line = QLineEdit(maxLength=8, objectName="QLineEdit_fname")
line.setValidator(validator)
layout.addRow("File name:", line)
class DirectoryDialog(QWidget):
pathChanged = pyqtSignal(str)
def __init__(self):
super().__init__()
self.line = QLineEdit()
self.button = QPushButton()
self.button.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
self.button.clicked.connect(self.browse_path)
layout = QHBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.line, stretch=1)
layout.addWidget(self.button)
self.line.textChanged.connect(self.pathChanged)
def path(self):
return self.line.text()
def setPath(self, path):
self.line.setText(path)
def browse_path(self):
path = QFileDialog.getExistingDirectory(
self, "Select directory", str(Path.home()), QFileDialog.ShowDirsOnly
)
if path:
self.line.setText(path)