QScrollArea с динамически меняющимся содержимым

У меня есть QScrollArea с некоторыми кнопками, как показано на рисунке.
введите описание изображения здесь

Идея макета заключается в:
1. Левая и правая кнопки должны использоваться для прокрутки кнопок, когда они слишком широкие

2. Количество кнопок в области прокрутки может быть изменено динамически
3. Любое свободное пространство должно быть использовано, чтобы максимально расширить область прокрутки. Если такого пространства не существует, для прокрутки следует использовать навигационные кнопки.

С моей текущей реализацией, когда я увеличиваю кнопки, у меня есть это:
введите описание изображения здесь

Но справа есть свободное место, так что это должно выглядеть так:
введите описание изображения здесь

Если я увеличу, например, до 10, то должна появиться полоса прокрутки (потому что макет содержит виджет).

Я хочу знать, есть ли другой способ, кроме ручного изменения размера виджетов (потому что пользовательский интерфейс может быть переведен, и кнопки могут изменить размер подсказки, также реальный дизайн более сложен 🙁

Вот моя реализация виджета ScrollAreaTest:

#include "MainWidget.h"
#include <QLineEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QScrollArea>
#include <QPushButton>
#include <QDebug>
#include "ButtonWidget.h"
#include "CheckableButtonGroup.h"
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent),
m_scrollArea( 0 ),
m_lineEdit( 0 ),
m_buttons( 0 )
{
QVBoxLayout* mainLayout = new QVBoxLayout( this );
QWidget* firstRow = new QWidget;
QHBoxLayout* firstRowLayout = new QHBoxLayout( firstRow );

QPushButton* left  = new QPushButton;
QPushButton* right = new QPushButton;

m_buttons = new CheckableButtonGroup( Qt::Horizontal );
m_buttons->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
m_buttons->setButtonsCount( 5 );
m_buttons->setStyleSheet( "border: none" );

QWidget* const buttonsContainer = new QWidget;
QHBoxLayout* const buttonsContainerLayout = new QHBoxLayout( buttonsContainer );
buttonsContainerLayout->setSpacing( 0 );
buttonsContainerLayout->setSizeConstraint( QLayout::SetMinAndMaxSize );
buttonsContainerLayout->setMargin( 0 );
buttonsContainerLayout->addWidget( m_buttons, 0, Qt::AlignLeft );

qDebug() << m_buttons->buttons()[ 0 ]->size();

m_scrollArea = new QScrollArea;
m_scrollArea->setContentsMargins( 0, 0, 0, 0 );
m_scrollArea->setWidget( buttonsContainer );
m_scrollArea->setWidgetResizable( true );
m_scrollArea->setStyleSheet( "border: 1px solid blue" );
m_scrollArea->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );

firstRowLayout->addWidget( left        , 0, Qt::AlignLeft );
firstRowLayout->addWidget( m_scrollArea, 1, Qt::AlignLeft );
firstRowLayout->addWidget( right       , 0, Qt::AlignLeft );

m_lineEdit = new QLineEdit;
QPushButton* button = new QPushButton;
QHBoxLayout* secondRowLayout = new QHBoxLayout;
secondRowLayout->addWidget( m_lineEdit );
secondRowLayout->addWidget( button );

connect( button, SIGNAL(clicked()), SLOT(setButtonsCount()) );

mainLayout->addWidget( firstRow, 1, Qt::AlignLeft );
mainLayout->addLayout( secondRowLayout );

button->setText( "Set buttons count" );

buttonsContainer->resize( m_buttons->buttonsOptimalWidth(), buttonsContainer->height() );
m_buttons->resize( m_buttons->buttonsOptimalWidth(), m_buttons->height() );

//area->resize( 100, area->height() );
//area->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
}

MainWidget::~MainWidget()
{
}

void MainWidget::setButtonsCount()
{
m_buttons->setButtonsCount( m_lineEdit->text().toInt() );
}

И вот весь проект Qt, содержащий проблему:
https://drive.google.com/file/d/0B-mc4aKkzWlxQzlPMEVuNVNKQjg/edit?usp=sharing

3

Решение

Основные шаги:

  1. Контейнерный виджет, который содержит кнопки (ваш CheckableButtonGroup) должен иметь QLayout::SetMinAndMaxSize ограничение размера установлено. Тогда он будет достаточно большим, чтобы удерживать кнопки. Его политика размера не имеет значения, так как вы просто помещаете его в QScrollArea, а не в другой макет.

  2. Область прокрутки должна установить максимальный размер в соответствии с размером виджета, который она содержит. Реализация по умолчанию не делает этого, поэтому нужно реализовать это, следя за событиями изменения размера встроенного виджета.

Код ниже является минимальным примером, который работает как в Qt 4.8, так и в 5.2.

Скриншот с двумя кнопками

Снимок экрана с несколькими кнопками

// https://github.com/KubaO/stackoverflown/tree/master/questions/scrollgrow-21253755
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

class ButtonGroup : public QWidget {
Q_OBJECT
QHBoxLayout m_layout{this};
public:
ButtonGroup(QWidget * parent = 0) : QWidget{parent} {
m_layout.setSizeConstraint(QLayout::SetMinAndMaxSize); // <<< Essential
}
Q_SLOT void addButton() {
auto n = m_layout.count();
m_layout.addWidget(new QPushButton{QString{"Btn #%1"}.arg(n+1)});
}
};

class AdjustingScrollArea : public QScrollArea {
bool eventFilter(QObject * obj, QEvent * ev) {
if (obj == widget() && ev->type() == QEvent::Resize) {
// Essential vvv
setMaximumWidth(width() - viewport()->width() + widget()->width());
}
return QScrollArea::eventFilter(obj, ev);
}
public:
AdjustingScrollArea(QWidget * parent = 0) : QScrollArea{parent} {}
void setWidget(QWidget *w) {
QScrollArea::setWidget(w);
// It happens that QScrollArea already filters widget events,
// but that's an implementation detail that we shouldn't rely on.
w->installEventFilter(this);
}
};

class Window : public QWidget {
QGridLayout         m_layout{this};
QLabel              m_left{">>"};
AdjustingScrollArea m_area;
QLabel              m_right{"<<"};
QPushButton         m_add{"Add a widget"};
ButtonGroup         m_group;
public:
Window() {
m_layout.addWidget(&m_left, 0, 0);
m_left.setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
m_left.setStyleSheet("border: 1px solid green");

m_layout.addWidget(&m_area, 0, 1);
m_area.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_area.setStyleSheet("QScrollArea { border: 1px solid blue }");
m_area.setWidget(&m_group);
m_layout.setColumnStretch(1, 1);

m_layout.addWidget(&m_right, 0, 2);
m_right.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_right.setStyleSheet("border: 1px solid green");

m_layout.addWidget(&m_add, 1, 0, 1, 3);
connect(&m_add, SIGNAL(clicked()), &m_group, SLOT(addButton()));
}
};

int main(int argc, char *argv[])
{
QApplication a{argc, argv};
Window w;
w.show();
return a.exec();
}

#include "main.moc"
7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]