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

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_OBJECTfriend 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);elsedeleteTab(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);elseinsertTab(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);elsewindow->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);elseinsertTab(insert_index, widget, label);}else{if (insert_index >= count()) // 加到末尾addTab(widget, icon, label);elseinsertTab(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.mzph.cn/web/7313.shtml

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

相关文章

Elementui的el-footer标签使用报错

Elementui的el-footer标签使用报错 其余标签的使用没有报错信息 el-footer的报错信息 原因: ​ 警告信息表示 Vue 不识别 <el-footer> 解决方式: 在组件中进行引入和暴露

Hive数据模型

Hive数据模型 1. 表&#xff08;Table&#xff09;&#xff1a; 表是数据库中的基本组成单位&#xff0c;用于存储数据。它由一系列的行和列组成&#xff0c;每行代表一个记录&#xff0c;每列代表一种属性或字段。创建表时&#xff0c;你需要定义列的数据类型、约束和索引等信…

Linux:进程概念(二.查看进程、父进程与子进程、进程状态详解)

Linux&#xff1a;进程概念&#xff08;二.查看进程、父进程与子进程、进程状态详解&#xff09; 上次讲了一下:Linux&#xff1a;冯诺依曼体系结构、操作系统、初识进程 文章目录 1.查看进程1.1准备工作1.2 指令&#xff1a;ps—显示当前系统中运行的进程信息1.3查看进程属性…

偶然发现了Python的一个BUG。。。

一般情况下&#xff0c;dict(id1, **{id: 1})这句代码应该报TypeError。但如果在捕获了其他异常的情况下&#xff0c;再来执行这句代码&#xff0c;却是会报KeyError&#xff0c;如下图&#xff1a; Python3.10和Python3.9也能复现该情况&#xff0c;正当我摩拳踩掌&#xff0c…

C语言知识点补充——ASCLL码表

1、ASCLL码表 ASCII码表&#xff08;American Standard Code for Information Interchange&#xff09;是一种用于将字符编码为数字的标准。它定义了128个字符的编码方式&#xff0c;包括数字、字母、标点符号和控制字符等。每个字符都对应一个唯一的7位或8位二进制数 2、Ascl…

【Ping】Windows 网络延迟测试 ping 、telnet、tcping 工具

ping 命令 属于网络层的ICMP协议&#xff0c;只能检查 IP 的连通性或网络连接速度&#xff0c; 无法检测IP的端口状态。 telnet telnet命令&#xff0c;属于应用层的协议&#xff0c;用于远程登录&#xff0c;也可用于检测IP的端口状态。但是功能有限&#xff0c;只能检测一时…

如何使git提交的时候忽略一些特殊文件?

认识.gitignore文件 在生成远程仓库的时候我们会看到这样一个选项&#xff1a; 这个.gitignore文件有啥用呢&#xff1f; .gotignore文件是Git版本控制系统中的一个特殊文件。用来指定哪些文件或者目录不被Git追踪或者提交到版本库中。也就意味着&#xff0c;如果我们有一些文…

linux学习笔记——硬盘原理以及linux中的sector与block

在计算机硬盘中&#xff0c;最小的存储单位叫做扇区sector&#xff0c;0.5kb&#xff0c;多个连续扇区组合在一起形成了块block&#xff0c;最小的块包含8个扇区&#xff0c;4kb 我们可以在linux中印证 创建一个新的文件2.txt&#xff0c;查看文件大小为0k 在文件中添加字符后…

Gradle基础学习(七) 认识插件

Gradle构建在一个插件系统上&#xff0c;本身主要由基础设施组成&#xff0c;比如有一个先进的依赖解析引擎&#xff0c;而其他功能则来自插件。 插件是提供额外功能给Gradle构建系统的软件组件。 插件可以被应用到Gradle构建脚本中&#xff0c;以添加新的任务、配置或其他与构…

第八篇:深入探索操作系统架构:从基础到前沿

深入探索操作系统架构&#xff1a;从基础到前沿 1 引言 在当今这个高速发展的数字时代&#xff0c;操作系统无疑是计算机科学领域的基石之一。它不仅是计算机硬件与最终用户之间的桥梁&#xff0c;更是实现高效计算和资源管理的关键。操作系统的架构&#xff0c;即其内部结构和…

51单片机入门:蜂鸣器

蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件&#xff0c;常用来产生设备的按键音、报警音等提示信号。 蜂鸣器的种类 1、从结构上&#xff1a;压电式蜂鸣器和电磁式蜂鸣器。 压电式蜂鸣器&#xff1a;通过压电陶瓷的压电效应原理工作的。当加有交变电压时&#xf…

企业微信主体能不能修改?

企业微信变更主体有什么作用&#xff1f;当我们的企业因为各种原因需要注销或已经注销&#xff0c;或者运营变更等情况&#xff0c;企业微信无法继续使用原主体继续使用时&#xff0c;可以申请企业主体变更&#xff0c;变更为新的主体。企业微信变更主体的条件有哪些&#xff1…

C 深入指针(2)

目录 1 野指针 1.1 成因 1.2 如何规避野指针 2 assert 断言 2.1 用法 2.2 assert 的优点 2.1 assert 的缺点 3 小注解 3.1 Debug 和 Release 1 野指针 【概念】&#xff1a; 野指针就是指针指向的位置是不可知的&#xff08;随机的、不正确的、没有明确限制的&#…

CSS Web服务器、2D、动画和3D转换

Web服务器 我们自己写的网站只能自己访问浏览&#xff0c;但是如果想让其他人也浏览&#xff0c;可以将它放到服务器上。 什么是Web服务器 服务器(我们也会称之为主机)是提供计算服务的设备&#xff0c;它也是一台计算机。在网络环境下&#xff0c;根据服务器提供的服务类型不…

初识C语言——第十三天

关键字2&#xff1a; static 修饰局部变量&#xff0c;改变了局部变量的生命周期&#xff08;本质上是改变了变量的存储类型&#xff09; static修饰全局变量&#xff0c;使得这个全局变量只能在自己所在的源文件&#xff08;.c)内部可以使用&#xff0c;其他源文件不能使用 …

Partisia Blockchain 生态zk跨链DEX上线,加密资产将无缝转移

在 5 月 1 日&#xff0c;由 Partisia Blockchain 与 zkCross 创建合作推出的 Partisia zkCrossDEX 在 Partisia Blockchain 生态正式上线。Partisia zkCrossDEX 是 Partisia Blockchain 上重要的互操作枢纽&#xff0c;其融合了 zkCross 的 zk 技术跨链互操作方案&#xff0c;…

linux上Redis安装使用

环境centOS8 redis是缓存数据库&#xff0c;主要是用于在内存中存储数据&#xff0c;内存的读写很快&#xff0c;加快系统读写数据库的速度 一、Linux 安装 Redis 1. 下载Redis 官网下载Downloads - Redis 历史版本Index of /releases/ 本文中安装的版本为&#xff1a;h…

小红书图片文案编辑生成图文工具

在小红书上&#xff0c;种草图文的变现通常涉及以下几个步骤&#xff1a; 1. **建立影响力**&#xff1a;首先&#xff0c;你需要通过持续发布高质量的内容&#xff0c;积累粉丝和影响力。你的内容应该围绕特定的领域或主题&#xff0c;比如时尚、美妆、美食或旅行&#xff0c…

KUKA机器人KR3 R540维护保养——涂润滑脂

KUKA机器人在保养时少不了润滑脂&#xff0c;不同型号的机器人需要的润滑脂类型也不一样&#xff0c;保养时注意选用合适的润滑脂。本篇文章以KUKA机器人KR3 R540为例&#xff0c;在轴盖板 A2、A3、A5 的内侧涂上润滑脂。 一、涂润滑脂的作用 拆开机器人一个轴的盖板&am…

Docker私有仓库与Harbor部署使用

目录 一、本地私有仓库 1. 下载registry镜像 2. 在daemon.json文件中添加私有镜像仓库地址 ​编辑 3. 运行registry容器 4. Docker容器的重启策略如下 5. 为镜像打标签 6. 上传到私有仓库 7. 列出私有仓库的所有镜像 8. 列出私有仓库的centos镜像有哪些tag 9. 先删…