1. 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)
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QButtonGroup>
#include <QMessageBox>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QButtonGroup *btp;
private slots:
void buttonClickedSlot(int);
};
#endif // DIALOG_H
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 btp;
delete ui;
}
void Dialog::buttonClickedSlot(int id)
{
if(id == 1)
{
QMessageBox::StandardButton stdb = QMessageBox::question(this,"question", "您是否需要关闭!!");
if(stdb == QMessageBox::Yes)
{
close();
}
else if(stdb == QMessageBox::No)
{
}
}
else if(id == 2)
{
QMessageBox::information(this,"information", "已经加载完成");
}
else if(id == 3)
{
QMessageBox::warning(this,"warning", "警告变量未使用!!!");
}
else if(id == 4)
{
QMessageBox::critical(this,"critical", "程序异常,使用了未定义的变量!!");
}
else
{
}
}
2. QWidget类(掌握)
QWidget类是所有窗口和组件的基类。之前认识此类更多的是站在组件的角度上,实际上QWidget作为所有窗口的基类,也具有很多窗口的特性。窗口类的继承结构如下:
新建一个项目,使自带的窗口类继承QWidget
创建完成后运行时发现,与QDialog窗口差别不大,表面区别有
#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类作为所有窗口类的基类,内部也有很多窗口的特性。
- windowTitle : QString
窗口标题
- windowFlags : Qt::WindowFlags(窗口标记)
setter函数进行设置。在使用setter函数设置多个标记时,使用 | 进行分隔(多个窗口标记之间可能会存在冲突)实现窗口无边框,且最上层。
// 设置窗口状态
// 参数要设置的窗口状态值
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参数,表示对对象的销毁还需要程序员手动回收。
这样做也有缺点,就是内存占用,主窗口还存在时,子窗口内存不会被释放。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void buttonClickedSlot();
void buttonThisClickedSlot();
};
#endif // DIALOG_H
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(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();
}
点击parent生成新窗口,此时关闭父窗口都关闭;点击“不parent“生成的新窗口没关系。
4、QMainWindow主窗口类
QMainWindow是最适合作为主窗口的类型,因为其有多个组成部分。
4.1 QMenuBar菜单栏
菜单栏的组成如下图所示
相关C++函数如下:
// 向菜单栏添加一级菜单
// 参数为菜单显式的文字
// 返回值:添加的菜单对象
Menu * QMenuBar::addMenu(const QString & title)
// 向一级菜单中添加动作
// 参数:动作显式的文字
// 返回值:添加的动作对象
QAction * QMenu::addAction(const QString & text)
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* menuRet = menuFile->addMenu("最近访问的文件");
// 向二级菜单中添加动作
QAction* actionH = menuRet->addAction("hello.h");
QAction* actionCpp = menuRet->addAction("hello.cpp");
}
MainWindow::~MainWindow()
{
delete ui;
}
为了使QAction点击后有触发效果,需要使用对应的信号连接槽函数,QAction的信号函数:
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
private slots:
void actionNewTriggedSlot();
void actionCppTriggedSlot();
};
#endif // MAINWINDOW_H
mainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->action,SIGNAL(triggered()),
this,SLOT(actionNewTriggedSlot()));
connect(ui->actionHello_cpp,SIGNAL(triggered()),
this,SLOT(actionCppTriggedSlot()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::actionNewTriggedSlot()
{
ui->textBrowser->append("新建了一个文件");
}
void MainWindow::actionCppTriggedSlot()
{
ui->textBrowser->append("打开了hello.cpp");
}
4.2 QToolBar 工具栏
工具栏按钮往往使用菜单栏中的Action动作。但是需要给QAction添加图标。
4.3 QStatusBar状态栏
// 在状态栏显式信息
// 参数1:展示的信息内容
// 参数2:信息展示的时间(毫秒),默认值0表示持续显式
void QStatusBar:: showMessage(const QString & message, int timeout = 0)[slot]
// 清除显式
void QStatusBar:: clearMessage()[slot]
void MainWindow::actionNewTriggedSlot(){
ui->textBrowser->append("新建了一个文件");
ui->statusBar->clearMessage ();
}
void MainWindow::actionCppTriggedSlot(){
ui->textBrowser->append("打开了hello.cpp");
//在状态栏中展示信息
ui->statusBar->showMessage("打开成功啦");
}
QStatusBar支持自定义样式,可以通过下面的函数添加组件:
// 状态栏显式信息
// 参数1:组件对象
// 参数2:拉伸因子
void QStatusBar:: addWidget(QWidget * widget, int stretch = 0)
QLabel *lab = new QLabel (this);
lab->setText("labl");
QLabel *lab2 = new QLabel(this);
lab2->setText("显式第二个");
ui->statusBar->addwidget (lab,5);
ui->statusBar->addwidget(lab2,1);
5、新建自定义窗口类
在一个项目中新建一个Qt的窗口界面类,操作步骤如下:
- 在Qt Creator中选中项目名称,鼠标右键,点击添加新文件。
- 在弹出的窗口中按照下图所示进行操作。
3. 在弹出的窗口中选择界面模板后,点击下一步
4. 在弹出的窗口中,输入类名(帕斯卡),点击下一步
5. 在项目管理界面直接点击完成,可以看到新的界面类文件已经添加到项目中了
点击按钮出现副窗口
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include "mydialog.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
private slots:
void btnClickedSlot();
};
#endif // DIALOG_H
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 *mydlog = new MyDialog(this);
mydlog->show();
}
6、对象传值
6.1 父对象 -> 子对象
此处指的是Qt的parent参数的依赖关系,并非继承关系。
【需求】转动父窗口中的球,子窗口中的球跟着转。
这种情况最佳的解决方案使用C++的成员函数传参的方式。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
#include "mydialog.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
MyDialog *mydlog;// 成员变量声明(关键!)让mydlog全局可用
private slots:
void btnClickedSlot();
void valueChangedSlot(int);
};
#endif // DIALOG_H
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()
{
mydlog = new MyDialog(this);
connect(ui->dial,SIGNAL(valueChanged(int)),
this,SLOT(valueChangedSlot(int)));
mydlog->show();
}
void Dialog::valueChangedSlot(int value)
{
mydlog->mySetValue(value);
}
myDialog.h
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include <QDialog>
namespace Ui {
class MyDialog;
}
class MyDialog : public QDialog
{
Q_OBJECT
public:
explicit MyDialog(QWidget *parent = 0);
~MyDialog();
void mySetValue(int value);
private:
Ui::MyDialog *ui;
};
#endif // MYDIALOG_H
myDialog.cpp
#include "mydialog.h"
#include "ui_mydialog.h"
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
}
MyDialog::~MyDialog()
{
delete ui;
}
void MyDialog::mySetValue(int value)
{
ui->dial->setValue(value);
}
6.2 子对象 -> 父对象
此处指的是Qt的parent参数的依赖关系,并非继承关系。
[需求]转动子窗口中的球,主窗口中的球跟着转。
这种情况最佳的解决方案就是信号槽传参(因为子拿不到父对象),子对象发射带参数的信号函数,父对象使用带参数的槽函数接收。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QDebug>
#include "mydialog.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
MyDialog *mydlog = NULL;
private slots:
void btnClickedSlot();// 按钮点击槽函数
//主窗口 QDial 变化时,调用 MyDialog::mySetValue 更新子窗口
void valueChangedSlot(int);//主旋钮值变化槽函数
//子窗口 QDial 变化时,发射 valueSignal
void setValueSlot(int);//接收子窗口信号,更新主窗口 QDial
// 接收子窗口值的槽函数
};
#endif // DIALOG_H
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()
{
mydlog = new MyDialog(this);
connect(ui->dial,SIGNAL(valueChanged(int)),
this,SLOT(valueChangedSlot(int)));
connect(mydlog,SIGNAL(valueSignal(int)),
this,SLOT(setValueSlot(int)));
mydlog->show();
}
//主窗口 → 子窗口
void Dialog::valueChangedSlot(int value)
{
mydlog->mySetValue(value); // 更新子窗口值
}
void Dialog::setValueSlot(int value)
{
ui->dial->setValue(value);// 更新主窗口值
}
myDialog.h
#ifndef MYDIALOG_H
#define MYDIALOG_H
#include <QDialog>
namespace Ui {
class MyDialog;
}
class MyDialog : public QDialog
{
Q_OBJECT
public:
explicit MyDialog(QWidget *parent = 0);
~MyDialog();
void mySetValue(int value);// 设置值的公共接口
private:
Ui::MyDialog *ui;
private slots:
void myValueChangedSlot(int);// 子旋钮值变化槽函数
signals:
void valueSignal(int); // 声明带参数的自定义信号 // 值变化信号
};
#endif // MYDIALOG_H
myDialog.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(myValueChangedSlot(int)));
}
MyDialog::~MyDialog()
{
delete ui;
}
void MyDialog::mySetValue(int value)
{
ui->dial->setValue(value);// 更新UI控件
}
void MyDialog::myValueChangedSlot(int value)
{
// 发射自定义信号
emit valueSignal(value);
}
主窗口 → 子窗口:主窗口检测到旋钮变化后,直接调用子窗口的mySetValue()方法
子窗口 → 主窗口:子窗口检测到旋钮变化后,发射valueSignal信号,主窗口通过槽函数接收并更新自身值
7. 事件机制(掌握)(常用)
事件是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]
事件函数的基础使用,只需要在对应的类中覆盖基类的事件函数即可,事件函数的参数就是包含了当前事件数据的对象。
绘制事件:
// 绘制事件
void Dialog::paintEvent(QPaintEvent *event)
{
// 创建一个画家对象
// 参数为QPaintDevice*表示可绘制的对象
QPainter painter(this);
QPixmap map(":/new/prefix1/fengjing.png");
// 绘制图片
// 参数1:横轴坐标
// 参数2:纵轴坐标
// 参数3:绘制的宽度
// 参数4:绘制的高度
// 参数5:绘制的内容
painter.drawPixmap(0,0,this->width(),this->height(),map);
qDebug() << this->width() << this->height();
}
【案例】:按下键盘的A D W S 键控制窗口移动。
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPainter>
#include <QPixmap>
#include <QDebug>
#include <QKeyEvent>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void paintEvent(QPaintEvent * event);
void keyPressEvent(QKeyEvent * event);
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
dialog.cpp
#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");
// 绘制图片
// 参数1:横轴坐标
// 参数2:纵轴坐标
// 参数3:绘制的宽度
// 参数4:绘制的高度
// 参数5:绘制的内容
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 x1 = this->x();
this->move(x1-10,this->y());
}
else if(event->key() == Qt::Key_D)
{
int x1 = this->x();
this->move(x1+10,this->y());
}
else if(event->key() == Qt::Key_W)
{
int y1 = this->y();
this->move(this->x(),y1-10);
}
else if(event->key() == Qt::Key_S)
{
int y1 = this->y();
this->move(this->x(),y1+10);
}
else
{
}
}