QT将QWidget设置为Tool弹出框,业务上需要根据实际回填内容改变窗体高度,比如一条数据30px高,那么空的时候为0,n条的时候为n*30。然后发现空的时候高度被固定为了160;

代码如下:

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QDebug>
#include <QObject>
#include <QVBoxLayout>
#include <QWidget>

class MyWidget : public QWidget {
    Q_OBJECT
  public:
    explicit MyWidget(QWidget* parent = nullptr);
    ~MyWidget() { qDebug() << this << "~MyWidget"; }

  signals:
  private:
};

#endif  // MYWIDGET_H
#include "mywidget.h"

MyWidget::MyWidget(QWidget* parent) : QWidget{parent} {
    QWidget::setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);

    QWidget::setAttribute(Qt::WA_ShowWithoutActivating);
    QWidget::setWindowFlag(Qt::Tool);
    QWidget::setWindowFlag(Qt::NoDropShadowWindowHint);

    QWidget::resize(500, 300);
    QWidget::move(500, 500);
    QWidget::hide();

    QWidget::setStyleSheet("background-color:skyblue;");
}

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>

#include "mywidget.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    MyWidget* m_popup;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    m_popup = new MyWidget(this);
    m_popup->resize(1000, 600);

    connect(ui->pushButton, &QPushButton::clicked, this, [this]() {
        m_popup->resize(m_popup->width(), 0);
        m_popup->show();
    });
    connect(ui->pushButton_2, &QPushButton::clicked, this, [this]() {
        m_popup->resize(m_popup->width(), 320);
        m_popup->show();
    });
}

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

效果如下所示:

image.png

image.png

原因分析

QWidget设置为Tool,变成弹出框,然后就调用了hide,一直没show过,所以对应的windows窗口没有被创建出来,调用show()的时候,会创建windows窗口,具体调用流程如下:

QWidget::show()->QWidget::setVisible(bool visible)-> QWidgetPrivate::setVisible(bool visible)->QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow)->QWidgetPrivate::create()->QWindow::create()->QWindowPrivate::create(bool recursive, WId nativeHandle)->QWindowsIntegration::createPlatformWindow(QWindow *window)->
QWindowsWindowData::create(const QWindow w,const QWindowsWindowData ¶meters,const QString &title)->WindowCreationData::create(const QWindow w, const WindowData &data, QString title)->QPlatformWindow::initialGeometry(const QWindow w, const QRect &initialGeometry, int defaultWidth, int defaultHeight,const QScreen resultingScreenReturn)->static QSize fixInitialSize(QSize size, const QWindow w, int deviceIndependentDefaultWidth,int deviceIndependentDefaultHeight)

最终设置窗体大小的代码就是fixInitialSize函数(在qplatformwindow.cpp),如下所示:

static QSize fixInitialSize(QSize size, const QWindow *w, int deviceIndependentDefaultWidth,
                            int deviceIndependentDefaultHeight)
{
    if (size.width() == 0) {
        const int minWidth = w->minimumWidth();
        size.setWidth(minWidth > 0 ? minWidth : deviceIndependentDefaultWidth);
    }
    if (size.height() == 0) {
        const int minHeight = w->minimumHeight();
        size.setHeight(minHeight > 0 ? minHeight : deviceIndependentDefaultHeight);
    }
    return size;
}

用到的defaultwidth和defaultheight定义在qwindowswindow.cpp文件,如下所示:

enum {
    defaultWindowWidth = 160,
    defaultWindowHeight = 160
};

在创建窗口的时候如果宽高为0,则会使用minimumWidth/minumumHeight(>0)或160.

解决方案

  1. MyWidget构造里先调用show,让它创建出窗体

image.png

缺点:
后面调用的resize(1000,600),宽度无效
提前创建窗体会耗费性能

  1. 在点击事件里调用show创建窗体,然后hide,再调用resize

image.png

  1. 在点击事件里判断一下宽高,然后决定是否调用show

image.png


点墨
26 声望3 粉丝

全栈前端开发工程师