QT(六)多窗口编程

发布于:2025-07-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

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的窗口界面类,操作步骤如下:

  1. 在Qt Creator中选中项目名称,鼠标右键,点击添加新文件。
  2. 在弹出的窗口中按照下图所示进行操作。

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
    {

    }
}


网站公告

今日签到

点亮在社区的每一天
去签到