Qt_事件的介绍

news2024/11/18 21:45:26

目录

1、理解事件

2、处理事件QEvent 

3、键盘事件QKeyEvent 

4、鼠标事件QMouseEvent 

4.1 鼠标点击事件

4.2 鼠标释放事件 

4.3 鼠标移动事件 

5、滚轮事件QWheelEvent 

6、定时器事件QTimerEvent 

7、窗口事件QMoveEvent

8、事件分发器event 

9、事件过滤器eventfilter

结语 


前言:

        在Qt中,事件指的是由程序内部或者外部设备产生的事情或动作,比如当程序中定时器的时间到了,就会发出一个事件,这就是内部产生的事件,当鼠标、键盘进行某些操作所产生的动作,这就是外部产生的事件。在Qt中事件被抽象成一个类,所有的事件都继承自QEvent类,如下图:

1、理解事件

        Qt中有一个重要的概念叫做信号与槽,指的是:用户的特定操作会产生信号,将该信号与槽函数连接,就能实现产生信号的同时自动去调用槽函数了。事件的用法和信号是一样的,当用户进行某些操作就会产生事件,给事件关联一个处理函数,这样事件产生的同时就会去调用该处理函数。实际上,由于事件使用起来较麻烦,因此Qt将事件做了一层封装就成了信号与槽,即信号与槽就是对于事件的进一步封装,事件是信号槽的底层机制。

2、处理事件QEvent 

         当产生了一个事件后,要做的就是对该事件进行处理。因为在Qt中事件被分成多个类型,比如鼠标事件、键盘事件、窗口事件,就拿鼠标事件来说,当点击鼠标或者移动鼠标,其实就已经产生了事件,而Qt系统默认产生事件后会自动调用相关的处理函数,只不过不同的事件会调用不同的处理函数,比如点击鼠标调用的是mousePressEvent()函数,而移动鼠标调用的是mouseMoveEvent()函数。最重要的是这些处理函数都是虚函数,开发者可以重写这些虚函数,就可以在事件产生时实现不同的效果了


        举一个例子,首先创建一个标签(QLabel),当鼠标进入标签和离开标签时会触发QEvent事件,为了验证这个过程,在触发事件时会打印提示信息。实现步骤如下:1、首先新建一个QWidget项目,2、由于要自定义标签的事件的处理函数,而所以需要手动自定义一个类,让该类继承标签,目的是可以手动操作继承类的构造函数,并且该继承类还拥有标签的功能,如下图:

        然后给自定义类起一个名字,并且选择要继承的类:

         创建完成后就会自动生成对应的文件和代码:

        3、至此,就可以在mylabel类中重写鼠标进入标签和( enterEvent)离开标签(leaveEvent)的虚函数了,然后就可以实现鼠标进入和离开标签时执行我们自定义的功能,上述虚函数介绍如下:

        因为触发的是QEvent事件,所以该虚函数的形参类型是QEvent*。重写虚函数的mylabel.cpp代码如下:

#include "mylabel.h"
#include <QDebug>

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

}

void mylabel::enterEvent(QEvent *event)
{
    qDebug()<<"enterEvent";
}

void mylabel::leaveEvent(QEvent *event)
{
    qDebug()<<"leaveEvent";
}

        现在自定义类的工作已经完成了,当然还需要在widget.cpp中将这个类创建出来,这样才能在最终的界面上显示标签,widget.cpp代码如下:

#include "widget.h"
#include "ui_widget.h"
#include "mylabel.h"

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

    mylabel* m = new mylabel(this);
    m->setText("这一个自定义的标签");
}

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

        运行结果:

        将鼠标进入和移除标签后就会打印以上信息。 

3、键盘事件QKeyEvent 

        Qt中的按键事件被抽象看成QKeyEvent类,当键盘上的按键被按下或松开时,键盘事件便会产生。该事件的处理函数主要是让系统知道用户按下了什么键,以便进行后续的工作。当键盘按下按键后产生的事件会自动调用keyPressEvent函数,因此只需要重写该函数就能自定义按键事件的处理动作了。


        1、测试单个按键,Qt中内置了一个枚举类型key,该类型中枚举了大部分键盘按键,并且QKeyEvent中提供了key方法,调用此方法就能让系统知道我们按下的具体是哪个按键了,测试代码如下(节选widget.cpp文件,.h文件的声明代码就不展示了):

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QKeyEvent>

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

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

void Widget::keyPressEvent(QKeyEvent* e)
{
    if(e->key()==Qt::Key_A)
    {
        qDebug()<<"按下的按键是A";
    }

}

        运行结果:

         只有当鼠标点击界面后(即让界面获取到焦点),并按下按键A才会触发打印信息。


        2、测试组合按键,同样的,Qt中也内置了一个枚举类型KeyboardModifier,该类型中枚举了组合键中的修改键(组合键=修改键+普通按键),常用的枚举量有如下几种:

Qt::NoModifier
⽆修改键
Qt::ShiftModifier
Shift键
Qt::ControlModifier
Ctrl 键
Qt::AltModifier
Alt 键

        并且QKeyEvent中提供了modifiers方法,调用该方式就能识别按键是否为修改键了,代码测试如下: 

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QKeyEvent>

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

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

void Widget::keyPressEvent(QKeyEvent* e)
{
    if(e->modifiers()==Qt::ControlModifier&&e->key()==Qt::Key_A)
    {
        qDebug()<<"按下的按键是A";
    }

}

        运行结果:

4、鼠标事件QMouseEvent 

         在Qt中,鼠标事件被抽象成QMouseEvent类,当在控件中按下⿏标或者移动⿏标时,都会以该控件为基础产生鼠标事件。

4.1 鼠标点击事件

         ⿏标点击产生的事件对应虚函数mousePressEvent。mousePressEvent函数原型如下:

[virtual] void QWidget::mousePressEvent(QMouseEvent *event)

        传统的鼠标有三个按键,分别是:鼠标左键、鼠标右键、鼠标滚轮键。这三个键都可以触发mousePressEvent,Qt中也为这三个键提供了对应的枚举常量,分别是:Qt::LeftButton 鼠标左键 、Qt::RightButton 鼠标右键 、Qt::MidButton 鼠标滚轮并且QMouseEvent也提供了一个方法button,调用该方法就能让系统知道我们按下的具体是哪个键了。


        测试代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>

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

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

void Widget::mousePressEvent(QMouseEvent *e)
{
    if(e->button()==Qt::LeftButton)
    {
        qDebug()<<"鼠标左键进行点击";
    }
}

        运行结果:

         鼠标右键点击和滚轮点击就在上述虚函数中添加条件判断即可。


        上述的点击事件为单击,除了单击触发可以事件,双击触发也可以触发事件,双击事件的虚函数为:mouseDoubleClickEvent。mouseDoubleClickEvent() 函数原型如下:

 [virtual] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)

        测试代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>

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

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

void Widget::mouseDoubleClickEvent(QMouseEvent *e)
{
    if(e->button()==Qt::LeftButton)
    {
        qDebug()<<"鼠标左键进行点击";
    }
}

        测试结果就是用鼠标任意一键双击界面后才会调用此函数,并且也可以通过button函数得知双击键的具体键。

4.2 鼠标释放事件 

        ⿏标释放事件是虚函数mouseReleaseEvent来处理的。mouseReleaseEvent函数介绍如下:

[virtual] void QWidget::mouseReleaseEvent(QMouseEvent *event) 

        使用逻辑和上述点击事件是一样的,只不过这里是在鼠标释放后调用,并且可以通过button函数得知释放的鼠标键,测试代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>

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

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

void Widget::mousePressEvent(QMouseEvent *e)
{
    if(e->button()==Qt::LeftButton)
    {
        qDebug()<<"鼠标左键进行点击";
    }
}

void Widget::mouseReleaseEvent(QMouseEvent *e)
{
    if(e->button()==Qt::LeftButton)
    {
        qDebug()<<"鼠标左键释放";
    }
}

        运行结果:

4.3 鼠标移动事件 

         鼠标移动事件的处理函数是mouseMoveEvent,表示只要鼠标一做移动操作就会调用该函数,这可能对于大部分人来说难以相信,因为实际生活中使用计算机,鼠标肯定是会大量的进行移动的,因此难以相信一个微不足道的移动鼠标操作背后竟然可以有如此复杂的逻辑,函数介绍如下:

[virtual] void QWidget::mouseMoveEvent(QMouseEvent *event)

        也正是因为使用计算机会造成大量的鼠标移动,若每次移动都调用对应事件处理函数,则会造成卡顿,因此Qt为了保证程序的流畅性,默认情况下不会让鼠标移动产生事件,即鼠标移动的时候不会调用mouseMoveEvent,除非显式告诉Qt就要产生事件,方法是调用函数setMouseTracking,该函数原型如下:

void setMouseTracking(bool enable)
//setMouseTracking函数默认是false,需要设置为true才能让鼠标移动产生事件

        测试代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMouseEvent>

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

    setMouseTracking(true);
}

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

void Widget::mouseMoveEvent(QMouseEvent *e)
{
    //获取鼠标的左边,以窗口左上角为原点
    qDebug()<<"["<<e->x()<<","<<e->y()<<"]";
}

        运行结果:

5、滚轮事件QWheelEvent 

        注意此处的滚轮事件和鼠标事件中的滚动点击是不一样的,滚轮事件表示用滚轮进行上下滑动时产生的事件,滚轮滑动所产生的事件调用的处理函数是WheelEvent,介绍如下:

[virtual] void QWidget::wheelEvent(QWheelEvent *event)

        滚轮事件着重在于滚轮滑动距离计算,滚轮滑动的距离可以通过delta函数获取。该函数介绍如下:

int QGraphicsSceneWheelEvent::delta() const
//其中返回值代表滚轮滑动的距离
//正数表⽰滚轮相对于⽤⼾向前滑动,负数表⽰滚轮相对于⽤⼾向后滑动。

        测试代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QWheelEvent>
#include <QDebug>

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

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

void Widget::wheelEvent(QWheelEvent *e)
{
    static int count = 0;
    count+=e->delta();
    if(e->delta()>0)
        qDebug()<<"向前滑动"<<count;
    else
        qDebug()<<"向后移动"<<count;
}

        运行结果:

6、定时器事件QTimerEvent 

        在一些需要周期性的执行某项任务的场景下,定时器事件尤为重要,他表示当设定的时间到后,会产生一个QTimerEvent事件,该事件的处理函数是timerEvent(即定时器“响了”就会去调用该函数),该函数介绍如下:

[virtual] void QTimer::timerEvent(QTimerEvent *e)

        因此只要重写timerEvent函数,就能在定时器到时后执行我们期望的工作,前提当然是要启动该定时器,需要通过startTimer函数来开启⼀个定时器,这个函数需要输入⼀个以毫秒为单位的整数作为参数来表明定时器设定的时间,他的返回值表示这个定时器的编号,因为实际运用中有多个定时器,他们都调用同一个函数,为了区分多个定时器对应不同的任务,因此需要用编号的形式来区分他们。startTimer函数介绍如下:

int QObject::startTimer(int interval, 
Qt::TimerType timerType = Qt::CoarseTimer)

//interval表示时间

        测试代码如下,需要定义两个定时器,widget.h文件代码如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
     void timerEvent(QTimerEvent *e);

private:
    Ui::Widget *ui;
    int time1;//定时器1
    int time2;//定时器2
};
#endif // WIDGET_H

        widget.cpp文件代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

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

    time1 = startTimer(2000);//2秒后定时器1触发
    time2 = startTimer(1000);//1秒后定时器2触发
}

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

void Widget::timerEvent(QTimerEvent *e)
{
    if(time1==e->timerId())//定时器1执行的任务
    {
        static int t1 = 0;
        t1++;
        qDebug()<<"定时器1每两秒加一次"<<t1;
    }
    if(time2==e->timerId())//定时器2执行的任务
    {
        static int t2 = 0;
        t2++;
        qDebug()<<"定时器2每一秒加一次"<<t2;
    }
}

        运行结果:

7、窗口事件QMoveEvent

        窗口产生的是被抽象成QMoveEvent类,当窗口进行移动或者窗口大小发生改变时都产生窗口事件,这两个操作对应的处理函数分别是:

[virtual] void QWidget::moveEvent(QMoveEvent *event)//窗口移动

[virtual] void QWidget::resizeEvent(QResizeEvent *event)//窗口大小发生改变

        测试代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMoveEvent>

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

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

void Widget::moveEvent(QMoveEvent *e)
{
    qDebug()<<e->pos();
}

void Widget::resizeEvent(QResizeEvent *e)
{
    qDebug()<<e->size();
}

        运行结果:

8、事件分发器event 

        产生事件后之所以可以自动执行处理函数,是因为中间有一个事件分发器,他接收所有的事件,然后对该事件的类型做分析,再去向下传递给具体的事件,再由具体的事件去调用合适的处理函数(事件分发器本身不直接调用处理函数),如下图所示:

        事件分发器实际上就是QObject对象里的event函数,产生的事件都是发送给该函数的,并且可以重写该函数,重写该函数表示由我们实现的事件分发器来接收事件,这样做还能起到一个拦截事件的作用。该函数介绍如下:

bool event(QEvent *e)
//其返回值为布尔类型,若为ture,代表拦截了该事件,不向下分发

        可以看到该函数参数是一个QEvent*的指针,QEvent是所有事件的基类,因此该函数可以接收所有的事件。


        重写event函数,实现一个事件拦截器,比如鼠标点击会产生事件,这个事件按理来说会被事件分发器传给下层,然后调用我们自己实现的处理函数,但是有了事件拦截器后,就会拦截这个点击事件,导致该事件不会被传给下一层也就无法调用我们自己实现的处理函数了,代码如下:

#include "widget.h"
#include "ui_widget.h"
#include <QMouseEvent>
#include <QDebug>
#include <QEvent>

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

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

void Widget::mousePressEvent(QMouseEvent *e)
{
    if(e->button()==Qt::LeftButton)
    {
        qDebug()<<"鼠标左键进行点击";
    }
}

bool Widget::event(QEvent *e)
{
    if(e->type()==QEvent::MouseButtonPress)
    {
        qDebug()<<"拦截鼠标点击事件";
        return true;//返回true表示不会将该事件传给下一层
    }
    //转交给下一层
    return QWidget::event(e);
}

        运行结果:

9、事件过滤器eventfilter

        从上面例子中可以发现一个逻辑,在哪个控件的类里面重写事件处理函数,则只有在该控件中产生事件才会调用该处理函数,比如一个界面widget中有一个label控件,如果在label控件中重写鼠标点击事件的处理函数,那么鼠标只有在该label中点击才会调用该函数,在界面的其他位置点击是不会调用该函数的。这样又会导致一个问题,即上述的event拦截功能只能拦截当前控件的事件,比如在label控件中实现的拦截器只能拦截label控件中产生的事件,不能拦截界面产生的事件,若想拦截多个控件的事件,只能在每个控件的类内都重写event,这样过于麻烦,并且代码内聚性不强。


        针对上面问题,Qt推出事件过滤器来解决,事件过滤器是在事件分发器的上一层,如下图:

        事件过滤器的创建也是重写eventFilter函数,该函数介绍如下:

//其返回值为布尔类型,若为ture,代表拦截了该事件,不向下分发
[virtual] bool eventFilter(QObject *obj, QEvent *event)

//obj表示事件是由哪个控件产生的
//event表示事件

        并且事件过滤器的使用逻辑和事件分发器是一样的,简单来说可以把事件过滤器看成是事件分发器之上的”事件分发器“,正因为事件过滤器在”源头“,因此只需要重写一个事件过滤器就可以接收窗口界面上所有控件产生的事件(前提是将事件过滤器安装到这些控件上,具体操作如下文)。


        举个例子,若一个界面widget上有一个控件label,在widget上实现事件过滤器,则可以捕捉到label的事件。首先先自定义一个mylabel继承自QLabel,然后在该类中重写事件处理函数和事件分发器,这时候按理来说触发mylabel的事件则会调用mylabel中重写的事件分发器,但是我们又在widget中重写了一个事件过滤器并安装到mylabel中,就会导致触发mylabel的事件时调用的是widget下的事件过滤器。实现代码如下(节选自widget.cpp):

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

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

    my = new mylabel(this);
    my->setText("自定义标签");

    //给自定义标签安装事件过滤器
    my->installEventFilter(this);
}

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

bool Widget::eventFilter(QObject *obj, QEvent *e)//widget的事件过滤器
{
    if(obj == my)//可以接收来自其他控件的事件
    {
        if(e->type()==QEvent::MouseButtonPress)
        {
            qDebug()<<"由widget的事件过滤器捕捉";
            return true;
        }
    }
    //交给下一层处理
    return QWidget::eventFilter(obj,e);
}

        运行结果:

        结果是点击标签时,调用的函数是widget中的事件过滤器而不是mylabel中的事件分发器,说明事件过滤器可以拦截其他控件的事件,并且事件过滤器的优先级在事件分发器之上

结语 

        以上就是关于Qt事件的讲解,Qt事件是Qt的一个底层机制,他是信号与槽的基础,虽然信号与槽使用起来很方便,并且可以满足大部分的开发场景,但是在一些特殊的场景下只能使用事件的方式进行界面DIY的操作,并且事件给予了开发者更多的开发空间。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2162173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言练习:通讯录

简单版代码讲解&#xff1a; 这个版本不涉及文件操作以及动态内存分配&#xff0c;有助于理解代码。 文件管理 这里我们分了三个文件&#xff0c;.h 文件里给出类型声明和函数声明&#xff0c;contact.c 文件是具体的实现&#xff0c;test.c文件里是游戏的实现逻辑。 test.c…

怎么一键更换PPT模板?2个做PPT必备的办公神器推荐!

在主打快节奏的当下&#xff0c;一份精美的PPT演示文稿往往能够为你赢得更多的关注和机会。但不可否认的是&#xff0c;制作一份高质量的PPT并非易事&#xff0c;特别是当你需要频繁更换PPT模板以应对不同场合时&#xff0c;根本抽不出时间来逐一修改。 本文将为大家介绍2款强…

ATTCK实战系列-Vulnstack靶场内网域渗透(二)

ATT&CK实战系列-Vulnstack靶场内网域渗透&#xff08;二&#xff09; 前言一、环境搭建1.1 靶场下载地址1.2 环境配置1.2.1 DC域控服务器&#xff1a;1.2.2 WEB服务器&#xff1a;1.2.3 PC域内主机&#xff1a;1.2.4 攻击者kali&#xff1a; 1.3 靶场拓扑图 二、外网渗透2.…

SpringCloud微服务实现服务熔断的实践指南

Spring Cloud是一套分布式系统的微服务框架&#xff0c;它提供了一系列的组件和工具&#xff0c;能够使我们更容易地构建和管理微服务架构。在实际开发中&#xff0c;由于各个服务之间的通信依赖&#xff0c;一旦某个服务出现故障或负载过高&#xff0c;可能会导致整个系统的性…

Growthly Quest 增长工具:助力 Web3 项目实现数据驱动的增长

作者&#xff1a;Stella L (stellafootprint.network) 在瞬息万变的 Web3 领域&#xff0c;众多项目在用户吸引、参与和留存方面遭遇重重难关。Footprint Analytics 推出 Growthly&#xff0c;作为应对这些挑战的全方位解决方案&#xff0c;其中创新性的 Quest&#xff08;任务…

Maya学习笔记:物体的层级关系

文章目录 父子关系设置父子关系同时显示两个大纲视图 组 父子关系 设置父子关系 设置父子物体&#xff1a; 方法1 先选择子物体&#xff0c;按住shift再选中父物体&#xff0c;按P或者G键 方法2 在大纲视图中按住鼠标中间&#xff0c;拖动一个物体到另一个物体上 取消父子关…

HC32F460JETA使用串口DMA循环传输数据时遇到问题,只传输了一次就停止传输,如何解决??

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——STM32代码实现篇

STM32代码实现 开启本章节需要完成下方的前置任务&#xff1a; 点击跳转&#xff1a; 物联网实践教程&#xff1a;微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 目标 1.连接OneNET&#xff1a;STM32使用串口与ESP8266/01s连接发送…

基于Vue3组件封装的技巧分享

本文在Vue3的基础上针对一些常见UI组件库组件进行二次封装&#xff0c;旨在追求更好的个性化&#xff0c;更灵活的拓展&#xff0c;提供一些个人的思路见解&#xff0c;如有不妥之处&#xff0c;敬请指出。核心知识点$attrs,$slots 需求 需求背景 日常开发中&#xff0c;我们经…

PHP判断微信或QQ访问

PHP判断微信或QQ访问 若是微信或者QQ打开&#xff0c;提示图会覆盖网页&#xff0c;但网页功能仍在运行&#xff01; <meta name"viewport" content"initial-scale1, maximum-scale1, user-scalableno, widthdevice-width"><style> .top-gui…

leetcode第169题:多数元素

给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#xff1a;3 示例 …

OpenHarmony(鸿蒙南向)——平台驱动开发【ADC】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 ADC&#xff08;Analog to Digital Converter&…

LOGO设计新革命:5款AI工具让你秒变设计大师(必藏)

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 你是否曾因设计一个既独特又专业的LOGO而感…

JUC高并发编程2:Lock接口

1 synchronized 1.1 synchronized关键字回顾 synchronized 是 Java 中的一个关键字&#xff0c;用于实现线程间的同步。它提供了一种简单而有效的方式来控制对共享资源的访问&#xff0c;从而避免多个线程同时访问同一资源时可能出现的竞态条件&#xff08;race condition&am…

【Linux网络 —— 网络基础概念】

Linux网络 —— 网络基础概念 计算机网络背景网络发展 初始协议协议分层协议分层的好处 OSI七层模型TCP/IP五层(或四层)模型 再识协议为什么要有TCP/IP协议&#xff1f;什么是TCP/IP协议&#xff1f;TCP/IP协议与操作系统的关系所以究竟什么是协议&#xff1f; 网络传输基本流程…

【openwrt】 libubox组件——ustream

文章目录 ustream 核心数据结构struct ustreamstruct ustream_buf_liststruct ustream_bufstruct ustream_fd ustream 核心APIustream_fd_initustream_uloop_cbustream_fd_read_pendingustream_fill_read ustream_write_pendingustream_writeustream_fd_write ustream 应用示例…

Python画笔案例-059 绘制甩曲彩点动图

1、绘制甩曲彩点动图 通过 python 的turtle 库绘制 甩曲彩点动图,如下图: 2、实现代码 绘制甩曲彩点动图,以下为实现代码: """甩曲彩点动图.py """ import time import turtlecs = [red,orange,

CVPT: Cross-Attention help Visual Prompt Tuning adapt visual task

论文汇总 当前的问题 图1:在VTAB-1k基准测试上&#xff0c;使用预训练的ViT-B/16模型&#xff0c;VPT和我们的CVPT之间的性能和Flops比较。我们将提示的数量分别设置为1、10、20、50,100,150,200。 如图1所示&#xff0c;当给出大量提示时&#xff0c;VPT显示了性能的显著下降…

串口问题汇总:串口发送乱码,重定义使用printf ,输出顺序出错,缓存区思想,串口项目应用

1.c51使用串口出现顺序被覆盖的情况&#xff0c;也就是输出time 最后输出的却是te 这是因为你没有等待上一个数据发送就开始发送下一个数据就会导致数据篡位 2.c51想使用串口重定义使用printf 首先c51是自带stdio.h不需要像32那样点击 include lib选项&#xff0c;你直接改…

力扣958:判断二叉树是否为完全二叉树

给你一棵二叉树的根节点 root &#xff0c;请你判断这棵树是否是一棵 完全二叉树 。 在一棵 完全二叉树 中&#xff0c;除了最后一层外&#xff0c;所有层都被完全填满&#xff0c;并且最后一层中的所有节点都尽可能靠左。最后一层&#xff08;第 h 层&#xff09;中可以包含 …