PyQt6 如何动态调整 QGroupBox 的大小以适应内容?
PyQt6 how to dynamically resize QGroupBox to fit content?
我正在使用 Python 3.9.5 和 PyQt6。
根据我之前的问题,我想问一下如何动态调整 QGroupBox 的大小以适应其内容。
我有一个QScrollArea,它的layout是一个QVBoxLayout,QVBoxLayout会加一堆QGroupBox
QGroupBox本身有一个QVBoxLayout,QVBoxLayout里面是一堆QVBoxLayout,QGroupBox的QVBoxLayout里面的QVBoxLayout里面是内容
每个最低级别的 QVBoxLayout 都有两个小部件和一个 QHBoxLayout。
从上到下,第一个小部件是 QLabel,固定大小为 100x20,第二个小部件是 QTextEdit,它会根据内容自动调整大小。 QHBoxLayout 内部是一个拉伸和固定大小的按钮,大小为 60x20,按钮可以隐藏或显示。
小部件和布局的层次结构是这样的:
QVBoxLayout — level 0
QScrollArea — level 1
QVBoxLayout0 — level 2
QGroupBox — level 3 (checkable)
QVBoxLayout1 — level 4
QLabel — level 5 (fixed)
QTextEdit — level 5 (auto-resizing)
QHBoxLayout — level 5
Stretch — level 6
QPushButton — level 6 (fixed)
我希望 QGroupBoxs 自动调整大小以适应它们的内容。
宽度由布局决定,我希望 QGroupBoxs 根据其内容具有固定的、最小的、最佳的高度。
基本上,高度应该是其所有 VISIBLE 小部件的高度的总和,加上上边距和下边距,以及小部件之间的边距,也许还有复选框的高度。
我想为 QGroupBoxs 设置固定高度,因为如果我不这样做,布局将拉伸 QGroupBoxs 并将其定位在中间,而不是顶部最小高度,这不是我想要的通缉
我将 post 下面的示例代码:
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
font = QFont('Noto Serif', 9)
def makebutton(text):
button = QPushButton()
button.setFont(font)
button.setFixedSize(60, 20)
button.setText(text)
return button
class Editor(QTextEdit):
doubleClicked = pyqtSignal(QTextEdit)
def __init__(self):
super().__init__()
self.setReadOnly(True)
self.setFont(font)
self.setFixedHeight(20)
self.setAttribute(Qt.WidgetAttribute.WA_DontShowOnScreen)
self.show()
self.textChanged.connect(self.autoResize)
def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
self.doubleClicked.emit(self)
def autoResize(self):
self.document().setTextWidth(self.viewport().width())
margins = self.contentsMargins()
height = int(self.document().size().height() + margins.top() + margins.bottom())
self.setFixedHeight(height)
def resizeEvent(self, event):
self.autoResize()
class textcell(QVBoxLayout):
def __init__(self, text):
super().__init__()
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.label = QLabel(text)
self.label.setFixedSize(80, 20)
self.apply = makebutton('Apply')
self.apply.hide()
self.editor = Editor()
self.editor.doubleClicked.connect(self.on_DoubleClick)
self.hbox = QHBoxLayout()
self.hbox.addStretch()
self.hbox.addWidget(self.apply)
self.addWidget(self.label)
self.addWidget(self.editor)
self.addLayout(self.hbox)
self.apply.clicked.connect(self.on_ApplyClick)
def on_DoubleClick(self):
self.editor.setReadOnly(False)
self.apply.show()
def on_ApplyClick(self):
self.editor.setReadOnly(True)
self.apply.hide()
class songpage(QGroupBox):
def __init__(self, texts):
super().__init__()
self.init(texts)
self.setCheckable(True)
self.setChecked(False)
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
def init(self, texts):
self.vbox = QVBoxLayout()
self.vbox.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.vbox.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
artist = textcell('Artist')
artist.editor.setText(texts[0])
album = textcell('Album')
album.editor.setText(texts[2])
title = textcell('Title')
title.editor.setText(texts[1])
self.height = 120 + artist.editor.height() + album.editor.height() + title.editor.height()
self.vbox.addLayout(artist)
self.vbox.addLayout(album)
self.vbox.addLayout(title)
print(self.children())
print(self.vbox.children())
print(self.vbox.count())
print(artist.count())
print(artist.children())
print(artist.contentsMargins().top())
print(artist.contentsMargins().bottom())
print(self.vbox.contentsMargins().top())
print(self.vbox.contentsMargins().bottom())
print(self.contentsMargins().top())
print(self.contentsMargins().bottom())
print(self.childrenRect().height())
print(self.contentsRect().height())
print(artist.apply.isHidden())
print(artist.editor.isHidden())
print(artist.label.isHidden())
print(artist.label.isVisible())
print(self.sizeHint().height())
self.setLayout(self.vbox)
self.setFixedHeight(self.height)
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.resize(405, 720)
frame = self.frameGeometry()
center = self.screen().availableGeometry().center()
frame.moveCenter(center)
self.move(frame.topLeft())
self.centralwidget = QWidget(self)
self.vbox = QVBoxLayout(self.centralwidget)
self.scrollArea = QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.add = makebutton('Add')
self.vbox.addWidget(self.add)
self.add.clicked.connect(lambda: adder.addItem())
self.vbox.addWidget(self.scrollArea)
self.setCentralWidget(self.centralwidget)
class Adder:
def __init__(self):
self.i = 0
def addItem(self):
window.verticalLayout.addWidget(songpage(items[self.i]))
if self.i < len(items) - 1:
self.i += 1
adder = Adder()
items = [('Herbert von Karajan',
"Orphée aux enfers, 'Orpheus in the Underworld'\u2014Overture",
'100 Best Karajan'),
('Herbert von Karajan', 'Radetzky March Op. 228', '100 Best Karajan'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014I. Adagio molto \u2014 Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014II. Andante cantabile con moto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014III. Menuetto (Allegro molto e vivace)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014IV. Finale (Adagio \u2014 Allegro molto e vivace)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014I. Adagio molto \u2014 Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014II. Larghetto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014III. Scherzo (Allegro)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014IV. Allegro molto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014I. Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014II. Marcia funebre (Adagio assai)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014III. Scherzo (Allegro vivace)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014IV. Finale (Allegro molto)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014I. Adagio \u2014 Allegro vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014II. Adagio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014III. Allegro vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014IV. Allegro ma non troppo',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014I. Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014II. Andante con moto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014III. Allegro',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014IV. Allegro',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014I. Poco sostenuto \u2014 Vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014II. Allegretto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014III. Presto \u2014 Assai meno presto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014IV. Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014I. Allegro vivace e con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014II. Allegretto scherzando',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014III. Tempo di menuetto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014IV. Allegro vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014I. Allegro ma non troppo, un poco maestoso',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014II. Molto vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014III. Adagio molto e cantabile',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014IV. Presto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014V. Presto\u2014 O Freunde, nicht diese T\u2014ne!\u2014Allegro assai',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014I. Erwachen heiterer Empfindungen bei der Ankunft auf dem Lande\u2014 Allegro ma non troppo',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014II. Szene am Bach\u2014 (Andante molto mosso)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014III. Lustiges Zusammensein der Landleute (Allegro)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014IV. Gewitter, Sturm (Allegro)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014V. Hirtengesang. Frohe und dankbare Gefühle nach dem Sturm\u2014 Allegretto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Cancan (Orpheus in the Underworld)',
'Best of the Millennium\u2014 Top 40 Classical Hits'),
('Herbert von Karajan',
'Hungarian Dance No. 5 in G Minor, WoO 1 No. 5',
'Complete Recordings on Deutsche Grammophon')]
app = QApplication([])
window = Window()
window.show()
app.exec()
您可以看到 QGroupBoxs 没有处于最佳高度,如果双击 QTextEdits 并出现 Apply 按钮,则当 QTextBoxs 调整大小时,groupboxs 的顶部会变大,复选框处于尴尬的位置减少线条,底部边框将变得不可见。
并且布局的边距始终为零,QGroupBox 的打印边距与观察到的边距不匹配,QGroupBoxs 的 childrenRects 高度始终为零,QGroupBoxs 的 contentsRects 高度始终为478,这没有任何意义,并且 .isHidden()
将始终为 True,.isVisible()
将始终为 False,如果我仅将小部件添加到布局而不调用其 .show()
。
但是当我将它们添加到布局时,它们总是自动可见,除非我明确地设置它们 .hide()
。并且调用 .show()
将使已经可见的小部件在屏幕中央闪烁,除非设置 Qt.WidgetAttribute.WA_DontShowOnScreen
。
那么我怎样才能真正动态地计算出最佳的最小高度呢?
而且我不能使用 .addStretch()
因为我需要 QGroupBox 之间的最小最佳间距并且我需要使用索引使内容可删除,.addStretch()
会破坏索引顺序,因为删除项目时不会删除拉伸。
我终于搞定了,问题是我刚刚创建了对象,并没有把它们添加到主window,它们都有默认的大小和值,当它们被添加时,值会改变添加到 window.
所以我只需要计算它们的大小并在将它们添加到 window 之后调整它们的大小。
据我观察,QGroupBoxs中的QVBoxLayouts总是有(9, 9, 9, 9)的边距,而所有widgets实际所在的布局都没有边距,而QGroupBoxs本身的边距为(3, 20, 3, 3) 在我的主脚本中,所以我可以明确设置这些边距并使用这些值。
我是这样做的:
class Label(QLabel):
def __init__(self, text):
super().__init__()
self.setFont(font)
self.fontRuler = QFontMetrics(font)
self.setText(text)
self.Height = self.fontRuler.size(0, text).height()
self.Width = self.fontRuler.size(0, text).width()
self.setFixedSize(self.Width, self.Height)
class Editor(QTextEdit):
doubleClicked = pyqtSignal(QTextEdit)
def __init__(self):
super().__init__()
self.setReadOnly(True)
self.setFont(font)
self.setFixedHeight(20)
self.textChanged.connect(self.autoResize)
def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
self.doubleClicked.emit(self)
def autoResize(self):
self.document().setTextWidth(self.viewport().width())
margins = self.contentsMargins()
height = int(self.document().size().height() +
margins.top() + margins.bottom())
self.setFixedHeight(height)
def resizeEvent(self, e: QResizeEvent) -> None:
self.autoResize()
class TextCell(QVBoxLayout):
resize = pyqtSignal(QVBoxLayout)
def __init__(self, text):
super().__init__()
self.setAlignment(Qt.AlignmentFlag.AlignLeft |
Qt.AlignmentFlag.AlignTop)
self.label = Label(text)
self.apply = makebutton('Apply')
self.apply.hide()
self.editor = Editor()
self.editor.doubleClicked.connect(self.on_DoubleClick)
self.editor.doubleClicked.connect(lambda: self.resize.emit(self))
self.hbox = QHBoxLayout()
self.hbox.addStretch()
self.hbox.addWidget(self.apply)
self.addWidget(self.label)
self.addWidget(self.editor)
self.addLayout(self.hbox)
self.apply.clicked.connect(self.on_ApplyClick)
self.apply.clicked.connect(lambda: self.resize.emit(self))
def on_DoubleClick(self):
self.editor.setReadOnly(False)
self.apply.show()
def on_ApplyClick(self):
self.editor.setReadOnly(True)
self.apply.hide()
def get_Height(self):
height = 0
for i in range(self.count()):
item = self.itemAt(i)
if item.widget():
widget = item.widget()
else:
widget = item.itemAt(1).widget()
if not widget.isHidden():
height += widget.height()
height += 9
return height
class SongPage(QGroupBox):
def __init__(self, texts):
super().__init__()
self.init(texts)
self.setCheckable(True)
self.setChecked(False)
self.setAlignment(Qt.AlignmentFlag.AlignLeft |
Qt.AlignmentFlag.AlignTop)
def init(self, texts):
self.vbox = QVBoxLayout()
self.vbox.setAlignment(Qt.AlignmentFlag.AlignLeft |
Qt.AlignmentFlag.AlignTop)
self.vbox.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.artist = TextCell('Artist')
self.artist.editor.setText(texts[0])
self.album = TextCell('Album')
self.album.editor.setText(texts[1])
self.title = TextCell('Title')
self.title.editor.setText(texts[2])
self.vbox.addLayout(self.artist)
self.vbox.addLayout(self.album)
self.vbox.addLayout(self.title)
self.setContentsMargins(3, 20, 3, 3)
self.vbox.setContentsMargins(9, 9, 9, 9)
self.setLayout(self.vbox)
self.artist.resize.connect(self.autoResize)
self.album.resize.connect(self.autoResize)
self.title.resize.connect(self.autoResize)
def autoResize(self):
height = sum(i.get_Height() for i in self.vbox.children()) + 23
self.setFixedHeight(height)
def resizeEvent(self, e: QResizeEvent) -> None:
self.autoResize()
QGroupBoxs 自动调整大小,唯一的问题是复选框的位置,在我的主脚本中,复选框的位置是正确的,但在另一个具有相同 类 的脚本中,复选框的位置是错误的,但这并不影响主脚本。
当 QTextEdits 缩小时,它们的底部边框变得不可见。
我正在使用 Python 3.9.5 和 PyQt6。
根据我之前的问题,我想问一下如何动态调整 QGroupBox 的大小以适应其内容。
我有一个QScrollArea,它的layout是一个QVBoxLayout,QVBoxLayout会加一堆QGroupBox
QGroupBox本身有一个QVBoxLayout,QVBoxLayout里面是一堆QVBoxLayout,QGroupBox的QVBoxLayout里面的QVBoxLayout里面是内容
每个最低级别的 QVBoxLayout 都有两个小部件和一个 QHBoxLayout。
从上到下,第一个小部件是 QLabel,固定大小为 100x20,第二个小部件是 QTextEdit,它会根据内容自动调整大小。 QHBoxLayout 内部是一个拉伸和固定大小的按钮,大小为 60x20,按钮可以隐藏或显示。
小部件和布局的层次结构是这样的:
QVBoxLayout — level 0
QScrollArea — level 1
QVBoxLayout0 — level 2
QGroupBox — level 3 (checkable)
QVBoxLayout1 — level 4
QLabel — level 5 (fixed)
QTextEdit — level 5 (auto-resizing)
QHBoxLayout — level 5
Stretch — level 6
QPushButton — level 6 (fixed)
我希望 QGroupBoxs 自动调整大小以适应它们的内容。
宽度由布局决定,我希望 QGroupBoxs 根据其内容具有固定的、最小的、最佳的高度。
基本上,高度应该是其所有 VISIBLE 小部件的高度的总和,加上上边距和下边距,以及小部件之间的边距,也许还有复选框的高度。
我想为 QGroupBoxs 设置固定高度,因为如果我不这样做,布局将拉伸 QGroupBoxs 并将其定位在中间,而不是顶部最小高度,这不是我想要的通缉
我将 post 下面的示例代码:
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
font = QFont('Noto Serif', 9)
def makebutton(text):
button = QPushButton()
button.setFont(font)
button.setFixedSize(60, 20)
button.setText(text)
return button
class Editor(QTextEdit):
doubleClicked = pyqtSignal(QTextEdit)
def __init__(self):
super().__init__()
self.setReadOnly(True)
self.setFont(font)
self.setFixedHeight(20)
self.setAttribute(Qt.WidgetAttribute.WA_DontShowOnScreen)
self.show()
self.textChanged.connect(self.autoResize)
def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
self.doubleClicked.emit(self)
def autoResize(self):
self.document().setTextWidth(self.viewport().width())
margins = self.contentsMargins()
height = int(self.document().size().height() + margins.top() + margins.bottom())
self.setFixedHeight(height)
def resizeEvent(self, event):
self.autoResize()
class textcell(QVBoxLayout):
def __init__(self, text):
super().__init__()
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.label = QLabel(text)
self.label.setFixedSize(80, 20)
self.apply = makebutton('Apply')
self.apply.hide()
self.editor = Editor()
self.editor.doubleClicked.connect(self.on_DoubleClick)
self.hbox = QHBoxLayout()
self.hbox.addStretch()
self.hbox.addWidget(self.apply)
self.addWidget(self.label)
self.addWidget(self.editor)
self.addLayout(self.hbox)
self.apply.clicked.connect(self.on_ApplyClick)
def on_DoubleClick(self):
self.editor.setReadOnly(False)
self.apply.show()
def on_ApplyClick(self):
self.editor.setReadOnly(True)
self.apply.hide()
class songpage(QGroupBox):
def __init__(self, texts):
super().__init__()
self.init(texts)
self.setCheckable(True)
self.setChecked(False)
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
def init(self, texts):
self.vbox = QVBoxLayout()
self.vbox.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.vbox.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
artist = textcell('Artist')
artist.editor.setText(texts[0])
album = textcell('Album')
album.editor.setText(texts[2])
title = textcell('Title')
title.editor.setText(texts[1])
self.height = 120 + artist.editor.height() + album.editor.height() + title.editor.height()
self.vbox.addLayout(artist)
self.vbox.addLayout(album)
self.vbox.addLayout(title)
print(self.children())
print(self.vbox.children())
print(self.vbox.count())
print(artist.count())
print(artist.children())
print(artist.contentsMargins().top())
print(artist.contentsMargins().bottom())
print(self.vbox.contentsMargins().top())
print(self.vbox.contentsMargins().bottom())
print(self.contentsMargins().top())
print(self.contentsMargins().bottom())
print(self.childrenRect().height())
print(self.contentsRect().height())
print(artist.apply.isHidden())
print(artist.editor.isHidden())
print(artist.label.isHidden())
print(artist.label.isVisible())
print(self.sizeHint().height())
self.setLayout(self.vbox)
self.setFixedHeight(self.height)
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.resize(405, 720)
frame = self.frameGeometry()
center = self.screen().availableGeometry().center()
frame.moveCenter(center)
self.move(frame.topLeft())
self.centralwidget = QWidget(self)
self.vbox = QVBoxLayout(self.centralwidget)
self.scrollArea = QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.add = makebutton('Add')
self.vbox.addWidget(self.add)
self.add.clicked.connect(lambda: adder.addItem())
self.vbox.addWidget(self.scrollArea)
self.setCentralWidget(self.centralwidget)
class Adder:
def __init__(self):
self.i = 0
def addItem(self):
window.verticalLayout.addWidget(songpage(items[self.i]))
if self.i < len(items) - 1:
self.i += 1
adder = Adder()
items = [('Herbert von Karajan',
"Orphée aux enfers, 'Orpheus in the Underworld'\u2014Overture",
'100 Best Karajan'),
('Herbert von Karajan', 'Radetzky March Op. 228', '100 Best Karajan'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014I. Adagio molto \u2014 Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014II. Andante cantabile con moto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014III. Menuetto (Allegro molto e vivace)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 1 in C, Op. 21\u2014IV. Finale (Adagio \u2014 Allegro molto e vivace)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014I. Adagio molto \u2014 Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014II. Larghetto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014III. Scherzo (Allegro)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 2 in D, Op. 36\u2014IV. Allegro molto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014I. Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014II. Marcia funebre (Adagio assai)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014III. Scherzo (Allegro vivace)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 3 in E\u2014Flat, Op. 55 \u2014Eroica\u2014IV. Finale (Allegro molto)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014I. Adagio \u2014 Allegro vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014II. Adagio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014III. Allegro vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 4 in B\u2014Flat, Op. 60\u2014IV. Allegro ma non troppo',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014I. Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014II. Andante con moto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014III. Allegro',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 5 in C Minor, Op. 67\u2014IV. Allegro',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014I. Poco sostenuto \u2014 Vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014II. Allegretto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014III. Presto \u2014 Assai meno presto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 7 in A, Op. 92\u2014IV. Allegro con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014I. Allegro vivace e con brio',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014II. Allegretto scherzando',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014III. Tempo di menuetto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 8 in F, Op. 93\u2014IV. Allegro vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014I. Allegro ma non troppo, un poco maestoso',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014II. Molto vivace',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014III. Adagio molto e cantabile',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014IV. Presto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No. 9 in D Minor, Op. 125 \u2014 Choral\u2014V. Presto\u2014 O Freunde, nicht diese T\u2014ne!\u2014Allegro assai',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014I. Erwachen heiterer Empfindungen bei der Ankunft auf dem Lande\u2014 Allegro ma non troppo',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014II. Szene am Bach\u2014 (Andante molto mosso)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014III. Lustiges Zusammensein der Landleute (Allegro)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014IV. Gewitter, Sturm (Allegro)',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Symphony No.6 in F, Op.68 \u2014Pastoral\u2014V. Hirtengesang. Frohe und dankbare Gefühle nach dem Sturm\u2014 Allegretto',
'Beethoven\u2014 The 9 Symphonies'),
('Herbert von Karajan',
'Cancan (Orpheus in the Underworld)',
'Best of the Millennium\u2014 Top 40 Classical Hits'),
('Herbert von Karajan',
'Hungarian Dance No. 5 in G Minor, WoO 1 No. 5',
'Complete Recordings on Deutsche Grammophon')]
app = QApplication([])
window = Window()
window.show()
app.exec()
您可以看到 QGroupBoxs 没有处于最佳高度,如果双击 QTextEdits 并出现 Apply 按钮,则当 QTextBoxs 调整大小时,groupboxs 的顶部会变大,复选框处于尴尬的位置减少线条,底部边框将变得不可见。
并且布局的边距始终为零,QGroupBox 的打印边距与观察到的边距不匹配,QGroupBoxs 的 childrenRects 高度始终为零,QGroupBoxs 的 contentsRects 高度始终为478,这没有任何意义,并且 .isHidden()
将始终为 True,.isVisible()
将始终为 False,如果我仅将小部件添加到布局而不调用其 .show()
。
但是当我将它们添加到布局时,它们总是自动可见,除非我明确地设置它们 .hide()
。并且调用 .show()
将使已经可见的小部件在屏幕中央闪烁,除非设置 Qt.WidgetAttribute.WA_DontShowOnScreen
。
那么我怎样才能真正动态地计算出最佳的最小高度呢?
而且我不能使用 .addStretch()
因为我需要 QGroupBox 之间的最小最佳间距并且我需要使用索引使内容可删除,.addStretch()
会破坏索引顺序,因为删除项目时不会删除拉伸。
我终于搞定了,问题是我刚刚创建了对象,并没有把它们添加到主window,它们都有默认的大小和值,当它们被添加时,值会改变添加到 window.
所以我只需要计算它们的大小并在将它们添加到 window 之后调整它们的大小。
据我观察,QGroupBoxs中的QVBoxLayouts总是有(9, 9, 9, 9)的边距,而所有widgets实际所在的布局都没有边距,而QGroupBoxs本身的边距为(3, 20, 3, 3) 在我的主脚本中,所以我可以明确设置这些边距并使用这些值。
我是这样做的:
class Label(QLabel):
def __init__(self, text):
super().__init__()
self.setFont(font)
self.fontRuler = QFontMetrics(font)
self.setText(text)
self.Height = self.fontRuler.size(0, text).height()
self.Width = self.fontRuler.size(0, text).width()
self.setFixedSize(self.Width, self.Height)
class Editor(QTextEdit):
doubleClicked = pyqtSignal(QTextEdit)
def __init__(self):
super().__init__()
self.setReadOnly(True)
self.setFont(font)
self.setFixedHeight(20)
self.textChanged.connect(self.autoResize)
def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
self.doubleClicked.emit(self)
def autoResize(self):
self.document().setTextWidth(self.viewport().width())
margins = self.contentsMargins()
height = int(self.document().size().height() +
margins.top() + margins.bottom())
self.setFixedHeight(height)
def resizeEvent(self, e: QResizeEvent) -> None:
self.autoResize()
class TextCell(QVBoxLayout):
resize = pyqtSignal(QVBoxLayout)
def __init__(self, text):
super().__init__()
self.setAlignment(Qt.AlignmentFlag.AlignLeft |
Qt.AlignmentFlag.AlignTop)
self.label = Label(text)
self.apply = makebutton('Apply')
self.apply.hide()
self.editor = Editor()
self.editor.doubleClicked.connect(self.on_DoubleClick)
self.editor.doubleClicked.connect(lambda: self.resize.emit(self))
self.hbox = QHBoxLayout()
self.hbox.addStretch()
self.hbox.addWidget(self.apply)
self.addWidget(self.label)
self.addWidget(self.editor)
self.addLayout(self.hbox)
self.apply.clicked.connect(self.on_ApplyClick)
self.apply.clicked.connect(lambda: self.resize.emit(self))
def on_DoubleClick(self):
self.editor.setReadOnly(False)
self.apply.show()
def on_ApplyClick(self):
self.editor.setReadOnly(True)
self.apply.hide()
def get_Height(self):
height = 0
for i in range(self.count()):
item = self.itemAt(i)
if item.widget():
widget = item.widget()
else:
widget = item.itemAt(1).widget()
if not widget.isHidden():
height += widget.height()
height += 9
return height
class SongPage(QGroupBox):
def __init__(self, texts):
super().__init__()
self.init(texts)
self.setCheckable(True)
self.setChecked(False)
self.setAlignment(Qt.AlignmentFlag.AlignLeft |
Qt.AlignmentFlag.AlignTop)
def init(self, texts):
self.vbox = QVBoxLayout()
self.vbox.setAlignment(Qt.AlignmentFlag.AlignLeft |
Qt.AlignmentFlag.AlignTop)
self.vbox.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.artist = TextCell('Artist')
self.artist.editor.setText(texts[0])
self.album = TextCell('Album')
self.album.editor.setText(texts[1])
self.title = TextCell('Title')
self.title.editor.setText(texts[2])
self.vbox.addLayout(self.artist)
self.vbox.addLayout(self.album)
self.vbox.addLayout(self.title)
self.setContentsMargins(3, 20, 3, 3)
self.vbox.setContentsMargins(9, 9, 9, 9)
self.setLayout(self.vbox)
self.artist.resize.connect(self.autoResize)
self.album.resize.connect(self.autoResize)
self.title.resize.connect(self.autoResize)
def autoResize(self):
height = sum(i.get_Height() for i in self.vbox.children()) + 23
self.setFixedHeight(height)
def resizeEvent(self, e: QResizeEvent) -> None:
self.autoResize()
QGroupBoxs 自动调整大小,唯一的问题是复选框的位置,在我的主脚本中,复选框的位置是正确的,但在另一个具有相同 类 的脚本中,复选框的位置是错误的,但这并不影响主脚本。
当 QTextEdits 缩小时,它们的底部边框变得不可见。