35

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

4

5 回答 5

21

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

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

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

注意:至少在 Qt 的较新版本中, QWidget 不再具有heightForWidthor widthForHeight(因此它们不能被覆盖),因此setWidthForHeight(true)orsetHeightForWidth(true) 仅对 QGraphicsLayout 的后代有影响

于 2009-07-21T17:08:48.103 回答
8

正确的答案是创建您的自定义布局管理器。这可以通过继承 QLayout来实现。

子类化 QLayout 时实现的方法

void addItem(QLayoutItem* item);
将项目添加到布局。
int count() 常量;
返回项目计数。
QLayoutItem* itemAt(int index) const;
返回索引处的项目引用,如果没有则返回 0。
QLayoutItem* takeAt(int index);
从索引的布局中获取并返回项目,如果没有则返回 0。
Qt::方向扩展方向()常量;
返回布局扩展方向。
bool hasHeightForWidth() 常量;
告诉布局是否处理宽度计算的高度。
QSize minimumSize() 常量;
返回布局的最小尺寸。
void setGeometry(const QRect& rect);
设置布局的几何形状和其中的项目。在这里,您必须保持纵横比并进行居中。
QSize sizeHint() 常量;
返回布局的首选大小。

进一步阅读

于 2009-02-18T19:29:50.197 回答
6

resize()从内部调用resizeEvent()对我来说从来都不是很好 - 充其量它会导致闪烁,因为窗口被调整了两次(就像你一样),最坏的情况是无限循环。

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

于 2009-01-17T00:13:34.437 回答
5

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

  • 实施heightForWidthhasHeightForWidth按照 marc-mutz-mmutz 的建议对我根本不起作用。
  • 我简要地研究了实现一个自定义布局管理器,但是 Bleadof 的所有链接都失效了,当我找到文档并通读它时,它看起来对我想要实现的目标来说太复杂了。

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

于 2020-05-04T10:19:41.860 回答
3

在我的情况下,覆盖 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 的事件并在调整大小事件时设置正确的高度。

于 2018-09-29T15:58:10.663 回答