在调整大小时保持子类化QWidget的纵横比

15
我正在创建一个新的小部件,通过子类化QWidget类。我想能够为这个小部件设置一个比例(高度和宽度),并始终保持该比例。
为此,我一直在使用Qt5文档、谷歌和Stackoverflow进行搜索。显然,我找到了答案:特别是this one。但是,不幸的是,没有一个完全有效。
  • 即使部件是窗口,设置sizeIncrement也完全没有作用。
  • 我试图重载resizeEvent,但我真的不知道如何做到这一点...
  • 如果我按照这个答案的方法:

    1. 如果部件是顶级窗口,比例根本没有得到保持,我可以随意调整它。
    2. 如果我将此部件放置在布局中,如果我只增加窗口的宽度和高度,比例是得以保持的。但是,一旦我将宽度或高度增加太多,部件就被压扁了。相反,我希望布局自动调整其大小以保持部件的比例。

那么,如何保持子类化QWidget的宽高比?


这是我另一个成功应用的帖子,至少在 QLabel 中有效。https://dev59.com/dGsy5IYBdhLWcg3w8Slc - phyatt
你尝试重新实现QWidget::heightForWidth()了吗?这允许您指定宽高比。如果这还不够,创建一个父级“虚拟”QWidget将您的widget置于3x3的QGridLayout中,将您的widget放在中间,并在边缘周围放置QSpacerItem(参见 QGridLayout::addItem() )。当调整大小时,QSpacerItem可以提供额外的间距(或缩小到0)。您可能需要使用QGridLayout::setColumnStretch()QGridLayout::setRowStretch() - Anthony
谢谢你们俩,你们的解决方案都有效,但是仍然存在同样的问题:如果我尝试仅增加小部件的宽度,则不再保持纵横比。 - Spirine
2个回答

17

创建一个父部件(例如AspectRatioWidget),将您的小部件放置在其中。 对于父部件,子类化QWidget并给它一个QBoxLayout。 将您的小部件放在中心位置,并在两侧放置QSpacerItems。 然后,在父部件的QWidget :: resizeEvent中根据需要调整方向和伸展性。 下面我提供了一个示例。 要使用,请只需创建一个AspectRatioWidget实例并将指针传递给您的小部件以及所需的纵横比。

// header
class AspectRatioWidget : public QWidget
{
public:
    AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent = 0);
    void resizeEvent(QResizeEvent *event);

private:
    QBoxLayout *layout;
    float arWidth; // aspect ratio width
    float arHeight; // aspect ratio height
};

// cpp
AspectRatioWidget::AspectRatioWidget(QWidget *widget, float width, float height, QWidget *parent) :
    QWidget(parent), arWidth(width), arHeight(height)
{
    layout = new QBoxLayout(QBoxLayout::LeftToRight, this);

    // add spacer, then your widget, then spacer
    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)) // too wide
    {
        layout->setDirection(QBoxLayout::LeftToRight);
        widgetStretch = height() * (arWidth/arHeight); // i.e., my width
        outerStretch = (width() - widgetStretch) / 2 + 0.5;
    }
    else // too tall
    {
        layout->setDirection(QBoxLayout::TopToBottom);
        widgetStretch = width() * (arHeight/arWidth); // i.e., my height
        outerStretch = (height() - widgetStretch) / 2 + 0.5;
    }

    layout->setStretch(0, outerStretch);
    layout->setStretch(1, widgetStretch);
    layout->setStretch(2, outerStretch);
}

2
谢谢!这个解决方案是唯一一个对我有效的,通过子类化 QOpenGLWidget 实现。我在构造函数中添加了 layout->setContentsMargins(0, 0, 0, 0); 来去除边距。 - ForeverLearning
@Anothoy 随机问题,但是有没有办法在QHBoxLayout中使用两个这些AspectRatioWidgets,并在两侧使用伸展,仍然使它们填充空间?当我在纵横比小部件的任一侧使用addStretch时,它会将它们强制缩小到最小尺寸,并且不再放大/缩小。 - 956MB

9

我已经用Python/PySide2重写了Anthony的代码:

from PySide2.QtWidgets import QBoxLayout, QSpacerItem, QWidget


class AspectRatioWidget(QWidget):
    def __init__(self, widget, parent):
        super().__init__(parent)
        self.aspect_ratio = widget.size().width() / widget.size().height()
        self.setLayout(QBoxLayout(QBoxLayout.LeftToRight, self))
        #  add spacer, then widget, then spacer
        self.layout().addItem(QSpacerItem(0, 0))
        self.layout().addWidget(widget)
        self.layout().addItem(QSpacerItem(0, 0))

    def resizeEvent(self, e):
        w = e.size().width()
        h = e.size().height()

        if w / h > self.aspect_ratio:  # too wide
            self.layout().setDirection(QBoxLayout.LeftToRight)
            widget_stretch = h * self.aspect_ratio
            outer_stretch = (w - widget_stretch) / 2 + 0.5
        else:  # too tall
            self.layout().setDirection(QBoxLayout.TopToBottom)
            widget_stretch = w / self.aspect_ratio
            outer_stretch = (h - widget_stretch) / 2 + 0.5

        self.layout().setStretch(0, outer_stretch)
        self.layout().setStretch(1, widget_stretch)
        self.layout().setStretch(2, outer_stretch)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接