Qt实现TabWidget通过addTab函数添加的页,页内控件自适应窗口大小

前言:因为项目的要求,需要把几个不同类型功能的界面集成在同一个窗口中,方便用户不切换窗口,也能快捷的操作不同类型的功能。我首先想到的是通过选项卡方式,让几个类别的功能界面通过不同选项卡进行切换,这在windows系统中也是很常见的一种方式。为了将不同类功能进行模块化,方便维护,通过自定义QWidget的子类,然后在主界面通过tabWidget.addTab()方法,将不同类功能添加到主界面。由于不同tab页内控件大小不一,为了让界面尽可能的美观,希望新增的tab页也能跟随窗口的大小随时改变尺寸。但是我使用通过遍历tab页内的控件,并在窗口改变事件(resizeEvent)中添加控件缩放操作,发现控件并没有达到我想要的缩放效果。经过不断尝试,发现问题出现在:新增的tab页中的布局控件无法轻易地获取真实大小,而且也无法触发tab页resizeEvent事件。知道了问题所在,经过不懈努力,终于找到了解决方法,于是有了本文。

一、主界面类

        主界面中包含一个TabWidget控件,并包含一个tab页。TabWidget通过addTab()方法将其他类型的功能模块加进来,既实现模块化,又能将多个功能集成到一个窗口。

以下为主界面内的主要代码

mainwindow.h

#include <QPlainTextEdit>
#include <QTextBrowser>
#include <QResizeEvent>
#include "tab2widght.h"
class MainWindow : public QMainWindow
{Q_OBJECT
public:MainWindow(QWidget *parent = nullptr);~MainWindow();
private:/** 初始化添加的TabWidget的尺寸 */void initialAddedTabWidgetSize(int tabIndex);// 需要添加的Tab2Widget模块Tab2Widget *tab2Widget = NULL;// 上次点击的tab页序号int preTabWidgetIndex = 0;// 控件根据窗口大小自动调整控件大小 @{
protected:void resizeEvent(QResizeEvent* event) override;
private:QMap<QWidget*, QRect> allWidgetMap;QMap<QLayout*, QRect> allLayoutMap;/** 窗口默认尺寸 */QSize windowOriginalSize;/** 查找所有子布局和子控件 */void findAllLayoutAndWidget(QObject *object);/** 检查文本是否超出了容器宽或高,方便修改文本大小 */bool checkTextOverflow(QWidget *widget);
// 控件根据窗口大小自动调整控件大小 @}
private slots:/** 当tabwidget切换tab时 **/void onTabClicked(int index);
};

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{// 实例化tab2模块类if(tab2Widget == NULL) {tab2Widget = new Tab2Widget();}// tab2模块添加到主界面的tabwidget中if (tab2Widget != NULL) {ui->tabWidget->addTab(tab2Widget, "Tab2");}// 注册tabwidget的页点击事件connect(ui->tabWidget, &QTabWidget::tabBarClicked, this, &MainWindow::onTabClicked);// 延时为了获取的控件初始大小是正确的QTimer::singleShot(200, this, [=] {findAllLayoutAndWidget(this);windowOriginalSize = this->size();});// 控件根据窗口大小自动调整控件大小 @}
}MainWindow::~MainWindow()
{if (tab2Widget != NULL) {tab2Widget->deleteLater();}
}/** 当tabview切换tab时 **/
void MainWindow::onTabClicked(int index) {// 根据tab的索引判断是否需要授权if (index == ui->tabWidget->indexOf(tab2Widget)) {// 首次设置新添加的tab的尺寸,只有切换到对应tab,才能拿到真实的layout尺寸数据,关键代码。initialAddedTabWidgetSize(index);}// 切换tabui->tabWidget->setCurrentIndex(index);preTabWidgetIndex = index;
}void MainWindow::initialAddedTabWidgetSize(int tabIndex)
{// 刷新切换后的tab的布局大小if (tabIndex < ui->tabWidget->count() && preTabWidgetIndex != tabIndex && ui->tabWidget->count() > 1) {// 调整通用参数选项卡页的尺寸if (tabIndex == ui->tabWidget->indexOf(tab2Widget) && tab2Widget != NULL) {if (tab2Widget->allWidgetMap.size() == 0 && tab2Widget->allLayoutMap.size() == 0) {tab2Widget->findAllLayoutAndWidget(tab2Widget);}// 设置通过addTab()函数新增的选项卡页内容的大小tab2Widget->setMinimumSize(ui->tabWidget->widget(0)->geometry().width(),ui->tabWidget->widget(0)->geometry().height());// tabwidget需要单独调用内部控件的大小if (tab2Widget != NULL) {tab2Widget->resizeAllComponents(ui->tabWidget->widget(0)->geometry());}}}
}// 控件根据窗口大小自动调整控件大小 @{
void MainWindow::resizeEvent(QResizeEvent* event)
{if (event == NULL) {return;}double scaleX = (double)event->size().width() / windowOriginalSize.width();double scaleY = (double)event->size().height() / windowOriginalSize.height();for (auto iter = allLayoutMap.begin(); iter != allLayoutMap.end(); ++iter) {QLayout* layout = iter.key();QRect originalGeometry = iter.value();QRect newGeometry(originalGeometry.x() * scaleX,originalGeometry.y() * scaleY,originalGeometry.width() * scaleX,originalGeometry.height() * scaleY);if (layout) {layout->setGeometry(newGeometry);}}for (auto iter = allWidgetMap.begin(); iter != allWidgetMap.end(); ++iter) {QWidget* widget = iter.key();if (widget) {QRect originalGeometry = iter.value();QRect newGeometry(originalGeometry.x() * scaleX,originalGeometry.y() * scaleY,originalGeometry.width() * scaleX,originalGeometry.height() * scaleY);widget->setGeometry(newGeometry);// 防止在多屏(选择扩展方式),移动到扩展屏时,tab页文字叠在一起的问题QTabWidget* tab = dynamic_cast<QTabWidget*>(widget);if (tab != NULL) {tab->setElideMode(Qt::ElideNone);}}// 调整带文本的控件的文本大小QPushButton* btn = dynamic_cast<QPushButton*>(widget);QLabel* label = dynamic_cast<QLabel*>(widget);QCheckBox* checkbox = dynamic_cast<QCheckBox*>(widget);QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(widget);QTextEdit* textEdit = dynamic_cast<QTextEdit*>(widget);QPlainTextEdit* plainTextEdit = dynamic_cast<QPlainTextEdit*>(widget);QTextBrowser* textBrowser = dynamic_cast<QTextBrowser*>(widget);// 条件为了防止在随意调节界面大小时,界面上出现黑块问题if (textBrowser == NULL && (btn != NULL || label != NULL || checkbox != NULL|| lineEdit != NULL || textEdit != NULL || plainTextEdit != NULL)) {int initialFontSize = widget->height() / 3;if (btn != NULL) {initialFontSize = widget->height() / 4;} else if (checkbox != NULL) {initialFontSize = widget->height() / 2;}// 使用Do...While语句是为了始终会设置字体大小一次,防止界面缩小后又放大后,字体还一直保持很小的问题do {QFont font = widget->font();int size = initialFontSize;bool needBreak = false;// 最小9if (size < 9) {size = 9;needBreak = true;}// 根据按钮的宽度/高度调整字体大小,可以根据默认控件的高度和字体的大小比率进行适当调整font.setPointSize(size);widget->setFont(font);initialFontSize--;if (needBreak) {break;}} while(checkTextOverflow(widget));}}if (windowOriginalSize.width() != -1) {// 设置通过addTab()函数新增的选项卡页内容的大小if (ui->tabWidget->count() > 1) {// 布局或控件拿到之后才能调整if (tab2Widget->allWidgetMap.size() > 0|| tab2Widget->allLayoutMap.size() > 0) {// tab2tab2Widget->setMinimumSize(ui->tabWidget->widget(0)->geometry().width(),ui->tabWidget->widget(0)->geometry().height());// tabwidget需要单独调用内部控件的大小if (tab2Widget != NULL) {tab2Widget->resizeAllComponents(ui->tabWidget->widget(0)->geometry());}}}}
}void MainWindow::findAllLayoutAndWidget(QObject *object) {QLayout *layout = qobject_cast<QLayout*>(object);QWidget *widget = qobject_cast<QWidget*>(object);if (layout) {if (layout->objectName() != "" && !allLayoutMap.contains(layout)) {allLayoutMap.insert(layout, layout->geometry());}for (int i = 0; i < layout->count(); ++i) {findAllLayoutAndWidget(layout->itemAt(i)->widget());}// 嵌套的选项卡,自适应尺寸放到对应类中,不在此类中调整} else if (widget && widget != tab2Widget) {if (widget != this && widget->objectName() != "" && !allWidgetMap.contains(widget)) {allWidgetMap.insert(widget, widget->geometry());}for (int i = 0; i < widget->children().size(); ++i) {findAllLayoutAndWidget(widget->children().at(i));}}
}bool MainWindow::checkTextOverflow(QWidget *widget) {if (widget == NULL) {return false;}QPushButton* btn = dynamic_cast<QPushButton*>(widget);QLabel* label = dynamic_cast<QLabel*>(widget);QCheckBox* checkbox = dynamic_cast<QCheckBox*>(widget);QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(widget);QTextEdit* textEdit = dynamic_cast<QTextEdit*>(widget);QPlainTextEdit* plainTextEdit = dynamic_cast<QPlainTextEdit*>(widget);// TODO 可增加其他可调节文本的控件,以下的text文本也要相应赋值控件文本// 获取按钮的字体和文本QString text = "";if (btn != NULL) {text = btn->text();} else if (label != NULL) {text = label->text();} else if (checkbox != NULL) {text = checkbox->text();} else if (lineEdit != NULL) {text = lineEdit->text();} else if (textEdit != NULL) {text = textEdit->toPlainText();} else if (plainTextEdit != NULL) {text = plainTextEdit->toPlainText();}if (text.isEmpty()) {return false;}QFont font = widget->font();QFontMetrics fm(font);// 计算文本的宽度和高度int textWidth = fm.horizontalAdvance(text);int textHeight = fm.height();// 获取按钮的尺寸(不包括边框)QSize buttonSize = widget->size();// 宽度多减去一些,为了防止有些时候按钮字体和左右边框切边buttonSize.rwidth() -= 8 * widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); // 减去边框宽度buttonSize.rheight() -= 2 * widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); // 减去边框高度// 检查是否溢出if (textWidth > buttonSize.width() || textHeight > buttonSize.height()) {return true;} else {return false;}
}// 控件根据窗口大小自动调整控件大小 @}

二、通过addTab()方法加入到主界面的类

相当于另一个模块,只是界面嵌套到了主界面,既方便用户操作,又能够模块化,方便维护。

tab2widget.h

class Tab2Widget : public QWidget
{Q_OBJECTpublic:explicit Tab2Widget(QWidget *parent = nullptr);~Tab2Widget();// 控件根据窗口大小自动调整控件大小 @{
public:void resizeAllComponents(QRect parentRect);QMap<QWidget*, QRect> allWidgetMap;QMap<QLayout*, QRect> allLayoutMap;/** 查找所有子布局和子控件 **/void findAllLayoutAndWidget(QObject *object);
private:/** 窗口默认尺寸 **/QSize windowOriginalSize = QSize(0,0);/** 检查文本是否超出了容器宽或高,方便修改文本大小 **/bool checkTextOverflow(QWidget *widget);// 控件根据窗口大小自动调整控件大小 @}
};

tab2widget.cpp

#include "tab2widget.h"
#include "ui_tab2widget.h"Tab2Widget::Tab2Widget(QWidget *parent): QWidget(parent), ui(new Ui::Tab2Widget)
{// 控件根据窗口大小自动调整控件大小 @{// 延时为了获取的控件初始大小是正确的QTimer::singleShot(200, this, [=] {windowOriginalSize = this->size();});// 控件根据窗口大小自动调整控件大小 @}
}Tab2Widget::~Tab2Widget() 
{
}// 控件根据窗口大小自动调整控件大小 @{
void Tab2Widget::resizeAllComponents(QRect parentRect)
{if (parentRect.width() == 0 || parentRect.height() == 0|| windowOriginalSize.width() == 0 || windowOriginalSize.height() == 0) {return;}double scaleX = (double)parentRect.width() / windowOriginalSize.width();double scaleY = (double)parentRect.height() / windowOriginalSize.height();for (auto iter = allLayoutMap.begin(); iter != allLayoutMap.end(); ++iter) {QLayout* layout = iter.key();QRect originalGeometry = iter.value();QRect newGeometry(originalGeometry.x() * scaleX,originalGeometry.y() * scaleY,originalGeometry.width() * scaleX,originalGeometry.height() * scaleY);if (layout) {layout->setGeometry(newGeometry);}}for (auto iter = allWidgetMap.begin(); iter != allWidgetMap.end(); ++iter) {QWidget* widget = iter.key();if (widget) {QRect originalGeometry = iter.value();QRect newGeometry(originalGeometry.x() * scaleX,originalGeometry.y() * scaleY,originalGeometry.width() * scaleX,originalGeometry.height() * scaleY);widget->setGeometry(newGeometry);}// 调整带文本的控件的文本大小QPushButton* btn = dynamic_cast<QPushButton*>(widget);QLabel* label = dynamic_cast<QLabel*>(widget);QCheckBox* checkbox = dynamic_cast<QCheckBox*>(widget);QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(widget);QTextEdit* textEdit = dynamic_cast<QTextEdit*>(widget);QPlainTextEdit* plainTextEdit = dynamic_cast<QPlainTextEdit*>(widget);QTextBrowser* textBrowser = dynamic_cast<QTextBrowser*>(widget);// 条件为了防止在随意调节界面大小时,界面上出现黑块问题if (textBrowser == NULL && (btn != NULL || label != NULL || checkbox != NULL|| lineEdit != NULL || textEdit != NULL || plainTextEdit != NULL)) {int initialFontSize = widget->height() / 3;if (btn != NULL) {initialFontSize = widget->height() / 4;} else if (checkbox != NULL) {initialFontSize = widget->height() / 2;}// 使用Do...While语句是为了始终会设置字体大小一次,防止界面缩小后又放大后,字体还一直保持很小的问题do {QFont font = widget->font();int size = initialFontSize;bool needBreak = false;// 最小9if (size < 9) {size = 9;needBreak = true;}// 根据按钮的宽度/高度调整字体大小,可以根据默认控件的高度和字体的大小比率进行适当调整font.setPointSize(size);widget->setFont(font);initialFontSize--;if (needBreak) {break;}} while(checkTextOverflow(widget));}}
}void Tab2Widget::findAllLayoutAndWidget(QObject *object) {QLayout *layout = qobject_cast<QLayout*>(object);QWidget *widget = qobject_cast<QWidget*>(object);if (layout) {if (layout->objectName() != "" && !allLayoutMap.contains(layout)) {allLayoutMap.insert(layout, layout->geometry());}for (int i = 0; i < layout->count(); ++i) {findAllLayoutAndWidget(layout->itemAt(i)->widget());}} else if (widget) {if (widget != this && widget->objectName() != "" && !allWidgetMap.contains(widget)) {allWidgetMap.insert(widget, widget->geometry());}for (int i = 0; i < widget->children().size(); ++i) {findAllLayoutAndWidget(widget->children().at(i));}}
}bool Tab2Widget::checkTextOverflow(QWidget *widget) {if (widget == NULL) {return false;}QPushButton* btn = dynamic_cast<QPushButton*>(widget);QLabel* label = dynamic_cast<QLabel*>(widget);QCheckBox* checkbox = dynamic_cast<QCheckBox*>(widget);QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(widget);QTextEdit* textEdit = dynamic_cast<QTextEdit*>(widget);QPlainTextEdit* plainTextEdit = dynamic_cast<QPlainTextEdit*>(widget);// TODO 可增加其他可调节文本的控件,以下的text文本也要相应赋值控件文本// 获取按钮的字体和文本QString text = "";if (btn != NULL) {text = btn->text();} else if (label != NULL) {text = label->text();} else if (checkbox != NULL) {text = checkbox->text();} else if (lineEdit != NULL) {text = lineEdit->text();} else if (textEdit != NULL) {text = textEdit->toPlainText();} else if (plainTextEdit != NULL) {text = plainTextEdit->toPlainText();}if (text.isEmpty()) {return false;}QFont font = widget->font();QFontMetrics fm(font);// 计算文本的宽度和高度int textWidth = fm.horizontalAdvance(text);int textHeight = fm.height();// 获取按钮的尺寸(不包括边框)QSize buttonSize = widget->size();// 宽度多减去一些,为了防止有些时候按钮字体和左右边框切边buttonSize.rwidth() -= 8 * widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); // 减去边框宽度buttonSize.rheight() -= 2 * widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); // 减去边框高度// 检查是否溢出if (textWidth > buttonSize.width() || textHeight > buttonSize.height()) {return true;} else {return false;}
}// 控件根据窗口大小自动调整控件大小 @}

注意:要实现界面自适应尺寸,控件的sizePolicy最好设置成Preferred或者Expanding,少用Fixed,否则会出现在缩小窗口尺寸的时候,由于控件最小尺寸设定死了,容易出现文字切边的问题。

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

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

相关文章

代码随想录算法训练营27天 | ​​56. 合并区间、738.单调递增的数字、968.监控二叉树(提高)

题目链接&#xff1a;56. 合并区间、738.单调递增的数字、968.监控二叉树 文章链接&#xff1a;代码随想录 贪心算法 1. 合并区间 &#xff08;待更新...&#xff09; class Solution { private:static bool cmp(const vector<int>& a, const vector<int>&…

从 H.264/H.265 到 H.266:RTSP播放器的跨代际演进

引言&#xff1a;H.266与实时视频的交汇点 视频编解码的发展历程&#xff0c;始终是 带宽效率与视觉体验的博弈。从 H.264 的普及&#xff0c;到 H.265/HEVC 的深化应用&#xff0c;每一次标准迭代&#xff0c;都在推动视频向更高分辨率、更高帧率、更复杂场景的应用迈进。而 …

oc-mirror plugin v2 错误could not establish the destination for the release i

openshift 4.19使用的镜像仓库为harbor. 运行disk to registry时出现下面的错误&#xff1a; 2025/08/19 17:51:13 [ERROR] : [Executor] [release collector] could not establish the destination for the release image 备注&#xff1a; 我没有账户&#xff0c;无法打开…

Android auncher3实现简单的负一屏功能

Android launcher3实现简单的负一屏功能 1.前言&#xff1a; 之前实现过Launcher3从凑提修改成单层&#xff0c;今天来讲解一下如何实现一个简单的负一屏功能&#xff0c;涉及的类如下&#xff0c;直接看代码。 2.NegativeScreenAdapter&#xff1a; package com.example.ne…

跨网闸数据库同步:在物理隔离中架起安全的数据桥梁

作者:DeepSeek-R1 | 日期:2025年8月17日 引言 在等保2.0和分级保护政策的要求下,高密级网络(如政务内网、金融核心网)必须与低密级网络(如互联网)物理隔离。但业务又要求数据跨网流动(如市民在线提交申请、分支机构数据回传)。如何解决这一矛盾?双向网闸与单向光闸成…

【Android】一文详解Android里的AOP编程

一文详解Android里的AOP编程 1. 基于 AspectJ&#xff08;编译期/打包期织入&#xff09; 思路&#xff1a;用 AspectJ 编译器在 编译阶段 或 Gradle Transform 阶段&#xff0c;把切面逻辑织入 class / bytecode。 特点&#xff1a; 能实现类似 Spring AOP 的注解切面&#…

AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年8月21日第167弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀4-5个和值&#xff0c;可以做到100-300注左右。(1)定位…

机器学习【十】neural network

系统梳理了机器学习与神经网络的基础知识&#xff0c;涵盖理论、核心概念及代码实践。理论部分包括线性模型&#xff08;向量表示、广义线性模型&#xff09;、分类与回归的区别、梯度下降&#xff08;批量/随机/小批量&#xff09;、激活函数&#xff08;Sigmoid、ReLU等&…

如何用算力魔方4060安装PaddleOCR MCP 服务器

在当今数字化快速发展的时代&#xff0c;OCR&#xff08;光学字符识别&#xff09;技术已经成为从图像中提取文本信息的重要工具。无论是在自动化办公、智能文档处理还是在内容创作领域&#xff0c;OCR 技术的应用都极大地提高了工作效率和准确性。本文将详细介绍如何利用算力魔…

Azure的迁移专业服务是怎么提供的

好的&#xff0c;这是一个非常实际的问题。Azure的迁移专业服务&#xff08;Professional Services for Migration&#xff09;并非一个单一的“产品”&#xff0c;而是一个由微软及其庞大的合作伙伴生态系统共同提供的、基于成熟方法论的综合服务框架。其提供方式可以概括为&a…

Seaborn数据可视化实战:Seaborn入门-环境搭建与基础操作

Seaborn环境搭建与配置 学习目标 本课程将指导学员如何在不同的操作系统&#xff08;Windows, macOS, Linux&#xff09;上安装Seaborn库&#xff0c;以及如何配置Python环境&#xff0c;包括使用Jupyter Notebook和Spyder等集成开发环境&#xff08;IDE&#xff09;的基本操作…

Windows下RabbitMQ完整安装指南

一、RabbitMQ 简介 RabbitMQ 是一款基于 Erlang 语言开发的开源消息队列中间件&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。其最初起源于金融系统&#xff0c;专为分布式系统中的消息存储与转发设计&#xff0c;在可靠性、扩展性和高可用性方面表现卓越…

thingsboard 通过Entities hierarchy部件实现左边菜单点击,右边的表格按左边的分类型进行过滤筛选数据源

在 ThingsBoard 中&#xff0c;要让“Entities hierarchy”部件&#xff08;左侧树形导航&#xff09;与右侧的数据表格实现联动——即点击左侧某个节点后&#xff0c;右侧表格立刻按该节点对应的实体类型/层级进行过滤——需要把“数据源别名&#xff08;Alias&#xff09; 仪…

【Ansible】核心概念解析:架构、清单管理与配置入门

本专栏文章持续更新&#xff0c;新增内容使用蓝色表示。对于系统管理员而言&#xff0c;手动管理每一台服务器不仅维护难度极大&#xff0c;而且即使经验丰富&#xff0c;也难免出现疏忽和错误。自动化技术能有效避免因手动管理系统和基础架构而产生的各类问题。其优点包括&…

rs-fMRI_两篇文章中分析方法的梳理(近乎翻译)

文章一文章信息APOE ε4 influences within and between network functional connectivity in posterior cortical atrophy and logopenic progressive aphasia2024美国梅奥诊所发表在Alzheimers Dement. 的文章。“APOE ε4等位基因对后皮质萎缩与进行性语言障碍型失语症的网络…

在互联网大厂的Java面试:谢飞机的搞笑历险记

在互联网大厂的Java面试&#xff1a;谢飞机的搞笑历险记 在一个阳光明媚的早上&#xff0c;我们的主角&#xff0c;程序员谢飞机&#xff0c;走进了一家著名的互联网大厂&#xff0c;准备迎接他人生中最严峻的挑战——Java面试。 第一轮&#xff1a;基础技术面试 面试官&#x…

微软AD国产化替换倒计时——不是选择题,而是生存题

一直以来&#xff0c;微软Active Directory&#xff08;AD&#xff09;作为企业身份管理和访问控制的核心组件&#xff0c;承担着用户认证、权限分配、资源目录管理等基础职能。然而&#xff0c;随着政策、合规与网络安全压力不断加剧&#xff0c;AD面临着前所未有的挑战&#…

MyBatis-Plus MetaObjectHandler的几个坑(主要是id字段)

1.背景 主要是要实现一个id字段的自增长&#xff0c;不依赖数据库的能力&#xff08;已避免后续换库的问题&#xff09;。姑且使用redis作为表的id分配器&#xff0c;因此使用MyBatis-Plus MetaObjectHandler对每个insert的id进行分配。 2.实施过程 以下是实现过程 1.实现MetaO…

Springboot 项目配置多数据源

Springboot 项目配置多数据源 基础环境 java8、springboot2.2.13、mybatis、mysql5.x、oracle 项目配置 1.application.yml spring:datasource:mysql1:username: abcpassword: 123456url: jdbc:mysql://127.0.0.1:3306/panda?useUnicodetrue&characterEncodingUTF-8&z…

STM32_0001 KEILMDK V5.36 编译一个STM32F103C8T6说core_cm3.h文件找不到以及编译器版本不匹配的解决办法

KEILMDK V5.36 编译一个STM32F103C8T6说core_cm3.h文件找不到的解决办法利用KEILMDK V5.36 编译一个STM32F103C8T6说core_cm3.h文件找不到。主要错误信息如下D:/stm32studio/Armmdk/Packs/Keil/STM32F1xx_DFP/2.4.1/Device/Include\stm32f10x.h(486): error: core_cm3.h file n…