QMainWindow
Qt中的主窗口以QMainWindow
表示,其总体结构如下:
菜单栏
菜单栏MenuBar,可包含多个菜单Menu,每个菜单也可以包含多个菜单项Action,以QtCreator的菜单栏为例。
Qt中,QMenuBar
表示菜单栏,QMenu
表示菜单,QAction
表示菜单项。
核心接口
接口 | 说明 |
---|---|
void QMainWindow::setMenuBar(QMenuBar *menuBar) | 为主窗口设置菜单栏 |
addMenu(QMenu *menu) | 为菜单栏添加菜单,或在菜单中嵌套添加子菜单 |
QWidget::addAction(QAction *action) QMenu::addAction(const QString &text) | 添加菜单项,可以使用QAction 添加,也可直接以文本添加 |
先上代码。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.创建一个菜单栏,并将其设置到窗口MainWindow中
QMenuBar* menuBar = this->menuBar();
this->setMenuBar(menuBar);
//2.为菜单栏创建菜单
QMenu* menu1 = new QMenu("文件 (&F)");
QMenu* menu2 = new QMenu("编辑 (&E)");
QMenu* menu3 = new QMenu("帮助 (&H)");
//3.将菜单加入菜单栏中
menuBar->addMenu(menu1);
menuBar->addMenu(menu2);
menuBar->addMenu(menu3);
//(嵌套子菜单)
QMenu* menu31 = new QMenu("目录");
menu3->addMenu(menu31);
menu31->addAction("h1");//直接使用字符串也可以添加菜单项
menu31->addAction("h2");
menu31->addAction("h3");
//4.为菜单创建一些菜单项Action
QAction* action1 = new QAction("新建 (&N)");
QAction* action2 = new QAction("打开 (&O)");
QAction* action3 = new QAction("另存为 (&S)");
menu1->addAction(action1);
menu1->addSeparator();//在菜单项之间增加分隔线
menu1->addAction(action2);
menu1->addSeparator();
menu1->addAction(action3);
//(为菜单项设置图标)
action1->setIcon(QIcon(":/create.png"));
action2->setIcon(QIcon(":/open.png"));
action3->setIcon(QIcon(":/save.png"));
//5.为Aciton的触发绑定槽函数
connect(action1, &QAction::triggered, this, &MainWindow::handle1);
connect(action2, &QAction::triggered, this, &MainWindow::handle2);
connect(action3, &QAction::triggered, this, &MainWindow::handle3);
//6.为菜单栏添加一个用于关闭窗口的Action
QAction* action = new QAction("关闭 (&C)");
menuBar->addAction(action);
connect(action, &QAction::triggered, this, &MainWindow::close);
}
-
创建菜单栏时,可以使用以下两种方式:
//1. QMenuBar* menuBar = new QMenuBar(); this->setMenuBar(menuBar); //2. QMenuBar* menuBar = this->menuBar(); this->setMenuBar(menuBar);
第一种方法是直接创建
QMenuBar
对象,然后通过setMenuBar()
设置菜单栏,这种方法可能会导致内存泄露,因为有时候菜单栏是已经存在的!比如ui文件会自动生成菜单栏,此时如果手动创建一个并set为菜单栏,就会覆盖原先的菜单栏,导致其内存泄漏。正确的方法是调用
QMainWindow
的接口menuBar()
,其功能:若菜单栏已存在,则返回已存在的菜单栏,若不存在,则创建一个新的菜单栏再返回。 -
创建菜单时,需要为菜单设置一个文本title,而你可以在title中使用
&
指定该菜单的快捷键,如:QMenu* menu1 = new QMenu("文件 (&F)")
,则F
为该菜单的快捷键,ALT + F
触发该菜单。同理,创建QAction
菜单项时也可用同样的方法设置菜单项的快捷键。 -
菜单可以嵌套,即为
QMenu
添加另外一个QMenu
-
addSeparator()
在菜单项之间添加分隔线,优化界面。还可以为菜单项添加图标icon。 -
菜单模块中,最后与用户交互的是
QAction
。用户点击某个菜单项,触发QAction
的triggered
信号,通过信号槽机制定义处理方法。需要注意的是,不仅菜单栏可以添加QAction
,菜单也可以添加QAction
,QAction
抽象出公共的用户交互动作。
最终效果:
工具栏
⼯具栏是应用程序中集成各种功能,实现快捷使⽤的⼀个区域。可有可无,不是程序中必须存在的部件。在Qt中QToolBar
表示工具栏,是一个可移动的组件,QAction
表示工具栏中的快捷项。一个窗口可以有多个工具栏。
先上代码。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.创建工具栏
QToolBar* toolBar = new QToolBar();
this->addToolBar(toolBar);//一个窗口可以有多个工具栏, 因此使用add而非set
//2.添加快捷项
QAction* action1 = new QAction("保存");
QAction* action2 = new QAction("打开");
toolBar->addAction(action1);
toolBar->addAction(action2);
//设置快捷项图标
action1->setIcon(QIcon(":/save.png"));
action2->setIcon(QIcon(":/open.png"));
//设置快捷项的提示信息
action1->setToolTip("点击此处保存文件");
action2->setToolTip("点击此处打开文件");
//绑定触发快捷项的槽函数
connect(action1, &QAction::triggered, this, [](){
qDebug() << "保存成功";
});
connect(action2, &QAction::triggered, this, [](){
qDebug() << "打开成功";
});
}
关于工具栏的停靠位置,可以在添加QToolBar
对象时指定,停靠位置由枚举类型Qt::ToolBarArea
提供,有四种.
enum ToolBarArea {
LeftToolBarArea = 0x1,//左
RightToolBarArea = 0x2,//右
TopToolBarArea = 0x4,//上
BottomToolBarArea = 0x8//下
};
//为窗口添加工具栏时,指定工具栏的初始停靠位置
QToolBar* toolBar = new QToolBar();
this->addToolBar(Qt::LeftToolBarArea, toolBar);
后续可以通过代码,决定工具栏允许停靠的位置、是否允许浮动、是否允许移动。
//3.工具栏的位置
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);//设置允许停靠的位置
toolBar->setFloatable(false);//设置是否可以浮动 (默认为true)
toolBar->setMovable(true);//设置是否可以移动 (默认为true)
同一个
QAction
可以为菜单项和工具栏中的快捷项共享,快捷项成为菜单项的快捷方式。在上述代码作出以下修改。
//创建菜单栏
QMenuBar* menuBar = this->menuBar();
this->setMenuBar(menuBar);
QMenu* menu = new QMenu("文件");
menuBar->addMenu(menu);
//...
//添加action1菜单项(action1已添加为工具栏的快捷项)
menu->addAction(action1);
状态栏
状态栏是应⽤程序中输出简要信息的区域。⼀般位于主窗口的最底部,⼀个窗口中最多只能有⼀个状态栏。在Qt中,状态栏是通过QStatusBar
类来实现的。在状态栏中可以显示的消息类型有:
• 实时消息:如当前程序状态
• 永久消息:如程序版本号,机构名称
• 进度消息:如进度条提示
我们可以调用状态栏的showMessage()
函数输出一些简单的临时信息,也可以在状态栏中添加一些控件widget,以显示特定的信息。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.创建状态栏
QStatusBar* statusBar = this->statusBar();//存在则直接获取,不存在则先创建
this->setStatusBar(statusBar);
//2.显示信息
statusBar->showMessage("This is statusBar!!");
//也可以为显示信息添加timeout超时时间,单位为ms
}
为状态栏添加一些控件
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.创建状态栏
QStatusBar* statusBar = this->statusBar();//存在则直接获取,不存在则先创建
this->setStatusBar(statusBar);
//2.创建控件
QLabel* label = new QLabel("This is a statusBar!!");
QProgressBar* progressBar = new QProgressBar();
progressBar->setValue(50);
//3.添加控件到状态栏中,可以为控件设置拉伸系数
statusBar->addWidget(label, 1);//从左往右添加
statusBar->addPermanentWidget(progressBar, 2);//从右往左添加
}
浮动窗口
在Qt中,浮动窗口也称之为铆接部件,一般作为主窗口内部的子窗口。浮动窗口是通过QDockWidget
类来实现浮动的功能。浮动窗口⼀般是位于核心部件的周围,可以有多个。
注意,Qt中规定QDockWidget
中只能有一个QWidget
,因此若需要在浮动窗口中实现更多效果,需要先创建一个QWidget
,对QWidget
进行创造,再将其设置到浮动窗口QDockWidget
中。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.创建浮动窗口
QDockWidget* dockWidget = new QDockWidget();
this->addDockWidget(Qt::LeftDockWidgetArea, dockWidget);//注意,添加浮动窗口时须指定其停靠位置(上下左右)
//2.为浮动窗口设置一个QWidget
QWidget* widget = new QWidget();
dockWidget->setWidget(widget);
//3.在widget中添加一些控件
QVBoxLayout* layout = new QVBoxLayout();
widget->setLayout(layout);
QLabel* label = new QLabel("This is a dockWidget!!");
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
layout->addWidget(label);
layout->addWidget(btn1);
layout->addWidget(btn2);
//4.设置浮动窗口只能停靠左侧或右侧
dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
}
对话框
对话框通常是⼀个顶层窗⼝,出现在程序最上层,⽤于实现短期任务或者简洁的用户交互。Qt的对话框由类QDialog
实现。Qt中还提供了一些内置对话框,方便特殊场景下的开发,Qt常用的内置对话框有:QFiledialog(⽂件对话框)、QColorDialog(颜⾊对话框)、QFontDialog(字体对话框)、QInputDialog(输⼊对话框)和 QMessageBox(消息框)。
//主窗口设置一个按钮,点击按钮弹出对话框
void MainWindow::on_pushButton_clicked()
{
//1.创建对话框
QDialog* dialog = new QDialog(this);
dialog->resize(500,300);
dialog->setWindowTitle("对话框");
//2.显示对话框
dialog->show();
}
-
关于对话框的内存泄漏问题
上述创建对话框的过程,虽然将对话框的父节点设置为上级主窗口,但是防不住用户在主窗口销毁之前,打开的对话框过多、或对话框占用内存过大,导致内存泄漏。我们想要达成的效果是:用户关闭一个对话框(点击对话框左上角的❌)时,就将该对话框delete。Qt中为对话框
QDialog
内置了一些属性,设置WA_DeleteOnClose
属性表示对话框关闭时释放内存。dialog->setAttribute(Qt::WA_DeleteOnClose);
-
对话框有两种类型,模态(modal) 和 非模态。
模态对话框:显示后无法与父窗口进行交互,是⼀种阻塞式的对话框。使用
QDialog::exec()
函数调用。模态对话框适用于一些必须让用户作出选择的场景,如:文件选择、打印设置等。非模态对话框:显示后依然可以与父窗口进行交互,是一种非阻塞式的对话框。使用
QDialog::show()
函数调用。
自定义对话框
- Qt提供对话框基类
QDialog
,开发者如果需要自定义一个对话框,就需要以QDialog
为父类,创建一个派生类,在派生类中自定义一些控件、GUI效果等。具体做法,可以通过QtCreator内的文件菜单,创建一个继承自QDialog
的派生类,并在自动生成的头文件和源文件中,自定义派生类。
//这里创建了一个Dialog类,继承自QDialog
//在其构造函数中,添加一些控件
Dialog::Dialog(QWidget* parent) : QDialog(parent)
{
QVBoxLayout* layout = new QVBoxLayout();
this->setLayout(layout);
QLabel* label = new QLabel("This is a Dialog!!");
QPushButton* btn = new QPushButton("按钮");
layout->addWidget(label);
layout->addWidget(btn);
}
- 通过创建对话框的ui文件,进行图形化的自定义。
Qt内置对话框
QMessageBox
QMessageBOX
是消息对话框,主要用于为用户提示主要的信息,或是让用户进行选择操作。
一个消息对话框中的内容,主要包括:标题、提示信息、消息类型(图标)和按钮。
先上代码。
void MainWindow::on_pushButton_clicked()
{
QMessageBox* msgBox = new QMessageBox(this);
msgBox->setWindowTitle("消息对话框");//设置标题
msgBox->setText("Warning");//设置提示信息
msgBox->setIcon(QMessageBox::Warning);//设置消息的类型
msgBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);//设置标准按钮
// 也可以调用addButton为消息对话框添加自定义按钮,但是一般用标准按钮就足够了
// msgBox->addButton()
}
消息类型
enum Icon {
NoIcon = 0,
Information = 1,
Warning = 2,
Critical = 3,
Question = 4
};
类型 | 适用场景 |
---|---|
Question | 用于正常操作过程中的提问 |
Information | 用于提示正常运行的消息 |
Warning | 用于提示非关键的错误(警告) |
Critical | 用于提示严重错误 |
事实上,QMessageBox
提供了一系列静态函数,用于创建一个消息对话框,一个函数涵盖了设置标题、文本、消息类型和按钮等功能。如下,四个不同的静态函数,表示创建四种不同类型的消息对话框。
void MainWindow::on_pushButton_clicked()
{
QMessageBox::warning(this,"消息对话框","Do you want to do something?", QMessageBox::Ok | QMessageBox::No);
}
使用静态函数生成的对话框是模态类型的。
-
如何获取用户与消息对话框交互的结果?
对于模态类型的消息对话框,其
exec()
返回值就是用户选择的结果!返回的是选择的StandardButton
的值。(Qt::StandardButton
是一个枚举类型,每一种按钮对应一个特定值)。而critical
,information
,warning
,question
四个静态函数的返回值是QMessageBox::StandardButton
,就是返回用户点击的按钮。我们可以根据这个返回值,对用户不同的选中作出不同的处理。
void MainWindow::on_pushButton_clicked() { int ret = QMessageBox::warning(this,"消息对话框","Do you want to do something?", QMessageBox::Ok | QMessageBox::No); if(ret == QMessageBox::Ok){ qDebug() << "Ok, lets do it"; } else if(ret == QMessageBox::No){ qDebug() << "Alright, Bye"; } }