前言:
在之前的学习中,了解了 Qt 当中的常用控件,现在来学习 窗口
Qt窗口 是通过QMainWindow类实现的
QMainWindow类是为用户提供主窗口程序的类,继承子 QWidget类;并且QMainWindow还提供了一个预定义的布局:

一个QMainWindow窗口包含:一个菜单栏、多个工具栏、多个浮动窗口、一个状态栏和一个中心部件。
这里学习 QMainWindow窗口,在创建项目时选择的类就不再是 QWidget了,而是QMainWindow。

菜单栏 QMenuBar

对于一个菜单栏,其中有很多菜单,而每一个菜单又有很多菜单项,每一个菜单项中也可以用子菜单。
创建菜单栏
- 如果默认生成了
ui文件,自带一个menubar菜单栏


在对应的ui界面中,输入内容(菜单、菜单项)即可完成添加菜单、菜单项。
- 通过代码创建菜单栏
这里可以使用new,创建一个新的菜单栏,然后把这个菜单栏设置到QMainWindow主窗口中。
也可以使用QMainWindow类中,内置的方法menubar,来获取当前窗口菜单栏(如果不存在就创建)。
注意 :一个窗口只能用一个菜单栏,使用
new新建一个菜单栏并设置到QMainWindow主窗口中,如果已经存在了菜单栏就会造成之前的菜单栏丢失,造成内存泄露问题。
cpp
// new 菜单栏
// QMenuBar* menubar = new QMenuBar();
// this->setMenuBar(menubar);
// 获取当前窗口的菜单栏,不存在就创建
QMenuBar* menubar = this->menuBar();
添加菜单
对于图像化界面添加菜单,这里就不过多描述了,在对应的界面输入内容即可完成添加。
在 Qt 当中,QMenu类表示菜单;添加菜单就要调用QMenuBar中的addMenu方法:
cpp
// 获取当前窗口的菜单栏,不存在就创建
QMenuBar* menubar = this->menuBar();
// 创建菜单
QMenu* menu1 = new QMenu("文件");
QMenu* menu2 = new QMenu("编辑");
// 添加菜单
menubar->addMenu(menu1);
menubar->addMenu(menu2);
预期效果:

添加菜单项
上述添加只是添加了菜单,并没有给每一个菜单添加菜单项;
菜单QMenu,而菜单项对应的类是QAction
使用ui文件图形化界面添加菜单项,直接在对应位置输入即可。
添加QAction:
cpp
// 添加菜单项
QAction* action1 = new QAction("新建");
QAction* action2 = new QAction("打开");
QAction* action3 = new QAction("保存");
menu1->addAction(action1);
menu1->addAction(action2);
menu1->addAction(action3);

添加分割线
上面操作中,无论是添加菜单、还是添加菜单项,都是没有设置分隔线的。
我们也可以在菜单和菜单之间、每一个菜单项之间设置分隔线:
cpp
menu1->addAction(action1);
menu1->addSeparator();
menu1->addAction(action2);
menu1->addSeparator();
menu1->addAction(action3);

子菜单
此外,在菜单中不仅仅可以添加菜单项,也可以添加子菜单。(对应接口:addMenu)
cpp
QMenu* childmenu = new QMenu("子菜单");
childmenu->addAction(action1);
childmenu->addAction(action2);
childmenu->addAction(action3);
menu1->addMenu(childmenu);

菜单项信号处理
这里这是设置了菜单栏、添加了菜单、菜单项;但是在实际的点击菜单项时,并没有什么反应。
QAction类,在被点击时会触发一个信号:triggered。
我们就可以绑定槽函数,在信号触发时执行特定的代码。
cpp
connect(action1,&QAction::triggered,this,[](){
qDebug() << "新建";
});
此外, 这里的菜单栏
QMenuBar、菜单QMenu、菜单项QAction都是可以设置图标的。(调用setIcon方法即可)
工具栏 QToolBar
工具栏是应用程序中集成各种功能实现的快捷键使用的一个区域。可以存在多个工具栏,也可以没有工具栏。
工具栏是一个可移动的组件,其中的每一个元素是各种窗口组件(可以是QAction,通常情况下都设置图标)
创建工具栏
默认情况下,ui文件中是不会新建工具栏的。
所以工具栏就需要我们通过代码创建 QToolBar对象来创建一个工具栏,并为整个窗口MainWindow添加上工具栏。
cpp
QToolBar* toolbar = new QToolBar();
this->addToolBar(toolbar);
添加组件
在工具栏中添加组件也是非常容易的,这里可以添加QAction对象、也可以添加各种控件。
并且这里的QAction对象也可以设置图像。
cpp
QAction* action1 = new QAction("新建");
action1->setIcon(QIcon(":/1.jpg"));
QAction* action2 = new QAction("打开");
action2->setIcon(QIcon(":/2.jpg"));
QAction* action3 = new QAction("保存");
action3->setIcon(QIcon(":/3.jpg"));
toolbar->addAction(action1);
toolbar->addAction(action2);
toolbar->addAction(action3);

这里鼠标停靠在对应图标位置,会提示出设置的文本内容;
也可以通过设置
ToolTip,
设置停靠位置
对于工具栏,它是可以拖动的,我们也可以设置这个停靠位置。
- 创建工具栏的同时指定停靠位置。
- 调用
QToolBar提供的setAllowedAreas()函数设置工具栏允许停靠位置。
Qt::LeftToolBarArea: 停靠在左侧Qt::RightToolBarArea: 停靠在右侧Qt::TopToolBarArea: 停靠在顶部Qt::ButtomToolBarArea: 停靠在底部Qt::AllToolBarArea: 上下左右四个位置都可以停靠
cpp
QToolBar* toolbar = new QToolBar();
// this->addToolBar(toolbar); // 默认停靠在顶部
this->addToolBar(Qt::LeftToolBarArea,toolbar); // 设置初始位置在左侧
toolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea); // 设置只允许停靠在左、右侧
设置了运行停靠位置后,拖动工具栏就可以将工具栏拖动到指定位置停靠。
设置是否允许 浮动和移动
上述虽然设置了停靠位置,但是工具栏默认是允许浮动的:

我们可以通过设置Floatable属性来设置是否运行工具栏浮动。
setFloatable:true表示允许浮动,false表示不允许浮动。
此外,有些情况下工具栏是不能移动的,这里就可以通过设置movable属性来设置工具栏是否可以移动。
setMovable:true表示允许移动,false表示不允许移动。
cpp
toolbar->setFloatable(false); // 设置不允许浮动
toolbar->setMovable(false); // 设置不允许移动
状态栏 QStatusBar
状态栏是应用程序输出简要信息的区域;一般情况下位于主窗口的最底部,一个窗口最多只能用一个状态栏。
在Qt当中,状态栏对应的类 QStatusBar。
在状态栏中一般显示内容:当前程序状态(状态信息 )、程序版本号、进度条/百分比(进度信息)
创建状态栏
如果在创建项目时,默认生成了ui文件,那默认是存在一个状态栏的;所以,创建状态栏就和创建菜单栏一样:
推荐使用:statusBar方法来获取当前状态栏(如果不存在就创建)
cpp
QStatusBar* statusbar = this->statusBar();
this->setStatusBar(statusbar);
设置信息
创建好了状态栏,运行程序和之前没什么区别,因为在状态栏中什么都没有。
要在状态栏中显示信息,可以调用QStatusBar提供的 showMessage方法
参数
text: 要显示的文本timeout: 信息的显示时间,单位ms(默认为0,表示一直显示)
cpp
// 设置状态栏信息
// statusbar->showMessage("提示信息,3秒后消失",3000); // 显示3s后消失
statusbar->showMessage("你能看见我吗"); // 一直显示
添加控件
除了可以在状态栏中设置信息之外,还可以在状态栏中增加一些控件(QLabel、QProgressBar等等)
cpp
// 添加控件
QLabel* label1 = new QLabel("提示信息");
QLabel* label2 = new QLabel("右侧提示信息");
QProgressBar* progressbar = new QProgressBar();
progressbar->setValue(25);
// 从左往右添加控件
statusbar->addWidget(label1);
statusbar->addWidget(progressbar);
// 从右往左添加控件
statusbar->addWidget(label2);
浮动窗口 QDockWidget
Qt 当中,浮动窗口也称为 铆接部件。对应的类 : QDockWidget
浮动窗口一般是位于核心部件的周围,可以存在多个。
创建浮动窗口
创建浮动窗口,其实就是通过代码创建一个QDockWidget对象,然后把这个QDockWidget对象添加到主窗口中。
注意 : 在将
QDockWidget添加到主窗口(调用addDockWidget方法)时,需要指明初始停靠位置。
cpp
QDockWidget* dockwidget1 = new QDockWidget();
this->addDockWidget(Qt::TopDockWidgetArea,dockwidget1);

这里没有设置窗口标题、图标等属性;也可以调用setWindowTitle方法来设置窗口标题
设置停靠位置
在调用addDockWidget方法时,需要指明初始停靠位置;
在程序运行起来时,通过鼠标拖动也是可以设置窗口的停靠位置的。
这里设置停靠位置和工具栏QToolBar一样,是设置可以停靠在上、下、左、右的哪些位置。
Qt::LeftDockWidgetArea: 停靠在左侧Qt::RightDockWidgetArea: 停靠在右侧Qt::TopDockWidgetArea: 停靠在顶部Qt::ButtomDockWidgetArea: 停靠在底部Qt::AllDockWidgetArea: 上下左右四个位置都可以停靠
cpp
//dockwidget1->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); // 设置可以停靠在左、右侧
dockwidget1->setAllowedAreas(Qt::AllDockWidgetAreas); // 设置可以停靠在上、下、左、右位置
对话框 QDialog
对话框是GUI程序中不可或缺的组成部分;通常是一个顶层窗口,出现在程序的最上层,来实现简洁的用户交互。
Qt 常用的内置对话框: QMessageBox(消息对话框)、 QFiledialog(文件对话框)、QColorDialog(颜色对话框)、QFontDialog(字体对话框)、QInputDialog(输入对话框)

模态/非模态
- 模态对话框:显示后无法和父窗口进行交互;阻塞式对话框。(调用
exec函数) - 非模态对话框:显示之后可以与父窗口进行交互;非阻塞对话框。(调用
show函数)
自定义对话框
要自定义对话框,就要基于QDialog类,实现一个子类;在特定情况(点击按钮)时,弹出这个对话框
cpp
// dialog 自定义对话框
Dialog::Dialog(QWidget* parent) : QDialog(parent)
{
QLabel* label = new QLabel("都说了啥也没有");
QPushButton* button = new QPushButton("点击关闭");
QVBoxLayout* layout = new QVBoxLayout();
this->setLayout(layout);
layout->addWidget(label);
layout->addWidget(button);
connect(button,&QPushButton::clicked,this,&Dialog::close);
}
Dialog::~Dialog()
{
qDebug() << "调用 Dialog 析构函数";
}
// pushButton 关联槽函数
void MainWindow::on_pushButton_clicked()
{
Dialog* dialog = new Dialog(this);
dialog->show(); // 非模态对话框
//dialog->exec(); // 模态对话框
}
这里就存在一个问题:通过点击按钮弹出的对话框,无论是点击关闭按钮还是窗口右上角关闭,对话框是关闭了,但是创建的Dialog对象并没有释放啊,当程序退出(主窗口关闭)时,才会被析构。
在QDialog类中存在一个属性:Attribute ,将这个属性设置成 Qt::WA_DeleteOnClose ,在窗口关闭时,QDialog对象就会自动被释放了。
cpp
void MainWindow::on_pushButton_clicked()
{
Dialog* dialog = new Dialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show(); // 非模态对话框
// dialog->exec(); // 模态对话框
}
此外,这里还可以给对话框设置窗口标题,图标等等属性。
消息框 QMessageBox
消息对话框是应用程序中最常用的界面对话框,主要用于为用户提示主要信息,让用户进行选择。
QMessageBox类 定义了静态函数,可以根据需要调用不同风格的消息对话框:
对话框图标 :

Question: 用于正常操作中的提问Information: 用于报告正常运行信息Warning: 用于报告非关键错误(警告)Critical: 用于报告严重错误
函数原型:

参数:
parent: 指定父元素title: 对话框窗口标题text: 对话框中显示的文本buttons: 设置按钮(在对话框中都存在哪些按钮)
按钮类别:

cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton,&QPushButton::clicked,this,[this](){
// 创建QMessageBox
// QMessageBox* messagebox = new QMessageBox(this);
// messagebox->setAttribute(Qt::WA_DeleteOnClose);
// messagebox->setWindowTitle("对话框");
// messagebox->setText("这是一个正常的对话框");
// messagebox->setIcon(QMessageBox::Question);
// messagebox->setStandardButtons(QMessageBox::Ok | QMessageBox::Close);
// messagebox->show();
// QMessageBox::warning(this,"对话框","这是一个简单的对话框");
// 设置按钮集
QMessageBox::question(this,"question","这是一个question对话框",QMessageBox::Ok | QMessageBox::Close);
});
}
颜色对话框 QColorDialog
颜色对话框QColorDialog类,提供了一个静态方法:getColor
调用该方法,弹出一个颜色对话框,用户选择完一个颜色并点击确认后,getColor就会返回,并根据用户选择的颜色返回一个QColor对象。

示例:通过颜色对话框,获取用户选择的颜色,然后修改窗口的背景色
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 颜色对话框
connect(ui->pushButton,&QPushButton::clicked, ui->pushButton,[this](){
// 获取用户选择的颜色
QColor color = QColorDialog::getColor();
qDebug() << color;
// 修改背景颜色
QString style = "background-color: rgb(" + QString::number(color.red()) +", " + QString::number(color.green()) + "," + QString::number(color.blue()) + ");";
this->setStyleSheet(style);
});
}
文件对话框 QFileDialog
QFileDialog,文件对话框,可以选择文件打开、保存文件等等。
打开文件:

保存文件:

对于打开和保存文件,QFileDialog类提供了静态方法:QFileDialog::getOpenName,QFileDialog::getSaveName
函数原型
cpp
static QString getOpenFileName(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
Options options = Options());
static QUrl getOpenFileUrl(QWidget *parent = nullptr,
const QString &caption = QString(),
const QUrl &dir = QUrl(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
Options options = Options(),
const QStringList &supportedSchemes = QStringList());
static QString getSaveFileName(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
Options options = Options());
static QUrl getSaveFileUrl(QWidget *parent = nullptr,
const QString &caption = QString(),
const QUrl &dir = QUrl(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
Options options = Options(),
const QStringList &supportedSchemes = QStringList());
Url返回的是一个 QUrl对象,其支持本地/网络路径;而Name系列返回一个QString对象,支持本地文件路径
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMenuBar* menubar = this->menuBar();
this->setMenuBar(menubar);
QMenu* menu = new QMenu("文件");
QAction* action1 = new QAction("打开");
QAction* action2 = new QAction("保存");
menu->addAction(action1);
menu->addSeparator();
menu->addAction(action2);
menubar->addMenu(menu);
connect(action1,&QAction::triggered, this,[this](){
//QUrl url = QFileDialog::getOpenFileUrl();
//qDebug() << url;
QString path = QFileDialog::getOpenFileName();
qDebug() << path;
});
connect(action2,&QAction::triggered, this,[this](){
QString path = QFileDialog::getSaveFileName();
qDebug() << path;
});
}
这里在选择要打开/保存的文件后,只是输出了文件路径,并没有进行文件读取/写入操作。
字体对话框 QFontDialog
QFontDialog字体对话框,可以让用户选择字体、大小等属性。

创建对话框也很简单,可以使用new的方式创建一个QFontDialog对象;
当然这里也可以调用 QFontDialog类提供的静态方法 getFont。

参数:
ok:输出型参数,true表示用户点击确认、false表示用户点击 取消。initial:字体对话框中,初始的字体属性。parent:指定父元素title:窗口标题
示例:根据用户选择,设置按钮的字体属性
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton,&QPushButton::clicked,this,[this](){
bool ok = false;
QFont init = ui->pushButton->font();
// QFont font = QFontDialog::getFont(&ok);
QFont font = QFontDialog::getFont(&ok,init,this,"字体属性");
if(!ok)
return;
qDebug() << font;
ui->pushButton->setFont(font);
});
}
输入对话框 QInputDialog
QInputDialog输入对话框,让用户进行输入,支持int、double、item等

- 整数输入框
cpp
static int getInt(QWidget *parent, const QString &title, const QString &label, int value = 0,
int minValue = -2147483647, int maxValue = 2147483647,
int step = 1, bool *ok = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
在调用该静态方法时,需要传参:parent(父元素)、title(窗口标题)、label(文本内容);
也可以指定value(初始值)、minValue(最小值)、maxValue(最大值)、step(微调框每次调整的大小)、ok(输出型参数,表示用户点击Ok还是Cancel)
- 浮点数输入框
cpp
static double getDouble(QWidget *parent, const QString &title, const QString &label, double value = 0,
double minValue = -2147483647, double maxValue = 2147483647,
int decimals = 1, bool *ok = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
// ### Qt 6: merge overloads
static double getDouble(QWidget *parent, const QString &title, const QString &label, double value,
double minValue, double maxValue, int decimals, bool *ok, Qt::WindowFlags flags,
double step);
参数和getInt类似,其中decimal表示小数的位数。
- 文本输入框
cpp
static QString getItem(QWidget *parent, const QString &title, const QString &label,
const QStringList &items, int current = 0, bool editable = true,
bool *ok = nullptr, Qt::WindowFlags flags = Qt::WindowFlags(),
Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
在文本输入框中存在一个下拉框(也可以进行输入),在调用QInputDialog::getItem方法时,就需要传递一个QStringList对象,来初始化这个下拉框中的选项。
QStringList是QList<QString>的子类,其用法也类似于数组。
cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton_int,&QPushButton::clicked,this,[this](){
int value = QInputDialog::getInt(this,"int输入框","请输入一个整数");
qDebug() << value;
});
connect(ui->pushButton_double,&QPushButton::clicked,this,[this](){
double value = QInputDialog::getDouble(this,"double输入框","请输入一个小数");
qDebug() << value;
});
connect(ui->pushButton_text,&QPushButton::clicked,this,[this](){
QStringList str_list;
str_list.push_back("选项一");
str_list.push_back("选项二");
str_list.push_back("选项三");
QString value = QInputDialog::getItem(this,"int输入框","请输入一个整数",str_list);
qDebug() << value;
});
}
:clicked,this,[this](){
int value = QInputDialog::getInt(this,"int输入框","请输入一个整数");
qDebug() << value;
});
connect(ui->pushButton_double,&QPushButton::clicked,this,[this](){
double value = QInputDialog::getDouble(this,"double输入框","请输入一个小数");
qDebug() << value;
});
connect(ui->pushButton_text,&QPushButton::clicked,this,[this](){
QStringList str_list;
str_list.push_back("选项一");
str_list.push_back("选项二");
str_list.push_back("选项三");
QString value = QInputDialog::getItem(this,"int输入框","请输入一个整数",str_list);
qDebug() << value;
});
}
本篇文章到这里就结束了,感谢支持
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws