如何在 Qt 中保持小部件的纵横比?
Posted
技术标签:
【中文标题】如何在 Qt 中保持小部件的纵横比?【英文标题】:How to maintain widgets aspect ratio in Qt? 【发布时间】:2010-10-01 22:15:06 【问题描述】:如何在 Qt 中保持小部件的纵横比以及如何使小部件居中?
【问题讨论】:
【参考方案1】:您不必实现自己的布局管理器。你可以继承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)
only have an effect for descendants of 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【参考方案2】:
正确的答案是创建您的自定义布局管理器。这可以通过继承QLayout 来实现。
子类化QLayout时要实现的方法
-
void addItem(QLayoutItem* item);
将项目添加到布局。
int count() const;
返回项目计数。
QLayoutItem* itemAt(int index) const;
返回索引处的项目引用,如果没有则返回 0。
QLayoutItem* takeAt(int index);
从索引中获取并返回布局中的项目,如果没有则返回 0。
Qt::Orientations expandingDirections() const;
返回布局扩展方向。
bool hasHeightForWidth() const;
判断布局是否处理宽度计算的高度。
QSize minimumSize() const;
返回布局的最小尺寸。
void setGeometry(const QRect& rect);
设置布局的几何形状和其中的项目。在这里,您必须保持纵横比并进行居中。
QSize sizeHint() const;
返回布局的首选大小。
进一步阅读
Maintaining square form for a widget in Qt @ Forum Nokia Implementing a layout manager in Qt @ Forum Nokia Writing custom layout managers @ Qt documentation【讨论】:
你说的是子类化QLayout
,但是你引用的函数来自QLayoutItem
,它只是调用了来自QWidget
的对应函数。那么继承QLayout
可以给你什么QWidget
本身不能?
还有,所有链接都失效了。【参考方案3】:
从resizeEvent()
内部调用resize()
对我来说从来没有奏效过——充其量它会在窗口大小调整两次时导致闪烁(就像你一样),最坏的情况是无限循环。
我认为保持固定纵横比的“正确”方法是创建自定义布局。您只需要重写两个方法,QLayoutItem::hasHeightForWidth()
和 QLayoutItem::heightForWidth()
。
【讨论】:
这似乎是正确的答案,但我必须在工作时研究它,并在完成后给出更详细的答案。【参考方案4】:我也试图实现所要求的效果:一个保持固定纵横比的小部件,同时保持在其分配空间的中心。起初我尝试了这个问题的其他答案:
按照 marc-mutz-mmutz 的建议实现heightForWidth
和 hasHeightForWidth
根本不适合我。
我简要地研究了实现自定义布局管理器,但 Bleadof 的所有链接都已失效,当我找到 the documentation 并通读它时,它看起来对我想要实现的目标来说太复杂了。
我最终创建了一个自定义小部件,它响应 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++ 或您选择的语言来表达。)
【讨论】:
这是少数几个真正有效的选项之一。我在调整大小时往往会发现窗口卡顿,这是与我的应用程序特别相关还是此解决方案的常见问题? @LorenzoCastellino 我在我的应用程序中看不到任何调整窗口大小的卡顿。【参考方案5】:在我的情况下,覆盖 heightForWidth() 不起作用。而且,对于某些人来说,获得使用调整大小事件的工作示例可能会有所帮助。
首先子类 qObject 来创建过滤器。 More about event filters.
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 定义之外定义以防止警告。 Thanks to this answer.
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 侦听器到目标对象。 Thanks to this answer.
FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();//let it be target object
filter->target=targetWidget;
targetWidget->installEventFilter(filter);
现在过滤器将接收所有 targetWidget 的事件并在调整大小事件时设置正确的高度。
【讨论】:
为避免闪烁 - 如上面的答案所述,不要将小部件添加到布局中,只需使其成为父级的子级,然后手动设置大小。以上是关于如何在 Qt 中保持小部件的纵横比?的主要内容,如果未能解决你的问题,请参考以下文章