【AI插件开发】Notepad++ AI插件开发实践:支持配置界面

一、引用

此前的系列文章已基本完成了Notepad++的AI插件的功能开发,但是此前使用的配置为JSON配置文件,不支持界面配置。

本章在此基础上集成支持配置界面,这样不需要手工修改配置文件,直接在界面上操作,方便快捷。

:项目已开源、镜像,欢迎StartFork使用和指正。

二、配置界面设计

在插件的菜单栏中支持参数配置,用户点击该菜单时弹出配置对话框,在该配置界面对配置进行增删改查。

{"platform": "infini","timeout": 90,"platforms": {"infini": {"enable_ssl": true,"base_url": "cloud.infini-ai.com","authorization": {"type": "Bearer","data": "sk-xxx"},"model_name": "deepseek-r1-distill-qwen-32b","models": [ "deepseek-r1-distill-qwen-32b", "deepseek-r1", "deepseek-v3" ],"generate_endpoint": {"method": "post","api": "/maas/v1/completions","prompt": ""},"chat_endpoint": {"method": "post","api": "/maas/v1/chat/completions","prompt": ""},"models_endpoint": {}}}
}

根据现有的配置文件格式,对配置界面分为两块区域,区域一配置插件相关的参数,区域二设计一个AI平台的下拉列表框,支持AI平台相关的参数。

考虑到AI平台有多个接口(虽然现在只用了对话接口),因此接口部分使用表单,但表单不方便修改,因此需要新增一个字段编辑对话框,双击列表行时支持编辑列表行。

这样,界面设计差不多这样了。因为使用原生的Windows编程,因此需要花费较多的时间处理界面、事件,对Windows接口也是半生不熟,一边做一边查,所以还是比较费时间的,整体功能流程如下:

选择平台
添加/删除模型
修改接口参数
确认保存
取消
用户点击参数配置菜单
弹出配置对话框
初始化控件/加载配置
用户操作
加载平台配置
更新模型列表
打开字段编辑对话框
保存字段修改
写入配置文件
关闭对话框
流程结束

先看下效果图:
配置界面

三、参数配置界面

1. 创建对话框资源

  • 先在插件的资源文件中新建一个ID为IDD_DIALOG_PLUG_CONFIG的对话框,设计菜单界面,如下:
    在这里插入图片描述

2. 新建一个类,关联该对话框资源

PluginConfigDlg::PluginConfigDlg(HINSTANCE hInstance, Scintilla::PluginConfig& plugConf) : m_hInstance(hInstance), m_plugConfig(plugConf)
{// 创建无模式对话框m_hDlg = CreateDialogParam(m_hInstance,MAKEINTRESOURCE(IDD_DIALOG_PLUG_CONFIG),nullptr,DlgProc,reinterpret_cast<LPARAM>(this));if (m_hDlg) {InitControls();LoadConfig();}
}

在构造函数中创建对话框,关联对话框资源ID,并指定消息处理函数为DlgProc

INT_PTR CALLBACK PluginConfigDlg::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {if (uMsg == WM_INITDIALOG) {// 关联类实例指针到窗口SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);}// 获取类实例指针PluginConfigDlg* pThis = reinterpret_cast<PluginConfigDlg*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));if (pThis) {return pThis->RealDlgProc(hDlg, uMsg, wParam, lParam);}return FALSE;
}

DlgProc中处理WM_INITDIALOG消息,关联类实例指针到窗口,并将事件透传到类的实际消息处理函数RealDlgProcRealDlgProc可以便捷地访问操作类对象成员变量及函数。

3. 初始化界面

因为界面部分控件是下拉组合框和列表,因此需先初始化该部分,主要是初始化下拉列表数据、表单头,方便后续直接使用:

void PluginConfigDlg::InitControls() 
{// 授权类型HWND hWnd = GetDlgItem(m_hDlg, IDC_COMBO_AUTH_TYPE);SendMessage(hWnd, CB_RESETCONTENT, 0, 0);SendMessageW(hWnd, CB_ADDSTRING, 0, (LPARAM)L"无");SendMessageW(hWnd, CB_ADDSTRING, 0, (LPARAM)L"Basic");SendMessageW(hWnd, CB_ADDSTRING, 0, (LPARAM)L"Bearer");SendMessageW(hWnd, CB_ADDSTRING, 0, (LPARAM)L"ApiKey");// 接口列表HWND hList = GetDlgItem(m_hDlg, IDC_LIST_ENDPOINT);// 1. 设置基础样式(必须包含 LVS_REPORT)SetWindowLongW(hList, GWL_STYLE, GetWindowLongW(hList, GWL_STYLE) | LVS_REPORT |      // 报表视图LVS_SINGLESEL     // 禁止多选);// 2. 配置扩展样式ListView_SetExtendedListViewStyle(hList, LVS_EX_GRIDLINES |    // 显示网格线LVS_EX_FULLROWSELECT  // 整行选中);// 3. 初始化列头LVCOLUMNW lvc = {0};lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;// 批量添加列const struct {int width;const wchar_t* title;} columns[] = {{50, L"名称"},{50, L"方法"},{300, L"接口"},{150, L"参数"}};for (size_t i = 0; i < _countof(columns); ++i) {lvc.fmt = LVCFMT_CENTER;lvc.cx = columns[i].width;lvc.pszText = const_cast<LPWSTR>(columns[i].title);ListView_InsertColumn(hList, i, &lvc);}
}

然后是将配置文件中的内容在界面上显示,设置超时、设置当前平台:

bool PluginConfigDlg::LoadConfig()
{SetDlgItemTextA(m_hDlg, IDC_EDIT_TIMEOUT, std::to_string(m_plugConfig.timeout).c_str());int nSel = 0;HWND hWnd = GetDlgItem(m_hDlg, IDC_COMBO_PLATFORM);SendMessage(hWnd, CB_RESETCONTENT, 0, 0);int nIdx = 0;for (auto& p : m_plugConfig.platforms){SendMessageA(hWnd, CB_ADDSTRING, 0, (LPARAM)p.first.c_str());if (!Scintilla::String::icasecompare(p.first, m_plugConfig.platform)){nSel = nIdx;}nIdx++;}SendMessage(hWnd, CB_SETCURSEL, (WPARAM)nSel, 0);auto& platform = m_plugConfig.Platform();Load(platform);return true;
}

其中平台配置信息只显示单个,即当前选中的平台,启动时显示当前配置的平台信息,然后调用Load函数加载该平台信息配置。考虑到配置需要支持增删改查,因此通过平台组合框下拉列表可以切换到不同平台:

void PluginConfigDlg::Load(const Scintilla::PlatformConfig& platform)
{// SSLCheckDlgButton(m_hDlg, IDC_CHECK_SSL, platform.enable_ssl ? BST_CHECKED : BST_UNCHECKED);// 授权类型HWND hWnd = GetDlgItem(m_hDlg, IDC_COMBO_AUTH_TYPE);SendMessage(hWnd, CB_SETCURSEL, (WPARAM)(int)platform.authorization.eAuthType, 0);// 授权数据SetDlgItemTextA(m_hDlg, IDC_EDIT_AUTH_DATA, platform.authorization.auth_data.c_str());// 根地址SetDlgItemTextA(m_hDlg, IDC_EDIT_ROOT_URL, platform.base_url.c_str());// 模型名称int nSel = 0;int nIdx = 0;hWnd = GetDlgItem(m_hDlg, IDC_COMBO_MODEL_NAME);SendMessage(hWnd, CB_RESETCONTENT, 0, 0);for (auto& m : platform.models){SendMessageA(hWnd, CB_ADDSTRING, 0, (LPARAM)m.c_str());if (m == platform.model_name){nSel = nIdx;}nIdx++;}SendMessage(hWnd, CB_SETCURSEL, (WPARAM)nSel, 0);hWnd = GetDlgItem(m_hDlg, IDC_LIST_ENDPOINT);ListView_DeleteAllItems(hWnd);if (!platform.base_url.empty()){auto pE = &platform.chat_endpoint;ListViewAddRow(hWnd, { "对话", pE->method, pE->api, pE->prompt});pE = &platform.generate_endpoint;ListViewAddRow(hWnd, { "生成", pE->method, pE->api, pE->prompt});pE = &platform.models_endpoint;ListViewAddRow(hWnd, { "模型", pE->method, pE->api, pE->prompt});}
}

4. 切换平台

处理平台切换事件,并显示切换后平台信息

#define OnDlgItemEvent(nItemId, nEventId, fnCall) if(LOWORD(wParam) == nItemId && HIWORD(wParam) == nEventId) { fnCall(); return TRUE; }
INT_PTR PluginConfigDlg::RealDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{switch (uMsg) {case WM_CLOSE:DestroyWindow(m_hDlg);return TRUE;case WM_INITDIALOG:return TRUE;case WM_COMMAND:{OnDlgItemEvent(IDC_COMBO_PLATFORM, CBN_SELCHANGE, OnPlatformChange);OnDlgItemEvent(IDC_COMBO_MODEL_NAME, CBN_SELCHANGE, OnModelChange);OnDlgItemEvent(IDC_BUTTON_MODEL_SAVE, BN_CLICKED, OnSaveMode);OnDlgItemEvent(IDC_BUTTON_MODEL_DEL, BN_CLICKED, OnRemoveModel);OnDlgItemEvent(IDC_BUTTON_PLATFORM_SAVE, BN_CLICKED, OnSavePlatform);OnDlgItemEvent(IDC_BUTTON_PLATFORM_DEL, BN_CLICKED, OnRemovePlatform);OnDlgItemEvent(IDCANCEL, BN_CLICKED, LoadConfig);OnDlgItemEvent(IDOK, BN_CLICKED, SaveConfig);}break;case WM_NOTIFY:LPNMITEMACTIVATE pNmItem = (LPNMITEMACTIVATE)lParam;if (pNmItem->hdr.idFrom == IDC_LIST_ENDPOINT &&  pNmItem->hdr.code == NM_DBLCLK) {OnEndpointListViewDBClick(pNmItem);return TRUE;}break;}return FALSE;
}

在窗口事件处理函数中,处理WM_COMMAND消息,根据消息参数识别出控件对象和消息类型,处理平台切换是OnDlgItemEvent(IDC_COMBO_PLATFORM, CBN_SELCHANGE, OnPlatformChange),实现内容在函数OnPlatformChange中:

void PluginConfigDlg::OnPlatformChange()
{std::string name;if (!GetComboSelectedText(GetDlgItem(m_hDlg, IDC_COMBO_PLATFORM), name) || name.empty()){return;}auto e = m_plugConfig.platforms.find(name);if (e == m_plugConfig.platforms.end()){return;}Load(e->second);
}

获取ComboBox的当前选中项,注意:不能取当前控件的文本即GetWindowText,否则取到的是选中前的内容。然后调用Load函数加载切换后的AI平台配置信息。

5. 模型删除

void PluginConfigDlg::OnRemoveModel()
{HWND hCombo = GetDlgItem(m_hDlg, IDC_COMBO_MODEL_NAME);if (hCombo == nullptr){return;}auto name = String::Trim(GetComboSelectedText(IDC_COMBO_MODEL_NAME));if (name.empty()){ShowConfigError("请输入或选择当前模型名称");return;}auto nRet = ShowMsgBox(String::Format("是否要删除模型【%s】配置?", name.c_str()), "删除确认", MB_YESNO | MB_ICONQUESTION);if (nRet != IDYES){return;}SendMessageA(hCombo, CB_DELETESTRING, 0, (LPARAM)name.c_str());
}

删除当前选中项的模型名称,仅是从列表中删除,不是从对象的内存中删除,后续通过保存平台配置时更新删除后的列表。

6. 模型添加

void PluginConfigDlg::OnSaveMode()
{HWND hCombo = GetDlgItem(m_hDlg, IDC_COMBO_MODEL_NAME);if (hCombo == nullptr){return;}auto name = String::Trim(GetComboSelectedText(IDC_COMBO_MODEL_NAME));if (name.empty()){ShowConfigError("请输入或选择当前模型名称");return;}SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)name.c_str());
}

删除当前选中项的模型名称,和删除类似,仅是从列表中删除。

7.删除平台配置

void PluginConfigDlg::OnRemovePlatform()
{HWND hPlatform = GetDlgItem(m_hDlg, IDC_COMBO_PLATFORM);if (hPlatform == nullptr){return;}std::string name;auto pPlat = GetCurSelPlatform(name);if (!pPlat){ShowConfigError("请选择当前平台名称");return;}auto nRet = ShowMsgBox(String::Format("是否要删除平台【%s】配置?", name.c_str()), "删除确认", MB_YESNO | MB_ICONQUESTION);if (nRet != IDYES){return;}SendMessageA(hPlatform, CB_DELETESTRING, 0, (LPARAM)name.c_str());m_plugConfig.platforms.erase(name);// 清空数据Load(PlatformConfig());
}

获取当前选中的平台配置,然后从列表和内存中删除该平台配置,删除后清空平台配置信息(通过加载一个空对象实现),此外为防止误删,删除前做了弹框确认。

8.修改或新增平台配置

bool PluginConfigDlg::OnSavePlatform()
{HWND hPlatform = GetDlgItem(m_hDlg, IDC_COMBO_PLATFORM);if (hPlatform == nullptr){return false;}std::string name;auto pPlat = GetCurSelPlatform(name);if (name.empty()){ShowConfigError("请输入或选择当前平台名称");return false;}Scintilla::PlatformConfig plat;if (!Save(plat)){return false;}if (pPlat == nullptr){// 新增SendMessageA(hPlatform, CB_ADDSTRING, 0, (LPARAM)name.c_str());m_plugConfig.platforms[name] = plat;}else{*pPlat = plat;}m_plugConfig.Save("");return true;
}

先把界面上平台信息配置保存到一个临时变量中(防止保存了部分就返回),然后根据是否已存在该名称的平台配置决定是新增还是更新信息。

9. 响应接口列表行双击事件

INT_PTR PluginConfigDlg::RealDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{switch (uMsg) {case WM_NOTIFY:LPNMITEMACTIVATE pNmItem = (LPNMITEMACTIVATE)lParam;if (pNmItem->hdr.idFrom == IDC_LIST_ENDPOINT &&  pNmItem->hdr.code == NM_DBLCLK) {OnEndpointListViewDBClick(pNmItem);return TRUE;}break;}return FALSE;
}

处理窗口的WM_NOTIFY消息,然后根据控件ID和事件类型识别出是列表双击事件,然后列表双击编辑函数OnEndpointListViewDBClick

四、字段编辑界面

字段编辑框设计为一个通用的编辑窗口,提供一个字段组合,然后界面显示并支持编辑字段信息。
步骤也是和创建配置对话框差不多,不过这里创建的是一个模态对话框,不使用Show显示,而是使用DoModal模态对话框显示。

1. 调用编辑窗口更新字段

void PluginConfigDlg::OnEndpointListViewDBClick(LPNMITEMACTIVATE& pNmItem)
{if (pNmItem == nullptr){return;}int nRow = pNmItem->iItem;if (nRow < 0){return;}std::map<std::string, std::string> fields;if (ListViewGetRow(GetDlgItem(m_hDlg, IDC_LIST_ENDPOINT), nRow, fields) <= 0){return;}FieldEditDlg dlg(m_hInstance, m_hDlg);for (auto& [k, v] : fields){dlg.m_mapField[k] = { v };}dlg.m_mapField["方法"].options = { "post", "get" };dlg.m_mapField["方法"].type = FieldEditDlg::FieldType::Combo;dlg.m_strTitle = "接口参数设置";dlg.m_nLabelWidth = 40;if (dlg.DoModal() != IDOK){return;}std::vector<std::string> vs;std::vector<std::string> names = { "名称", "方法", "接口", "参数" };for (auto& name : names){auto e = dlg.m_mapField.find(name);if (e == dlg.m_mapField.end()){return;}vs.push_back(e->second.val);}ListViewSetRow(GetDlgItem(m_hDlg, IDC_LIST_ENDPOINT), nRow, vs);
}

这里使用map存储字段,感觉使用vector更合适,有序且字段存取方便,后续改一下。

2. 创建模态编辑窗口

#pragma once
#include <windows.h>
#include "PluginConf.h"
#include <commctrl.h>namespace Ui
{class Util{public:// 局长显示窗口static void Show(HWND hWnd, bool bShow, HWND hParent = nullptr);static std::string GetText(HWND hWnd);};
}class FieldEditDlg
{
public:enum class FieldType{Edit,Combo,};struct Field{std::string val;std::vector<std::string> options;FieldType type = FieldType::Edit;bool readonly = false;};FieldEditDlg(HINSTANCE hInstance, HWND hParent);~FieldEditDlg();static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);INT_PTR DoModal();private:INT_PTR RealDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);void CreateDynamicControls();void OnInitDialog();void OnSave();public:std::map<std::string, Field> m_mapField;int m_nLabelWidth = 100;int m_nBoxWidth = 300;std::string m_strTitle = "字段设置";private:HINSTANCE m_hInstance = nullptr;HWND m_hDlg = nullptr;HWND m_hParent = nullptr;HFONT m_hFont = nullptr;std::map<HWND, std::string> m_hwndMap;
};

注意,这里不在构造函数中初始化创建窗口,而是在DoModal中创建,并等待窗口结束:

INT_PTR FieldEditDlg::DoModal()
{// 创建模态对话框(需提前定义对话框模板ID,假设为IDD_FIELD_EDIT_DLG)return DialogBoxParam(m_hInstance, MAKEINTRESOURCE(IDD_DIALOG_EDIT_FIELD),m_hParent,FieldEditDlg::DlgProc,reinterpret_cast<LPARAM>(this));
}// 对话框消息处理
INT_PTR CALLBACK FieldEditDlg::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{if (uMsg == WM_INITDIALOG) {// 关联类实例指针到窗口SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);auto* pThis = reinterpret_cast<FieldEditDlg*>(lParam);pThis->m_hDlg = hDlg;pThis->OnInitDialog();Ui::Util::Show(hDlg, true);return TRUE;}// 获取类实例指针auto pThis = reinterpret_cast<FieldEditDlg*>(GetWindowLongPtr(hDlg, GWLP_USERDATA));if (pThis) {return pThis->RealDlgProc(hDlg, uMsg, wParam, lParam);}return FALSE;
}INT_PTR FieldEditDlg::RealDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{switch (uMsg) {case WM_INITDIALOG: {SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)this);OnInitDialog();return TRUE;}case WM_COMMAND: {int wmId = LOWORD(wParam);int wmEvent = HIWORD(wParam);if (wmId == IDOK && wmEvent == BN_CLICKED) {OnSave();EndDialog(m_hDlg, IDOK);return TRUE;} else if (wmId == IDCANCEL && wmEvent == BN_CLICKED) {EndDialog(m_hDlg, IDCANCEL);return TRUE;}break;}case WM_CLOSE:EndDialog(m_hDlg, IDCLOSE);return TRUE;}return FALSE;
}void FieldEditDlg::OnInitDialog()
{if(!m_mapField.empty()) CreateDynamicControls();SetWindowTextA(m_hDlg, m_strTitle.c_str());
}

3. 更新保存数据

void FieldEditDlg::OnSave()
{for (auto& [k, v] : m_hwndMap){m_mapField[v].val = Ui::Util::GetText(k);}
}

5. 总结说明

这一篇文章主要介绍了关于配置的两个对话框的实现,完成了手工编辑JSON配置文件到界面快捷配置的革命转换,本文主要涉及的技术要点如下:

核心架构设计
对话框资源系统
配置数据管理
动态控件引擎
事件处理中枢
Windows API创建对话框
类实例与窗口绑定
消息循环处理
JSON结构内存映射
平台配置对象树
双向数据同步机制
智能列表视图
动态组合框
多态控件渲染
WM_COMMAND处理
WM_NOTIFY响应
异步操作队列
关键技术实现
核心创新点
原生窗口性能优化
多层级配置继承
零拷贝数据交换
字段类型自适配
B1,B2,B3

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

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

相关文章

Android12 ServiceManager::addService源码解读

源码 Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {auto ctx mAccess->getCallingContext();// apps cannot add servicesif (multiuser_get_app_id(ctx.uid) >…

第十四节:实战场景-何实现全局状态管理?

React.createElement调用示例 Babel插件对JSX的转换逻辑 React 全局状态管理实战与 JSX 转换原理深度解析 一、React 全局状态管理实现方案 1. Context API useReducer 方案&#xff08;轻量级首选&#xff09; // 创建全局 Context 对象 const GlobalContext createConte…

第四十八篇 电信行业数仓建设实战指南:从架构设计到场景落地

目录 一、云原生架构设计实战1.1 计算存储分离架构搭建1.2 实时离线融合方案 二、维度建模深度解析2.1 电信业务建模方法论2.2 典型模型设计示例 三、ETL流程优化实践3.1 增量同步技术选型3.2 数据清洗规范 四、核心场景实现方案4.1 用户流失预警模型 五、数据治理实施指南5.1 …

2025年山东燃气瓶装送气工考试真题练习

燃气瓶装送气工考试真题练习 单选题 1、液化石油气主要成分是&#xff08; &#xff09;。 A. 甲烷 B. 丙烷、丁烷 C. 一氧化碳和氢气 答案&#xff1a;B 2、燃气钢瓶搬运过程中&#xff0c;正确的做法是&#xff08; &#xff09;。 A. 滚动钢瓶 B. 踢钢瓶 C. 轻拿轻…

《AI大模型应知应会100篇》第24篇:限定输出格式:如何让AI回答更加结构化

第24篇&#xff1a;限定输出格式&#xff1a;如何让AI回答更加结构化 摘要 在日常使用AI的过程中&#xff0c;我们经常希望得到的不仅仅是“正确”的答案&#xff0c;更是一个清晰、规范、易于处理的回答。无论是生成数据分析报告、代码片段&#xff0c;还是教学内容&#xff…

【MySQL】数据库和表的操作详解

目录 一、数据库&#xff1a; 1、查看数据库&#xff1a; 2、创建数据库&#xff1a; 3、删除数据库&#xff1a; 4、数据库的编码问题&#xff1a; 5、校验规则对数据库的影响&#xff1a; 6、修改数据库&#xff1a; 7、库的备份与恢复&#xff1a; 8、查看链接情况…

Docker--Docker镜像原理

docker 是操作系统层的虚拟化&#xff0c;所以 docker 镜像的本质是在模拟操作系统。 联合文件系统&#xff08;UnionFS&#xff09; 联合文件系统&#xff08;UnionFS&#xff09; 是Docker镜像实现分层存储的核心技术&#xff0c;它通过将多个只读层&#xff08;Image Laye…

双层Key缓存

双层 Key 缓存是一种针对 缓存击穿 和 雪崩问题 的优化方案&#xff0c;其核心思想是通过 主备双缓存 的机制&#xff0c;确保在热点数据过期时仍能提供可用服务&#xff0c;同时降低对数据库的瞬时压力。以下是其核心原理、实现细节及适用场景的深度解析&#xff1a; 一、核心…

力扣每日打卡 2176. 统计数组中相等且可以被整除的数对(简单)

力扣 2176. 统计数组中相等且可以被整除的数对 简单 前言一、题目内容二、解题方法1. 暴力解法2.官方题解官方也是暴力解法 前言 这是刷算法题的第十三天&#xff0c;用到的语言是JS 题目&#xff1a;力扣 2176. 统计数组中相等且可以被整除的数对(简单) 一、题目内容 给你一…

云服务器和物理服务器

服务器&#xff0c;作为互联网世界中数据存储与处理的关键枢纽&#xff0c;其重要性不言而喻。在众多服务器类型中&#xff0c;云服务器和物理服务器占据了主导地位&#xff0c;它们各自有着独特的特点和应用场景。咱们就来深入探讨一下这两者的区别。

Kubernetes Pod 调度策略:从基础到进阶

文章目录 环境Kubernetes 部署Kubernetes Pod 调度策略Kubernetes Pod 调度策略对照表调度流程经历阶段案例展示生成yaml文件默认调度节点选择器为节点添加标签编写 Deployment 配置文件应用资源并查看调度结果 Node Affinity&#xff08;节点亲和性&#xff09;为节点添加标签…

SQLite、MySQL、SQL Server、Oracle 和 PostgreSQL 五种数据库的区别

以下是 SQLite、MySQL、SQL Server、Oracle 和 PostgreSQL 五种主流关系型数据库管理系统(RDBMS)的区别,从多个维度进行对比: 1. 架构与部署 SQLite(Structured Query Language Lite‌): 嵌入式数据库,无服务器架构。数据库存储在一个单一的磁盘文件中。部署简单,适合轻量…

电路安全智控系统与主机安全防护系统主要功能是什么

电路安全智控系统被称为电路安全用电控制系统。电路安全智控系统具备一系列强大且实用的功能。电路安全智控系统能够对总电压、总电流、总功率、总电能&#xff0c;以及各分路的电压、电流、功率、电能和功率因素等进行全方位的监控。在大型工厂的电力分配中&#xff0c;通过对…

使用Lean 4和C#进行数学定理证明与逻辑推理

步骤1&#xff1a;安装与配置环境 安装Lean 4 访问Lean官网或GitHub仓库&#xff0c;按照指南安装Lean 4及配套工具链&#xff08;如VS Code扩展&#xff09;。 设置C#开发环境 安装.NET SDK及IDE&#xff08;如Visual Studio或Rider&#xff09;&#xff0c;确保C#开发环境正…

八股文---MySQl(3)

目录 12.事务的特性是什么&#xff1f;可以详细说一下吗&#xff1f; 回答 13并发事务带来哪些问题&#xff1f;怎么解决这些问题呢&#xff1f;MySQL的默认隔离级别是&#xff1f; 脏读&#xff1a;一个事务读到另外一个事务还没有提交的数据。 不可重复读&#xff1a;一个…

实验五 内存管理实验

实验五 内存管理实验 一、实验目的 1、了解操作系统动态分区存储管理过程和方法。 2、掌握动态分区存储管理的主要数据结构--空闲表区。 3、加深理解动态分区存储管理中内存的分配和回收。 4、掌握空闲区表中空闲区3种不同放置策略的基本思想和实现过程。 5、通过模拟程…

【MySQL】MySQL表的增删改查(CRUD) —— 上篇

目录 MySQL表的增删改查&#xff08;CRUD&#xff09; 1. 新增&#xff08;Create&#xff09;/插入数据 1.1 单行数据 全列插入 insert into 表名 values(值, 值......); 1.2 单行数据 指定列插入 1.3 多行数据 指定列插入 1.4 关于时间日期&#xff08;datetime&am…

【MATLAB代码例程】AOA与TOA结合的高精度平面地位,适用于四个基站的情况,附完整的代码

本代码实现了一种基于到达角(AOA) 和到达时间(TOA) 的混合定位算法,适用于二维平面内移动或静止目标的定位。通过4个基站的协同测量,结合最小二乘法和几何解算,能够有效估计目标位置,并支持噪声模拟、误差分析和可视化输出。适用于室内定位、无人机导航、工业监测等场景…

ModbusTCP 转 Profinet 主站网关

一、 功能概述 1.1 设备简介 本产品是 ModbusTCP 和 Profinet(M) 网关&#xff08;以下简称网关&#xff09;&#xff0c;使用数据映射 方式工作。 本产品在 ModbusTCP 侧作为 ModbusTCP 从站&#xff0c;接 PLC 、上位机、 wincc 屏 等&#xff1b;在 Profin…

《AI大模型应知应会100篇》第25篇:Few-shot与Zero-shot使用方法对比

第25篇&#xff1a;Few-shot与Zero-shot使用方法对比 摘要 在大语言模型的应用中&#xff0c;**Few-shot&#xff08;少样本&#xff09;和Zero-shot&#xff08;零样本&#xff09;**是两种核心的提示策略。它们各自适用于不同的场景&#xff0c;能够帮助用户在不进行额外训练…