如何在 Qt 中保持小部件的长宽比?

发布于 2024-07-11 06:34:08 字数 36 浏览 9 评论 0原文

如何在 Qt 中保持小部件的纵横比以及使小部件居中怎么样?

How is it possible to maintain widgets aspect ratio in Qt and what about centering the widget?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

魂牵梦绕锁你心扉 2024-07-18 06:34:08

您不必实现自己的布局管理器。 您可以继承QWidget并重新实现

int QWidget::heightForWidth( int w ) { return w; }

以保持方形。 但是,heightForWidth() 不适用于 X11 上的顶级窗口,因为显然 X11 协议不支持这一点。 至于居中,可以将Qt::AlignCenter作为QBoxLayout::addWidget()的第三个参数或QGridLayout::addWidget()的第五个参数传递

注意:至少在较新版本的 Qt 中,QWidget 不再具有 heightForWidthwidthForHeight (因此它们无法被覆盖),因此setWidthForHeight(true)setHeightForHeight(true) 仅对 QGraphicsLayout 的后代有效

You don't have to implement your own layout manager. You can do with inheriting QWidget and reimplementing

int QWidget::heightForWidth( int w ) { return w; }

to stay square. However, heightForWidth() doesn't work on toplevel windows on X11, since apparently the X11 protocol doesn't support that. As for centering, you can pass Qt::AlignCenter as the third parameter of QBoxLayout::addWidget() or the fifth parameter of QGridLayout::addWidget().

Note: In newer versions of Qt at least, QWidget does not have the heightForWidth or widthForHeight anymore (so they cannot be overriden), and therefore setWidthForHeight(true) or setHeightForWidth(true) only have an effect for descendants of QGraphicsLayout.

墨洒年华 2024-07-18 06:34:08

正确的答案是创建自定义布局管理器。 这可以通过子类化 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;
返回布局的首选大小。

进一步阅读

The right answer is to create your custom layout manager. That is possible by subclassing QLayout.

Methods to implement when subclassing QLayout

void addItem(QLayoutItem* item);
Adds item to layout.
int count() const;
Returns the item count.
QLayoutItem* itemAt(int index) const;
Returns item reference at index or 0 if there's none.
QLayoutItem* takeAt(int index);
Takes and returns item from the layout from index or returns 0 if there is none.
Qt::Orientations expandingDirections() const;
Returns the layouts expanding directions.
bool hasHeightForWidth() const;
Tells if the layout handles height for width calculation.
QSize minimumSize() const;
Returns the layouts minimum size.
void setGeometry(const QRect& rect);
Sets the geometry of the layout and the items inside it. Here you have to maintain the aspect ratio and do the centering.
QSize sizeHint() const;
Returns the preferred size for the layout.

Further reading

眼趣 2024-07-18 06:34:08

resizeEvent() 内部调用 resize() 对我来说从来都没有很好的效果——充其量它会导致闪烁,因为窗口大小调整了两次(就像你所做的那样),在最糟糕的是无限循环。

我认为保持固定宽高比的“正确”方法是创建自定义布局。 您只需重写两个方法:QLayoutItem::hasHeightForWidth()QLayoutItem::heightForWidth()

Calling resize() from within resizeEvent() has never worked well for me -- at best it will cause flickering as the window is resized twice (as you have), at worst an infinite loop.

I think the "correct" way to maintain a fixed aspect ratio is to create a custom layout. You'll have to override just two methods, QLayoutItem::hasHeightForWidth() and QLayoutItem::heightForWidth().

云胡 2024-07-18 06:34:08

我也试图实现所要求的效果:一个保持固定纵横比同时保持在其分配空间中心的小部件。 起初,我尝试了这个问题的其他答案:

  • 按照 marc-mutz-mmutz 的建议实现 heightForWidthhasHeightForWidth 对我来说根本不起作用。
  • 我简单地考虑过实现自定义布局管理器,但所有 Bleadof 的链接都已失效,当我发现 文档并通读它,对于我想要实现的目标来说,它看起来太复杂了。

我最终创建了一个响应 resizeEvent< 的自定义小部件/a> 并使用 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++ 或您选择的语言表达应该很简单。)

I too was trying to achieve the requested effect: a widget that keeps a fixed aspect ratio while staying centred in its allocated space. At first I tried other answers from this question:

  • implementing heightForWidth and hasHeightForWidth as suggested by marc-mutz-mmutz simply didn't work for me.
  • I briefly looked at implementing a custom layout manager, but all Bleadof's links were dead, and when I found the documentation and read through it, it looked way too complicated for what I was trying to achieve.

I ended up creating a custom widget that responds to resizeEvent and uses setContentsMargin to set margins such that the remaining content area keeps the desired ratio.

I found I also had to set the widget's size policy to QSizePolicy::Ignored in both directions to avoid odd resizing issues resulting from the size requests of child widgets—the end result is that my widget accepts whatever size its parent allocates to it (and then sets its margins as described above to keep the desired aspect ratio in its content area).

My code looks like this:

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)

(Obviously, this code is in Python, but it should be straightforward to express in C++ or your language of choice.)

如果没有你 2024-07-18 06:34:08

就我而言,覆盖 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 的事件并设置正确的高度调整事件大小。

In my case overriding heightForWidth() doesn't work. And, for someone, it could be helpful to get working example of using resize event.

At first subclass qObject to create filter. 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
};

Then eventFilter function. It's code should be defined outside of FilterObject definition to prevent warning. 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;
};

And then in main code create FilterObject and set it as EventFilter listener to target object. Thanks to this answer.

FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();//let it be target object
filter->target=targetWidget;
targetWidget->installEventFilter(filter);

Now filter will receive all targetWidget's events and set correct height at resize event.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文