QT跨平台应用程序开发框架(10)—— Qt窗口
目录
一,关于窗口
二,菜单栏
2.1 菜单介绍
2.2 添加菜单
2.3 添加快捷键
2.4 添加其子菜单
2.5 添加分割线和图标
三,工具栏
3.1 添加和使用工具栏
3.2 设置位置属性
四,状态栏
五,浮动窗口
六,对话框
6.1 介绍
6.2 自定义对话框
6.3 模态对话框
6.4 内置对话框
6.4.1 QMessageBox
6.4.2 QColorDialog
6.4.3 QFileDialog
6.4.4 QfontDialog
6.4.5 QInputDialog
一,关于窗口
我们前面学习的所有控件的代码,都是基于 QWidget 的,但是 QWidget 更多的是作为别的窗口的一个部分
在创建项目时,选择 QMainWindow,这个才是 Qt 窗口的完全体:
一个 QMainWindow 包含了下列内容:
从上往下依次是:
- 标题:操作系统加的,与Qt关系不大,可以修改名称
- 菜单栏:与标题紧挨,只能置于最上面
- 工具栏:类似于菜单栏,本质上是把菜单栏一些常用选项直接放到工具栏,目的是方便使用,上图的粉色区域四个方向都有,表示可以置于四个方向上
- 子窗口:一个程序里可能包含多个窗口,比如 Qt 界面就同时包含了:“文件管理窗口”,“代码编辑窗口”,“输出窗口”等等
- 中央控件:窗口最核心的部分
- 状态栏:显示一些信息,和上面所有内容一样,都可以在 Qt 的界面找到对应的内容
二,菜单栏
2.1 菜单介绍
Qt 中的菜单栏通过 QMenuBar 这个类来实现,并且一个主窗口最多只能有一个菜单栏,下面是Qt的菜单栏:
工具栏里的选项也叫做 QAction,因为前面说过,工具栏本质是菜单栏一些功能的快捷选项
2.2 添加菜单
以后我们创建项目时,选择 QMainWindow,而不是 QWidget 了:
生成的代码和 QWidget 的很相似,也是在 mainwindow.cpp 里编写代码,在 .h 里声明函数
唯一的区别也就是 ui 界面了,如下图:
添加菜单栏和添加菜单项都可以通过上面的图形化界面来创建,下面是通过代码创建的演示:
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//1,创建并设置菜单栏QMenuBar* menuBar = new QMenuBar(); this->setMenuBar(menuBar); //设置进窗口里,其实是挂到对象树里去//2,创建菜单QMenu* m1 = new QMenu("文件");QMenu* m2 = new QMenu("编辑");QMenu* m3 = new QMenu("构建");menuBar->addMenu(m1);menuBar->addMenu(m2);menuBar->addMenu(m3);//3,给菜单添加菜单项QAction* a1 = new QAction("新建");QAction* a2 = new QAction("打开");QAction* a3 = new QAction("保存");QAction* a4 = new QAction("另存为");QAction* a5 = new QAction("退出");m1->addAction(a1);m1->addAction(a2);m1->addAction(a3);m1->addAction(a4);m1->addAction(a5);//4,给 action 添加信号槽,上面的菜单项被点击后会产生一个 triggered 信号connect(a1, &QAction::triggered, this, &MainWindow::handle); //a1发信号,发triggered信号,this接收,执行handle函数
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::handle()
{qDebug() << "触发操作";
}
效果如下:
2.3 添加快捷键
直接在创建项目时,添加下列内容即可:
但是默认是 alt + 随机键,如果是要用 Ctrl + 随机键,就需要用到 setShortcuts,如下给菜单项添加 Ctrl 的快捷键:
2.4 添加其子菜单
有时候菜单栏下面是菜单,但是往下还有一层或多层子菜单,如下:
QMenu 也提供了 addMenu 功能给某个菜单项添加子菜单,QMenuBar 也可以, 如下代码:
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QMenuBar* menuBar = new QMenuBar();this->setMenuBar(menuBar);QMenu* menuParent = new QMenu("父菜单");QMenu* menuChild = new QMenu("子菜单");menuBar->addMenu(menuParent);menuParent->addMenu(menuChild); //直接在对应的父菜单调用 addMenu函数添加QAction* a1 = new QAction("菜单项1");QAction* a2 = new QAction("菜单项2");menuChild->addAction(a1);menuChild->addAction(a2);
}
如果还想添加子菜单,直接继续直接用菜单对象调用 addMenu 函数即可
2.5 添加分割线和图标
如标题,当菜单里的菜单项特别多时,可以通过 QMenu 提供的 addSeparator 方法添加分割线进行分组;同时也可以通过 setIcon 来进行设置,如下代码:
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QMenuBar* menuBar = new QMenuBar();this->setMenuBar(menuBar);QMenu* m = new QMenu("你好");menuBar->addMenu(m);QAction* a1 = new QAction("菜单项1");a1->setIcon(QIcon(":/deepseek.png")); //设置图标QAction* a2 = new QAction("菜单项2");QAction* a3 = new QAction("菜单项3");m->addAction(a1);m->addSeparator(); //添加分割线m->addAction(a2);m->addSeparator();m->addAction(a3);
}
如果给主菜单的 QMune 设置图标,由于 QMenu是直接覆盖在 MenuBar 上的,那么图标就会覆盖文本,可以根据需要自行设置
三,工具栏
3.1 添加和使用工具栏
使用 QToolBar 表示工具栏对象,一个窗口可以有多个工具栏也可以没有,而且工具栏也可以手动移动位置,如下代码:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QDebug>
#include<QToolBar>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//创建菜单栏QMenuBar* menuBar = this->menuBar();this->setMenuBar(menuBar);QMenu* menu = new QMenu("文件");menuBar->addMenu(menu);//创建并添加工具栏QToolBar* toolBar = new QToolBar();this->addToolBar(toolBar);QAction* a1 = new QAction("保存");QAction* a2 = new QAction("新建");a1->setToolTip("点击以保存文件"); //鼠标放在对应选项时的提示信息,默认是上面创建时的a2->setToolTip("点击新建文件");a1->setIcon(QIcon(":/deepseek.png")); //工具栏一般也是用图标代替,这里暂时用 老图片代替a2->setIcon(QIcon(":/deepseek.png"));//菜单项可以放在菜单和工具栏中menu->addAction(a1);menu->addAction(a2);toolBar->addAction(a1);toolBar->addAction(a2);connect(a1, &QAction::triggered, this, &MainWindow::handle);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::handle()
{qDebug() << "触发操作";
}
3.2 设置位置属性
工具栏的位置并不是固定的:
- 很多工具栏并不是指放置于菜单栏下面,也可以放置在靠左靠右和靠下位置
- 而且鼠标长按工具栏本体,可以直接将工具栏分离出来,以浮动窗口的形式显示在屏幕上
- 所以我们也可以将我们上面的工具栏添加一些属性,让其也有类似的效果
①设置位置
可以设置初始位置和停靠位置,如下:
有四种参数可以设置:
- Qt::LeftToolBarArea:停靠在左侧
- Qt::RightToolBarArea:停靠在右侧
- Qt::TopToolBarArea:停靠在顶部
- Qt::BottomToolBarArea:停靠在底部
- Qt::AllToolBarArea:四个位置都可以
也可以使用 selAllowedAreas 方法设置工具栏允许停靠的位置,如下:
②设置浮动属性
工具栏的浮动可以通过 setFloatable 方法来设置,就是可以将工具栏以浮动小窗口的形式显示在屏幕上,如下:
设置不能浮动后,工具栏也是可以移动到允许停靠的位置的
③设置移动属性
设置工具栏移动属性可以通过 setMovable 方法来设置,并且若工具栏设置了不移动状态,则停靠位置和浮动的操作都会禁用,相当于固定死了位置,只能在初始化的时候指定位置,所以这个方法类似于总开关的效果,如下:
四,状态栏
状态栏是应用程序输出简要信息的区域,一般位于窗口最底层,且一个窗口只能有一个状态栏
由 QStatusBar 来实现,可以显示三种信息:
- 实时消息:比如当前程序状态
- 永久消息:比如版本号,机构名称等
- 进度消息:入进度条显示,百分百提示等
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QStatusBar* s = this->statusBar(); //直接获取this->setStatusBar(s);//1,显示临时消息//s->showMessage("你好", 2000); //设置显示大约2秒的信息//2,设置永久消息,需要通过 label 控件来显示s->addWidget(new QLabel("这是一条永久消息"));//3,调整显示消息的位置s->addPermanentWidget(new QLabel("右侧提示消息"));
}
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QStatusBar* s = this->statusBar(); //直接获取this->setStatusBar(s);//1,显示临时消息//s->showMessage("你好", 2000); //设置显示大约2秒的信息//2,设置永久消息,需要通过 label 控件来显示s->addWidget(new QLabel("你好111"), 1); //第二个参数表示拉伸系数s->addWidget(new QLabel("你好222"), 2);//3,在右下角显示进度条QProgressBar* p = new QProgressBar();p->setValue(50);s->addPermanentWidget(p); //表示从右往左设置
}
五,浮动窗口
上面我们也讲到了,一个程序里可能包含多个窗口,比如 Qt 界面就同时包含了:“文件管理窗口”,“代码编辑窗口”,“输出窗口”等等
我们可以使用 QDockWidget 类来实现浮动的功能,可以有多个,并且部分操作和工具栏的操作类似,如下演示:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QDebug>
#include<QLabel>
#include<QDockWidget>
#include<QVBoxLayout>
#include<QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QDockWidget* dockWidget = new QDockWidget();this->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); //将浮动窗口添加到左边子窗口中dockWidget->setWindowTitle("这是浮动窗口"); //给浮动窗口设置标题//也可以给浮动窗口添加控件,但是需要先创建出一个单独的 QWidget,把控件假如其中,然后再将 QWidget 设置进 dockWidget 才行QWidget* w = new QWidget();dockWidget->setWidget(w);//创建布局遍历器,并设置进 QWidget 中QVBoxLayout* l = new QVBoxLayout;w->setLayout(l);//再创建其它控件,添加到 layout 中l->addWidget(new QLabel("这是一个 QLabel"));l->addWidget(new QPushButton("这是按钮"));
}MainWindow::~MainWindow()
{delete ui;
}
六,对话框
6.1 介绍
对话框是 GUI 程序中不可或缺的部分,往往是程序与用户之间进行“短平快” 的操作
Qt 中使用 QDialog 类表示对话框,通常是一个顶层窗口,出现在程序最上层,用于实现短期热内或者简洁的用户交互
Qt 常用的内置对话框有:QFiledialog(文件对话框)、QColorDialog(颜色对话框)、QFontDialog (字体对话框)、QInputDialog(输入对话框)和QMessageBox(消息框)
创建项目时选择 QDialog :
直接运行程序会显示:
总体来说,基于 QDialog 作为父类创建出来的程序窗口和之前通过 QWidget 创建出来的非常相似,就是部分属性不一样,后面我们会详细介绍
实际开发中,更多的情况不是在创建项目时继承自 QDialog,而是在代码中创建额外的类,让这个类继承自 QDialog,从而让主窗口产出一些其他的对话框
我们也可以在 QMainWindow 项目里用按钮生成一个对话框,如下槽函数:
void MainWindow::on_pushButton_clicked()
{QDialog* d = new QDialog();d->setWindowTitle("你好"); //设置对话框标题d->resize(400, 300); //设置对话框的大小d->show(); //显示对话框
}
问题:我们按下按钮弹出弹窗,关闭后再次点击按钮,此时弹出的弹窗和第一此弹出的是同一个对象吗?
解答:每次按下按钮都是创建一个新的 QDialog 对象,并进行显示
追问:那么多次甚至无数次点击按钮创建对象,内存泄漏问题如何解决?
解答:将 delete 释放逻辑和关闭按钮的点击信号关联起来,在用户点击关闭的时候,触发 delete;并且 Qt 也给QDialog 设置了一个属性,可以通过属性来完成上述操作,如下:
6.2 自定义对话框
上面我们通过按钮弹出一个对话框,但是目前这个对话框是空白的,如何在里面添加控件呢?
要自定义对话框,就需要继承自 QDialog 创建类,然后有两种继承方式,下面分别演示一下:
①纯代码方式来自定义 QDialog 界面
先新键一个类:
然后就会创建出一个头文件和一个 .cpp 文件,然后我们就可以把这两个文件当初是我们之前用的另一个 QWidget 的头文件和 .cpp 文件去编写控件代码了,如下:
dialog.h内容如下:
#ifndef DIALOG_H
#define DIALOG_H#include <QWidget>
#include <QDialog>class Dialog : public QDialog
{Q_OBJECT
public:Dialog(QWidget* parent); //添加进 QWidget 的对象树中void handle();
};#endif // DIALOG_H
dialog.cpp 内容如下:
#include "dialog.h"
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>Dialog::Dialog(QWidget* parent) : QDialog(parent)
{//创建出一些控件,加入到 Dialog 中QVBoxLayout* layout = new QVBoxLayout();this->setLayout(layout);QLabel* label = new QLabel("你好");QPushButton* b = new QPushButton("关闭", this);layout->addWidget(label);layout->addWidget(b);connect(b, &QPushButton::clicked, this, &Dialog::handle);
}void Dialog::handle()
{this->close();
}
mainwindow.cpp 里将原来的 QDialog 替换为我们的 Dialog,效果如下:
②通过图形化的方式
我们上面的按钮保存,但是按下按钮后显示的对话框不再是纯代码的了,所以先删除槽函数内容
而通过图形化界面创建,重要的就是先创建一个新的 .ui 文件出来,如下:
这个操作是创建一个 ui 文件和对应的类,也可以指定创建的模板,选定好类名后直接下一步即可
然后双击这个 ui 文件,就可以和前面一样创建控件了,同时右键按钮转到槽,就可以像前面那样编写槽函数了,如下:
然后就可以直接在 mainwindow.cpp 的槽函数里创建 Dialog 对象然后显示了:
效果和上面纯代码的一致
6.3 模态对话框
在上面 6.1 我们直接创建的对话框项目的 ui 界面,右下角属性有一个这个:
表示 模态 / 非模态(true / false),这个我们也很常见,就是如果为 true,表示模态,此时用户无法操作父窗口,必须先完成对话框内容并关闭对话框之后,我们才可以继续操作;非模态就是是相反
这个模态用于非常关键的,必须要让用户做出决策的场合;非模态就是用于不受特别关键的场景
要使我们的对话框变成模态对话框也很简单,只要把我们前面的 .show 方法换成 .exec 方法即可:
6.4 内置对话框
6.4.1 QMessageBox
消息对话框是应用程序中最常见的界面元素,我们还是通过一个按钮显示对话框,槽函数如下:
void MainWindow::on_pushButton_clicked()
{// 创建消息对话框QMessageBox* m = new QMessageBox(this);m->setWindowTitle("你好");m->setText("这是对话框文本");m->setIcon(QMessageBox::Warning); //设置图标m->setAttribute(Qt::WA_DeleteOnClose);m->setStandardButtons(QMessageBox::Ok | QMessageBox::Save); //添加内置按钮//我们也可以使用自定义按钮//QPushButton* b = new QPushButton("按钮", m); /也可以使用 connect 连接信号槽//m->addButton(b, QMessageBox::AcceptRole); //第一个参数是 QAbstractButton,第二个是 ButtonRole,负责返回点击按钮的一些情况int result = m->exec();if(result == QMessageBox::Ok) qDebug() << "ok";else if(result == QMessageBox::Save) qDebug() << "Save";//上面的内置按钮,没办法进行信号槽的连接,因为按钮是 QMessageBox 自己生成的//所以为了解决这个问题,用户点击按钮使对话框关闭之后,能通过 exec 的返回值,来获取用户点击的是哪个按钮了
}
除了上面的方式,QMessageBox 类定义了静态成员函数,可以直接调用并创建不同内容的消息对话框,包括:
void MainWindow::on_pushButton_clicked()
{// 创建消息对话框int result = QMessageBox::warning(this, "对话框标题", "对话框文本", QMessageBox::Ok | QMessageBox::Cancel);if(result == QMessageBox::Ok) qDebug() << "Ok";else if(result == QMessageBox::Cancel) qDebug() << "Cancel";
}
6.4.2 QColorDialog
这个是颜色对话框,继承自 QDialog 类, 可以让用户来选择一个颜色,而且 QColorDialog 内置了“调色板”,常用函数如下:
函数 | 说明 |
---|---|
QColorDialog(QWidget*parent=nullptr) | 创建对象的同时设置父对象 |
QColorDialog(const QColor &initial, QWidget *parent = nullptr) | 创建对象的同时通过QColor 对象设置默认颜色和父对象 |
voidsetCurrentColor(const QColor &color) | 设置当前颜色对话框 |
QColorcurrentColor() const | 获取当前颜色对话框 |
QColorgetColor(const QColor &initial = Qt::white, QWidget *parent = nullptr, const QString &title = QString(), QColorDialog::ColorDialogOptions options = ColorDialogOptions()) | 打开颜色 选择对话框,并返回⼀个QColor对象 |
voidopen(QObject* receiver, constchar* member) | 打开颜色对话框 |
部分参数:
- initial:设置默认颜色
- parent:设置父对象
- title:设置对话框标题
- options:设置选项
下面直接用代码演示一下:
void MainWindow::on_pushButton_clicked()
{//getColor 能弹出一个模态的对话框,用户选择颜色后,确定关闭,getColor 就可以返回一个QColor 对象,包含一些颜色信息//并且是一个静态的,不需要创建对象就能使用QColor c = QColorDialog::getColor(QColor(255, 0, 0), this, "选择颜色");qDebug() << c; //打印QColor(ARGB 1, 1, 0, 0) 第一个数表示不透明度,后面三个就是颜色的RGB数值,1表示255,0 就是 整数0,是百分比的形式//可以基于颜色,修改其它控件的颜色,比如下面是设置背景颜色//QString s = "background-color: rgb(" + QString::number(c.red()) + ", " + QString::number(c.green()) + ", " + QString::number(c.blue()) + ")";char s[1024] = { 0 };sprintf(s, "background-color: rgb(%d, %d, %d)", c.red(), c.green(), c.blue()); //上面的太长,也可以这样搞,以格式化方式设置this->setStyleSheet(s); //可以通过 QSS 的方式设置背景色
};
6.4.3 QFileDialog
通过 QFileDialog 可以选择一个文件,能够获取到这个文件的路径,然后对指定文件进行一些操作,常用函数如下:
函数 | 说明 |
---|---|
QString getOpenFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = Options()) | 打开文件(一次只能打开一个) |
QStringList getOpenFileNames(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = Options()) | 打开文件(能打开多个) |
QString getSaveFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = Options()) | 保存文件 |
参数说明:
- parent:父控件
- caption:对话框标题
- dir:默认打开的路径
- filter:文件过滤器
下面我们来演示一下,我们搞两个按钮,左边的用来弹出打开文件,右边的用来弹出保存文件,如下代码:
//注意这里仅仅只是获取文件路径,真正打开/保存文件的操作需要我们实现额外的逻辑,后面会详细介绍
void MainWindow::on_pushButton_clicked()
{QString filePath = QFileDialog::getOpenFileName(this); //返回文件路径,此时右下角显示“打开”qDebug() << filePath; //打印文件路径
};void MainWindow::on_pushButton_2_clicked()
{QString filePath = QFileDialog::getSaveFileName(this); //此时右下角显示“返回”,并且需要输入文件名qDebug() << filePath; //打印文件路径
}
6.4.4 QfontDialog
这个表示字体对话框,用于提供选择字体的对话框部件,如下代码:
void MainWindow::on_pushButton_clicked()
{bool ok = false; //作为输出型参数,当用户选择确定时设置为true,选择取消时设置为falseQFont font = QFontDialog::getFont(&ok); //返回字体信息ui->pushButton->setFont(font); //设置按钮字体
};
6.4.5 QInputDialog
表示一个输入对话框,用于进行临时数据输入的场合,常用函数如下:
函数 | 说明 |
---|---|
double getDouble(QWidget*parent,constQString&title, const QString&label, double value =0,doublemin=-2147483647,doublemax=2147483647,intdecimals=1,bool*ok =nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); | 双精度浮点型输入数据对话框 |
int getInt (QWidget *parent, const QString &title, const QString &label, int value = 0, int min=-2147483647,intmax=2147483647,intstep=1,bool*ok=nullptr,Qt::WindowFlags flags = Qt::WindowFlags()); | 整型输入数据对话框 |
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) ; | 选择条目输入数据框 |
下面是输入数字的代码:
void MainWindow::on_pushButton_clicked()
{double result = QInputDialog::getDouble(this, "这是标题", "浮点数");//getInt就是输入整数qDebug() << result;
};
可以看到他是自带微调框的,我们也可以自己去设置
第三个是条目框,顾名思义就是将上面的输入框换成了条目按钮输入,如下代码:
void MainWindow::on_pushButton_clicked()
{QStringList items; //可以当成是 QList<QString>,字符串数组items << tr("你好") << tr("hello world") <<tr("Qt Creator");QString s = QInputDialog::getItem(this, "条目输入对话框", "请输入条目", items);qDebug() << s;
};
效果如下: