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

ops/2024/9/30 4:03:30/

0926

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

myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H#include <QObject>class myclass : public QObject
{Q_OBJECT//定义了名字位health的动态属性,对应的底层是m_healthQ_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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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   //创建树结构

my_widgets.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()" << ev->pos();QWidget::mousePressEvent(ev);}void mouseReleaseEvent(QMouseEvent *ev) override{qDebug() << "MyWidget event handler()" << ev->pos();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();
}

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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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; //按钮dequeQStack<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; //按钮dequebtlist<<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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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 简述事件的产生、分发和处理流程,中间会产生哪些类型的对象,调用了什么方法。

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

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

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

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

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

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

A的event和event_handler

B的event

C的event和event_handler


http://www.ppmy.cn/ops/118734.html

相关文章

数据库连接池详解

数据库连接池是什么&#xff1f;下文会为你讲解 一、 数据库连接之殇&#xff1a;慢、卡、崩溃 想象一下&#xff0c;你的应用程序就像一家餐厅&#xff0c;数据库就像食材仓库。每次顾客点餐&#xff0c;都需要厨师跑到仓库取食材&#xff0c;做完菜再把食材送回仓库。这种模…

Maya学习笔记:软选择

文章目录 打开软选择调整软选择范围衰减模式 软选择可以很好的进行渐变修改 打开软选择 方法1&#xff1a; 进入点线面模式&#xff0c;按B键进入软选择模式&#xff0c;再按B取消 方法2&#xff1a;双击左侧的选择按钮打开选择面板&#xff0c;勾选软选择 调整软选择范围 …

Flink CDC

全增量一体化架构 自 2.0 版本起&#xff0c;Flink CDC 引入了增量快照框架&#xff0c;实现了数据库全量和增量数据的一体化读取&#xff0c;并可以在全量和增量读取之间进行无缝切换。在读取全量数据时&#xff0c;Flink CDC source 会首先将数据表中的已有数据根据主键分布切…

建立分支提交代码

git分支 git branch 产看当前分支 git branch -a 查看所有分支 git checkout 分支名 切换分支 git checkout -b 分支名 建立分支&#xff08;仅仅是在本地建立了&#xff0c;并没有关联线上&#xff09; git push --set-upstream origin 分支名 把本地分支推到先线上 gti add …

RVC变声器入门

主要参考资料&#xff1a; RVC变声器官方教程&#xff1a;10分钟克隆你的声音&#xff01;一键训练&#xff0c;低配显卡用户福音&#xff01;: https://www.bilibili.com/video/BV1pm4y1z7Gm/?spm_id_from333.337.search-card.all.click&vd_sourcedd284033cd0c4d1f3f59a2…

Android开发小贴士

Android开发小贴士 1.使用Glide库提取视频帧 图片加载框架Glide就可以做到获取本地视频的缩略图(不能获取网络视频文件): String filePath "/storage/emulated/0/Pictures/example_video.mp4"; Glide .with( context ).load( Uri.fromFile( new File( filePath …

【MySql】在ubuntu下安装MySql数据库

目录 查看操作系统版本 添加 MySql APT源 访问下载页面并下载发布包 安装发布包 执行安装命令 从MySql APT源更新包信息 安装MySql 执行安装命令 查看MySql状态 开启自启动 登录MySql 查看操作系统版本 rootVM-24-2-ubuntu:~# lsb_release -a No LSB modules are ava…

深入理解Java中的序列化与反序列化

目录 1. 引言 2. 什么是序列化&#xff1f; 3. 为什么需要序列化&#xff1f; 4. 如何实现序列化&#xff1f; 5. 示例代码 6. 序列化和反序列化操作 7. 注意事项 8. 拓展&#xff1a;Transient关键字 9. 拓展&#xff1a;序列化的性能优化 10. 结论 1. 引言 在软件…