目录
signal_slot //创建自定义的信号和槽函数,携带参数,多次connect,去重连接
signal_slot _complex //槽函数执行过程中发射信号
my_widgets.h //过滤事件,毁灭吧世界,老师的也没有跑过滤掉
01 实现一个井字棋 点击空白按钮可以显示'X'或者'O' //坏了,一次回退了两个,先这样吧
02 根据如下ui绘制一个计算器,先不实现计算的功能,只实现显示表达式和编辑表达式的功能
04 信号和槽机制的目的是什么?书写代码实现一个自定义信号和自定义槽函数
01 简述事件的产生、分发和处理流程,中间会产生哪些类型的对象,调用了什么方法。
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会创建一个相应的事件对象,例如
QMouseEvent
、QKeyEvent
等。事件对象:创建的事件对象都是
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
及其子类(如QMouseEvent
、QKeyEvent
、QTimerEvent
等)
QObject
及其子类(如QWidget
、QMainWindow
等)主要方法:
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