gpt4 book ai didi

c++ - 如何在 Qt 中制作可展开/可折叠的部分小部件

转载 作者:IT老高 更新时间:2023-10-28 12:30:46 24 4
gpt4 key购买 nike

我想在 Qt 中创建一个具有以下功能的自定义小部件:

  • 它是一个容器
  • 它可以填充任何 Qt 布局
  • 它可以在任何 Qt 布局中
  • 按钮允许垂直折叠/折叠内容,因此只有按钮可见,所有包含的布局都是不可见的。
  • 上一个按钮允许将其再次展开/展开到布局内容的大小。
  • 展开/折叠基于尺寸(不是显示/隐藏)以允许动画。
  • 可在 QDesigner 中使用

提供一个想法,这是一个类似小部件(不是 Qt)的图像: enter image description here

我已经有一个可以正常工作并在 QDesigner 中公开的框架。我现在需要让它扩展/折叠,这似乎并不那么简单。

我尝试使用 resize()、sizePolicy()、sizeHint() 但这不起作用:当框架折叠时,我得到以下值:

sizeHint: (500,20)
size : (500,20)
closestAcceptableSize: (518,150)
Painted size: (518, 150)

QLayout::closestAcceptableSize 不是小部件的一部分,所以我无法更改它。

任何提示或/和代码片段来实现这一点?

编辑:这里举一个简单的例子。除必要外,我删除了所有内容。

main.cpp 示例

#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>

#include "section.hpp"


using namespace myWidgets;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);


// Create the main Window
QWidget window;
window.resize(500,500);
window.setStyleSheet("QPushButton:{background-color:rgba(128,128,128,192);}");

// Create the main window layout
QVBoxLayout topLayout(&window);
QWidget *w1 = new QWidget();
w1->setStyleSheet("background-color:rgba(128,128,128,192);");
topLayout.addWidget(w1);

Section section(&window);
topLayout.addWidget(&section);

QVBoxLayout inLayout(&section);
QPushButton *button = new QPushButton();
button->setMinimumHeight(100);
inLayout.addWidget(button);

QWidget *w2 = new QWidget();
w2->setStyleSheet("background-color:rgba(128,128,128,192);");
topLayout.addWidget(w2);



window.show();

return a.exec();
}

Section.hpp

#ifndef SECTION_HPP
#define SECTION_HPP

#include <QPushButton> //for the expand/collapse button
#include <QtDesigner/QDesignerExportWidget>
#include <QLayout>
#include <QPainter>
#include <QPaintEvent>
#include <QDebug>


// Compatibility for noexcept, not supported in vsc++
#ifdef _MSC_VER
#define noexcept throw()
#endif

#if defined SECTION_BUILD
#define SECTION_BUILD_DLL_SPEC Q_DECL_EXPORT
#elif defined SECTION_EXEC
#define SECTION_BUILD_DLL_SPEC
#else
#define SECTION_BUILD_DLL_SPEC Q_DECL_IMPORT
#endif

namespace myWidgets
{

class SECTION_BUILD_DLL_SPEC Section : public QWidget
{
Q_OBJECT

Q_PROPERTY( bool is_expanded MEMBER isExpanded)

public:
// Constructor, standard
explicit Section( QWidget *parent=0 ): QWidget(parent),
expandButton(this)
{
expandButton.resize(20,20);
expandButton.move(0,0);
expandButton.connect(&expandButton, &QPushButton::clicked,
this, &Section::expandCollapseEvent);

QMargins m= contentsMargins();
m.setTop(m.top()+25);
setContentsMargins(m);
//setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Minimum);

}

virtual void expand( bool expanding ) noexcept
{
resize(sizeHint());
isExpanded = expanding;
updateGeometry();

qDebug() << (isExpanded? "expanded":"collapsed") << sizeHint() << QWidget::size() <<
parentWidget()->layout()->closestAcceptableSize(this, size());
}

virtual QSize sizeHint() const noexcept override
{
if (isExpanded) return QSize(layout()->contentsRect().width(),
layout()->contentsRect().height());
else return QSize(layout()->contentsRect().width(), 20);
}

// Implement custom appearance
virtual void paintEvent(QPaintEvent *e) noexcept override
{
(void) e; //TODO: remove
QPainter p(this);
p.setClipRect(e->rect());
p.setRenderHint(QPainter::Antialiasing );
p.fillRect(e->rect(), QColor(0,0,255,128));
}

protected:

// on click of the expandButton, collapse/expand this widget
virtual void expandCollapseEvent() noexcept
{
expand(!isExpanded);
}


bool isExpanded = true; //whenever the section is collapsed(false) or expanded(true)
QPushButton expandButton; //the expanding/collapsing button
};

}


#endif // SECTION_HPP

最佳答案

我偶然发现了同样的问题,并通过将可折叠小部件实现为 QScrollArea 来解决它,其最大高度由 QPropertyAnimation 设置动画。

但由于我不使用 QDesigner,我无法告诉你它是否可以在那里工作。

我还有一个问题:可折叠小部件不仅可以向底部方向扩展,还可以向顶部和底部方向扩展。如果它们还没有达到最小高度,这可能会导致位于其上方的小部件收缩。但这与我们必须自己构建这个东西的事实相比,这确实是一个细节……

Spoiler.h

#include <QFrame>
#include <QGridLayout>
#include <QParallelAnimationGroup>
#include <QScrollArea>
#include <QToolButton>
#include <QWidget>

class Spoiler : public QWidget {
Q_OBJECT
private:
QGridLayout mainLayout;
QToolButton toggleButton;
QFrame headerLine;
QParallelAnimationGroup toggleAnimation;
QScrollArea contentArea;
int animationDuration{300};
public:
explicit Spoiler(const QString & title = "", const int animationDuration = 300, QWidget *parent = 0);
void setContentLayout(QLayout & contentLayout);
};

Spoiler.cpp

#include <QPropertyAnimation>

#include "Spoiler.h"

Spoiler::Spoiler(const QString & title, const int animationDuration, QWidget *parent) : QWidget(parent), animationDuration(animationDuration) {
toggleButton.setStyleSheet("QToolButton { border: none; }");
toggleButton.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toggleButton.setArrowType(Qt::ArrowType::RightArrow);
toggleButton.setText(title);
toggleButton.setCheckable(true);
toggleButton.setChecked(false);

headerLine.setFrameShape(QFrame::HLine);
headerLine.setFrameShadow(QFrame::Sunken);
headerLine.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);

contentArea.setStyleSheet("QScrollArea { background-color: white; border: none; }");
contentArea.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
// start out collapsed
contentArea.setMaximumHeight(0);
contentArea.setMinimumHeight(0);
// let the entire widget grow and shrink with its content
toggleAnimation.addAnimation(new QPropertyAnimation(this, "minimumHeight"));
toggleAnimation.addAnimation(new QPropertyAnimation(this, "maximumHeight"));
toggleAnimation.addAnimation(new QPropertyAnimation(&contentArea, "maximumHeight"));
// don't waste space
mainLayout.setVerticalSpacing(0);
mainLayout.setContentsMargins(0, 0, 0, 0);
int row = 0;
mainLayout.addWidget(&toggleButton, row, 0, 1, 1, Qt::AlignLeft);
mainLayout.addWidget(&headerLine, row++, 2, 1, 1);
mainLayout.addWidget(&contentArea, row, 0, 1, 3);
setLayout(&mainLayout);
QObject::connect(&toggleButton, &QToolButton::clicked, [this](const bool checked) {
toggleButton.setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
toggleAnimation.setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
toggleAnimation.start();
});
}

void Spoiler::setContentLayout(QLayout & contentLayout) {
delete contentArea.layout();
contentArea.setLayout(&contentLayout);
const auto collapsedHeight = sizeHint().height() - contentArea.maximumHeight();
auto contentHeight = contentLayout.sizeHint().height();
for (int i = 0; i < toggleAnimation.animationCount() - 1; ++i) {
QPropertyAnimation * spoilerAnimation = static_cast<QPropertyAnimation *>(toggleAnimation.animationAt(i));
spoilerAnimation->setDuration(animationDuration);
spoilerAnimation->setStartValue(collapsedHeight);
spoilerAnimation->setEndValue(collapsedHeight + contentHeight);
}
QPropertyAnimation * contentAnimation = static_cast<QPropertyAnimation *>(toggleAnimation.animationAt(toggleAnimation.animationCount() - 1));
contentAnimation->setDuration(animationDuration);
contentAnimation->setStartValue(0);
contentAnimation->setEndValue(contentHeight);
}

如何使用:


auto * anyLayout = new QVBoxLayout();
anyLayout->addWidget(…);

Spoiler spoiler;
spoiler.setContentLayout(*anyLayout);

Spoiler example

关于c++ - 如何在 Qt 中制作可展开/可折叠的部分小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32476006/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com