科林C++_9 QT

一、认识QT

1.1 简介

1.2 Qt建立工程

1.2.1 命令行

.pro 工程文件

1.2.2 带窗口界面

1.2 QString

1.2.1 初始化

1.2.2 拼接

1.2.3 sprintf格式化

1.2.4 arg灵活的格式化

1.2.5 QString与数字转换

1.2.6 定位

1.2.7 截取

1.2.8 统计

1.2.9 去除空白符

1.2.10 分割

#include "mainwindow.h"

#include <QApplication>
#include <QDebug>
#include <string>
using namespace std;
#include <QString>  //Qt中的字符串

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

//    MainWindow w;   //创建主窗口的对象
//    w.show();   //显示主窗口

    int aa=0;
    double b=3.14;
    const char* p="123";
    qDebug()<<aa<<' '<<b<<' '<<p<<endl;
    //应用调试输出
    string str="abc";
    qDebug()<<str.c_str()<<endl;    //转成const char *
    qDebug("%d %.1f %s %s\n",aa,b,p,str.c_str());   //printf格式化输出

    QString Qstr="qwertyu";
    qDebug()<<Qstr<<endl;   //"qwertyu"

    str=Qstr.toStdString();     //QString转为标准库string
    qDebug()<<"str="<<str.c_str()<<endl;
    str="zxc";
    Qstr=Qstr.fromStdString(str);    //string转为QString
    qDebug()<<"Qstr"<<Qstr<<endl;

    Qstr.sprintf("%d %f %s %s",aa,b,p,str.c_str());
    qDebug()<<Qstr<<endl;

    Qstr="%1 %2 %3";
    Qstr=Qstr.arg(aa).arg(b).arg('C');  //每一个arg替换一个%n,按照数字从小到大替换
    qDebug()<<Qstr<<endl;

    Qstr=QString("%12 %3 %789").arg("qwe").arg(1).arg(114.14);  //只替换%数字后两位,如果有多余的数字,保留
    qDebug()<<Qstr<<endl;

    Qstr=QString("%1%3%2").arg('C').arg(123).arg("qwe");
    //上一步的替换结果可能影响当前替换(两位数字)
    qDebug()<<Qstr<<endl;

    Qstr=QString("%1%3%2").arg("C",QString().sprintf("%d",123),"abc");
    qDebug()<<Qstr<<endl;

    qDebug()<<Qstr.contains("abc")<<endl;
    qDebug()<<Qstr.contains("3")<<endl;

    Qstr="qwertyuiopty";
    qDebug()<<Qstr.indexOf("ty")<<endl;
    qDebug()<<Qstr.lastIndexOf("ty")<<endl; //反向定位

    //substr

    Qstr=" 123    2323 \tq w\nq 456 \n ";
    Qstr=Qstr.simplified();
    qDebug()<<Qstr<<endl;

    QStringList strLst=Qstr.split(" ");
    for(QString s:strLst){
        qDebug()<<s;
    }

    return a.exec();
}

1.3 MessageBox

1.3.1 简单提示框

    #include <QMessageBox>
    QMessageBox::StandardButton button = QMessageBox::information(nullptr,"info","提示",QMessageBox::Yes|QMessageBox::No);
    if(button==QMessageBox::Yes){
        qDebug()<<"yes";
    }
    else if(button==QMessageBox::No){
        qDebug()<<"no";
    }

二、信号与槽

2.1 概念

Q_OBJECT

2.2 添加信号与槽

2.2.1 槽函数

2.2.1.1 选择使用套件的功能

2.2.1.2 手动添加槽函数

//mainwindow.cpp
void MainWindow::on_checkBox_stateChanged(int arg1)
{
    //勾选 arg1=2,不勾选 grg1=0
    if(arg1==2){    //编辑框不可用
        //首先找到要操作的组件,调用方法
        ui->lineEdit->setDisabled(true);
    }
    else if(arg1==0){   //编辑框可用
        ui->lineEdit->setDisabled(false);
    }
}
修饰槽函数:访问修饰符 + slots关键字,两者缺一不可

通过信号名连接信号与槽函数

通过名字连接,规则:on_信号的发出者_发出的信号,信号和槽函数的参数应当保持一致

checkbox半选状态

设置标题

//mainwindow.h
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:  //修饰槽函数:访问修饰符 + slots关键字,两者缺一不可
    void on_checkBox_stateChanged(int arg1);
    void slot_dealbutton(QAbstractButton *button);  //自定义槽函数,参数应当与信号参数一致

private:
    Ui::MainWindow *ui;
public:
    QMessageBox * pMsgBox;
    QPushButton * pOK;
    QPushButton * pCancel;
};

绑定连接(静态的)
connect ( 1:信号的发出者 , 2:使用带参数的宏SIGNAL(信号的函数名(参数列表)),注:若有形参名应当删去 , 3:信号的传输者 , 4:使用带参数的宏SLOT(槽的函数名(参数列表)),注:若有形参名应当删去 )

//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //半选
    ui->checkBox->setTristate(true);    //为checkbox设置三态

    pMsgBox = new QMessageBox;
    pMsgBox->setWindowTitle("提示");
    pMsgBox->setText("这是个提示");
    pOK=pMsgBox->addButton("确定",QMessageBox::YesRole);
    pCancel=pMsgBox->addButton("取消",QMessageBox::NoRole);
    
    void buttonClicked(QAbstractButton *button);    //信号
    //绑定连接
    //1:信号的发出者 2:使用带参数的宏SIGNAL(信号的函数名(参数列表)),注:若有形参名应当删去
    //3:信号的传输者 4:使用带参数的宏SLOT(槽的函数名(参数列表)),注:若有形参名应当删去
    connect(pMsgBox,SIGNAL(buttonClicked(QAbstractButton *)),this,SLOT(slot_dealbutton(QAbstractButton *)));
}

//参数传递:触发信号的按钮
void MainWindow::slot_dealbutton(QAbstractButton *button){
    if(button==pOK){
        qDebug()<<"确定";
    }
    else if(button==pCancel){
        qDebug()<<"取消";
    }
}

2.2.2 自定义信号

2.2.2.1 发射自定义信号
//mainwindow.h
class MainWindow : public QMainWindow
{
signals:    //只需要signals关键字,不要加修饰符
    void signals_sendData(int,int,QString); //信号只需要声明,不需要定义
    
};
//mainwindow.cpp
void MainWindow::on_pushButton_2_clicked()
{
    //发射自定义信号
    int a=1;
    QString s="假发";
    emit signals_sendData(a,2,s);
}

新建窗口 

//dialog.h
class Dialog : public QDialog
{
public slots:   //槽函数,有声明,有定义
    void slots_recvData(int,int,QString);
};
//dialog.cpp
void Dialog::slots_recvData(int a,int b,QString s){
    int sum=a+b;
    QString ss=s+QString::number(sum);  //数字转为字符串
    ui->label->setText(ss); //显示字符串
}

绑定连接

2.2.2.2. 通过一个信号控制多个槽

通过 水平滑块Horizontal Bar 控制 进度条Progress Bar 以及  时间显示Time Edit


设置时钟显示的格式timeEdit

声明定义两个槽

//mainwindow.h
class MainWindow : public QMainWindow
{
    Q_OBJECT
private slots: 

    void slots_time(int);
    void slots_progress(int);

};
//mainwindow.cpp
void MainWindow::slots_time(int a){
    qDebug()<<a;
    QTime time(0,0,0);  //定义时间 时分秒
    time=time.addSecs(60*60*24*a/100);  //增加对应的秒数
    ui->timeEdit->setTime(time);    //设置时间
}

void MainWindow::slots_progress(int a){
    ui->progressBar->setValue(a);
}

绑定连接

//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    //设置水平滑块的范围
    ui->horizontalSlider->setRange(0,100);
    //设置时钟显示的格式
    ui->timeEdit->setDisplayFormat("h:mm:ss");
    //valueChanged(int)
    connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_time(int)));
    connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_progress(int)));
}
2.2.2.3 多个信号控制一个槽

通过两个 按钮Push Button 控制窗口dialog

在dialog里声明定义槽函数

//dialog.cpp
void Dialog::slots_isLight(){
    if(this->isVisible())   //如果显示隐藏窗口
        this->hide();
    else
        this->show();
}

在主函数中绑定连接

//main.cpp

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    Dialog dia; //并行的窗口
    dia.show();

    QObject::connect(w.getui()->pb_1,SIGNAL(clicked()),&dia,SLOT(slots_isLight()));
    QObject::connect(w.getui()->pb_2,SIGNAL(clicked()),&dia,SLOT(slots_isLight()));

    return a.exec();
}

注:因为class MainWindow 中 ui为私有

需要公有接口来调用

2.2.2.4 中转信号

要求:主界面的信号先连接主界面再连接其他窗口

主界面:数字输入框Spin Box

dialog窗口:LCD显示框LCD Number

为主界面添加一个中转信号

//mainwindow.h
signals:
    void signals_transit(int);    //信号,不需要定义

将输入框信号与中转信号绑定

//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    //数字输入框的信号 valueChanged(int)
    //主界面的中转信号 signals_transit(int)
    connect(ui->spinBox,SIGNAL(valueChanged(int)),this,SIGNAL(signals_transit(int)));
}

dialog窗口声明定义槽函数

//dialog.cpp
void Dialog::slots_showNum(int a){
    ui->lcdNumber->display(a);
}

绑定中转信号与槽函数

//main.cpp
    QObject::connect(&w,SIGNAL(signals_transit(int)),&dia,SLOT(slots_showNum(int)));

2.3 断开连接

勾选框CheckBox

//mainwindow.h
class MainWindow : public QMainWindow
{
public:
    QMetaObject::Connection con;
};
//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow)
{
    //valueChanged(int)
    connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_time(int)));
    con = connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_progress(int)));

    if(con)
        qDebug()<<"success";
    else qDebug()<<"failed";
}

void MainWindow::on_cb_time_stateChanged(int arg1)  //方法1
{
    if(arg1==2){    //勾选则取消连接
        disconnect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_time(int)));
    }else if(arg1==0){  //不勾选,则重新连接
        connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_time(int)));
    }
}

void MainWindow::on_cb_bar_stateChanged(int arg1)   //方法2
{
    if(arg1==2){    //勾选则取消连接
        disconnect(con);
    }else if(arg1==0){  //不勾选,则重新连接
        con=connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(slots_progress(int)));
    }
}

三、界面布局

3.1 布局Layouts

标签 Label

单行文本输入框 Line Edit

下拉框 Combo Box

数字输入框 Spin Box

日期 Date Edit

//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFile>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

public slots:
    void slots_setpic();
    void slots_updateUserinfo();
    void slots_clearForm();

public:
    QString file;
    QFile myFile;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //准备工作
    ui->cb_sex->addItem("男");   //默认显示第一项
    ui->cb_sex->addItem("女");
    ui->cb_sex->setCurrentText("女");    //设置当前文本

    ui->sb_age->setValue(1);

    ui->de_birth->setDate(QDate::currentDate());    //设置当前日期

    QStringList strLst={"qq.com","163.com","gmail.com"};
    ui->cb_suffix->addItems(strLst);
    ui->cb_suffix->setCurrentIndex(0);  ///设置当前下标

    connect(ui->pb_pic,SIGNAL(clicked()),this,SLOT(slots_setpic()));

    //绑定路径
    QString path="D:/系统默认/桌面";
    QDir dir(path); //定义变量,并绑定路径
    //验证路径是否存在
    if(!dir.exists()){   //不存在路径
        if(!dir.mkpath(path)){   //创建路径失败
            QMessageBox::critical(this,"错误","创建路径失败");
            exit(0);
        }
    }
    file=path;
    if(path.right(1)!="/" && path.right(1)!="\\")
        file+="/";
    file+="userinfo.txt";

    //绑定文件
    myFile.setFileName(file);
    if(!myFile.open(QIODevice::ReadWrite|QIODevice::Text)){
        QMessageBox::critical(this,"错误","文件打开失败");
        exit(0);
    }

    connect(ui->pb_update,SIGNAL(clicked()),this,SLOT(slots_updateUserinfo()));
    connect(ui->pb_clear,SIGNAL(clicked()),this,SLOT(slots_clearForm()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::slots_setpic()
{
    QString picFile = QFileDialog::getOpenFileName(this,"上传图片","D:/系统默认","Images (*.png *.xpm *.jpg)");
    if(picFile!=""){    //选择了一张图
        qDebug()<<picFile;
        QPixmap pic(picFile);   //定义图片变量

        QRect rect = ui->lb_pic->geometry();

        pic=pic.scaled(rect.width(),rect.height(),Qt::KeepAspectRatio);   //图片等比例缩放

        ui->lb_pic->setPixmap(pic); //设置图片

    }
}

void MainWindow::slots_updateUserinfo()
{
    //清空文件内容
    myFile.resize(0);
    //获取信息
    QString userinfo = QString("%1\n%2\n%3\n%4\n%5\n%6\n%7\n%8\n%9\n")
            .arg(ui->le_nickname->text())
            .arg(ui->te_sign->toPlainText())
            .arg(ui->le_name->text())
            .arg(ui->cb_sex->currentText())
            .arg(ui->sb_age->text())
            .arg(ui->de_birth->text())
            .arg(ui->le_tel->text())
            .arg(ui->le_mail->text()+'@'+ui->cb_suffix->currentText())
            .arg(ui->le_sch->text());
    //写入文件
    if(!myFile.write(userinfo.toStdString().c_str())){
        QMessageBox::critical(this,"错误","写入文件失败");
    }
    //手动刷新缓冲区
    myFile.flush();
}

void MainWindow::slots_clearForm()
{
    ui->le_nickname->clear();
    ui->te_sign->setText("");
    ui->le_name->setText("");
    ui->cb_sex->setCurrentText("女");
    ui->sb_age->setValue(1);
    ui->de_birth->setDate(QDate::currentDate());
    ui->le_tel->clear();
    ui->le_mail->clear();
    ui->cb_suffix->setCurrentIndex(0);
    ui->le_sch->clear();
    ui->lb_pic->setText("图片");
}

图片缩放模式

3.2 一些知识点

3.2.1 编辑伙伴

ALT+ (按键)
可以指定快捷键,指向到他的伙伴上

3.2.2 TAB键顺序

TAB 下一个

SHIFT+TAB 上一个

四、 事件

由系统或者QT在不同的时间发出

信号由具体对象发出,然后马上交给connect函数连接的slot处理。

对于事件,QT使用了一个事件队列

4.1 重写事件

//mylabel.h
#ifndef MYLABEL_H
#define MYLABEL_H

#include <QLabel>

class mylabel : public QLabel
{
    Q_OBJECT
public:
    explicit mylabel(QWidget *parent = nullptr);

signals:
public:
    void mousePressEvent(QMouseEvent *ev) override;
    void mouseMoveEvent(QMouseEvent *ev) override;
    void mouseReleaseEvent(QMouseEvent *ev) override;
};

#endif // MYLABEL_H
//mylabel.cpp
#include "mylabel.h"
#include <QMouseEvent>

mylabel::mylabel(QWidget *parent) :QLabel(parent)
{

}
void mylabel::mousePressEvent(QMouseEvent *ev){
    if(ev->button()==Qt::LeftButton){   //如果事件触发了,并且是左键摁下
        this->setText(QString("鼠标左键按下,%1,%2").arg(ev->x()).arg(ev->y()));
    }
}
/*
    button:触发当前事件的按钮
    buttons:触发当前事件时,有哪些按键是按下的
*/
void mylabel::mouseMoveEvent(QMouseEvent *ev){
    if(ev->buttons()==Qt::LeftButton){
        this->setText(QString("鼠标左键按下并移动,%1,%2").arg(ev->x()).arg(ev->y()));
    }
    else if(ev->buttons()==Qt::NoButton){
        this->setText(QString("鼠标移动,%1,%2").arg(ev->x()).arg(ev->y()));
    }
}
void mylabel::mouseReleaseEvent(QMouseEvent *ev){
    if(ev->button()==Qt::LeftButton){
        this->setText(QString("鼠标左键抬起,%1,%2").arg(ev->x()).arg(ev->y()));
    }
}

4.1.1 事件分发

C++ 强制类型转换(二) (qq.com)

//myline.h
#ifndef MYLINE_H
#define MYLINE_H

#include <QLineEdit>

class myline : public QLineEdit
{
    Q_OBJECT
public:
    explicit myline(QWidget *parent = nullptr);

signals:
public:
    bool event(QEvent * ev) override;
    void keyPressEvent(QKeyEvent *) override;
    void keyReleaseEvent(QKeyEvent *event) override;

    QString m_tel;
};

#endif // MYLINE_H
#include "myline.h"
#include <QKeyEvent>
#include <QDebug>
#include <QMessageBox>

myline::myline(QWidget *parent) : QLineEdit(parent)
{

}

//当前这个组件,所有的事件都会进入事件分发
bool myline::event(QEvent * ev){
    if(ev->type()==QEvent::KeyPress){   //如果是键盘
        //((QKeyEvent*)ev)->key();
        //动态转换,将正确的父类指针转为子类
        QKeyEvent *pkey=dynamic_cast<QKeyEvent *>(ev);
        if(pkey){
            if(pkey && pkey->key()>=Qt::Key_0 && pkey->key()<=Qt::Key_9 || pkey->key()==Qt::Key_Backspace){
                //不拦截 放行
                qDebug()<<"event:"<<pkey->key();
                return QLineEdit::event(ev);    //调用父类的分发函数
            }
            else {
                //拦截
                qDebug()<<"拦截"<<pkey->key();
                return true;    //当前这个事件已经处理了,不需要后续处理
            }
        }
        else {
            qDebug()<<"转化失败";
        }

    }
    else{   //其他类型事件
        return QLineEdit::event(ev);
    }
}
void myline::keyPressEvent(QKeyEvent *ev){
    qDebug()<<"keyPressEvent:"<<ev->key();

    if(ev->key()==Qt::Key_Backspace){
        setText(this->text().left(text().length()-1));
        m_tel=m_tel.left(m_tel.length()-1);
        return ;
    }

    QString num=this->text();
    if(num.length()<11){
        m_tel+=QString::number(ev->key()-Qt::Key_0);
        int len=m_tel.length();
        if(len<=3){ //前3位正常显示
            this->setText(m_tel);
        }
        else if(len>3 && len<=7){   //隐藏号码
            QString s=m_tel.left(3);
            for(int i=4;i<=len;i++)
                s+="*";
            this->setText(s);
        }
        else{
            QString s=m_tel.left(3)+"****"+m_tel.right(len-7);
            this->setText(s);
        }
    }
}

/*
    回车键
    Key_Return 字母区的回车
    Key_Enter 数字小键盘的回车
*/

void myline::keyReleaseEvent(QKeyEvent *event){
    qDebug()<<"keyReleaseEvent:"<<event->key();
    if(event->key()==Qt::Key_Return){
        QMessageBox::information(this,"电话号码",m_tel);
    }
}

4.1.2 事件过滤

//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //选择安装组件
    ui->lb_pass->installEventFilter(this);
    ui->te_info->installEventFilter(this);

    ui->lb_pass->setAttribute(Qt::WA_InputMethodEnabled,false);   //设置属性,禁用中文输入法

}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event){
    if(watched==ui->lb_pass){   //如果是密码组件
        if(event->type()==QEvent::KeyPress){    //如果是键盘按下
            QKeyEvent *pkey=dynamic_cast<QKeyEvent *>(event);
            if(pkey->key()>=Qt::Key_A && pkey->key()<=Qt::Key_Z){
                qDebug()<<"放行"<<pkey->key();
                return QMainWindow::eventFilter(watched,event);
            }
            else{   //过滤拦截
                qDebug()<<"过滤"<<pkey->key();
                return true;
            }
        }
    }
    else if(watched==ui->te_info){
        if(event->type()==QEvent::Wheel){   //滑轮
            QWheelEvent *pwheel=dynamic_cast<QWheelEvent *>(event);
            if(pwheel->buttons()==Qt::MidButton){   //中键
                //pwheel->angleDelta().x();   //水平滚动 ALT+中键
                //pwheel->angleDelta().y();   //上+120下-120
                qDebug()<<"y="<<pwheel->angleDelta().y();
                if(pwheel->angleDelta().y()>0){
                    ui->te_info->zoomIn();  //放大
                }
                else if(pwheel->angleDelta().y()<0){
                    ui->te_info->zoomOut(); //缩小
                }
                //return true;
            }
        }
    }
}

4.2 自定义事件

//dialog.h
#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

    virtual void customEvent(QEvent *event);

    int m_timerId;

    void timerEvent(QTimerEvent *event) override;

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H
//dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include "myevent.h"
#include <QDebug>

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::customEvent(QEvent *event){
    qDebug()<<"customEvent";
    extern QEvent::Type myEveID;
    if(event->type()==myEveID){
        MyEvent* peve=dynamic_cast<MyEvent*>(event);
        if(peve){
            ui->label->setText(QString("%1 %2 %3 = %4").arg(peve->n1).arg(peve->m_rule).arg(peve->n2).arg(peve->m_ret));
            this->show();
            m_timerId=this->startTimer(3000);   //频率间隔,window:hwnd,id,interval,proc
        }
    }
    qDebug()<<"customEvent 处理完毕";
}

void Dialog::timerEvent(QTimerEvent *event){
    qDebug()<<"timerEvent";
    if(m_timerId==event->timerId()){    //先判断是哪个定时器
        killTimer(m_timerId);   //停止计时器
        this->hide();   //隐藏窗口
    }
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include "myevent.h"
#include "dialog.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //添加初始规则
    QStringList rules{"+","-","x","/"};
    ui->cb_rule->addItems(rules);
}

MainWindow::~MainWindow()
{
    delete ui;
}

//定义全局的事件ID User=1000 MaxUser=65535
QEvent::Type myEveID=QEvent::User;

void MainWindow::on_pushButton_clicked()
{
    //获取数据
    int n1=ui->sb1->value();
    int n2=ui->sb2->value();

    //获取规则
    QString rule=ui->cb_rule->currentText();
    qDebug()<<n1<<rule<<n2;

    //自定义事件
    MyEvent myeve(myEveID);

    /*
    myeve.n1=n1;
    myeve.n2=n2;
    if(rule=="+")
        myeve.m_ret=n1+n2;
    else if(rule=="-")
        myeve.m_ret=n1-n2;
    else if(rule=="x")
        myeve.m_ret=n1*n2;
    else if(rule=="/")
        myeve.m_ret=n1/n2;
    myeve.m_rule=rule;
    qDebug()<<myeve.m_ret;
    */

    MyEvent *peve= new MyEvent(myEveID);
    peve->n1=n1;
    peve->n2=n2;
    if(rule=="+")
        peve->m_ret=n1+n2;
    else if(rule=="-")
        peve->m_ret=n1-n2;
    else if(rule=="x")
        peve->m_ret=n1*n2;
    else if(rule=="/")
        peve->m_ret=n1/n2;
    peve->m_rule=rule;

    //发送事件
    extern Dialog* pdia;

    //阻塞的函数,直到对方处理完后才返回
    //QCoreApplication::sendEvent(pdia,&myeve);

    //只管发不管接收,立即返回
    QCoreApplication::postEvent(pdia,peve);   //ERROR:QCoreApplication::removePostedEvent: Event of type 1000 deleted while posted to Dialog Dialog

    qDebug()<<"postEvent 投递完毕";
}

//myevent.h
#ifndef MYEVENT_H
#define MYEVENT_H

#include <QEvent>
#include <QString>

class MyEvent : public QEvent
{
public:
    MyEvent(Type type);
    int n1,n2;
    int m_ret;    //计算的结果
    QString m_rule;
};

#endif // MYEVENT_H
//main.cpp
#include "mainwindow.h"
#include <QApplication>
#include "dialog.h"

Dialog *pdia=nullptr; //放在全局,便于跨文件调用

//Dialog dia;   //ERROR:Must construct a QApplication before a QWidget

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    Dialog dia;
    pdia=&dia;

    return a.exec();
}

五、进程线程

5.1 线程

#include <QCoreApplication>
#include <windows.h>
#include <QDebug>

bool isQuit = false;    //退出
bool isOut = false;     //已经退出

//unsigned long
DWORD (WINAPI Thread) (LPVOID p){

    /*
    int n=*(int*)p;
    for(int i=0;i<n;i++){
        qDebug()<<"Thread"<<i;
        Sleep(1000);
    }
    */
    while(!isQuit){
        qDebug()<<"work";
        Sleep(30000);
    }
    //isOut=true;

    return 0;   //正常退出
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int num=20;

    //返回线程的句柄
    HANDLE handle = CreateThread(nullptr/*安全属性:nullptr,使用默认的安全属性*/,
                 0/*线程栈大小:0,使用默认大小(window:1M)*/,
                 &Thread/*线程函数*/,
                 &num/*线程函数的参数*/,
                 0/*创建标志:0,立即运行;CREATE_SUSPENDED:挂起*/,
                 nullptr/*指针的地址,这里用不到*/);


    for(int i=0;i<num;i++){
        qDebug()<<"Sleep"<<i;

/*
        //挂起计数器=0 没挂起。挂起时++,恢复时--,等于0时恢复运行
        if(i==5){
            //返回的是挂起之前的计数器的值
            DWORD n1=SuspendThread(handle);  //挂起线程
            qDebug()<<"n1="<<n1;
        }
        if(i==8){
            DWORD n2=SuspendThread(handle); //挂起
            qDebug()<<"n2="<<n2;
        }
        if(i==11){
           //返回的是恢复之前的计数器的值
           DWORD n3= ResumeThread(handle);  //恢复线程
           qDebug()<<"n3="<<n3;
        }
        if(i==14){
           DWORD n4= ResumeThread(handle);  //恢复
           qDebug()<<"n4="<<n4;
        }
*/

        if(i==7){
            isQuit=true;
            qDebug()<<"通知子线程退出";

            /*
            while(1){
                if(isOut){
                    qDebug()<<"已经等到子线程退出了";
                    break;
                }
            }
            */

            DWORD waitFalg = WaitForSingleObject(handle,3000);
            if(waitFalg==WAIT_OBJECT_0){    //等待的时间内正常退出了
                qDebug()<<"等待的时间内正常退出了";
            }
            else if(waitFalg==WAIT_TIMEOUT){    //等待超时了
                qDebug()<<"杀死线程";
                //强制杀死线程,有风险,万不得已的强制手段
                TerminateThread(handle,-1);
            }

            if(handle)
                CloseHandle(handle);    //将使用计数-1
            handle=nullptr;

        }



        Sleep(1000);
    }

    return a.exec();
}

5.1.1 线程并发

5.1.2 线程同步

5.1.2.1 原子访问
::_InterlockedIncrement((long*)&count); 
5.1.2.2 关键段

对于懒汉式,在多线程下可能会创建出多个对象

#include<iostream>
#include <QCoreApplication>
#include <windows.h>
#include <QDebug>

using namespace std;

//CRITICAL_SECTION cs;    //关键段

class MyLock{
    CRITICAL_SECTION cs;
public:
    MyLock(){
        ::InitializeCriticalSection(&cs);
    }
    ~MyLock(){
        DeleteCriticalSection(&cs);
    }
    void lock(){
        ::EnterCriticalSection(&cs);
    }
    void unlock(){
        ::LeaveCriticalSection(&cs);
    }
};

class CSingleton {
private:
    CSingleton():m_a(0){}
    CSingleton(const CSingleton&) = delete;
    ~CSingleton(){}

    static CSingleton* m_psin;

    static class DelSingleton {
    public:
        ~DelSingleton() {
            CSingleton::DeleteSingleton();
        }
    }m_delsin;

public:
    //在多线程下可能会创建出多个对象
    static CSingleton* GetSingleton() {

        static MyLock lock;
        
        if (!m_psin){   //再加一层判断,提高效率
            //加锁
            //::EnterCriticalSection(&cs);
            lock.lock();
            if (!m_psin)
                m_psin = new CSingleton;
            //解锁
            //::LeaveCriticalSection(&cs);
            lock.unlock();
        }
        return m_psin;
    }

    static void DeleteSingleton() {
        if (m_psin)
            delete m_psin;
        m_psin = nullptr;
    }
public:
    int m_a;
};

CSingleton* CSingleton::m_psin = nullptr;
CSingleton::DelSingleton CSingleton::m_delsin;

DWORD WINAPI threadProc(void*){
    Sleep(100);
    CSingleton *pSin= CSingleton::GetSingleton();
    qDebug()<<pSin;
    return 0;
}


int main() {
    //::InitializeCriticalSection(&cs);   //初始化关键段(关键段变量的地址)
    for(int i=0;i<100;i++){
        CreateThread(nullptr,0,&threadProc,nullptr,0,nullptr);
    }
    Sleep(3000);
    //::DeleteCriticalSection(&cs);   //删除关键段(关键段变量的地址)
    return 0;
}

对关键段进行封装:保证用之前一定初始化,防止漏写删除

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值