0926-27,元对象模型,信号和槽函数,connect函数,事件处理的详细过程,widgets模块

目录

0926

QMetaProperty   //创建一个支持元对象系统的类

myclass.h

myclass.cpp

main.cc

signal_slot   //创建自定义的信号和槽函数,携带参数,多次connect,去重连接

a.h

a.cpp

main.cpp

signal_slot _complex    //槽函数执行过程中发射信号

a.h

mian.cpp

pingpong    //自己给自己发消息

a.h

a.cpp

sender()   //在槽函数中,返回发出该信号的信号的指针(pushbutton)

mainwindow.h

mainwindow.cpp

0927   

tree   //创建树结构

my_widgets.h    //过滤事件,毁灭吧世界,老师的也没有跑过滤掉

c.h

main.cpp

kunkun     //事件和槽函数结合在一起

mylabel.h  //重写事件

mainwindow.h   //设置组件和槽函数

mainwindow.cpp  //实现,连接

0926作业

01 实现一个井字棋 点击空白按钮可以显示'X'或者'O'       //坏了,一次回退了两个,先这样吧

mainwindow.h

mainwindow.cpp

02 根据如下ui绘制一个计算器,先不实现计算的功能,只实现显示表达式和编辑表达式的功能

03 简述信号和槽机制的优势和劣势

04 信号和槽机制的目的是什么?书写代码实现一个自定义信号和自定义槽函数

05 connect函数有几种重载形式?哪一种更好为什么?

06 在有三个对象A,B,C需要实现下面效果:

        a.h

        main.cpp

927作业

01 简述事件的产生、分发和处理流程,中间会产生哪些类型的对象,调用了什么方法。

02 实现一个“打蚊子”游戏

03 现在有三个对象ABC,A的父亲是B,B的父亲是C

0926

QMetaProperty   //创建一个支持元对象系统的类

myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>

class myclass : public QObject
{
    Q_OBJECT
    //定义了名字位health的动态属性,对应的底层是m_health
    Q_PROPERTY(int health MEMBER m_health)
    Q_PROPERTY(int mana MEMBER m_mana)
public:
    explicit myclass(QObject *parent = nullptr);

//signals:
private:
    int m_health;
    int m_mana;

};
//继承QObject,QObject必须是第一个基类
//类的私有域添加Q_OBJECT宏
//类的定义不能放在main.cpp文件(不支持MOC
//头文件保护

#endif // MYCLASS_H
myclass.cpp
#include "myclass.h"

myclass::myclass(QObject *parent) : QObject(parent),m_health(100),m_mana(100)
{

}
main.cc
#include "mainwindow.h"
#include "myclass.h"

#include <QApplication>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDebug>


void loss(QObject*monster,const char* property){
    const QMetaObject* meta=monster->metaObject();
    //Q_OBJECT宏引入的,返回指向该对象的元对象的指针
    qDebug()<<"property count= "<<meta->propertyCount();
    for(int i=0;i<meta->propertyCount();++i){
        //遍历所有的动态属性
        QMetaProperty metaProperty=meta->property(i);
        qDebug()<<metaProperty.name();
    }

    //根据属性的名字找到下标
    int index=meta->indexOfProperty(property);
    QMetaProperty metaProperty=meta->property(index);
    int current=metaProperty.read(monster).toInt();
    qDebug()<<property<<"   "<<current;
    ++current;
    metaProperty.write(monster,current);
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    myclass monster;
    loss(&monster,"health");
    loss(&monster,"mana");
    return a.exec();
}

signal_slot   //创建自定义的信号和槽函数,携带参数,多次connect,去重连接

a.h
#ifndef A_H
#define A_H

#include <QObject>

class A : public QObject
{
    Q_OBJECT
public:
    explicit A(QObject *parent = nullptr);

signals:
    void a_signal0();
    void a_signal1(int x);
    void a_signal1();

//如何设计一个自定义信号
    //元对象系统  访问权限修饰符signals  返回值void的成员函数,只声明不定义
};



class B : public QObject
{
    Q_OBJECT
public:
    explicit B(QObject *parent = nullptr);

public slots:
    void b_slot0();
    void b_slot1(int x);
    void b_slot1();
//如果设计一个自定义的槽函数
    //元对象系统 访问权限修饰符public/privated/protected slots
    //参数和返回值和信号匹配的成员函数  写定义

};

#endif // A_H
a.cpp
#include "a.h"
#include <QDebug>
#include <QtWidgets/QtWidgets>

A::A(QObject *parent) : QObject(parent)
{

}

B::B(QObject *parent) : QObject(parent)
{

}



void B::b_slot0(){
    qDebug()<<"b_solt 0000";
    //Sleep(3000);
    qDebug()<<"b_solt 0000";
}
void B::b_slot1(int x){
    qDebug()<<"b+slot 1111__"<<x;
}

void B::b_slot1(){
    qDebug()<<"b+slot 1111";
}
main.cpp
#include "mainwindow.h"
#include "a.h"

#include <QApplication>
#include <QDebug>

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

    A a;
    B b;

    //SIGNAL SLOT   运行时错误
//    QObject::connect(&a,SIGNAL(a_signal0()),
//                     &b,SLOT(b_slot0()));
    //指向成员函数的指针   编译时错误
//    QObject::connect(&a,&A::a_signal0,&b,&B::b_slot0);
//    qDebug()<<"before emit";
//    emit a.a_signal0();
//    emit a.a_signal0();
//    qDebug()<<"after emit";

//    QObject::connect(&a,SIGNAL(a_signal1(int)),
//                     &b,SLOT(b_slot1(int)));
//    emit a.a_signal1(12241);
//信号的参数可以多,不可以少,报错时机有差别

//    QObject::connect(&a,QOverload<int>::of(&A::a_signal1),
//                     &b,QOverload<int>::of(&B::b_slot1));
//    emit a.a_signal1(5345);

//    QObject::connect(&a,QOverload<>::of(&A::a_signal1),
//                     &b,QOverload<>::of(&B::b_slot1));
//    emit a.a_signal1();



//    //1 signal  connect  2 slot  发送一次信号依次调用
//    QObject::connect(&a,QOverload<>::of(&A::a_signal0),
//                      &b,QOverload<>::of(&B::b_slot0));
//    QObject::connect(&a,QOverload<>::of(&A::a_signal0),
//                      &b,QOverload<>::of(&B::b_slot1));
//    emit a.a_signal0();



//    //相同的connect两次  --->  solt执行两次
//    QObject::connect(&a,QOverload<>::of(&A::a_signal0),
//                      &b,QOverload<>::of(&B::b_slot0));
//    QObject::connect(&a,QOverload<>::of(&A::a_signal0),
//                      &b,QOverload<>::of(&B::b_slot0));
//    emit a.a_signal0();




    //connect多次  --->  加上状态 不会重复发送
    QObject::connect(&a,&A::a_signal0,
                      &b,&B::b_slot0,
                     Qt::ConnectionType(Qt::AutoConnection|Qt::UniqueConnection));
    QObject::connect(&a,&A::a_signal0,
                      &b,QOverload<>::of(&B::b_slot1),
                     Qt::ConnectionType(Qt::AutoConnection|Qt::UniqueConnection));
    QObject::connect(&a,&A::a_signal0,
                      &b,QOverload<>::of(&B::b_slot1),
                     Qt::ConnectionType(Qt::AutoConnection|Qt::UniqueConnection));
    QObject::connect(&a,&A::a_signal0,
                      &b,QOverload<>::of(&B::b_slot1),
                     Qt::ConnectionType(Qt::AutoConnection|Qt::UniqueConnection));
    emit a.a_signal0();



    return app.exec();
}

signal_slot _complex    //槽函数执行过程中发射信号

a.h
#ifndef A_H
#define A_H

#include <QObject>
#include <QDebug>

class A : public QObject
{
    Q_OBJECT
public:
    explicit A(QObject *parent = nullptr);

signals:
    void a_signal();
};


class B : public QObject
{
    Q_OBJECT
public:
    explicit B(QObject *parent = nullptr);
signals:
    void b_signal();
public slots:
    void b_slot(){
        qDebug()<<"b_slot";
        emit b_signal();
    }
};


class C : public QObject
{
    Q_OBJECT
public:
    explicit C(QObject *parent = nullptr);

public slots:
    void c_slot(){
        qDebug()<<"c_slot";
    }

};




#endif // A_H
mian.cpp
#include "mainwindow.h"
#include "a.h"
#include <QApplication>

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

    A a;
    B b;
    C c;

    //槽函数可以发射信号
//    QObject::connect(&a,&A::a_signal,&b,&B::b_slot);
//    QObject::connect(&b,&B::b_signal,&c,&C::c_slot);
//    emit a.a_signal();


    //信号可以当槽函数使用
//    QObject::connect(&a,&A::a_signal,&b,&B::b_signal);
//    QObject::connect(&b,&B::b_signal,&c,&C::c_slot);
//    emit a.a_signal();

    //栈溢出
//    QObject::connect(&a,&A::a_signal,&b,&B::b_slot);
//    QObject::connect(&b,&B::b_signal,&a,&A::a_signal);
//    emit a.a_signal();

    QObject::connect(&a,&A::a_signal,[](){
       qDebug()<<"hello world!";
    });
    emit a.a_signal();


    return app.exec();
}

pingpong    //自己给自己发消息

a.h
#ifndef A_H
#define A_H

#include <QObject>
#include <QDebug>


class A : public QObject
{
    Q_OBJECT
public:
    explicit A(QObject *parent = nullptr);

signals:
    void a_signal();
private slots:
    void a_slot(){
        qDebug() << "a_slot";
        emit a_signal();
    }
};

#endif // A_H
a.cpp
#include "a.h"

A::A(QObject *parent) : QObject(parent)
{
    QObject::connect(this,&A::a_signal,this,&A::a_slot);
}

sender()   //在槽函数中,返回发出该信号的信号的指针(pushbutton)

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 do_mmslot();
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

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

    QObject::connect(ui->b1,&QPushButton::clicked,this,&MainWindow::do_mmslot);
    QObject::connect(ui->b2,&QPushButton::clicked,this,&MainWindow::do_mmslot);
    QObject::connect(ui->b3,&QPushButton::clicked,this,&MainWindow::do_mmslot);
}

void MainWindow::do_mmslot(){
    qDebug()<<"hello";
    qDebug()<<sender();
    QPushButton *b=qobject_cast<QPushButton*>(sender());
    b->setText("hell");
}

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

0927   

tree   //创建树结构

//main.cpp
#include "mainwindow.h"

#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);


        QLabel parent;
        parent.resize(200, 100); // 设置父标签的大小
        parent.show();

        QLabel* child1=new QLabel;
        child1->setParent(&parent);
        QLabel* child2=new QLabel;
        child2->setParent(&parent);

        QLabel child3(&parent);

        child1->setText("Child 1");
        child1->move(20,20);
        child1->show();
        child2->setText("Child 2");
        child2->move(40,40);
        child2->show();
        child3.setText("Child 3");
        child3.move(60,60);
        child3.show();



    return a.exec();
}

my_widgets.h    //过滤事件,毁灭吧世界,老师的也没有跑过滤掉

c.h
#ifndef CC_H
#define CC_H

#include <QWidget>
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>

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

//    bool event(QEvent *ev) override {
//        qDebug() << "MyWidget event()" << ev->type();
//        if (ev->type() == QEvent::MouseButtonPress) {
//            return true; // 完全过滤掉 MouseButtonPress 事件
//        }
//        return QWidget::event(ev); // 处理其他事件
//    }

//    void mousePressEvent(QMouseEvent *ev) override {
        qDebug() << "PPP  MyWidget event handler()" << ev->pos();
//        qDebug() << "aaaaaaaaaaaaaaaaaaaa" << ev->pos();
        QWidget::mousePressEvent(ev); // 可选,根据需要决定是否调用
//    }

//    void mouseReleaseEvent(QMouseEvent *ev) override {
//        qDebug() << "RRRR   MyWidget event handler()" << ev->pos();
//        QWidget::mouseReleaseEvent(ev); // 正确调用 mouseReleaseEvent
//    }

    bool event(QEvent *ev) override {
            qDebug() << "MyWidget event()" << ev->type();
            if(ev->type() == QEvent::MouseButtonPress){
                return false;
            }
            //在子类的虚函数中调用父类的虚函数
            return QWidget::event(ev);
        }

        void mousePressEvent(QMouseEvent *ev) override{
            qDebug() << "MyWidget event                   handler()" ;
            QWidget::mousePressEvent(ev);
        }
        void mouseReleaseEvent(QMouseEvent *ev) override{
            qDebug() << "MyWidget event handler()" ;
            QWidget::mousePressEvent(ev);
        }


signals:

};

#endif // CC_H
main.cpp
#include "mainwindow.h"
#include "cc.h"

#include <QApplication>
#include <QEvent>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
//    MainWindow w;
    CC c;
    c.show();
//    w.show();
    return a.exec();
}

kunkun     //事件和槽函数结合在一起

mylabel.h  //重写事件
#ifndef MYLABEL_H
#define MYLABEL_H

#include <QWidget>
#include <QLabel>
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>

//Widget <- QLabel  派生类替代子类
class Mylabel : public QLabel
{
    Q_OBJECT
public:
    explicit Mylabel(QWidget *parent = nullptr);

    //event
    bool event(QEvent *ev) override{
        if(ev->type()==QEvent::MouseButtonRelease){
            qDebug()<<"event";
        }
        return QLabel::event(ev);
    }

    //event handler
    void mouseReleaseEvent(QMouseEvent *ev) override{
        qDebug()<<"handler pos "<<ev->pos();
        emit myClieck();
    }

signals:
    void myClieck();
};

#endif // MYLABEL_H
mainwindow.h   //设置组件和槽函数
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "mylabel.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();



public slots:
    void addScore();
private:
    Ui::MainWindow *ui;
    Mylabel* kunkun;
    int score;
};
#endif // MAINWINDOW_H
mainwindow.cpp  //实现,连接
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPixmap>

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


    kunkun=new Mylabel(this);
    kunkun->resize(300,300);
    kunkun->setFrameStyle(QFrame::Panel | QFrame ::Plain);
    kunkun->setLineWidth(1);
    QPixmap pixmap(":/new/prefix1/res/drf_20240930093322.png");
    kunkun->setPixmap(pixmap);

    QObject::connect(kunkun,&Mylabel::myClieck,this,
                     &MainWindow::addScore);


}

void MainWindow::addScore(){
    score+=100;
    ui->label->setText(QString("score: %1").arg(score));
}

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

0926作业

01 实现一个井字棋 点击空白按钮可以显示'X'或者'O'       //坏了,一次回退了两个,先这样吧

mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QList>
#include <QStack>
#include <QPushButton>

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 do_play();
    void on_bt_clear_clicked();
    void on_bt_undo_clicked();

private:
    Ui::MainWindow *ui;
    QList<QPushButton *> btlist; //按钮deque
    QStack<int> opstack;  //操作栈,回退
    QString currUser;//当前玩家/o/x
};
#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);

    //QList<QPushButton *> btlist; //按钮deque
    btlist<<ui->bt1<<ui->bt2<<ui->bt3<<ui->bt4<<ui->bt5
            <<ui->bt6<<ui->bt7<<ui->bt8<<ui->bt9;
//    QStack<int> opstack;  //操作栈,回退
    //QString currUser="X";//当前玩家/o/x

     //发出信号的对象  信号  接收方  槽函数
    for(int i=0;i<btlist.size();++i){
        QMainWindow::connect(btlist[i],&QPushButton::clicked,this,&MainWindow::do_play);
    }
    QMainWindow::connect(ui->bt_undo,&QPushButton::clicked,this,&MainWindow::on_bt_undo_clicked);
    QMainWindow::connect(ui->bt_clear,&QPushButton::clicked,this,&MainWindow::on_bt_clear_clicked);

}

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

void MainWindow::do_play(){
    //获取按钮

    //检查按钮内容是否为空,不为空,返回不执行下面的操作
    QPushButton *nowbt=qobject_cast<QPushButton*>(sender());
    if(!nowbt->text().isEmpty()){
        return;
    }

     nowbt->setText(currUser);
    int nownum=btlist.indexOf(nowbt);
    currUser = (currUser == "O") ? "X" : "O";

    opstack.push(nownum);//stack.push 压栈
}

void MainWindow::on_bt_undo_clicked(){
    if(opstack.isEmpty()){return;}
    QPushButton *bfbt=btlist[opstack.pop()];
    bfbt->setText("");
    currUser = (currUser == "O") ? "X" : "O";
}

void MainWindow::on_bt_clear_clicked()
{

    opstack.clear();
    for(int i=0;i<btlist.size();++i){
        btlist[i]->setText("");
    }
}

02 根据如下ui绘制一个计算器,先不实现计算的功能,只实现显示表达式和编辑表达式的功能

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:



    //检查最后一个字符
     bool _check_last_isnumber();
     void _check_and_clear();

    void do_pushButton_number();



    void do_pushButton_symbol();

    void on_pushButton_c_clicked();//清空
    void on_pushButton_del_clicked();//回退

    void on_pushButton_deng_clicked();//等号




private:
    Ui::MainWindow *ui;
    QString _current_text;
    bool _is_expression_valid;//是否有表达式


};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"


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

    QString _current_text="hello";
    ui->label->setText(_current_text);
    bool _is_expression_valid=false;

    QObject::connect(ui->pushButton_0,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_1,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_2,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_3,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_4,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_5,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_6,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_7,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_8,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);
    QObject::connect(ui->pushButton_9,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_number);


    QObject::connect(ui->pushButton_jia,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_symbol);
    QObject::connect(ui->pushButton_jian,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_symbol);
    QObject::connect(ui->pushButton_chu,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_symbol);
    QObject::connect(ui->pushButton_cheng,&QPushButton::clicked,
                     this,&MainWindow::do_pushButton_symbol);
    QObject::connect(ui->pushButton_dian,&QPushButton::clicked,
                      this,&MainWindow::do_pushButton_symbol);
    QObject::connect(ui->pushButton_010,&QPushButton::clicked,
                      this,&MainWindow::do_pushButton_symbol);


}

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

//最后一位数字
bool MainWindow::_check_last_isnumber(){
    if(!_current_text.isEmpty()&&_current_text[_current_text.length()-1].isDigit()){
        return true;
    }
    return false;
}

//是否第一次输入
void MainWindow::_check_and_clear(){
    if(!_is_expression_valid){
        _current_text.clear(); // 清空当前表达式
        _is_expression_valid = true;  // 将有效标志设置为true
    }
}


void MainWindow::do_pushButton_number()
{
   _check_and_clear();
   QPushButton *currbt=qobject_cast<QPushButton*>(sender());
   _current_text.append(currbt->text());
   ui->label->setText(_current_text);
}

void MainWindow::do_pushButton_symbol(){
    _check_and_clear();
    QPushButton *currbt=qobject_cast<QPushButton*>(sender());
    if(_check_last_isnumber()){
        _current_text.append(currbt->text());
        ui->label->setText(_current_text);
    }else{
        ui->label->setText(_current_text);
    }

}


//c  清空
void MainWindow::on_pushButton_c_clicked()
{
    _current_text.clear();
    ui->label->setText("hello");
    _is_expression_valid=false;
}


void MainWindow::on_pushButton_del_clicked()
{
    _check_and_clear();
    _current_text.chop(1);
    ui->label->setText(_current_text);
}



//等于  清空  输出  设置表达式
void MainWindow::on_pushButton_deng_clicked()
{
    _current_text.clear();
    ui->label->setText("result is ...");
    _is_expression_valid=false;
}

03 简述信号和槽机制的优势和劣势

优势,在频繁进行对象间通信的情况下,信号槽机制使得代码变得优雅,可扩展性好

优势:1,松耦合,2,类型安全3,关联自由 4,生态(QT有很多自带的信号和槽

劣势,牺牲了一部分的性能开销

优势

解耦:信号和槽允许对象间的松耦合。发送信号的对象不需要知道接收信号的对象的具体类型或实现,从而提高了代码的灵活性和可重用性。
易于维护:由于对象之间的耦合度低,修改一个类的实现不会影响到其他类,只要信号和槽的接口保持不变。
多对多连接:一个信号可以连接多个槽,反之亦然。这使得同一个事件可以被多个处理程序响应,增强了系统的扩展性。
线程安全:在 Qt 中,信号和槽机制支持跨线程通信,能够自动处理线程间的调用,使得多线程编程更加简单和安全。
简洁的语法:Qt 提供了方便的宏和模板,使得连接信号和槽的代码简单易读,减少了样板代码的数量。

劣势

调试困难:当信号和槽链复杂时,调试可能变得困难。找到信号的源头和对应的槽可能需要更多的时间和精力。
性能开销:信号和槽的机制引入了一定的性能开销,尤其是在连接大量信号和槽时,可能会影响系统的性能。
隐式行为:信号的发射和槽的调用是隐式的,可能导致程序的执行流不够清晰,特别是对于大型项目,容易让开发者感到混乱。
编译依赖:使用信号和槽需要使用 Qt 的元对象系统,这意味着在编译时需要依赖 moc(元对象编译器),增加了构建过程的复杂性。
类型安全:虽然 Qt 提供类型安全的连接,但如果不小心使用错误的参数类型,可能会导致运行时错误,而不是编译时错误。

04 信号和槽机制的目的是什么?书写代码实现一个自定义信号和自定义槽函数

目的是为了——松耦合,实现对象之间的通信但是不需要创建接收方的对象,提高代码的灵活性和可扩展性

05 connect函数有几种重载形式?哪一种更好为什么?

三种,

1,SIGNAL,SLOT,函数以字符串的形式传入,不推荐,会在运行时报错

2,使用成员函数的指针形式(会有函数重载的问题,通过QOverload携带参数信息解决

3,槽函数是一个函数对象,适用于不想使用继承的


QMetaObject::Connection 
connect(const QObject *sender, const char *signal, const QObject *receiver,
 const char *method, Qt::ConnectionType type = Qt::AutoConnection)
    使用字符串形式的信号和槽/*
sender: 发出信号的对象。
signal: 信号的名称,以字符串形式表示(例如,"clicked()")。
receiver: 响应信号的对象。
method: 槽的名称,以字符串形式表示(例如,"onButtonClicked()")。
type: 连接类型,可以是 Qt::AutoConnection、Qt::DirectConnection 或 Qt::QueuedConnection。
用法:适用于简单的信号槽连接,通常使用字符串来表示信号和槽的名称。这种方式会在运行时进行解析,可能会影响性能,但使用方便,特别是在动态创建对象时。*/
    
    
QMetaObject::Connection 
connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, 
const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)
    使用QMetaMethod/*
sender: 发出信号的对象。
signal: 信号的 QMetaMethod 对象,提供了更强的类型安全性。
receiver: 响应信号的对象。
method: 槽的 QMetaMethod 对象。
type: 连接类型。
用法:使用 QMetaMethod 可以避免字符串解析带来的性能损失,同时提供编译时检查,提高代码的安全性和可维护性。*/
    
    
QMetaObject::Connection 
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, 
PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)
    使用指向成员函数的指针/*
sender: 发出信号的对象。
signal: 指向成员函数的指针,表示信号。
receiver: 响应信号的对象。
method: 指向成员函数的指针,表示槽。
type: 连接类型。
用法:这种方式提供了更强的类型安全性,因为成员函数指针在编译时就会被检查。适用于静态分析和代码优化*/
    
    
QMetaObject::Connection 
connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
    使用自定义可调用对象/*
sender: 发出信号的对象。
signal: 指向成员函数的指针,表示信号。
functor: 自定义可调用对象(如 Lambda 表达式或函数对象)。
用法:适用于将信号连接到不属于 QObject 派生类的函数或对象,非常灵活,可以使用任何符合函数签名的可调用对象。*/
    
    
    
QMetaObject::Connection 
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, 
Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
    带上下文的自定义可调用对象/*
sender: 发出信号的对象。
signal: 指向成员函数的指针,表示信号。
context: 上下文对象,通常用于确定信号的作用域。
functor: 自定义可调用对象。
type: 连接类型。
用法:类似于第 4 种,但可以指定上下文,确保在特定对象的上下文中处理信号,适合需要特定上下文的情况*/

06 在有三个对象A,B,C需要实现下面效果:

A发射信号会导致 B调用槽函数 C调用槽函数

B发射信号会导致 C调用槽函数

C发射信号会导致 A发射信号

a.h
#ifndef A_H
#define A_H

#include <QObject>
#include <QDebug>

class A : public QObject
{
    Q_OBJECT
public:
    explicit A(QObject *parent = nullptr);

signals:
    void a_signal();
};


class B : public QObject
{
    Q_OBJECT
public:
    explicit B(QObject *parent = nullptr);
signals:
    void b_signal();
public slots:
    void b_slot(){
        qDebug()<<"b_slot";
        emit b_signal();
    }
};


class C : public QObject
{
    Q_OBJECT
public:
    explicit C(QObject *parent = nullptr);

signals:
    void c_signal();
public slots:
    void c_slot(){
        qDebug()<<"c_slot";
    }

};




#endif // A_H
main.cpp
#include "mainwindow.h"
#include "a.h"
#include <QApplication>

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

    A a;
    B b;
    C c;

    QObject::connect(&a,&A::a_signal,&b,&B::b_slot);
    QObject::connect(&b,&B::b_signal,&c,&C::c_slot);
    emit a.a_signal();

    emit b.b_signal();

    QObject::connect(&c,&C::c_signal,&a,&A::a_signal);
    emit c.c_signal();

    return app.exec();
}

927作业

01 简述事件的产生、分发和处理流程,中间会产生哪些类型的对象,调用了什么方法。

1. 事件产生

事件通常由用户交互(如鼠标点击、键盘输入)、系统生成(如定时器超时)、或其他程序逻辑触发。具体步骤如下:

  • 事件生成:当某个操作发生时(如按钮被点击),Qt会创建一个相应的事件对象,例如QMouseEventQKeyEvent等。

  • 事件对象:创建的事件对象都是QEvent类的子类,包含了关于事件的信息。

2. 事件分发

事件对象被创建后,会通过事件队列进行分发。分发流程如下:

  • 事件入队:事件对象被放入到事件队列中,通常是通过调用QCoreApplication::postEvent()QWidget::event()方法。

  • 事件循环:Qt的应用程序使用事件循环来处理事件。事件循环通过调用QCoreApplication::exec()进入循环状态,等待事件发生。

  • 事件分发:事件循环从事件队列中取出事件,通过QApplication::notify()方法分发事件。

3. 事件处理

事件在Qt中被分发给接收者进行处理,具体步骤如下:

  • 事件接收:接收事件的对象通常是一个继承自QObject的类,例如窗口或控件。

  • 事件处理方法:接收者会重写某些事件处理函数,如mousePressEvent(QMouseEvent *event)keyPressEvent(QKeyEvent *event)等,以响应特定类型的事件。

  • 调用处理:在notify()方法中,Qt会调用相应的事件处理方法,将事件对象作为参数传递。例如,对于鼠标点击事件,它会调用接收者的mousePressEvent()方法。

4. 对象与方法概述

  • 对象类型

    • QEvent及其子类(如QMouseEventQKeyEventQTimerEvent等)

    • QObject及其子类(如QWidgetQMainWindow等)

  • 主要方法

    • QCoreApplication::postEvent(QObject *receiver, QEvent *event):将事件放入队列。

    • QApplication::exec():启动事件循环。

    • QApplication::notify(QObject *receiver, QEvent *event):分发事件给接收者。

    • QObject::event(QEvent *event):处理事件的通用方法。

    • QWidget::mousePressEvent(QMouseEvent *event):专门处理鼠标按下事件的方法(需重写)。

02 实现一个“打蚊子”游戏

在屏幕中央有一个600*400的QWidget,一个用来统计分数的QLabel

一开始会在QWidget内部随机位置生成一个蚊子,当鼠标点击到蚊子以后,旧蚊子消失然后在另一个位置生成新的蚊子,分数增加。

提示:这里需要继承QWidget类然后重写paintEvent,在paintEvent当中可以创建QPainter对象绘制各种图形和图片

mywidgets.h
#ifndef MYLABEL_H
#define MYLABEL_H

#include <QWidget>
#include <QLabel>
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>
#include <QtWidgets/QtWidgets>

#include <time.h>
#include <stdlib.h>


//Widget <- QLabel  派生类替代子类
class Mylabel : public QWidget
{
    Q_OBJECT

public:
    explicit Mylabel(QWidget *parent = nullptr);


    void mouseReleaseEvent(QMouseEvent *ev) override{
//        srand(time(nullptr));
        if(ev->x() - mos_x<=40&&ev->x()-mos_x>=0 &&
                ev->y()-mos_y<=40&&ev->y()-mos_y>=0){
            emit addPoint();
            mos_x=rand()%560;
            mos_y=rand()%360;
            update();//强行重绘
        }
        QWidget::mouseReleaseEvent(ev);
    }

    void paintEvent(QPaintEvent *event) override{
        qDebug()<<mos_x<<","<<mos_y;
        QPainter paitner(this);
        QPixmap pixmap(":/new/prefix1/res/drf_20240930093322.png");
        paitner.drawPixmap(mos_x,mos_y,40,40,pixmap);
        QWidget::paintEvent(event);
    }


signals:
    void addPoint();

private:
    int mos_x;
    int mos_y;
};

#endif // MYLABEL_H
mywidgets.cpp
#include "mylabel.h"

Mylabel::Mylabel(QWidget *parent) : QWidget(parent)
{
    mos_x=rand()%560;
    mos_y=rand()%360;
}
main.cpp
#include "mainwindow.h"
#include "mylabel.h"

#include <QApplication>

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

    Mylabel *pw=new Mylabel(&w);
    pw->move(50,50);
    pw->resize(600,400);
    QObject::connect(pw,&Mylabel::addPoint,&w,&MainWindow::changePoint);


    w.show();
    return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPixmap>
#include <QString>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)

{
    ui->setupUi(this);
    srand(time(nullptr));

}

void MainWindow::changePoint(){
    int score=ui->label->text().toInt();
    score+=10;
    ui->label->setText(QString("%1").arg(score));
}

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

03 现在有三个对象ABC,A的父亲是B,B的父亲是C

点击A的内部,要求执行:

A的event和event_handler

B的event

C的event和event_handler

#ifndef OUT_H
#define OUT_H

#include <QWidget>
#include <QLabel>
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>

class Out : public QLabel
{
    Q_OBJECT
public:
    explicit Out(QWidget *parent = nullptr);
    //parent 参数是指向父窗口的指针,可以为 nullptr,表示没有父窗口

    bool event(QEvent *e) override{
        if(e->type()==QEvent::MouseButtonRelease){
            qDebug()<<"out event out";
        }
        return QLabel::event(e);
        //调用基类的 event 方法,确保其他事件仍然被处理
    }
    void mouseReleaseEvent(QMouseEvent *ev) override{
        qDebug()<<"out handler out";
        ev->ignore();
        //忽略这个事件,意味着不将事件传递给其他可能的处理者(如父级控件)
    }

};



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

    bool event(QEvent *e) override{
        if(e->type()==QEvent::MouseButtonRelease){
            qDebug()<<"mid event mid";
        }
        return QLabel::event(e);
        //调用基类的 event 方法,确保其他事件仍然被处理
    }
//    void mouseReleaseEvent(QMouseEvent *ev) override{
//        qDebug()<<"mid handler mid";
//        ev->ignore();
//        //忽略这个事件,意味着不将事件传递给其他可能的处理者(如父级控件)
//    }

};


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

    bool event(QEvent *e) override{
        if(e->type()==QEvent::MouseButtonRelease){
            qDebug()<<"in event in";
        }
        return QLabel::event(e);
    }
    void mouseReleaseEvent(QMouseEvent *ev) override{
        qDebug()<<"in handler in";
        ev->ignore();
    }

};



#endif // OUT_H

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值