重载resizeEvent
2018-09-20 本文已影响12人
downdemo
简单实现
- 目标:改变窗口大小时,窗口中的控件布局将相应改变。如:窗口中包含两个QPushButton控件,宽度小于400时,两个按钮垂直排列,宽度拉伸不低于400时,两个按钮则变为垂直排列;
- 方法:重载resizeEvent;
- Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QMainWindow>
#include "qpushbutton.h"
#include "qboxlayout.h"
namespace Ui {
class Form;
}
class widget : public QMainWindow
{
Q_OBJECT
public:
explicit widget(QWidget *parent = 0);
~widget();
private:
Ui::Form *ui;
QVBoxLayout* layout;
QHBoxLayout* layout2;
QPushButton* btn1;
QPushButton* btn2;
QWidget* centralWidget;
protected:
void resizeEvent(QResizeEvent* event);
};
#endif // WIDGET_H
- Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
widget::widget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Form)
{
ui->setupUi(this);
btn1 = new QPushButton;
btn1->setText("1");
btn2 = new QPushButton;
btn2->setText("2");
centralWidget = new QWidget;
setCentralWidget(centralWidget);
}
widget::~widget()
{
delete ui;
}
void widget::resizeEvent(QResizeEvent* event)
{
int w = width();
if (w < 400)
{
if (centralWidget->layout()) delete centralWidget->layout();
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(btn1);
layout->addWidget(btn2);
centralWidget->setLayout(layout);
}
else
{
if (centralWidget->layout()) delete centralWidget->layout();
QHBoxLayout* layout2 = new QHBoxLayout;
layout2->addWidget(btn1);
layout2->addWidget(btn2);
centralWidget->setLayout(layout2);
}
}
实现效果


存在问题
- 当窗口尺寸不低于400,点击最大化和还原不会改变布局(正常预期)

- 但当窗口尺寸低于400,按钮纵向排列,点击最大化之后按钮仍纵向排列(预期是窗口不低于400宽度则横向排列)

- 再次点击还原,布局却又变为横向排列(预期是窗口低于400宽度则纵向排列)

- 不断点击最大化和还原则将重复上述两种情况,直到拖动窗口大小时又会变为正常情况
改进
- 通过qDebug测试,最大化和还原都正确触发了事件
void widget::resizeEvent(QResizeEvent* event)
{
int w = width();
if (w < 400)
{
if (centralWidget->layout())
{
delete centralWidget->layout();
qDebug() << "d1";
}
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(btn1);
layout->addWidget(btn2);
centralWidget->setLayout(layout);
qDebug() << "aaa";
}
else
{
if (centralWidget->layout())
{
delete centralWidget->layout();
qDebug() << "d2";
}
QHBoxLayout* layout2 = new QHBoxLayout;
layout2->addWidget(btn1);
layout2->addWidget(btn2);
centralWidget->setLayout(layout2);
qDebug() << "bbb";
}
}
- 虽然找不出原因,但可以换个思路绕过此问题,不选择delete布局,而是直接new新的QWidget
// file "Widget.cpp"
#include "Widget.h"
#include "ui_Widget.h"
widget::widget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Form)
{
ui->setupUi(this);
btn1 = new QPushButton;
btn1->setText("1");
btn2 = new QPushButton;
btn2->setText("2");
}
widget::~widget()
{
delete ui;
}
void widget::resizeEvent(QResizeEvent* event)
{
int w = width();
// centralWidget = new QWidget;
if (w < 400)
{
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(btn1);
layout->addWidget(btn2);
centralWidget = new QWidget; // 也可以在if else判断之前new
centralWidget->setLayout(layout);
setCentralWidget(centralWidget); // 也可以在if else之后设置
}
else
{
QHBoxLayout* layout2 = new QHBoxLayout;
layout2->addWidget(btn1);
layout2->addWidget(btn2);
centralWidget = new QWidget;
centralWidget->setLayout(layout2);
setCentralWidget(centralWidget);
}
// setCentralWidget(centralWidget);
}
- 现在最大化和还原将达到预期效果



加入滚动条
- 目标:当控件超出窗口大小时,需要提供滚动条,否则控件将缩小并排满窗口。如:窗口中包含10张表格,每张表格高度至少为300,当窗口宽度小于800时,表格垂直排列,不低于800时则水平排列;
- 方法:使用QScrollArea设置一片带滚动条的区域,这个区域是空的,还需要用setWidget添加一个控件,setWidget只能添加一个控件,如果多次使用只保留最后的设置,若之前setWidget的控件未释放则将成为悬挂指针。在处理上面举例的多个控件问题时,setWidget添加的应该是一个QWidget,QTableWidget添加到layout后,再对QWidget使用setLayout,即
QMainWindow - QScrollArea - QWidget - QLayout - QTableWidget
;
- 注意:在setWidget之前,必须给QWidget添加layout,否则将不显示widget,只显示一片空的滚动条区域。并且必须使用QMainWindow作为主窗口,因为QWidget没有setCentralWidget的功能,将出现无法显示滚动条的问题!
scrollArea = new QScrollArea;
centralWidget = new QWidget;
layout = new QVBoxLayout;
... // 设置layout
// layout->setSizeConstraint(QLayout::SetFixedSize);
centralWidget->setLayout(layout);
scrollArea->setWidget(centralWidget);
setCentralWidget(scrollArea);

- 因为setCentralWidget方法只能使用一次,所以仍要写在构造函数中,此时的中心控件是QScrollArea
- layout是写在resizeEvent中的,为QWidget setLayout和为QScrollArea setWidget必须连写在一起,因此两者也要写在resizeEvent中
- QWidget必须在resizeEvent中new,若在构造函数中new,则会出现溢出
- QScrollArea只需要声明一次,使用时不需要销毁,因此最好在构造函数中new,若在resizeEvent中new,则会在改变窗口大小时出现闪屏现象(每次改动窗口都要重新new一个新的QScrollArea)
- 可以通过样式改变QScrollArea的背景色
scrollArea->setStyleSheet("background-color:transparent;"); // 透明背景:所有子控件也会受影响
scrollArea->viewport()->setStyleSheet("background-color:transparent;"); // 这句实测无效
详细实现
- Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QMainWindow>
#include "qpushbutton.h"
#include "qboxlayout.h"
#include "qscrollarea.h"
#include "qtablewidget.h"
namespace Ui {
class Form;
}
class widget : public QMainWindow
{
Q_OBJECT
public:
explicit widget(QWidget *parent = 0);
~widget();
private:
Ui::Form *ui;
QVBoxLayout* layout;
QHBoxLayout* layout2;
QPushButton* btn1;
QPushButton* btn2;
QWidget* centralWidget;
QScrollArea* scrollArea;
QTableWidget* table1;
QTableWidget* table2;
protected:
void resizeEvent(QResizeEvent* event);
};
#endif // WIDGET_H
- Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
widget::widget(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Form)
{
ui->setupUi(this);
centralWidget = new QWidget;
btn1 = new QPushButton;
btn1->setText("1");
btn2 = new QPushButton;
btn2->setText("2");
table1 = new QTableWidget(20, 10, 0);
table2 = new QTableWidget(20, 10, 0);
table1->setMinimumHeight(200); // 表格设置最小高度
table2->setMinimumHeight(200);
for(int i = 0; i < 20; ++i)
for (int j = 0; j < 10; ++j)
{
QTableWidgetItem* item = new QTableWidgetItem;
QTableWidgetItem* item2 = new QTableWidgetItem;
QString txt = QString("%1%2").arg(i).arg(j);
QString txt2 = QString("%1%2").arg(j).arg(i);
item->setText(txt);
item2->setText(txt2);
table1->setItem(i, j, item);
table2->setItem(i, j, item2);
}
scrollArea = new QScrollArea;
setCentralWidget(scrollArea);
}
widget::~widget()
{
delete ui;
}
void widget::resizeEvent(QResizeEvent* event)
{
int w = width();
if (w < 400)
{
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(table1);
layout->addWidget(table2);
centralWidget = new QWidget; // 必须在此处创建
centralWidget->setLayout(layout);
scrollArea->setWidget(centralWidget);
}
else
{
QHBoxLayout* layout2 = new QHBoxLayout;
layout2->addWidget(btn1);
layout2->addWidget(btn2);
layout2->addWidget(table1);
layout2->addWidget(table2);
centralWidget = new QWidget;
centralWidget->setLayout(layout2);
scrollArea->setWidget(centralWidget);
}
}
实现效果


设置控件大小
可以使用setFixedSize为每个控件指定固定尺寸,也可以用setMinimumSize、setMaximumSize设置最小、最大尺寸,或者用setFixedHeight、setFixedWidth、setMinimumHeight、setMinimumWidth、setMaximumHeight、setMaximumWidth单独设置高度或宽度。在构造函数中或resizeEvent中设置均可,若不根据窗口大小变化则在构造函数中设置,若需要变化则在resizeEvent中设置。
void widget::resizeEvent(QResizeEvent* event)
{
int w = width();
if (w < 400)
{
QVBoxLayout* layout = new QVBoxLayout;
btn1->setFixedSize(100, 50);
btn2->setFixedSize(50, 100);
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(table1);
layout->addWidget(table2);
centralWidget = new QWidget;
centralWidget->setLayout(layout);
scrollArea->setWidget(centralWidget);
}
else
{
QHBoxLayout* layout2 = new QHBoxLayout;
btn1->setFixedSize(50, 100);
btn2->setFixedSize(100, 50);
layout2->addWidget(btn1);
layout2->addWidget(btn2);
layout2->addWidget(table1);
layout2->addWidget(table2);
centralWidget = new QWidget;
centralWidget->setLayout(layout2);
scrollArea->setWidget(centralWidget);
}
}


拖动中间边线同时改动左右窗口大小
- 目标:主窗口分为左右两部分,按下鼠标拖动中间的边框线可以减小(增大)左窗口宽度,同时增大(减小)右窗口宽度
- 方法:所有窗口类都必须派生自QWidget,且setMouseTracking(true)。若窗口为QMainWindow派生类,则创建一个QWidget,添加布局以把QMainWindow放置到QWidget上。使用setGeometry设置左右窗口位置,然后重载mouseMoveEvent。此外考虑,拉伸窗口时,右边的子窗口增大,还要重载resizeEvent
B1::B1(QWidget* parent) : QWidget(parent), ui(new Ui::B1)
{
ui->setupUi(this);
setMouseTracking(true);
// 左窗口为QMainWindow的处理方法
left = new QWidget(this);
QHBoxLayout* leftLayout = new QHBoxLayout;
My_MainWindow* leftWidget = new My_MainWindow;
leftLayout->addWidget(leftWidget);
left->setLayout(leftLayout);
// 右窗口为QWidget无需处理
right = new My_Widget(this);
const int defaultWidth = 500; // 左窗口初始宽度
left->setGeometry(0, 0, defaultWidth, height());
right->setGeometry(defaultWidth, 0, width()-defaultWidth, height());
right->setMinimumWidth(100); // 如果是osg模型要加上这句,否则拖动到最右侧删掉模型
left->setMouseTracking(true);
right->setMouseTracking(true);
}
void B1::resizeEvent(QResizeEvent* event)
{
left->setGeometry(0, 0, left->width(), height());
right->setGeometry(left->width(), 0, width() - left->width(), height());
}
void B1::mouseMoveEvent(QMouseEvent* event)
{
if (event->pos().x() >= 10 && event->pos().x() <= width() - 10)
{
setCursor(Qt::SizeHorCursor);
if (event->pos().x() >= (left->width() - 500) && event->pos().x() <= width() - (right->width() - 500))
{
if (event->buttons() == Qt::LeftButton)
{
left->setGeometry(0, 0, event->x(), height());
right->setGeometry(left->width(), 0, width() - left->width(), height());
}
}
}
else
{
setCursor(Qt::ArrowCursor);
}
}