如何在Qt中维护小部件的宽高比?

时间:2009-01-16 22:50:36

标签: user-interface qt widget aspect-ratio

如何在Qt中维护窗口小部件的宽高比以及如何使窗口小部件居中呢?

5 个答案:

答案 0 :(得分:16)

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

int QWidget::heightForWidth( int w ) { return w; }
保持正方形。但是,heightForWidth()不适用于X11上的顶层窗口,因为显然X11协议不支持这种情况。至于居中,您可以将Qt::AlignCenter作为QBoxLayout::addWidget()的第三个参数或QGridLayout::addWidget()的第五个参数。

注意:至少在较新版本的Qt中,QWidget不再具有heightForWidthwidthForHeight(因此无法覆盖),因此{{1 }或setWidthForHeight(true) only have an effect for descendants of QGraphicsLayout

答案 1 :(得分:8)

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

进一步阅读

答案 2 :(得分:5)

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

  • 按照marc-mutz-mmutz的建议实施heightForWidthhasHeightForWidth根本不适合我。
  • 我简要地看了一下实现自定义布局管理器的过程,但是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 ++或您选择的语言表达应该很简单。)