六、多窗口编程
- QMessageBox消息对话框(掌握)
QMessageBox继承自QDialog,显示一个模态对话框。用于用户前台信息通知或询问用户问题,并接收问题答案。
QDialog的Qt源码中,派生类往往都是一些在特定场合下使用的预设好的对话框窗口,这些窗口无需创建对象,直接使用静态成员函数弹窗,使用函数的返回值作为这个窗口的结果。
// 参数1:parent参数
// 参数2:窗口标题
// 参数3:信息内容,窗口展示的信息
// 返回值:用户点击的按钮类型
StandardButton critical(QWidget * parent, const QString & title, const QString & text)
StandardButton information(QWidget * parent, const QString & title, const QString & text)
StandardButton question(QWidget * parent, const QString & title, const QString & text)
StandardButton warning(QWidget * parent, const QString & title, const QString & text)
[static]
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
btp = new QButtonGroup(this);
btp->addButton(ui->pushButtonQ,1);
btp->addButton(ui->pushButtonI,2);
btp->addButton(ui->pushButtonW,3);
btp->addButton(ui->pushButtonC,4); connect(btp,SIGNAL(buttonClicked(int)),
this,SLOT(buttonClickedSlot(int)));
}Dialog::~Dialog()
{
delete ui;
}void Dialog::buttonClickedSlot(int id)
{
if(id == 1)
{
// 设置弹窗 question
QMessageBox::StandardButton stdb = QMessageBox::question(this,"question","是否需要关闭?");
if(stdb == QMessageBox::Yes)
{
close();
}
else if(stdb == QMessageBox::No)
{ }
}
else if(id == 2)
{
// 设置弹窗 Information
QMessageBox::information(this,"information","已加载完成!");
}
else if(id == 3)
{
// 设置弹窗 warning
QMessageBox::warning(this,"warning","您的输入有误!");
}
else if(id == 4)
{
QMessageBox::critical(this,"critical","程序异常404,缺少必要文件");
}
else
{ }
}
- QWidget类(掌握)
QWidget类时所有窗口和组件的基类,之前认识此类更多的是站在组件的角度上,实际上QWidget本身身为所有窗口的基类,也具有很多窗口的特性。
新建一个项目,使自带窗口继承自QWidget
创建完成后运行时发现,与dialog的窗口并无太大区别,表面区别有。
#include "widget.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);// 当QWidget的构造函数parent参数使用默认值0时。表示创建的是独立窗口// 当QWidget的构造函数parent参数传递参数时,新创建的QWidget类对象会作为组件内嵌到父窗口中(内嵌窗口)Widget w;
w.show();return a.exec();
}
QWidget类作为所有窗口的基类,内部也规定了很多窗口的特性:
- windowFlags : Qt::WindowFlags(窗口标记)
在使用seter函数设置多个标记时,使用 | 分割(多个窗口标记之间可能会存在冲突)。
实现窗口置于最上层,且没有边框。
setWindowFlags(Qt::FramelessWindowHint |Qt::WindowStaysOnTopHint);
setWindowState
// 设置窗口状态
// 参数为要设置的状态
void QWidget::setWindowState(Qt::WindowStates windowState)
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{
ui->setupUi(this);setWindowTitle("无敌");setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);// 设置窗口最大化setWindowState(Qt::WindowMaximized);
}Widget::~Widget()
{
delete ui;
}
3、parent参数(掌握)
目前对parent参数的理解有以下几点:
- parent参数表示子组件位于那个窗口中。
- parent参数决定了QWidget对象是独立的还是内嵌窗口。
实际上parent参数还表示Qt的内存回收机制,如果对象a作为对象b构造函数时的parent参数,表示对象a是对象b的父对象。这是一种内存回收机制的依赖关系,即对象b跟随对象a一并销毁。此时无需手动控制对象b的销毁过程(手动调用delete)。
如果堆内存对象创建时不传递parent参数,表示对象调用需要程序员手动delete。
绝大多数情况下,建议堆内存对象传递parent参数。
这样做也有缺点, 缺点就是内存占用,主窗口还存在时,子窗口内存不会被释放掉。
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this); connect(ui->pushButton0,SIGNAL(clicked()),this,
SLOT(buttonClickedSlot()));
connect(ui->pushButtonThis,SIGNAL(clicked()),this,
SLOT(buttonThisClickedSlot()));
}Dialog::~Dialog()
{
qDebug() << "析构函数";
delete ui;
}void Dialog::buttonClickedSlot()
{
Dialog *dlg = new Dialog;
dlg->show();
}void Dialog::buttonThisClickedSlot()
{
Dialog * dlg = new Dialog(this);
dlg->show();
}
4、堆栈窗口(QStackedWidget)(熟悉)
通常作为独立的内嵌窗口(组件),并于QListWidget进行联动。
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 连接信号槽
connect(ui->listWidget,SIGNAL(currentRowChanged(int)),
ui->stackedWidget,SLOT(setCurrentIndex(int)));
}Dialog::~Dialog()
{
delete ui;
}
5、QMainWindow主窗口类(重点)
QMainWindow是最适合作为主窗口的类型,因为包含多个组成部分。
5.1 QMenuBar菜单栏
菜单栏的组成如下所示:
相关函数如下:
// 向菜单栏中添加一级菜单
// 参数为菜单的文字
// 返回值是添加的菜单对象
QMenu * QMenuBar::addMenu(const QString & title)
// 向菜单栏中添加动作
// 参数为动作的文字
// 返回值是添加的动作对象
QAction * QMenu::addAction(const QString & text)
// 向菜单中添加菜单
// 参数为菜单显示的文字
// 返回值是新添加的菜单对象
QMenu * QMenu::addMenu(const QString & title)
dialog.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMenu *menuFile = ui->menuBar->addMenu("文件");
QMenu *menuEdit = ui->menuBar->addMenu("编辑");
QMenu *menuHelp = ui->menuBar->addMenu("帮助"); // 向一级菜单中添加动作
QAction *actionNew = menuFile->addAction("新建");
QAction *actionOpen = menuFile->addAction("打开");
QAction *actionClose = menuFile->addAction("关闭"); // 向一级菜单中添加二级菜单
QMenu *menuRecent = menuFile->addMenu("访问最近的文件"); // 向二级菜单中添加动作
QAction *actionCpp = menuRecent->addAction("hello.cpp");
QAction *actionH = menuRecent->addAction("hello.h");}MainWindow::~MainWindow()
{
delete ui;
}
信号函数:
dialog.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{
ui->setupUi(this);
QMenu *menuFile = ui->menuBar->addMenu("文件");
QMenu *menuEdit = ui->menuBar->addMenu("编辑");
QMenu *menuHelp = ui->menuBar->addMenu("帮助");// 向一级菜单中添加动作
QAction *actionNew = menuFile->addAction("新建");
QAction *actionOpen = menuFile->addAction("打开");
QAction *actionClose = menuFile->addAction("关闭");// 向一级菜单中添加二级菜单
QMenu *menuRecent = menuFile->addMenu("访问最近的文件");// 向二级菜单中添加动作
QAction *actionCpp = menuRecent->addAction("hello.cpp");
QAction *actionH = menuRecent->addAction("hello.h");connect(actionCpp,SIGNAL(triggered()),this,SLOT(triggeredCppSlot()));connect(actionH,SIGNAL(triggered()),this,SLOT(triggeredHSlot()));}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::triggeredHSlot()
{
ui->textEdit->append("打开一个.h文件");
}void MainWindow::triggeredCppSlot()
{
ui->textEdit->append("打开一个.Cpp文件");
}
5.2 QToolBar工具栏
工具栏往往是使用菜单栏中的QAction对象,但是需要给QAction设置图标。
dialog.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this); connect(ui->action_4,SIGNAL(triggered()),
this,SLOT(triggeredSaveSlot()));
// 在状态栏展示信息
ui->mainToolBar->addAction(ui->action_5);
}MainWindow::~MainWindow()
{
delete ui;
}void MainWindow::triggeredSaveSlot()
{
ui->textEdit->append("保存了");
}
5.3 QStatusBar 状态栏
// 在状态栏中展示信息
// 参数1:展示的信息内容
// 参数2:信息显示的时间(单位毫秒),默认0表示持续显示
void QStatusBar::showMessage(const QString & message, int timeout = 0)[slot]
// 清空显示
void QStatusBar::clearMessage()[slot]
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{
ui->setupUi(this);
lab = new QLabel(this);
QLabel *lab2 = new QLabel(this);
lab->setText("显示的内容");
lab2->setText("2222");
ui->statusBar->addWidget(lab2);
ui->statusBar->addWidget(lab);connect(ui->action_4,SIGNAL(triggered()),this,SLOT(triggeredSaveSlot()));connect(ui->action_5,SIGNAL(triggered()),this,SLOT(triggeredCloseSlot()));}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::triggeredSaveSlot()
{
// ui->textEdit->append("保存了");
// ui->statusBar->showMessage("保存成功",99999);
// ui->statusBar->showMessage("保存111111",99999);
}void MainWindow::triggeredCloseSlot()
{
// ui->textEdit->append("清除成功");
// ui->statusBar->clearMessage();
}
6、新建自定义窗口类
在一个项目中创建一个Qt的窗口界面类,操作步骤如下:
- 在Qt Creator中选中项目名称,鼠标右键,点击“添加新文件”。
- 在弹出的窗口中,按照下图所示操作。
- 在弹出的窗口中选择界面模板后,点击”下一步“。
- 在弹窗的窗口中,输入类型(注意帕斯卡/大驼峰命名法),然后点击下一步。
- 在项目管理界面中,点击完成,可以看到新的窗口类文件已经添加到项目中了。
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
}Dialog::~Dialog()
{
delete ui;
}void Dialog::btnClickedSlot()
{
MyDialog *mydlg = new MyDialog(this);
mydlg->show();
}
- 对象传值(重点)
7.1 父对象→子对象
此处指的是Qt的parent参数的依赖关系,并非继承关系(后文同)。
[需求]:转动主窗口球,子窗口跟着转。
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
mydlg = new MyDialog(this);
connect(ui->pushButton,SIGNAL(clicked()),
this,SLOT(btnClickedSlot()));
connect(ui->dial,SIGNAL(valueChanged(int)),
this,SLOT(valueChangedSlot(int)));}Dialog::~Dialog()
{
delete ui;
}void Dialog::btnClickedSlot()
{
mydlg->show();
}void Dialog::valueChangedSlot(int value)
{
mydlg->setValue(value);
}
7.2 子对象→父对象
【需求】:转动子对象球,父对象球跟着转。
这种情况最佳的解决方案信号槽传参,子对象发射带参数的信号函数,父对象使用带参数的槽函数接收。
mydilog.cpp
#include "mydialog.h"
#include "ui_mydialog.h"MyDialog::MyDialog(QWidget *parent) :QDialog(parent),ui(new Ui::MyDialog)
{
ui->setupUi(this);connect(ui->dial,SIGNAL(valueChanged(int)),this,SLOT(valueChangeSlot(int)));
}MyDialog::~MyDialog()
{delete ui;
}void MyDialog::setValue(int value)
{
ui->dial->setValue(value);
}void MyDialog::valueChangeSlot(int value)
{// 发射带参数的自定义信号给父对象
emit valueSignal(value);
}
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{
ui->setupUi(this);
mydlg = new MyDialog(this);connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));connect(ui->dial,SIGNAL(valueChanged(int)),this,SLOT(valueChangedSlot(int)));connect(mydlg,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));}Dialog::~Dialog()
{delete ui;
}void Dialog::btnClickedSlot()
{
mydlg->show();
}void Dialog::valueChangedSlot(int value)
{
mydlg->setValue(value);
}void Dialog::valueSlot(int value)
{
ui->dial->setValue(value);
}
8、事件机制(掌握)
事件时Qt的一种底层机制,经过层层传递,用户输出可以得到处理。程序员也可以使用对应的消息处理函数,来处理当前发生的事件。
本次学习主要在窗口类中实现事件函数,从而检测到事件的传递,利用事件的触发机制,从而实现一些特定的效果。
// 绘制事件
void QWidget::paintEvent(QPaintEvent * event) [virtual protected] // 大小改变事件
void QWidget::resizeEvent(QResizeEvent * event) [virtual protected] // 鼠标按压事件
void QWidget::mousePressEvent(QMouseEvent * event) [virtual protected]
// 鼠标释放事件
void QWidget::mouseReleaseEvent(QMouseEvent * event) [virtual protected]
// 鼠标双击事件
void QWidget::mouseDoubleClickEvent(QMouseEvent * event) [virtual protected]
// 鼠标移动事件
void QWidget::mouseMoveEvent(QMouseEvent * event) [virtual protected]// 移动事件
void QWidget::moveEvent(QMoveEvent * event) [virtual protected]// 按键按压事件
void QWidget::keyPressEvent(QKeyEvent * event) [virtual protected]
// 按键释放事件
void QWidget::keyReleaseEvent(QKeyEvent * event) [virtual protected]// 获取焦点事件
void QWidget::focusInEvent(QFocusEvent * event) [virtual protected]
// 失去焦点事件
void QWidget::focusOutEvent(QFocusEvent * event) [virtual protected]// 关闭事件
void QWidget::closeEvent(QCloseEvent * event) [virtual protected]// 鼠标进入事件
void QWidget::enterEvent(QEvent * event) [virtual protected]
// 鼠标离开事件
void QWidget::leaveEvent(QEvent * event) [virtual protected]
事件函数的基础使用只需要在对应的类中覆盖基类的事件函数即可。事件函数的参数就是包含当前事件数据的对象。
// 绘制图片
// 参数1:横坐标
// 参数2:纵坐标
// 参数3:绘制宽度
// 参数4:绘制高度
// 参数5:绘制
void QPainter::drawPixmap(int x, int y, int width, int height, const QPixmap & pixmap)
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{
ui->setupUi(this);
}Dialog::~Dialog()
{delete ui;
}void Dialog::paintEvent(QPaintEvent *event)
{// 创建一个画家对象// 参数为QPaintDevice * 表示可绘制对象
QPainter painter(this);
QPixmap map(":/new/prefix1/fengjing.png");
painter.drawPixmap(0,0,this->width(),this->height(),map);qDebug() << this->width() << this->height();
}void Dialog::keyPressEvent(QKeyEvent *event)
{if(event->key() == Qt::Key_A){int value = ui->progressBar->value();
ui->progressBar->setValue(--value);}else if(event->key() == Qt::Key_D){int value = ui->progressBar->value();
ui->progressBar->setValue(++value);}else if(event->key() == Qt::Key_W){
ui->progressBar->setValue(100);}else if(event->key() == Qt::Key_S){
ui->progressBar->setValue(0);}else{}
}