Сохранение соотношения сторон подкласса QWidget при изменении размера
Описание проблемы
По умолчанию при изменении размера виджета, наследуемого от QWidget, соотношение его сторон не сохраняется на постоянном значении. Но встречаются случаи, когда это недопустимо, поскольку искажается изображение, выводимое виджетом. Возьмем для примера QChartView, выводящий диаграмму Вольперта-Смита. В простейшем случае данная диаграмма представляет собой окружности постоянного активного сопротивления и дуги постоянного реактивного сопротивления.
Но при изменении размеров окна приложения появляются искажения диаграммы:
Решение
Возможный вариант решения (найден на stackoverflow) заключается в следующем:
- Создать родительский виджет (например,
AspectRatioWidgetподклассаQWidget) для размещения на нем целевого виджета. - В этом родительском виджете создать
QBoxLayout. Поместить целевой виджет в центр, а заполнителиQSpacerItemпо бокам. - Затем в этом родительском виджете переопределить метод
QWidget::resizeEvent, где по мере необходимости изменять направлениеQBoxLayoutи растягивание его элементов.
Ниже приведен пример. Чтобы использовать его, просто создайте экземпляр AspectRatioWidget и передайте его конструктору указатель на ваш виджет и необходимое соотношение сторон.
Исходный код примера, используемого в данной статье, также доступен на GitHub.
aspectratiowidget.h
#ifndef ASPECTRATIOWIDGET_H
#define ASPECTRATIOWIDGET_H
#include <QWidget>
#include <QBoxLayout>
class AspectRatioWidget : public QWidget
{
public:
AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent = 0);
protected:
void resizeEvent(QResizeEvent *event) override;
private:
QBoxLayout *layout;
float arWidth; // ширина для задания соотношения сторон
float arHeight; // высота для задания соотношения сторон
};
#endif // ASPECTRATIOWIDGET_H
aspectratiowidget.cpp
#include "aspectratiowidget.h"
#include <QResizeEvent>
//---------------------------------------------------------------------------------------
AspectRatioWidget::AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent) :
QWidget(parent), arWidth(width), arHeight(height)
{
layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
layout->setContentsMargins(0, 0, 0, 0);
// добавляем сначала заполнитель, затем свой виджет, затем снова заполнитель
layout->addItem(new QSpacerItem(0, 0));
layout->addWidget(widget);
layout->addItem(new QSpacerItem(0, 0));
}
//---------------------------------------------------------------------------------------
void AspectRatioWidget::resizeEvent(QResizeEvent *event)
{
float thisAspectRatio = (float)event->size().width() / event->size().height();
int widgetStretch, outerStretch;
if (thisAspectRatio > (arWidth/arHeight)) // слишком широкий
{
layout->setDirection(QBoxLayout::LeftToRight);
widgetStretch = height() * (arWidth/arHeight); // т.е. ширина виджета
outerStretch = (width() - widgetStretch) / 2 + 0.5;
}
else // слишком высокий
{
layout->setDirection(QBoxLayout::TopToBottom);
widgetStretch = width() * (arHeight/arWidth); // т.е. высота виджета
outerStretch = (height() - widgetStretch) / 2 + 0.5;
}
layout->setStretch(0, outerStretch);
layout->setStretch(1, widgetStretch);
layout->setStretch(2, outerStretch);
}
//---------------------------------------------------------------------------------------
Результат
При размещении диаграммы QChartView в виджете AspectRatioWidget искажения, связанные с соотношением сторон, больше не появляются.
