《QT实用小工具·五十八》模仿VSCode的可任意拖拽的Tab标签组

news2024/11/14 15:14:24

1、概述
源码放在文章末尾

该项目实现了模仿VSCode的可任意拖拽的Tab标签组,包含如下功能:

拖拽标签页至新窗口
拖拽标签页合并控件
无限嵌套的横纵分割布局(类似Qt Creator的编辑框)
获取当前使用的标签组、标签页
自动向上合并标签组
左右拖拽排序(Qt自带)

下面是demo演示:
在这里插入图片描述
项目部分代码如下所示:


#ifndef DRAGABLETABGROUP_H
#define DRAGABLETABGROUP_H

#include <QObject>
#include <QTabWidget>
#include <QMimeData>
#include <QDrag>
#include <QScreen>
#include <QApplication>
#include <QHBoxLayout>
#include <QTimer>
#include <QDesktopWidget>
#include <QStyle>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include "dragabletabbar.h"

#define DRAGABLE_TAB_WINDOW_MIME_KEY "DRAGABLE_TAB_WINDOW_MIME_KEY"
#define DRAGABLE_TAB_WIDGET_MIME_KEY "DRAGABLE_TAB_WIGET_MIME_KEY"
#define DRAGABLE_TAB_LABEL_MIME_KEY "DRAGABLE_TAB_LABEL_MIME_KEY"
#define DRAGABLE_TAB_ICON_MIME_KEY "DRAGABLE_TAB_ICON_MIME_KEY"

#define WIN_FRAME_LEFE_OFFSET 8 // 移动时左边需要偏差的像素;可能是左边的阴影?

#define DRAG_DEB if (0) qDebug()

struct TabPageBean
{
    TabPageBean(QString label, QWidget* widget)
        : label(label), widget(widget){}
    TabPageBean(QIcon icon, QString label, QWidget* widget)
        : icon(icon), label(label), widget(widget){}
    QIcon icon;
    QString label;
    QWidget* widget;
};

class DragableTabGroup : public QTabWidget
{
    Q_OBJECT
    friend class DragableTabArea;
public:
    DragableTabGroup(QWidget* parent = nullptr);

    virtual void tabInserted(int index) override;
    void removeTab(int index);

    void split(QBoxLayout::Direction direction, bool copy = true);

    bool isFocusing();
    bool hasTab(QWidget* widget);
    void deleteTab(int index);
    void deleteAllWidget();
    void removeTabs(QList<int> indexes, bool del = false);
    void removeTabs(int start, int end, bool del = false);
    void deleteIfEmptyWindow();
    bool isInMain();
    bool isRestoreTabEnabled();
    bool canRestoreTab();

    virtual QJsonObject toJson();
    virtual QJsonObject widgetToJson(QWidget* widget);
    virtual void jsonToWidget(QJsonObject object);

public slots:
    virtual void closeLeftTabs(int index = -1);
    virtual void closeRightTabs(int index = -1);
    virtual void closeOtherTabs(int index = -1);
    virtual void closeAllTabs();
    virtual void restoreClosedTab(int index = -1);

protected:
    void dragEnterEvent(QDragEnterEvent *event) override;
    void dragMoveEvent(QDragMoveEvent *event) override;
    void dropEvent(QDropEvent *event) override;
    
    virtual void tabWidgetInserted(int index, QWidget *widget);
    virtual void tabWidgetRemoved(int index, QWidget *widget);
    virtual void tabWidgetFocused(int index, QWidget *widget);

    virtual DragableTabBar* newTabBar(QWidget* parent = nullptr);
    virtual DragableTabGroup* newTabGroup(QWidget* parent = nullptr);

    virtual void tabDraggingEvent(QWidget* widget);
    virtual void tabDragMergedEvent(QWidget* widget);
    virtual void tabDragWindowedEvent(DragableTabGroup* window, QWidget* widget);
    virtual void tabDropMergedEvent(QWidget* widget);

protected slots:
    void moveTabInNewWindow(int index);
    void slotStartDrag(int index);
    DragableTabGroup *createDraggedNewWindow();
    bool mergeDroppedLabel(QDropEvent* event);

signals:
    void signalNewTabWindowCreated(DragableTabGroup* window);
    void signalWidgetFocused(QWidget* widget);
    void signalSplitCurrentTab(QBoxLayout::Direction direction, bool copy);

    void signalTabDragged(QWidget* widget); // 开始拖拽
    void signalTabMerged(QWidget* widget); // 被拖至其他标签组
    void signalTabWindowed(DragableTabGroup* window, QWidget* widget); // 被拖出来形成新窗口
    void signalTabDropped(QWidget* widget); // 合并来自其他标签组的标签
    void signalJsonToWidget(QJsonObject object);

protected:
    DragableTabBar* tab_bar;
    int dragging_index;
    QWidget* dragging_widget;
    static QString dragging_label;
    static QIcon dragging_icon;
    QPoint dragging_point_delta; // 拖拽的 鼠标-子窗口左上角

    bool _is_main; // 是不是主窗口
    static bool _drag_merged;
    bool frameless = false; // 使用无窗口边框

    static int closed_stack_max; // 已关闭标签页的最大数量
    static QList<TabPageBean> closed_stack; // 已关闭的标签页
};

#endif // DRAGABLETABGROUP_H


#include "dragabletabgroup.h"

bool DragableTabGroup::_drag_merged = false;
QString DragableTabGroup::dragging_label;
QIcon DragableTabGroup::dragging_icon;
int DragableTabGroup::closed_stack_max = 20;
QList<TabPageBean> DragableTabGroup::closed_stack;

DragableTabGroup::DragableTabGroup(QWidget *parent)
    : QTabWidget(parent), tab_bar(newTabBar(this)),
      dragging_index(0), dragging_widget(nullptr), _is_main(false)
{
    setAcceptDrops(true);
    setTabBar(tab_bar);
    setMovable(true);
    //    setTabsClosable(true);
    //    tab_bar->setAutoHide(true);

    // 无窗口,看起来更像浏览器的
    if (frameless)
    {
        setWindowFlag(Qt::FramelessWindowHint, true);
    }

    connect(tab_bar, SIGNAL(signalStartDrag(int)), this, SLOT(slotStartDrag(int)));
    connect(tab_bar, SIGNAL(signalEndDrag()), this, SLOT(createDraggedNewWindow()));

    connect(qApp, &QApplication::focusChanged, this, [=](QWidget *, QWidget *now) {
        int index = indexOf(now);
        if (index > -1)
        {
            tabWidgetFocused(index, now);
        }
    });
}

void DragableTabGroup::tabInserted(int index)
{
    auto widget = QTabWidget::widget(index);
    if (widget)
        tabWidgetInserted(index, widget);
}

/**
 * 替换自带的removeTab方法
 * 所有的移除都可以在这里监听到
 */
void DragableTabGroup::removeTab(int index)
{
    if (index < 0 || index >= count())
        return;

    if (isRestoreTabEnabled())
    {
        closed_stack.append(TabPageBean(
            tabIcon(index),
            tabText(index),
            widget(index)));

        if (closed_stack.count() > closed_stack_max)
        {
            closed_stack.takeFirst().widget->deleteLater();
        }
    }

    tabWidgetRemoved(index, this->widget(index));
    QTabWidget::removeTab(index);
}

/**
 * 分割标签组
 * @param direction 方向
 * @param copy      是否复制(默认复制,保留旧tab)
 */
void DragableTabGroup::split(QBoxLayout::Direction direction, bool copy)
{
    emit signalSplitCurrentTab(direction, copy);
}

/**
 * 是否拥有焦点
 */
bool DragableTabGroup::isFocusing()
{
    QWidget *widget = QApplication::focusWidget();
    return widget != nullptr && hasTab(widget);
}

/**
 * 是否包含某一widget
 */
bool DragableTabGroup::hasTab(QWidget *widget)
{
    return indexOf(widget) > -1;
}

void DragableTabGroup::deleteTab(int index)
{
    QWidget *widget = this->widget(index);
    removeTab(index);
    widget->deleteLater();
}

/**
 * 移除tab,并删除所有控件
 */
void DragableTabGroup::deleteAllWidget()
{
    while (this->count())
    {
        auto widget = this->widget(0);
        widget->deleteLater();
        removeTab(0);
    }
}

/**
 * 移除或删除多个标签页
 */
void DragableTabGroup::removeTabs(QList<int> indexes, bool del)
{
    // 排好序
    std::sort(indexes.begin(), indexes.end(), [=](int a, int b) {
        return a < b;
    });

    for (int i = indexes.size() - 1; i >= 0; i--)
    {
        if (del)
            removeTab(i);
        else
            deleteTab(i);
    }
}

/**
 * 移除或者删除指定范围内的标签页
 */
void DragableTabGroup::removeTabs(int start, int end, bool del)
{
    QList<int> indexes;
    if (start == -1)
        start = 0;
    else if (end == -1)
        end = this->count() - 1;

    for (int i = start; i <= end; i++)
    {
        indexes << i;
    }
    removeTabs(indexes, del);
}

/**
 * 判断自己的数量是不是空的
 * 如果是空窗口,则主动删除
 */
void DragableTabGroup::deleteIfEmptyWindow()
{
    if (!_is_main && count() == 0)
        deleteLater();
}

/**
 * 判断标签组是否在主窗口中
 */
bool DragableTabGroup::isInMain()
{
    return parentWidget() != nullptr;
}

bool DragableTabGroup::isRestoreTabEnabled()
{
    return closed_stack_max > 0;
}

/**
 * 能否恢复已关闭的标签页
 */
bool DragableTabGroup::canRestoreTab()
{
    return closed_stack.size() > 0;
}

QJsonObject DragableTabGroup::toJson()
{
    QJsonObject object;
    object.insert("width", this->width());
    object.insert("height", this->height());

    QJsonArray array;
    for (int i = 0; i < this->count(); i++)
    {
        QWidget *widget = this->widget(i);
        QJsonObject obj = widgetToJson(widget);
        array.append(obj);
    }

    object.insert("tabs", array);
    object.insert("current", this->currentIndex());
    return object;
}

QJsonObject DragableTabGroup::widgetToJson(QWidget *widget)
{
    Q_UNUSED(widget)
    return QJsonObject();
}

/**
 * 从JSON恢复标签组的控件
 * 有两种方式:
 * - TabGroup中继承,添加widget
 * - TabsArea中在group中添加widget
 */
void DragableTabGroup::jsonToWidget(QJsonObject object)
{
    emit signalJsonToWidget(object);
}

void DragableTabGroup::closeLeftTabs(int index)
{
    if (index == -1)
        index = currentIndex();
    removeTabs(-1, index - 1);
}

void DragableTabGroup::closeRightTabs(int index)
{
    if (index == -1)
        index = currentIndex();
    removeTabs(index + 1, -1);
}

void DragableTabGroup::closeOtherTabs(int index)
{
    if (index == -1)
        index = currentIndex();

    QList<int> indexes;
    for (int i = 0; i < this->count(); i++)
        indexes << i;
    indexes.removeOne(index);
    removeTabs(indexes);
}

void DragableTabGroup::closeAllTabs()
{
    QList<int> indexes;
    for (int i = 0; i < this->count(); i++)
        indexes << i;
    removeTabs(indexes);
    deleteIfEmptyWindow();
}

void DragableTabGroup::restoreClosedTab(int index)
{
    if (!closed_stack.size())
        return;

    TabPageBean page = closed_stack.takeLast();
    if (index == -1)
        addTab(page.widget, page.icon, page.label);
    else
        insertTab(index, page.widget, page.icon, page.label);
    setCurrentWidget(page.widget);
}

void DragableTabGroup::dragEnterEvent(QDragEnterEvent *event)
{
    const QMimeData *mime = event->mimeData();
    if (mime->hasFormat(DRAGABLE_TAB_WIDGET_MIME_KEY)) // Tab拖拽
    {
        event->accept();
    }

    return QTabWidget::dragEnterEvent(event);
}

void DragableTabGroup::dragMoveEvent(QDragMoveEvent *event)
{
    const QMimeData *mime = event->mimeData();
    if (count() == 0)
    {
        event->accept();
    }
    else if (mime->hasFormat(DRAGABLE_TAB_WIDGET_MIME_KEY)) // 整行拖拽
    {
        QPoint pos = event->pos();
        if (pos.y() >= tab_bar->geometry().top() - qMax(tab_bar->height(), 32) && pos.y() <= tab_bar->geometry().bottom() + qMax(tab_bar->height(), 32)) // 只有 tabBar 的位置可拖拽
        {
            event->accept();
        }
        else
        {
            event->ignore();
        }
    }
    else
    {
        return QTabWidget::dragMoveEvent(event);
    }
}

void DragableTabGroup::dropEvent(QDropEvent *event)
{
    if (mergeDroppedLabel(event))
        event->accept();

    return QTabWidget::dropEvent(event);
}

/**
 * 任何Tab的添加都会触发这个方法
 * 包括拖动tab到此页面
 * 在插入之后调用
 */
void DragableTabGroup::tabWidgetInserted(int index, QWidget *widget)
{
}

/**
 * 任何tab的删除都会触发这个方法
 * 包括拖拽tab出去
 * 在删除之前调用
 */
void DragableTabGroup::tabWidgetRemoved(int index, QWidget *widget)
{
    if (this->count() <= 1) // 全部移除完了,则删除当前的TabGroup
    {
        this->deleteLater();
    }
}

/**
 * 标签控件获得焦点事件
 */
void DragableTabGroup::tabWidgetFocused(int index, QWidget *widget)
{
}

/**
 * 创建一个自定义TabBar
 * 注意:实际上,这个虚函数不会被覆盖
 * 因为是在构造函数中调用,此时不会调用子类重写的方法
 */
DragableTabBar *DragableTabGroup::newTabBar(QWidget *parent)
{
    return new DragableTabBar(parent);
}

/**
 * 创建一个标签组的新方法
 * 如果需要继承的话,则需要把所有创建的地方都覆盖掉
 */
DragableTabGroup *DragableTabGroup::newTabGroup(QWidget *parent)
{
    return new DragableTabGroup(parent);
}

/**
 * 标签被拖拽事件
 * widget所在的tab还在自己这儿
 */
void DragableTabGroup::tabDraggingEvent(QWidget *widget)
{
}

/**
 * 本标签组的标签被其他标签组合并
 * 此时的widget所在的tab已经不是自己的了
 * 如果是独立窗口的唯一标签被合并,那么合并后标签组自动删除,不会触发此方法
 * (另外……这个方法会导致崩溃,已经注释掉了,永远不会触发)
 */
void DragableTabGroup::tabDragMergedEvent(QWidget *widget)
{
}

/**
 * 本标签组的标签拖动至新的窗口
 * tab也已经不在自己这儿了
 */
void DragableTabGroup::tabDragWindowedEvent(DragableTabGroup *window, QWidget *widget)
{
}

/**
 * 标签拖到上面事件
 * 在合并到自己之后
 */
void DragableTabGroup::tabDropMergedEvent(QWidget *widget)
{
}

/**
 * 移动标签到新的窗口
 * 其实是相当于触发拖拽事件一样的
 */
void DragableTabGroup::moveTabInNewWindow(int index)
{
    dragging_index = index;
    dragging_widget = this->widget(index);
    dragging_point_delta = QCursor::pos() - (parent() == nullptr ? tabBar()->mapToGlobal(tabBar()->pos()) : mapToGlobal(QPoint(0, 0)));
    createDraggedNewWindow();
}

/**
 * 开始标签拖拽
 */
void DragableTabGroup::slotStartDrag(int index)
{
    dragging_index = index;
    dragging_widget = this->widget(index);
    dragging_point_delta = QCursor::pos() - (parent() == nullptr ? tabBar()->mapToGlobal(tabBar()->pos()) : mapToGlobal(QPoint(0, 0)));
    tabDraggingEvent(dragging_widget);
    emit signalTabDragged(dragging_widget);
    _drag_merged = false;

    QPixmap pixmap(this->size());
    pixmap.fill(Qt::transparent);
    //    dragging_widget->render(&pixmap, dragging_widget->mapToGlobal(pos()) - this->mapToGlobal(pos()));
    this->render(&pixmap, this->mapToGlobal(pos()) - this->mapToGlobal(pos()));

    QMimeData *mime = new QMimeData;
    mime->setData(DRAGABLE_TAB_WINDOW_MIME_KEY, QString::number(reinterpret_cast<int>(this)).toUtf8());
    mime->setData(DRAGABLE_TAB_WIDGET_MIME_KEY, QString::number(reinterpret_cast<int>(dragging_widget)).toUtf8());
    mime->setData(DRAGABLE_TAB_LABEL_MIME_KEY, tab_bar->tabText(index).toLocal8Bit());
    dragging_label = tab_bar->tabText(index);
    dragging_icon = tab_bar->tabIcon(index);
    QDrag *drag = new QDrag(this);
    drag->setMimeData(mime);
    drag->setPixmap(pixmap);
    drag->setHotSpot(dragging_point_delta);
    bool is_one = (count() == 1 && !isInMain());
    connect(drag, &QDrag::destroyed, this, [=](QObject *) {
        DRAG_DEB << "destroyed";
        // 顺序:先触发 dropEvent,在 drag::destroyed

        // 判断有没有被合并到窗口
        if (_drag_merged)
        {
            DRAG_DEB << "drag 合并标签";
            // 判断是不是唯一标签的窗口
            // 如果单窗口只有这一个标签,那么拖动结束后自己也删除了,后面的代码并没有什么意义
            if (!is_one)
            {
                // tabDragMergedEvent(dragging_widget); // 这句话会导致莫名的崩溃
                emit signalTabMerged(dragging_widget);
            }
            return;
        }

        // 没有合并到其他窗口
        if (this->count() == 1 && !isInMain())
        {
            // 单个标签拖动,移动窗口
            int titlebar_height = style()->pixelMetric(QStyle::PM_TitleBarHeight);
            this->move(QCursor::pos() - dragging_point_delta - QPoint(WIN_FRAME_LEFE_OFFSET, titlebar_height));
            DRAG_DEB << "drag 移动窗口";
        }
        else
        {
            DRAG_DEB << "drag 创新窗口";
            // 多个标签拖出,创建新窗口
            createDraggedNewWindow();
        }
    });

    // 如果只有一个标签,则假装移动整个窗口
    if (this->count() == 1)
    {
        QTimer::singleShot(0, [=] {
            //            this->hide();
            this->move(-30000, -30000); // 隐藏起来
        });
    }
    DRAG_DEB << "----------开始drag--------";
    // exec 操作会一直阻塞后面的代码,除非使用多线程或者信号槽
    drag->exec();
}

/**
 * 自己的标签拖出到新窗口
 */
DragableTabGroup *DragableTabGroup::createDraggedNewWindow()
{
    if (count() == 1) // 只有一个标签,直接移动窗口
    {
        // 会导致没有 update,第一次按下无法操作,已取消
        // move(QCursor::pos()-dragging_point_delta-QPoint(0,tab_bar->height()));
        // return ;
    }

    int titlebar_height = style()->pixelMetric(QStyle::PM_TitleBarHeight);

    DragableTabGroup *window = newTabGroup(nullptr /*_is_main ? this : this->parentWidget()*/);
    window->setAttribute(Qt::WA_DeleteOnClose, true);
    window->resize(this->size());
    window->move(QCursor::pos() - dragging_point_delta - QPoint(WIN_FRAME_LEFE_OFFSET, titlebar_height));
    window->show();
    QString label = tab_bar->tabText(dragging_index);
    QIcon icon = tab_bar->tabIcon(dragging_index);
    removeTab(dragging_index);
    if (!icon.isNull())
        window->addTab(dragging_widget, icon, label);
    else
        window->addTab(dragging_widget, label);
    emit signalNewTabWindowCreated(window);
    if (!_is_main && count() == 0) // 标签拖完了
        deleteLater();
    window->raise();
    window->setFocus();
    dragging_widget->setFocus();
    dragging_widget->setFocus();
    QTimer::singleShot(0, dragging_widget, [=] {
        // 为啥要延迟……
        window->setFocus();
    });
    tabDragWindowedEvent(window, dragging_widget);
    emit signalTabWindowed(window, dragging_widget);
    return window;
}

/**
 * 另一个窗口拖拽本窗口的tabbar,合并标签
 */
bool DragableTabGroup::mergeDroppedLabel(QDropEvent *event)
{
    int insert_index = count();
    // 根据鼠标的位置判断插入的位置
    for (int i = count() - 1; i >= 0; i--)
    {
        if (tab_bar->tabRect(i).center().x() + tab_bar->pos().x() >= event->pos().x())
            insert_index = i;
    }

    // 被拖拽的信息
    const QMimeData *mime = event->mimeData();
    DragableTabGroup *window = reinterpret_cast<DragableTabGroup *>(mime->data(DRAGABLE_TAB_WINDOW_MIME_KEY).toInt());
    QWidget *widget = reinterpret_cast<QWidget *>(mime->data(DRAGABLE_TAB_WIDGET_MIME_KEY).toInt());
    QString label = QString::fromLocal8Bit(mime->data(DRAGABLE_TAB_LABEL_MIME_KEY));
    QIcon icon = dragging_icon;
    if (window == this) // 被拖拽的就是自己
    {
        if (insert_index == currentIndex()) // 根本就没有被拖动
            return false;
        // 交换标签顺序
        removeTab(currentIndex());
        insertTab(insert_index, widget, label);
        setCurrentIndex(insert_index);
        return false;
    }

    // 可以拖拽合并
    _drag_merged = true;

    // 移除旧的
    window->removeTab(window->currentIndex());
    window->deleteIfEmptyWindow(); // 标签拖完了(标签移除事件中也会自己删除)

    // 插入新的
    if (icon.isNull())
    {
        if (insert_index >= count()) // 加到末尾
            addTab(widget, label);
        else
            insertTab(insert_index, widget, label);
    }
    else
    {
        if (insert_index >= count()) // 加到末尾
            addTab(widget, icon, label);
        else
            insertTab(insert_index, widget, icon, label);
    }

    setCurrentIndex(insert_index);
    //    this->raise(); // 如果用frameless,raise会一直生效,导致界面会被挡住……
    this->setFocus();
    QTimer::singleShot(0, this, [=] {
        widget->setFocus();
    });
    tabDropMergedEvent(widget);
    emit signalTabDropped(widget);
    return true;
}

源码下载

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

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

相关文章

测径仪视窗镜片的维护和保养步骤

关键字:测径仪镜片,测径仪保养,测径仪维护,视窗镜片维护,视窗镜片擦拭保养,视窗镜片的检查, 视窗镜片定期保养 视窗镜片是保护光学镜头免受污染和损伤的光学平镜片&#xff0c;它的污染和破损会直接影响光学系统的测量结果。 视窗镜片一般在受到轻微污染&#xff08;指镜片上…

项目管理-项目采购管理2/2

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 本文承接 项目采购管理第二部分&#xff0c;详细讲解项目合同管理。 项目采购管理过程--重点&#xff1a; ①ITTO 输入&#xff0c;输出…

测试环境搭建:JDK+Tomcat+Mysql+Redis

基础的测试环境搭建&#xff1a; LAMPLinux(CentOS、ubuntu、redhat)ApacheMysqlPHP LTMJLinux(CentOS、ubuntu、redhat)TomcatMysql(Oracle)RedisJava 真实的测试环境搭建&#xff1a;&#xff08;企业真实的运维&#xff09; 基于SpringBoot&#xff08;SpringCloud分布式微…

分析:Palo Alto在从SASE向SASO演进中定位不佳

摘要 我们通过上一篇文章&#xff08;Fortinet的愿景——超越SASE&#xff09;中应用于Fortinet的相同框架来回顾Palo Alto Network在网络和网络安全方面的前景。 SASE涉及数据传输的第一英里。不过&#xff0c;随着SASE的发展&#xff0c;投资者还需要考虑中间和最后一英里。…

javaweb学习week7

javaweb学习 十四.Springboot 1.配置优先级 Springboot中支持三种格式的配置文件&#xff1a; 注意&#xff1a;虽然Springboot支持多种格式配置文件&#xff0c;但是在项目开发时&#xff0c;推荐使用一种格式的配置&#xff08;yml是主流&#xff09; Springboot除了支持…

【Osek网络管理测试】[TG3_TC3]tSleepRequestMin_L

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT进入NMLimpHome状态后请求睡眠的最短时间是否正确…

周刊是聪明人筛选优质知识的聪明手段!

这是一个信息过载的时代&#xff0c;也是一个信息匮乏的时代。 这种矛盾的现象在 Python 编程语言上的表现非常明显。 它是常年高居编程语言排行榜的最流行语言之一&#xff0c;在国外发展得如火如荼&#xff0c;开发者、项目、文章、播客、会议活动等相关信息如海如潮。 但…

【LeetCode刷题记录】105. 从前序与中序遍历序列构造二叉树 106. 从中序与后序遍历序列构造二叉树

105 从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,1…

近50亿元国资助阵,全球最大量子独角兽登场!

4月30日&#xff0c;澳大利亚与PsiQuantum公司宣布签订一项近10亿澳元&#xff08;约6.2亿美元、47.24亿人民币&#xff09;的协议&#xff0c;旨在建造世界上第一台商业上“有用”的量子计算机。 仅在一天前&#xff0c;澳大利亚还投资了1840万澳元&#xff0c;在悉尼大学成立…

【Osek网络管理测试】[TG3_TC5]等待总线睡眠状态_1

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT在满足进入等待睡眠状态的条件时是否进入该状态 …

Linux学习(一)-- 简单的认识

目录 1. Linux的诞生 2.Linux发行版 拓展&#xff1a; &#xff08;1&#xff09;什么是Linux系统的内核&#xff1f; &#xff08;2&#xff09;什么是Linux系统发行版&#xff1f; 1. Linux的诞生 Linux创始人: 林纳斯 托瓦兹 Linux 诞生于1991年&#xff0c;作者上大学…

沃伦·巴菲特将AI比做原子弹:“瓶中精灵”使诈骗成为最快增长产业|TodayAI

在伯克希尔哈撒韦公司的年度股东大会上&#xff0c;投资大师沃伦巴菲特对人工智能的未来提出了严重警告。巴菲特对这项可以模拟现实并产生误导性内容的技术表示担忧&#xff0c;他认为这将成为史上增长最快的行业之一。 巴菲特在会上说&#xff1a;“当你思考诈骗人们的潜力时…

KernelSU 如何不通过模块,直接修改系统分区

刚刚看了术哥发的视频,发现kernelSU通过挂载OverlayFS实现无需模块,即可直接修改系统分区,很是方便,并且安全性也很高,于是便有了这篇文章。 下面的教程与原视频存在差异,建议观看原视频后再结合本文章进行操作。 在未进行修改前,我们打开/system/文件夹,并在里面创建…

【前端】输入时字符跳动动画实现

输入时字符跳动动画实现 在前端开发中&#xff0c;为了提升用户体验&#xff0c;我们经常需要为用户的交互行为提供即时的反馈。这不仅让用户知道他们的操作有了响应&#xff0c;还可以让整个界面看起来更加生动、有趣。本文将通过一个简单的例子讲解如何实现在用户输入字符时…

2分钟快速了解!全网最详细的性能测试教程之【Redis 简介和安装】

本篇文章主要介绍基于Redis的的简介和安装&#xff0c;其中参考了许多大佬写的文章&#xff0c;算是做一个Redis的基础教程吧。 Redis 简介 Redis 是完全开源的&#xff0c;遵守 BSD 协议&#xff0c;是一个高性能的 key-value 数据库。 Redis 与其他 key - value 缓存产品有…

SCI一区 | WOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Matlab)

SCI一区 | WOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Matlab&#xff09; 目录 SCI一区 | WOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Matlab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现WOA-BiTCN-BiGRU-A…

【C++】学习笔记——list

文章目录 八、list1. list的介绍2. list的使用3. list的模拟实现4. list模拟实现的代码整合1. list.h2. test.cpp 未完待续 八、list list链接 1. list的介绍 是的&#xff0c; list 就是带头双向循环链表。 2. list的使用 通过 string 和 vector 的学习&#xff0c;我们差…

Mybatis逆向工程笔记小结

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1.前言 2.实现方案 2.1. mybatis-generator生成 2.1.1. 环境说明 2.1.2. 数…

Infuse for Mac激活版:多媒体播放器

Infuse Pro 是一款非常强大的视频播放器&#xff0c;它被誉为 Apple TV 上最强的播放器。它支持广泛的视频格式和解码器&#xff0c;包括 DTS、DTS-HD、AC3、E-AC3 等高清视频的音频播放任务。此外&#xff0c;Infuse Pro 还支持所有常见的高清格式。 Infuse Pro 的特点之一是其…

软件设计师-应用技术-数据库设计题2

基础知识及技巧&#xff1a; 1. 数据库设计过程&#xff1a; 四个阶段&#xff1a;需求分析、概念结构设计、逻辑结构设计、物理设计。每个阶段的产物&#xff1a; 需求分析&#xff1a;数据流图、数据字典、需求说明书。概念结构设计&#xff1a;ER模型逻辑机构设计&#xf…