Qt布局 - 调整大小时保持小部件的宽高比

4
我希望在QGroupBox上保持16/9的宽高比(如下图左侧的那个)。为了测试,我创建了一个简单的程序,在垂直布局中有4个groupbox,全部在水平布局中:

main.cpp

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();


private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"

#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug() << "horizontalLayout->hasHeightForWidth: " << ui->horizontalLayout->hasHeightForWidth();
}

MainWindow::~MainWindow()
{
    delete ui;
}

MainWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1000</width>
    <height>703</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
     <widget class="GroupBoxHFW" name="groupBox_1">
      <property name="title">
       <string>GroupBox</string>
      </property>
     </widget>
    </item>
    <item>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <widget class="QGroupBox" name="groupBox_2">
        <property name="minimumSize">
         <size>
          <width>485</width>
          <height>204</height>
         </size>
        </property>
        <property name="maximumSize">
         <size>
          <width>485</width>
          <height>204</height>
         </size>
        </property>
        <property name="title">
         <string>GroupBox</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QGroupBox" name="groupBox_3">
        <property name="minimumSize">
         <size>
          <width>485</width>
          <height>204</height>
         </size>
        </property>
        <property name="maximumSize">
         <size>
          <width>485</width>
          <height>204</height>
         </size>
        </property>
        <property name="title">
         <string>GroupBox</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QGroupBox" name="groupBox_4">
        <property name="minimumSize">
         <size>
          <width>485</width>
          <height>204</height>
         </size>
        </property>
        <property name="maximumSize">
         <size>
          <width>485</width>
          <height>204</height>
         </size>
        </property>
        <property name="title">
         <string>GroupBox</string>
        </property>
       </widget>
      </item>
      <item>
       <spacer name="verticalSpacer">
        <property name="orientation">
         <enum>Qt::Vertical</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
          <width>20</width>
          <height>40</height>
         </size>
        </property>
       </spacer>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>1000</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="toolBar">
   <property name="windowTitle">
    <string>toolBar</string>
   </property>
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <customwidgets>
  <customwidget>
   <class>GroupBoxHFW</class>
   <extends>QGroupBox</extends>
   <header>GroupBoxHFW.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

GroupBoxHFW.h

#ifndef GROUPBOXHFW_H
#define GROUPBOXHFW_H

#include <QGroupBox>

class GroupBoxHFW : public QGroupBox
{
public:
    GroupBoxHFW(QWidget * parent = 0);
    GroupBoxHFW(const QString & title, QWidget * parent = 0);

    int heightForWidth(int w) const;
    bool hasHeightForWidth() const;
};

#endif // GROUPBOXHFW_H

GroupBoxHFW.cpp

#include "GroupBoxHFW.h"

#include <QDebug>

GroupBoxHFW::GroupBoxHFW(QWidget * parent) : QGroupBox(parent)
{
    QSizePolicy policy(this->sizePolicy());
    policy.setHeightForWidth(true);

    this->setSizePolicy(policy);
}

GroupBoxHFW::GroupBoxHFW(const QString & title, QWidget * parent) : QGroupBox(title, parent)
{
    QSizePolicy policy(this->sizePolicy());
    policy.setHeightForWidth(true);

    this->setSizePolicy(policy);
}


int GroupBoxHFW::heightForWidth(int w) const
{
    qDebug() << "GroupBoxHFW::heightForWidth called";
    return 9.f / 16.f * w;
}

bool GroupBoxHFW::hasHeightForWidth() const
{
    qDebug() << "GroupBoxHFW::hasHeightForWidth called";
    return true;
}

如您所见,我重新实现了虚函数int heightForWidth(int w) constbool hasHeightForWidth() const,希望我的QGroupBox能够自动调整为16/9的宽高比。不幸的是,它并没有起作用。GroupBoxHFW::heightForWidth()只在程序启动时被调用一次,甚至没有调整小部件大小。
以下是程序的输出:
GroupBoxHFW::hasHeightForWidth called
horizontalLayout->hasHeightForWidth:  true
GroupBoxHFW::hasHeightForWidth called
GroupBoxHFW::heightForWidth called
GroupBoxHFW::hasHeightForWidth called
GroupBoxHFW::hasHeightForWidth called
GroupBoxHFW::hasHeightForWidth called
GroupBoxHFW::hasHeightForWidth called

更奇怪的是,当我调整窗口大小时,上述任何一个函数(hasHeightForWidth()heightForWidth())都没有被调用,而QGroupBox却被调整了大小。
出了什么问题?

如何在Qt中保持小部件的宽高比?中提到,子类化QLayout并覆盖heightForWidth方法是更好的方法。 - Trilarion
那我只需要继承 QLayout 并重新实现 hasHeightForWidth()heightForWidth(),就像我为 QGroupBox 所做的一样? - GuiTeK
我认为是这样,但我自己从未尝试过,所以无法保证。 - Trilarion
我尝试了,但没有任何效果。 - GuiTeK
1个回答

2

如果此组框是在垂直布局内部,则您的方法可能有效。

正如名称所示,hasHeightForWidth在高度取决于宽度时(这是为文本换行而设计的),而不是反之(您的情况)。

你可以怎么做?尝试这个(我曾经为QGraphicsWidget实现过类似的东西,效果相当不错):

QSize GroupBoxHFW::sizeHint() const {
    QSize s = size();
    lastHeight = s.height();
    s.setWidth((s.height()*16)/9);
    s.setHeight(QGroupBox::sizeHint().height());
    return s;
}

void GroupBoxHFW::resizeEvent(QResizeEvent * event) {
    QGroupBox::resizeEvent(event);

    if (lastHeight!=height()) {
        updateGeometry(); // it is possible that this call should be scheduled to next iteration of event loop
    }
}

小题外话:
如果我要实现这个功能,我会尝试通过子类化QLayout来实现,而不是作为某个QWidget的子类。这样可以多次用于不同的小部件。


它还是不起作用。 注意:您忘记了sizeHint()是const,您不能在此函数中分配成员变量(lastHeight)。我删除了resizeEvent()中的条件以解决问题。 - GuiTeK
1
为该字段添加关键字“mutable”。在“resizeEvent”中加入条件是必须的,以防止无限循环。 - Marek R
1
我还假设您已经使用了适当的大小策略。水平大小策略应为“固定”。 - Marek R
谢谢,它有效了!也学到了关键字mutable :P! - GuiTeK

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