Как можно поддерживать соотношение сторон виджетов в Qt и как насчет центрирования виджета?





Вызов resize() изнутри resizeEvent() никогда не работал для меня хорошо - в лучшем случае это вызовет мерцание, когда размер окна изменяется дважды (как у вас), в худшем случае - бесконечный цикл.
Я думаю, что «правильный» способ сохранить фиксированное соотношение сторон - это создать собственный макет. Вам нужно будет переопределить всего два метода: QLayoutItem::hasHeightForWidth() и QLayoutItem::heightForWidth().
Правильный ответ - создать свой собственный менеджер по расположению. Это возможно путем создания подкласса QLayout.
Вы говорите о создании подкласса QLayout, но цитируемые вами функции взяты из QLayoutItem, который просто вызывает соответствующие функции из QWidget. Так что же наследование QLayout может дать вам такого, чего не может дать сам QWidget?
также все ссылки мертвы.
Вам не нужно реализовывать свой собственный менеджер по расположению. Вы можете унаследовать QWidget и переопределить
int QWidget::heightForWidth( int w ) { return w; }
оставаться квадратным. Однако heightForWidth() не работает с окнами верхнего уровня на X11, поскольку, очевидно, протокол X11 этого не поддерживает. Что касается центрирования, вы можете передать Qt::AlignCenter в качестве третьего параметра для QBoxLayout::addWidget() или пятого параметра для QGridLayout::addWidget().
Примечание: По крайней мере, в более новых версиях Qt QWidget больше не имеет heightForWidth или widthForHeight (поэтому они не могут быть переопределены) и, следовательно, setWidthForHeight(true) или setHeightForWidth(true)имеют эффект только для потомков QGraphicsLayout.
Да. Вам нужно установить sizePolicy, а не hasHeightForWidth() == true, и макет часто дает виджету большую ширину или высоту, которые он запрашивает, но этот макет пытается удовлетворить ограничения. Вы получите тот же эффект с решением QLayoutItem, поскольку они эквивалентны (см. QWidgetLayoutItem).
@ MarcMutz-mmutz: Конечно, X11 поддерживает это: посмотрите XSizeHints tronche.com/gui/x/xlib/ICC/client-to-window-manager/…, которые позволяют сообщать оконному менеджеру соотношение сторон окна.
Что, если я хочу вместо этого установить widthForHeight? QWidget не имеет метода widthForHeight.
Итак, как это сделать в новых версиях Qt?
Ммм ... как для Qt 5.7 doc.qt.io/qt-5/qwidget.html#hasHeightForWidth
В моем случае переопределение heightForWidth () не работает. А кому-то может быть полезно получить рабочий пример использования события изменения размера.
Сначала создайте подкласс qObject, чтобы создать фильтр. Подробнее о фильтрах событий.
class FilterObject:public QObject{
public:
QWidget *target = nullptr;//it holds a pointer to target object
int goalHeight=0;
FilterObject(QObject *parent=nullptr):QObject(parent){}//uses QObject constructor
bool eventFilter(QObject *watched, QEvent *event) override;//and overrides eventFilter function
};
Затем функция eventFilter. Его код должен быть определен вне определения FilterObject, чтобы предотвратить предупреждение. Благодаря этому ответу.
bool FilterObject::eventFilter(QObject *watched, QEvent *event) {
if (watched!=target){//checks for correct target object.
return false;
}
if (event->type()!=QEvent::Resize){//and correct event
return false;
}
QResizeEvent *resEvent = static_cast<QResizeEvent*>(event);//then sets correct event type
goalHeight = 7*resEvent->size().width()/16;//calculates height, 7/16 of width in my case
if (target->height()!=goalHeight){
target->setFixedHeight(goalHeight);
}
return true;
};
Затем в основном коде создайте FilterObject и установите его как прослушиватель EventFilter для целевого объекта. Благодаря этому ответу.
FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();//let it be target object
filter->target=targetWidget;
targetWidget->installEventFilter(filter);
Теперь фильтр будет получать все события targetWidget и устанавливать правильную высоту при изменении размера.
Чтобы избежать мерцания - как упоминалось в ответе выше, не добавляйте виджет в макет, просто сделайте его дочерним по отношению к родительскому, а затем установите размер вручную.
Я тоже пытался добиться запрошенного эффекта: виджет, который сохраняет фиксированное соотношение сторон, оставаясь центрированным в выделенном пространстве. Сначала я попробовал другие ответы на этот вопрос:
heightForWidth и hasHeightForWidth, предложенная marc-mutz-mmutz, просто не сработала для меня.В итоге я создал собственный виджет, который реагирует на resizeEvent и использует setContentsMargin для установки полей таким образом, чтобы оставшаяся область содержимого сохраняла желаемое соотношение.
Я обнаружил, что мне также пришлось установить политику размера виджета на QSizePolicy::Ignored в обоих направлениях, чтобы избежать нечетных проблем с изменением размера, возникающих из-за запросов размера дочерних виджетов - конечный результат состоит в том, что мой виджет принимает любой размер, который ему присваивает родительский элемент (а затем устанавливает его поля, как описано выше, чтобы сохранить желаемое соотношение сторон в области содержимого).
Мой код выглядит так:
from PySide2.QtWidgets import QWidget, QSizePolicy
class AspectWidget(QWidget):
'''
A widget that maintains its aspect ratio.
'''
def __init__(self, *args, ratio=4/3, **kwargs):
super().__init__(*args, **kwargs)
self.ratio = ratio
self.adjusted_to_size = (-1, -1)
self.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored))
def resizeEvent(self, event):
size = event.size()
if size == self.adjusted_to_size:
# Avoid infinite recursion. I suspect Qt does this for you,
# but it's best to be safe.
return
self.adjusted_to_size = size
full_width = size.width()
full_height = size.height()
width = min(full_width, full_height * self.ratio)
height = min(full_height, full_width / self.ratio)
h_margin = round((full_width - width) / 2)
v_margin = round((full_height - height) / 2)
self.setContentsMargins(h_margin, v_margin, h_margin, v_margin)
(Очевидно, что этот код написан на Python, но его должно быть просто выразить на C++ или на вашем выбранном языке.)
Это один из немногих вариантов, который действительно работает. Я часто заикаюсь при изменении размера окна, это что-то связано с моим приложением, в частности, или это что-то общее для этого решения?
Это кажется правильным ответом, но мне нужно разобраться в нем, когда я на работе, и, возможно, дать более подробный ответ после того, как я это сделаю.