Сохранение соотношения сторон подкласса QWidget при изменении размера

Добавлено 27 июня 2021 в 22:30

Описание проблемы

По умолчанию при изменении размера виджета, наследуемого от QWidget, соотношение его сторон не сохраняется на постоянном значении. Но встречаются случаи, когда это недопустимо, поскольку искажается изображение, выводимое виджетом. Возьмем для примера QChartView, выводящий диаграмму Вольперта-Смита. В простейшем случае данная диаграмма представляет собой окружности постоянного активного сопротивления и дуги постоянного реактивного сопротивления.

Диаграмма Вольперта-Смита (как должна выглядеть)
Рисунок 1 – Диаграмма Вольперта-Смита (как должна выглядеть)

Но при изменении размеров окна приложения появляются искажения диаграммы:

Искажение представления диаграммы при изменении размеров виджета
Рисунок 2 – Искажение представления диаграммы при изменении размеров виджета
Искажение представления диаграммы при изменении размеров виджета
Рисунок 3 – Искажение представления диаграммы при изменении размеров виджета

Решение

Возможный вариант решения (найден на stackoverflow) заключается в следующем:

  1. Создать родительский виджет (например, AspectRatioWidget подкласса QWidget) для размещения на нем целевого виджета.
  2. В этом родительском виджете создать QBoxLayout. Поместить целевой виджет в центр, а заполнители QSpacerItem по бокам.
  3. Затем в этом родительском виджете переопределить метод 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 искажения, связанные с соотношением сторон, больше не появляются.

Отсутствие искажений представления диаграммы при изменении размеров виджета
Рисунок 4 – Отсутствие искажений представления диаграммы при изменении размеров виджета
Отсутствие искажений представления диаграммы при изменении размеров виджета
Рисунок 5 – Отсутствие искажений представления диаграммы при изменении размеров виджета
Отсутствие искажений представления диаграммы при изменении размеров виджета
Рисунок 6 – Отсутствие искажений представления диаграммы при изменении размеров виджета
 

Теги

AspectRatioWidgetC++ / CppGUI / Графический интерфейс пользователяQBoxLayoutQChartQChartViewQSpacerItemQtQt WidgetsQtChartsQWidget

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.