PyQt6如何动态调整QGroupBox的大小以适应内容?

2024-05-23 16:32:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用Python 3.9.5和PyQt6

在前面的问题之后,我想问一下如何动态调整QGroupBox的大小以适应其内容

我有一个qscrollara,它的布局是QVBoxLayout,一堆qgroupbox将被添加到QVBoxLayout中

QGroupBox本身有一个QVBoxLayout,QVBoxLayout内有一堆QVBoxLayout,QGroupBox的QVBoxLayout内有内容

每个最低级别的QVBoxLayout都有两个小部件和一个QHBoxLayout

从上到下,第一个小部件是固定大小为100x20的QLabel,第二个小部件是根据内容自动调整大小的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根据其内容有一个固定的、最小的、最佳的高度

基本上,高度应该是所有可见小部件的高度之和,加上上顶部边距和底部边距,以及小部件之间的边距,也许还有复选框的高度

我想为QGROMPPBOX设置固定的高度,因为如果我不这样做,布局将扩展QGROMPPBOX并将其放置在中间,而不是在顶部,以最小的高度,这不是我想要的。p>

我将在下面发布示例代码:

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按钮,groupboxs的顶部将增长,复选框处于尴尬的位置,当QTextBoxs调整到较少的行时,底部边框将不可见

布局的边距始终为零,QGroupBox的打印边距与观察到的边距不匹配,QGroupBox的childrenRects高度始终为零,QGroupBoxs的contentsRects高度始终为478,这没有任何意义,.isHidden()始终为真,.isVisible()始终为假,如果我只将小部件添加到布局中,而不调用它的.show()

但是当我将它们添加到布局中时,它们总是自动可见的,除非我显式地使它们.hide()。调用.show()将使已经可见的小部件在屏幕中央闪烁,除非设置了Qt.WidgetAttribute.WA_DontShowOnScreen

那么,我怎样才能动态地计算出最佳的最小高度呢

我不能使用.addStretch(),因为我需要QGroupBox之间最小的、最佳的间距,并且我需要通过使用索引使内容可删除,.addStretch()将打破索引顺序,因为删除项时不会删除拉伸


Tags: thenoinselfallegroprintopvbox
1条回答
网友
1楼 · 发布于 2024-05-23 16:32:50

我终于做到了,问题是当我刚刚创建了对象,没有将它们添加到主窗口时,它们都有默认的大小和值,当它们添加到窗口时,值会发生变化

因此,我只需要计算它们的大小,并在将它们添加到窗口后调整它们的大小

根据我的观察,QGroupBoxs中的QVBoxLayouts总是有(9,9,9,9)的边距,而小部件实际所在的所有布局都没有边距,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收缩时,其底部边框将不可见

相关问题 更多 >