【QT】事件系统入门——QEvent 基础与示例

news2025/3/14 13:28:27

一、事件介绍

事件是 应用程序内部或者外部产生的事情或者动作的统称

  • 在 Qt 中使用一个对象来表示一个事件。所有的 Qt 事件均继承于抽象类 QEvent。
  • 事件是由系统或者 Qt 平台本身在不同的时刻发出的。
  • 当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。
  • 一些事件是在用户操作时发出,如键盘事件、鼠标事件等,另一些事件则是由系统本身自动发出,如定时器事件。

事件本身是 操作系统提供的 机制,Qt 也同样把操作系统事件机制进行了封装拿到了 Qt 中,但由于 事件 对应的代码编写起来不方便,因此 Qt 对于事件机制 又进行了进一步的 封装,就得到了 信号槽

常见的 QT 事件如下:

image-20250128131332619

不同场景下,要关注的点不一样。这些事件的子类中就会包含一些对应的不同属性。

常见事件描述:

从图片中提取的文字信息如下表所示:

事件名称描述
鼠标事件鼠标左键、鼠标右键、鼠标滚轮,鼠标的移动,鼠标按键的按下和松开
键盘事件按键类型、按键按下、按键松开
定时器事件定时时间到达
进入离开事件鼠标的进入和离开
滚轮事件鼠标滚轮滚动
绘屏事件重绘屏幕的某些部分
显示隐藏事件窗口的显示和隐藏
移动事件窗口位置的变化
窗口事件是否为当前窗口
大小改变事件窗口大小改变
焦点事件键盘焦点移动
拖拽事件用鼠标进行拖拽

二、事件的处理

事件处理一般常用的方法为:重写相关的 Event 函数。

在 Qt 中,几乎所有的 Event 函数都是虚函数,所以可以重新实现。

比如:在实现鼠标的进入和离开事件时,直接重新实现 enterEvent()leaveEvent() 即可。enterEvent()leaveEvent() 函数原型如下:
img

🐇标签提升 & 演示

1)新建 Qt 项目

基类选择 QWidget,同时勾选 UI 界面文件,并且设计 UI 文件,如下:

image-20250129160200592

  • 有了边框,方便观察当前鼠标是否进入和离开

这里我们还需要创建 QLabel 子类,来重写 enterEventleaveEvent

2)在项目中新添加一个类

先选中项目名称 QEvent,点击鼠标右键,选择 add new … ,弹出如下对话框:

img

3)定义类名并选择基类

image-20250129160800887

4)此时项目中会新添加以下两个文件

img

5)修改基类,重写 enterEvent()leaveEvent

如果我们想了解这两个函数,则可以在 帮助文档 中查找对应的内容

  • 要想重写父类的函数就需要确保写的函数名字和函数的参数列表完全一致(形参名无所谓)。
  • 然后对这两个函数进行重写

label.hlabel.cpp 代码如下:

image-20250129161706755

但是当前代码还是有问题的,如下:

image-20250129163141755

  • 在 UI 文件中我们可以看到当前在界面上创建的这个 Label 并不是自己写的 Label,而是 QLabel
  • 但是我们需要确保界面上的这个 Label 是自己定义的 Label 类实例才会被执行,因此需要 提升

6)在 UI 文件中选中 Label,右键 ——> 提升为…,点击之后弹出如下:

image-20250129161405787

  • 这里需要确保这里填写的类名以及头文件 和 上述自定义的 类名头文件 匹配

💡 通过 “提升为…” 这样的方式就可以把 Qt Designer 中托上去的控件的类型转换成自定义的控件类型,如下:

img

7)执行效果

当鼠标进入设计好的标签之后,就会在应用程序输出栏中打印:enterEvent;鼠标移出设计好的标签之后,就会在应用程序输出栏中打印:leaveEvent

image-20250129163712359

  • 这个时候就说明当前的 enterEventleaveEvent 这两个事件就被我们给捕获到了。

🐇 示例 – 当鼠标点击时,获取对应的坐标值

该示例主要基于上面代码,实现:当鼠标点击时,获取对应的坐标值

void Label::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "按下左键";
    } else if (event->button() == Qt::RightButton) {
        qDebug() << "按下右键";
    }
    // 当前 event 对象就包含了鼠标点击位置的坐标.
     qDebug() << event->x() << ", " << event->y();
    // globalX 和 globalY 是以屏幕左上角为原点, 获取的坐标.
     qDebug() << event->globalX() << ", " << event->globalY();
}

实现效果如下:

img

  • 我们这里就通过事件 获取到 鼠标点击 的位置
  • mouseEvent 这个函数 其实按左键、右键、滚轮都可以触发的,甚至还有 前进 后退侧键

三、键盘按键事件

  • Qt 中 QShortCut 是信号槽机制封装过 获取 键盘按键的 方式
  • 当然我们也可以从更底层角度,通过事件获取到当前用户键盘按下情况

Qt 中的按键事件是通过 QKeyEvent 类来实现的。当键盘上的按键被按下或者被释放时,键盘事件便会触发。

在帮助文档中查找 QKeyEvent 类,然后查找按键事件中所有的按键类型:在帮助文档中输入:Qt::Key,如下图:

img

1. 单个按键

示例:当某个按键被按下时,输出:某个按键被按下了;

  1. 新建项目,在头文件 “widget.h” 中声明虚函数 keyPressEvent();
  2. 然后重写 keyPressEvent() 虚函数,如下图

image-20250129202354185

2. 组合按键

在 Qt 助手中搜索:Qt::KeyboardModifier,如下图示:

img

Qt::KeyboardModifier 中定义了在处理键盘事件时对应的修改键。

在 Qt 中,键盘事件可以与修改键以起使用,以实现一些复杂的交互操作。

KeyboardModifier 中修改键的具体描述如下:

修饰键类型描述
Qt::NoModifier无修改键
Qt::ShiftModifierShift 键
Qt::ControlModifierCtrl 键
Qt::AltModifierAlt 键
Qt::MetaModifierMeta键(在Windows上指Windows键,在macOS上指Command键)
Qt::KeypadModifier使用键盘上的数字键盘进行输入时,Num Lock键处于打开状态
Qt::GroupSwitchModifier用于在输入法组之间切换

这些修饰键常用于编程中处理键盘事件,特别是在使用Qt框架开发跨平台应用程序时。

代码如下:

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

四、鼠标事件

在 Qt 中,鼠标事件是用 QMouseEvent 类来实现的。当在窗口中按下鼠标或者移动鼠标时,都会产生鼠标事件。

  • 利用 QMouseEvent 类可以获取鼠标的哪个键被按下了以及鼠标的当前位置等信息。

在 Qt 帮助文档中查找 QMouseEvent 类如下图示:

img

鼠标单击 | 释放 | 双击 | 移动 事件

① 在 Qt 中,鼠标按下是通过虚函数 mousePressEvent() 来捕获的。mousePressEvent() 函数原型如下:

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

鼠标左右键及滚的表示如下:

  1. Qt::LeftButton 鼠标左键

  2. Qt::RightButton 鼠标右键

  3. Qt::MidButton 鼠标滚轮

void Label::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "按下左键";
    } else if (event->button() == Qt::RightButton) {
        qDebug() << "按下右键";
    }
}

② 鼠标释放事件是通过虚函数 mouseReleaseEvent() 来捕获的。mouseReleaseEvent() 函数原型如下:

  • [ virtual protected] void QWidget:: mouseReleaseEvent (QMouseEvent * event )
void Label::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "释放左键";
    } else if (event->button() == Qt::RightButton) {
        qDebug() << "释放右键";
    }
}

③ 鼠标双击事件是通过虚函数:mouseDoubleClickEvent() 来实现的。mouseDoubleClickEvent() 函数原型如下:

  • [ virtual protected] void QWidget:: mouseDoubleClickEvent (QMouseEvent * event )
void Label::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        qDebug() << "双击左键";
    } else if (event->button() == Qt::RightButton) {
        qDebug() << "双击右键";
    }
}

④ 鼠标移动事件是通过虚函数:mouseMoveEvent() 来实现的。同时为了实时捕获鼠标位置信息,需要通过函数 setMouseTracking() 来追踪鼠标的位置。mouseMoveEvent()函数原型如下:

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

setMouseTracking() 函数原型如下:

  • void setMouseTracking( bool enable )

说明:setMouseTracking() 函数默认是 false,需要设置为 true,才能实时捕获鼠标位置信息。

否则只有当鼠标按下时才能捕获其位置信息。
image-20250129172741229

  • 上面的操作其实和 二中说的标签提升 那里一样,可以仔细看看

最后演示结果如下:

image-20250129173003843

  • 这里演示的话,还是把实时捕捉关了,敏感太高不适合演示现象,我们这里坐标可以长按拖拽鼠标也可以显示 鼠标位置信息
  • 我们这里重写鼠标事件的操作都是放在 自定义的 Label 中完成的,此时只有鼠标在 Label 范围内进行动作才能捕捉到
  • 当前也可以把其放到 Widget(Qwidget 子类) 来完成,此时鼠标在整个窗口都可以捕捉到

五、定时器

Qt 中在进行窗口程序的处理过程中,经常要周期性的执行某些操作,或者制作一些动画效果,使用定时器就可以实现。

所谓 定时器就是在间隔一定时间后,去执行某一个任务

定时器在很多场景下都会使用到,如弹窗自动关闭之类的功能等。

Qt 中的定时器分为 QTimerEventQTimer 这 2 个类。

  • QTimerEvent 类用来描述一个定时器事件。
    • 在使用时需要通过 startTimer() 函数来开启⼀个定时器,这个函数需要输入一个以毫秒为单位的整数作为参数来表明设定的时间,它返回的整型值代表这个定时器。
    • 当定时器溢出时(即定时时间到达)就可以在 timerEvent() 函数中获取该定时器的编号来进行相关操作。
  • QTimer 类来实现一个定时器,它提供了更高层次的编程接口,如:可以使用信号和槽,还可以设置只运行一次的定时器。

1. QTimerEvent 类

联系前文:【QT】 控件 – 显示类

在 UI 界面上放置一个 LCD Number 控件,让其 10 秒数字不断递减到 0,相当于倒计时。

  1. 新建项目,在 UI 界面文件放置一个 LCD Number 控件,并且给定初始值为 10
  2. 在 “widget.h” 头文件中声明 timerEvent() 函数,并定义一个整型变量
  3. 在 “widget.cpp” 文件中重写 timerEvent() 函数
#include "widget.h"
#include "ui_widget.h"

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

    // 开启定时器事件
    // 此处 timerId 是一个定时器的身份标识(类似于 Linux 中的文件描述符)
    timerId = this->startTimer(1000); // 这里 timerId 设为成员变量
}

void Widget::timerEvent(QTimerEvent *event)
{
    // 如果一个程序中存在多个定时器 (startTimer 创建的定时器), 此时每个定时器都会触发 timerEvent 函数.
    // 先判定一下这次触发是否是想要的定时器触发的.
    if (event->timerId() != this->timerId) {
        // 如果不是我们的定时器触发的, 就直接忽略.
        // 当前程序中只有这一个定时器.
        return;
    }
 
    int value = ui->lcdNumber->intValue();
    if (value <= 0) {
        // 停止定时器
        this->killTimer(this->timerId);
        return;
    }
    value -= 1;
    ui->lcdNumber->display(value);
}
  • 此时运行程序就可以获得我们想要的倒计时结果了

但是相比于 QTimer ,使用 timerEvent 还是要更加复杂一点,因为需要手动管理 timerId,需要区分整个函数调用是由哪个 timer 引起

2. QTimer 类

纯代码实现,无调用 ui

  • 实现基本的计时功能
  • 还实现了**【获取系统日期及实时时间】**,如下:
#include "widget.h"
#include "ui_widget.h"

#include <QVBoxLayout>
#include <QLabel>
#include <QTimer>
#include <QPushButton>
#include <QDateTime>

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

    QVBoxLayout *layout = new QVBoxLayout(this);
    // 创建控件
    QLabel *label = new QLabel("0");
    label->setStyleSheet("font-size: 50px; color: red;");
    QLabel *label_date = new QLabel("0");
    label_date->setStyleSheet("font-size: 50px; color: blue;");

    QPushButton *startBtn = new QPushButton("开始");
    QPushButton *stopBtn = new QPushButton("停止");

    // 按钮布局
    QHBoxLayout *btnLayout = new QHBoxLayout;
    btnLayout->addWidget(startBtn);
    btnLayout->addWidget(stopBtn);

    // 主布局
    layout->addWidget(label, 0, Qt::AlignCenter);
    layout->addWidget(label_date, 0, Qt::AlignCenter);
    layout->addLayout(btnLayout);

    // 创建定时器和计数器
    QTimer *timer = new QTimer(this);
    
    // 连接信号与槽
    connect(timer, &QTimer::timeout, [=](){
        static int num=1;
        label->setText(QString::number(num++));
    });
    
    connect(timer, &QTimer::timeout, [=](){
        QString str=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
        label_date->setText(str);
    });
    
    
    connect(startBtn, &QPushButton::clicked, [=](){
        timer->start(1000);
    });
    connect(stopBtn, &QPushButton::clicked, [=]{
        timer->stop();
    });
}

结果如下:

image-20250129220136849

六、事件分发器

1. 概述

在 Qt 中,事件分发器(Event Dispatcher)是一个核心概念,用于处理 GUI 应用程序中的事件。

事件分发器负责 将事件从一个对象传递到另一个对象,直到事件被处理或被取消。

每个继承自 QObject 类QObject 类 本身都可以在本类中 重写 bool event(QEvent *e) 函数,来实现相关事件的捕获和拦截。

2. 事件分发器工作原理

在 Qt 中,我们发送的事件是传给了 QObject 对象的 event() 函数。

  • 所有的事件都会进入到这个函数里面,那么我们处理事件就要重写这个 event() 函数。
  • event() 函数本身不会去处理事件,而是根据 事件类型(type值)调用不同的事件处理函数。事件分发器就是工作在应用程序向下分发事件的过程中,如下图:

如上图,事件分发器用于分发事件。在此过程中,事件分发器也可以做 拦截操作

  • 事件分发器主要是通过 bool event(QEvent *e) 函数来实现。其返回值为布尔类型,若为 true,代表拦截,不向下分发。
  • Qt 中的事件是封装在 QEvent类 中,在 Qt 助手中输入 QEvent 可以查看其所包括的事件类型,如下图示:

image-20250128133052036

演示代码如下:

void Widget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        qDebug() << "鼠标左键按下";
    }
}

bool Widget::event(QEvent *ev)
{
    if(ev->type() == QEvent::MouseButtonPress)
    {
        qDebug() << "Event 中鼠标被按下";
        return true; // 表示不向下分发
    }
    // 其他事件交给父类处理(默认)
    return QWidget::event(ev);
}

结果如下:

image-20250129220805361

当我们 return false 时,就会出现一下结果

image-20250129221037788

  • 单机鼠标右键,鼠标被按下
  • 单击鼠标左键, event 函数和 mousePressEvent 函数交替触发

七、事件过滤器

在 Qt 中,一个对象可能经常要查看或拦截另外一个对象的事件,如对话框想要拦截按键事件,不让别的组件接收到,或者修改按键的默认值等。

  • 通过上面的学习,我们已经知道,Qt 创建了 QEvent 事件对象之后,会调用 QObjectevent() 函数 处理事件的分发。
  • 显然,我们可以在 event() 函数中实现拦截的操作。由于 event() 函数是 protected 的,因此,需要继承已有类。
  • 如果组件很多,就需要重写很多个 event() 函数。这当然相当⿇烦,更不用说重写 event() 函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器

事件过滤器是在 应用程序 分发到 event 事件分发器 之前,再做一次更高级的拦截

img

事件过滤器的⼀般使用步骤:

  1. 安装事件过滤器;
  2. 重写事件过滤器函数:eventfilter()

【示例】:基于文章上面演示的 标签提升 那的操作,在 "Label.cpp" 中代码如下:

#include "label.h"
#include <QDebug>
#include <QMouseEvent>

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

}

void Label::mousePressEvent(QMouseEvent *event)
{
    QString str = QString("鼠标按下: x = %1, y = %2").arg(event->x()).arg(event->y());
    qDebug() << str.toUtf8().data();
}

bool Label::event(QEvent *e)
{
    //如果是鼠标按下,在event事件分发时做拦截操作
    if (e->type() == QEvent::MouseButtonPress)
    {
        QMouseEvent *event = static_cast<QMouseEvent *>(e);
        QString str = QString("Event函数中鼠标按下: x = %1, y = %2").arg(event->x()).arg(event->y());
        qDebug() << str.toUtf8().data();

        return true; //返回true,代表用户自己处理,不向下分发
    }

    //其他事件交给父类处理
    return QLabel::event(e);
}

Widget.cpp 代码如下:

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

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

    // 给 label 装上事件过滤器 this;当前窗口安装事件过滤器
    ui->label->installEventFilter(this);
}


bool Widget::eventFilter(QObject *obj, QEvent *e)
{
    if(obj == ui->label) // 判断控件
    {
        if(e->type() == QEvent::MouseButtonPress){
            QMouseEvent *event = static_cast<QMouseEvent *>(e);
            QString str = QString("事件过滤器中鼠标按下: x = %1, y = %2").arg(event->x()).arg(event->y());
            qDebug() << str.toUtf8().data();

            return true; // 返回true,代表用户自己处理,不向下分发
        }
    }
    return QWidget::eventFilter(obj, e);
}
  • 注意书写函数实现时,记得先声明函数名

结果如下:

img

八、其他

  • moveEvent:窗口移动时触发的事件
  • resizeEvent:窗口大小改变时触发的事件

image-20250130163923426

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

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

相关文章

5-27 临摹大师-IP-Adapter

前言&#xff1a; 前一节我们主要介绍ControlNet中如何对黑白照片进行上色 主要介绍ControlNet中的IP-Adapter。这个也是一种类似的风格借鉴&#xff0c;类似Reference的能力。 当然IP-Adapter有两点或许可以吸引我们&#xff0c;一个是国人腾讯公司制作的。另一个在速度和效…

Spring MVC面试题(一)

1.什么是Spring MVC&#xff1f; 全称为Model View Controller&#xff0c;Spring MVC是Spring的一个模块&#xff0c;基于MVC架构模式的一个框架 2.Spring MVC优点&#xff1f; 1.可用各种视图技术&#xff0c;不仅限于JSP 2.支持各种请求资源映射策略 3. Spring MVC工作原…

Unity开发的抖音小游戏接入抖音开放平台中的流量主(抖音小游戏接入广告)

前言:作者在进行小游戏审核版本的过程中,碰到了下列问题,所以对这个抖音小游戏接入广告研究了下。 还有就是作者的TTSDK版本号是6.2.6,使用的Unity版本是Unity2022.3.29f1,最好和作者的两个版本号保持一致,因为我发现TTSDK旧版的很多函数在新版中就已经无法正常使用了,必…

统一 Elastic 向量数据库与 LLM 功能,实现智能查询

作者&#xff1a;来自 Elastic Sunile Manjee 利用 LLM 功能进行查询解析&#xff0c;并使用 Elasticsearch 搜索模板&#xff0c;将复杂的用户请求转换为结构化的、基于模式的搜索&#xff0c;从而实现高精度查询结果。 想象一下&#xff0c;你在搜索“距离 Belongil Beach 25…

[操作系统] 学校课程关于“静态优先级抢占式调度“作业

今天我们来分享两道题目哈, 学校弄得题目. T1: 静态优先级, 抢占式(1为高优先级) 图解: 以下是静态优先级抢占式调度的解题过程和结果&#xff1a; 解题思路&#xff1a; 优先级规则&#xff1a; 数值越小优先级越高。新进程到达时&#xff0c;若其优先级高于当前运行进程&…

【SpringBoot】MD5加盐算法的详解

目录 一、什么是加盐算法 二、如何实现加盐算法 2.1 加盐算法代码实现 2.2 注册页面中进行密码加盐 2.3 登录页面进行加盐的解密 2.4 注册和登录 一、什么是加盐算法 加盐算法是一种用于增强密码安全性的技术。这种技术通过在密码存储过程中添加一个随机生成的盐值&…

累计完工数量达到了xxxx超过了最大可完工数量xxxx

之前解决过一次&#xff0c;没有记录下来&#xff0c;不记得发生什么事情。又浪费几个小时去分析问题。这次的经历有点痛苦&#xff0c;碰上多表关连数据的勾稽。分析是河南用户的非法操作造成的。没有领料记录入不了库&#xff0c;跨月了。财务要求删单处理。删单之后&#xf…

飞鸟与鱼不同路

看&#xff0c;好美的太阳。 正是因为有人看才会觉得美&#xff0c;若无人问津&#xff0c;美又从何而来。 嘿嘿&#xff0c;今天提出辞去综合教研室主任一职&#xff0c;不想在这个管理上废时间啦~ 把时间用来考试.........用来做自己的事情&#xff0c;花在自己的身上&…

若依RuoYi-Cloud-Plus微服务版(完整版)前后端部署

一.目标 在浏览器上成功登录进入 二.源码下载 后端源码&#xff1a;前往Gitee下载页面(https://gitee.com/dromara/RuoYi-Cloud-Plus)下载解压到工作目录。 前端源码&#xff1a; 前往Gitee下载页面(https://gitee.com/JavaLionLi/plus-ui)下载解压到工作目录。 文档地址&a…

【redis】list类型:基本命令(下)

文章目录 LLENLREMLTRIMLSET阻塞版本命令BLPOP 和 BRPOP区别使用方式 命令小结内部编码 LLEN 获取 list 的长度 语法&#xff1a; LLEN key时间复杂度&#xff1a; O ( 1 ) O(1) O(1)返回值&#xff1a; list 长度 LREM 删除 count 个 key 中的元素 语法&#xff1a; LREM…

【数据挖掘】知识蒸馏(Knowledge Distillation, KD)

1. 概念 知识蒸馏&#xff08;Knowledge Distillation, KD&#xff09;是一种模型压缩和知识迁移技术&#xff0c;旨在将大型复杂模型&#xff08;称为教师模型&#xff09;中的知识传递给一个较小的模型&#xff08;称为学生模型&#xff09;&#xff0c;以减少计算成本&…

VSCode 搭建C++编程环境 2025新版图文安装教程(100%搭建成功,VSCode安装+C++环境搭建+运行测试+背景图设置)

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、VScode下载及安装二、安装 MinGW-w64 工具链三、Windows环境变量配置四、检查 M…

Ubuntu24.04 LTS 版本 Linux 系统在线和离线安装 Docker 和 Docker compose

一、更换软件源并更新系统 在 Ubuntu 24.04 LTS 中&#xff0c;系统引入了全新的软件源配置格式。现在的源配置文件内容更加结构化且清晰&#xff0c;主要包含了软件类型 (Types)、源地址 (URIs)、版本代号 (Suites) 以及组件 (Components) 等信息。 # cat /etc/apt/sources.li…

MTK Android12 最近历史任务 最左侧的清除历史任务改到页面底部

Android最近历史任务页面 -清除所有- 功能按钮放到底部 文章目录 需求需求原因 修改的核心文件实现方案最近历史任务基本UI结构了解代码实现思路实现方案RecentsViewTaskOverlayFactory在overview_actions_containerOverviewActionsView 实际效果 总结 需求 最近历史任务重&am…

TCP协议支持全双工原因TCP发送接收数据是生产者消费者模型

一、TCP支持全双工的原因 TCP协议支持全双工&#xff0c;即使用TCP协议进行通信时&#xff0c;服务端和客户端可以同时进行数据的发送和接收&#xff0c;互不干扰&#xff0c;实现同时双向传输数据。 这是因为使用TCP协议通信时&#xff0c;读写套接字的文件描述符既用来发送…

文件操作2

7. ⽂件读取结束的判定 7.1 被错误使用的 feof 牢记&#xff1a;在文件读取过程中&#xff0c;不能用 feof 函数的返回值直接来判断文件的是否结束。 feof 的作用是&#xff1a;当文件读取结束的时候&#xff0c;判断读取结束的原因是否是&#xff1a;遇到文件尾结束。 1. …

《又是二叉树?递归与回溯的经典应用》

“ 我喜欢晴天&#xff0c;你恰好是最好的太阳” 226.翻转二叉树 力扣题目链接(opens new window) 翻转一棵二叉树。 这道题我们可以通过递归法解决&#xff0c;我们只要递归的把每一个节点的左右孩子反转一下就能解决了。 代码如下&#xff1a; var invertTree function(ro…

Qt/C++音视频开发82-系统音量值获取和设置/音量大小/静音

一、前言 在音视频开发中&#xff0c;音量的控制分两块&#xff0c;一个是控制播放器本身的音量&#xff0c;绝大部分场景都是需要控制这个&#xff0c;这个不会影响系统音量的设置。还有一种场景是需要控制系统的音量&#xff0c;因为播放器本身的音量是在系统音量的基础上控…

从零到精通文本指令:打造个人AI助理的完整指令库(Prompt 指令实操)

文章目录 从零到精通文本指令&#xff1a;打造个人AI助理的完整指令库(Prompt 指令实操)创作指令创作指令**润色指令****扩写指令** 问答指令直接问答材料问答时间逻辑问答 总结、摘要、翻译指令总结信息抽取翻译 从零到精通文本指令&#xff1a;打造个人AI助理的完整指令库(Pr…

C# NX二次开发:获取模型中所有的草图并获取草图中的对象

大家好&#xff0c;今天接着讲NX二次开发获取草图相关。 获取草图的方法是从workPart中获取&#xff0c;如下面的例子所示&#xff1a; List<Tag> tags new List<Tag>(); SketchCollection sketchCollection workPart.Sketches; …