問題描述
我想在 Qt 中創建一個具有以下功能的自定義小部件:
I would like to create a custom widget in Qt with the following features:
- 這是一個容器
- 它可以填充任何 Qt 布局
- 它可以在任何 Qt 布局中
- 按鈕允許垂直折疊/折疊內容,因此只有按鈕可見,所有包含的布局都不可見.
- 上一個按鈕允許將其再次展開/展開到布局內容的大小.
- 展開/折疊基于尺寸(不是顯示/隱藏)以允許動畫.
- 可在 QDesigner 中使用
為了提供一個想法,這是一個類似小部件(不是 Qt)的圖像:
To provide an idea, here is an image of a similar widget (not Qt):
我已經有一個框架可以正常工作并在 QDesigner 中公開.我現在需要讓它展開/折疊,這看起來并不那么簡單.
I already have a frame that work correctly and is exposed in QDesigner. I need now to make it to extend/collapse, which does not seem so simple.
我嘗試使用 resize()、sizePolicy()、sizeHint() 但這不起作用:當框架折疊時,我得到以下值:
I tried to play with resize(), sizePolicy(), sizeHint() but that does not work: When the frame is collapsed I got following values:
sizeHint: (500,20)
size : (500,20)
closestAcceptableSize: (518,150)
Painted size: (518, 150)
QLayout::closestAcceptableSize 不是小部件的一部分,因此我無法更改它.
QLayout::closestAcceptableSize is not part of the widget so I cannot change it.
任何提示或/和代碼片段來實現這一點?
這里有一個簡單的例子.除必要外,我刪除了所有內容.
EDITED: Here a simple example. I removed all except necessary.
main.cpp 示例
main.cpp example
#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(§ion);
QVBoxLayout inLayout(§ion);
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
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
.
I stumbled upon the same problem and solved it by implementing the collapsible widget as a QScrollArea
whose maximum height is animated by a QPropertyAnimation
.
但因為我不使用 QDesigner,所以我不能告訴你它是否在那里工作.
But since I don't use QDesigner, I can't tell you if it works there.
我仍然有一個問題:可折疊小部件不僅可以向底部方向擴展,還可以向頂部和底部擴展.如果尚未達到其最小高度,這可能會導致位于其上方的小部件縮小.但這與我們必須自己構建這個東西的事實相比確實是一個細節......
I still have one problem: Instead of only expanding towards the bottom direction, the collapsible widget can expand towards the top and bottom. This can cause widgets located above it to shrink if they haven't reached their minimum height, yet. But this is really a detail compared to the fact that we have to build this thing ourselves…
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);
…
這篇關于如何在 Qt 中制作可展開/可折疊的部分小部件的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!