当前位置: 首页 > news >正文

GUI07-学工具栏,懂MVC

MVC模式,是天底下编写GUI程序最为经典、实效的一种软件架构模式。当一个人学完菜单栏、开始学习工具栏时,就是他的一生中,最适合开始认识 MVC 模式的好时机之一。这节将安排您学习:

  1. Model-View-Controller 模式
  2. 如何创建工具栏以及如何于其上创建普通、多选、单选工具按钮
  3. 控件之间如何通过“空闲”事件实现状态统一

wxToolBar & MVC

0. 课堂视频

建议先看视频,再看文本教程。最后到 d2school 课堂做本课的学习强化练习。

GUI07-学工具栏,懂 MVC

1. 才不是题外话:MVC

MVC模式,是天底下编写GUI程序最为经典、实效的一种软件架构模式。当一个人学完菜单栏、开始学习工具栏时,就是他的一生中,最适合开始认识 MVC 模式的好时机之一。

MVC 是 “Model-View-Controller” 的简写:

  • Model / 模型:可认为就是我们在代码中定义的各种业务数据;
  • View / 视图:业务数据的展现形式,一种模型(数据)往往有多种展现形式;
  • Controller / 控制器: 用户通过控制器,以可定制的方式,获取数据模式,及指定形式展现(视图)。

程序员开发软件时,将代码按照 Model-View-Controller 加以分离,能让软件代码:

  1. 逻辑,意图更清晰
  2. 耦合更低
  3. 更可维护

MVC 比较适用于中大型软件代码的组织,我们前几节课所写的程序,看似简单,但其实, GUI软件没有小项目,是底层的库(比如 wxWidgets)默默地做出大量支持,包括 MVC 架构。

以前面课堂写的小例程为例:我们在窗体上鼠标位置下显示鼠标所在的坐标值,并且允许用户做如下控制:

  1. 是否显示坐标值,或仅显示一行提示文字;
  2. 文字以蓝或红哪种颜色显示。

则,至少有两个数据模型:

编号Model
M1表达坐标的两个整数:x, y
M2表达用户选中颜色的菜单项ID

以及,这两个数据,至少四种组合态的展现形式:

编号View (数据视图)
V1蓝色 + 显示坐标
V2红色 + 显示坐标
V3蓝色 + 不显示坐标(仅显示提示)
V4红色 + 不显示坐标(仅显示提示)

以下是实际运行时,显示的 V2 状态界面截图:
工具栏框架窗口运行效果
正如本课视频所讲到,View 并不仅仅包含业务数据的展现,用于实现和用户交互的 GUI 界面控制,比如我们已学的菜单、工具栏、状态栏等界面控件,也是 View 的组成。它们也会有和业务数据紧密关联的状态,比如菜单或工具图标的多选或单选状态。

以上归纳了 Model 和 View,那 Controller是什么呢?是控制流程。在我们所写的程序中,程序接收用户的输入(鼠标点击等),然后调用相应的事件函数,在事件函数中修改 Model 数据,最后引发视图刷新,包括业务视图刷新和人机交互界面中控件状态的改变,这整个过程即为 Controller。

初接触时,对 MVC 的三个最常见的误解为:

  • 误解一:以为 View的展现内容只能给看。正解:一个程序的输出可以是另一个程序的输入,此时可视为后者是前者的“用户”,同理,同一个程序的控制,也可能是来自另一个程序的输入。正因为 Views 不一定真实的人,所以在服务端程序中,特别是多层网络架构的软件系统中,MVC 的应用也广泛可见;

  • 误解二:以为人机交互过程中界面控件,就是 Controller 。正解:Controller 是接受用户(不一定是人,见上)的输入,然后引发(即动作的发起者)Model 被修改,并最终体现到 View 变化的每一个完整过程。

  • 误解三:以为只有业务数据的展现是 Views。正解:凡是需要展现给用户看的内容,基本都是 View,包括控件的状态。

2. 创建图标

在 Windows 系统下,应用程序工具栏按钮所需要的图标,需要使用 ICON 类型的图标,在 wxWidgets中,对应到 wxIcon 类。

2.1 从文件直接创建

可在程序运行时,通过读取指定路径(相对或绝对)读取指定的图标文件,以构造出一个wxIcon对象,示例代码:

wxIcon myIcon = wxIcon(wxT("路径/图标文件.ico"), wxBITMAP_TYPE_ICO, 16, 16);

注意,创建所得是普通的栈对象,非堆对象。

  • 入参1:图标的磁盘位置(包括路径和文件名,可使用绝对或相对路径);
  • 入参2:图标文件的类型(注意是 wxBITMAP_TYPE_ICO,而非 wxBITMAP_TYPE_ICON);
  • 入参3,4:两个整数,图标的长、宽(像素)。

在程序运行时读取外部文件以创建图标(或其它资源)——

  • 好处:可随时更换图标——不少应用程序支持 “换肤”,对于工具栏按钮来说,这是一种简便可行的方法;
  • 坏处:程序无法单独运行。比如你想把程序发给你的朋友,就得同时附上这些图标文件,并需确保路径正确。

2.2 从程序嵌入资源中创建

Windows 支持将一些资源,嵌入到可执行文件(.exe)“体内”,从而实现可执行文件可独立运行。

Code::Blocks 向导生成的 wxWidgets 项目中,已经自带 resource.rc 的文件,打开它,默认内容为:

aaaa ICON "wx/msw/std.ico"
#include "wx/msw/wx.rc"

默认包含的 wxWidgets 程序图标。其中,aaaa 为资源的名字,ICON 为资源类型,此处为图标,最后一列 “wx/msw/std.ico” 为图标在磁盘上的路径和名字(仅编译时需用到,运行时不再需要此路径)。

假设我们已经在当前项目下创建名为 icons 的子目录,并于其下准备好 about.ico、show_info.ico、blue_txt.ico、red_txt.ico、quit.ico 等图标,则可将 resouce.rc 内容修改成:

aaaa ICON "wx/msw/std.ico"about ICON  "icons/about.ico"
blue_txt ICON "icons/blue_txt.ico"
red_txt ICON "icons/red_txt.ico"
quit ICON "icons/quit.ico"
show_info ICON "icons/show_info.ico"#include "wx/msw/wx.rc"

这样,在编译后,相关图标资源就会内嵌在编译生成的可执行文件中,并可通过 wxIcon 的另一个构造函数读出并且成图标对象:

wxIcon icon = wxIcon(wxT("资源名"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16);

比如,创建 quit 图标:

wxIcon iconQuit = wxIcon(wxT("quit"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16);

本文后续代码均从内嵌资源创建图标。

3. 工具栏与工具栏按钮

3.1 创建工具栏

wxFrame::CreateToolBar() 用于创建一个空白的工具栏,该方法返回一个 wxToolBar * (指针)。

示例代码:

wxToolBar* tb = this->CreateToolBar(); // 创建工具栏

以上代码中的 this 工具栏所属的框架窗口,它也将负责该工具栏的生命周期(在框架窗口关闭前,将自释放上面创建工具栏堆对象)。

以下各种工具栏按钮,都需要由工具栏(wxToolBar)对象来创建。

3.2 普通工具按钮

使用工具栏对象,创建一个普通工具栏按钮,方法名为 “AddTool()”示例代码:

// 准备图标 (从内嵌资源中读取)
wxIcon iconQuit = wxIcon(wxT("quit"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16);// 创建带图标的普通工具按钮
tb->AddTool(idMenuQuit, _("Quit"), iconQuit, _("Quit the Application"));
  • 入参1:工具按钮要执行的命令对应的ID,通常来自已经创建的菜单项所绑定的事件ID。比如本例的 idMenuQuit 为 “Quit” 菜单项的ID,该命令用于退出整个应用程序;
  • 入参2:工具按钮的标签,默认状态下并不显示,复杂情况下,可设置为在图标底部或右侧显示按钮的名字;
  • 入参3:前面创建的图标对象(并非指针);
  • 入参4:鼠标在该工具按钮上浮动时出现的提示。

也可将构造图标对象和创建工具按钮合成一步:

tb->AddTool(idMenuQuit, _("Quit"), wxIcon(wxT("quit"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16)_("Quit the Application"));

实际还需要第三步,但当连续为同一个工具栏创建多个工具按钮时,仅需在最后调用一次:

tb->Realize(); // 真实展现,最后调用,只需一次

仅在调用该方法后,工具栏才真正地将新创建的各工具按钮展现出来。

3.3 复选工具按钮

使用方法为:AddCheckTool(),示例代码:

//复选工具按钮(是否显示鼠标坐标)
tb->AddCheckTool(idMenuShowMotionInfo, _("Show Info"),wxIcon(wxT("show_info"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16),wxNullBitmap, // 按钮变灰时显示的图标,由程序自动生成_("Show motion info or no")); // 提示

第四个入参用于指定,该复选按钮在不可用(disabled)状态下如何展现,除非你确实想为之提供一种特别的样子,否则,可如代码所示,指定使用代表空图片的对象 wxNullBitmap,系统将自动以 “灰度” 方式生成该状态的展现样式。

因为方法名已经是 “AddCheckTool ()”,故无需像普通的 “AddTool ()” 那样,再由入参指定按钮的类型,下面用于创建单选工具按钮的方法,也是如此。

3.4 单选工具按钮

使用方法为:AddRadioTool(),示例代码:

//单选工具按钮(两个:选择使用蓝色文本或使用红色文本)
tb->AddRadioTool(idMenuBlueText, _("Blue Text"),wxIcon(wxT("blue_txt"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16),wxNullBitmap, // 按钮变灰时显示的图标,由程序自动生成_("Set text blue")); // 提示tb->AddRadioTool(idMenuRedText, _("Red Text"),wxIcon(wxT("red_txt"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16),wxNullBitmap,_("Set text red"));

和复选按钮不同,单独一个单选按钮没有意义,通常需要接连创建一组,让用户从多个中选择一个(即为单选之意)。

同一工具栏上如有多组单选按钮,除了可考虑使用分隔(见下面小节)在视觉上加以分组之外,并不需要再额外在逻辑上分组,因为工具按钮通常对应到菜单项,而对菜单项,我们已经作了逻辑分组,详见 《第5节 玩转主菜单》。

3.5 分隔线

tb->AddSeparator(); // 分隔线

3.6 连续代码

以下是在示例项目中, wxToolBarFrame 构造函数尾部,创建工具栏及相应按钮、分隔线的代码片段:

    // 工具栏wxToolBar* tb = this->CreateToolBar(); // 创建工具栏wxIcon iconQuit = wxIcon(wxT("quit"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16);tb->AddTool(idMenuQuit, _("Quit"), iconQuit, _("Quit the Application"));tb->AddSeparator(); // 分隔线//复选工具按钮(是否显示鼠标坐标)tb->AddCheckTool(idMenuShowMotionInfo, _("Show Info"),wxIcon(wxT("show_info"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16),wxNullBitmap, // 按钮变灰时显示的图标,由程序自动生成_("Show motion info or no")); // 提示tb->AddSeparator(); // 分隔线// 单选工具按钮(两个)tb->AddRadioTool(idMenuBlueText, _("Blue Text"),wxIcon(wxT("blue_txt"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16),wxNullBitmap,_("Set text blue"));tb->AddRadioTool(idMenuRedText, _("Red Text"),wxIcon(wxT("red_txt"), wxBITMAP_TYPE_ICO_RESOURCE, 16, 16),wxNullBitmap,_("Set text red"));tb->Realize(); // 真实展现,最后调用,只需一次

4. 控件状态同步

为了使用方便,GUI 程序往往允许用户从不同控件入口发起操作,在本例,用户通过菜单栏或工具栏,都可以修改文字的颜色。窗体上输出的文字归属 View,但是菜单项或工具按钮的显示状态,也归于 View 范畴,它们的选中状态,必须保持同步。

举例:用户选中 “变帅” 菜单项,则 工具栏上 “变帅” 按钮也需要在操作后,变成“被按下”的状态;反过来也一样:用户按下 “变帅” 按钮,则同名的菜单项之前,必须打上勾。这还是复选控件,如果是单选控件,则需要的维护一组控件的状态,会更加啰嗦,容易出错。

因此,无论是大家比较熟悉的 Windows 或 苹果的 GUI 系统,还是 Linux 下的各种 GUI 桌面系统,或者是新兴的智能手机 Android 系统,或者是为浏览器前端开发提供支持各种前端库,基本都会提供一套机制,来辅助程序员实现界面组件的状态同步。

GUI 程序有一个共同点:它不可能很忙。有人说,我打开 Word,然后以平均每分钟 120 个汉字,接近 500 次按键的速度打字,这 Word 肯定忙坏了呀? 不是的,这点输入对现在的计算机和程序来说真是一点压力没有,程序仍然能在你的指法间隙间,“挤出”一大把无所事事的时间(不信你可以打开 Windows 任务管理器,看看你要能以怎样的打字速度,让 CPU 占用率提高 0.1%?)。

程序这么闲,自然就可以利用起来,闲着也是闲着,不如打打孩子——不是,不如去检查一下,有哪个控件状态不对,我们好把它纠正过来。

这种空闲事件,在很多 GUI 系统(比如 Windows 或 Android)中称为 “Idle-Event” (对应的事件处理器称为 “Idle-Handler”)。我们正在学习的 wxWidgets 又将它细分出一个 “wxUpdateUIEvent”,即专门用来处理用户界面(UI)的控件状态更新(Update)的事件。对应的,绑定事件函数的宏为:“EVT_UPDATE_UI”。

以用户可以切换坐标是否显示这一功能为例,按照 MVC 的划分原则,我们首先需要有的 Model 数据是:

  • a) 坐标数据 x,y (之前课堂已经添加了);
  • b) 是否显示,之前课堂没有添加,因此本课需为框架窗口类加上该成员数据,取名 showMotionInfo,一个 bool 值。

接着,当用户依赖某个操作路径(菜单或工具按钮),发起切换操作时,作为一个GUI开发框架,wxWidget将配合作为一个 GUI 操作系统的 Windows,共同帮我们完成复杂的 Controller 过程(视频里说的那些“线”),最终调用到我们写的事件响应函数,该函数修改作为 Model 数据之一的 showMotionInfo。如何知道将它修改成 true 或 false 呢?也就是说,如何知道用户当前操作是选中或是取消选中呢?仍然得感谢 wxWidgets 和其下的操作系统,它已经将该信息,放在事件数据中的 “IsCheck()” 方法里了。

接受用户输入,修改 Model 数据(showMotionInfo)的示例代码:

// 1 绑定到菜单上:
EVT_MENU(idMenuShowMotionInfo, HelloToolBarFrame::OnShowMotionInfo)...// 2 在事件响应函数中,修改 Model,状态信息在事件中:
void HelloToolBarFrame::OnShowMotionInfo(wxCommandEvent& event)
{this->showMotionInfo = event.IsChecked(); // 修改 model
}

接下来,程序在某个时刻发现自己很闲,于是它去开始检查并纠正哪个“孩子”(控件)状态不对——但是,它自己并不知道,也不记录具体哪个控件所应该处于正确状态是什么——谁知道,当然只有作为程序员的我们知道,所以程序会自动触发 UpdateUIEvent 事件,让我们在这个事件里去告诉它,某个“孩子”现在的正确状态应该是:在睡觉……如果有十个不同ID的控件需要更新状态,程序会触发十次这样的事件。

以 “idMenuShowMotionInfo” 为例,空闲的程序想知道绑定该ID的菜单项和工具图标当前应该处于什么状态时,它会触发以下的事件函数被调用:

// 3 绑定控件ID
EVT_UPDATE_UI(idMenuShowMotionInfo, HelloToolBarFrame::OnUpdateShowMotionInfo)...// 4 在事件中,由程序员告诉程序,当前控件的正确状态(是否选中)
void HelloToolBarFrame::OnUpdateShowMotionInfo(wxUpdateUIEvent& event)
{event.Check(this->showMotionInfo); // 有没有选中
}

请对比代码中的 3 和 4,从 “SET/写” 和 “GET/读” 的角度,加以理解。

以上讲的是控件的复选状态,它的状态就是 “选中” 或 “没选中”;对比之下,单选状态稍显复杂。因为单选状态的控件通常成组出现(至少两个),对应的详细状态应该是:“选中哪一个了?”。再次感谢所有 GUI 库,当它们在触发控件的 “UpdateEvent” 时,会带上这个控件的 ID,因此,我们只需检查它是不是我们记录的,用户选中的那个控件ID即可。

比如颜色选择有两项:蓝或红,当用户做出选择,我们就用 Model 数据(selectedColorId) 记录下来:

// 5 绑定控件ID,同一组单选控件需要全部绑定到同一个事件函数:
EVT_UPDATE_UI(idMenuBlueText, HelloToolBarFrame::OnUpdateTextColor)
EVT_UPDATE_UI(idMenuRedText, HelloToolBarFrame::OnUpdateTextColor)...// 6 记录用户选的是哪个ID
void HelloToolBarFrame::OnTextColorSelected(wxCommandEvent& event)
{selectedColorId = event.GetId();
}

对应的,当程序因为闲着也是闲着,又要 “打孩子” 时,我们是这样告诉它哪个 “孩子” 需要被选中,哪个 “孩子” 不需要:

// 7 同样需要绑定一组单选控件(的ID)
EVT_UPDATE_UI(idMenuBlueText, HelloToolBarFrame::OnUpdateTextColor)
EVT_UPDATE_UI(idMenuRedText, HelloToolBarFrame::OnUpdateTextColor)...// 8 在事件中,由程序员告诉程序,它来“问”的控件,是不是被选中的那个
void HelloToolBarFrame::OnUpdateTextColor(wxUpdateUIEvent& event)
{event.Check(event.GetId() == this->selectedColorId);
}

5 Model 数据

最后,汇总一下本例程用到的 Model 数据:

int xPos, yPos;int selectedColorId = idMenuBlueText;bool showMotionInfo = false; 

其中,前两项是在之前的课堂定义的。

相关文章:

GUI07-学工具栏,懂MVC

MVC模式,是天底下编写GUI程序最为经典、实效的一种软件架构模式。当一个人学完菜单栏、开始学习工具栏时,就是他的一生中,最适合开始认识 MVC 模式的好时机之一。这节将安排您学习: Model-View-Controller 模式如何创建工具栏以及…...

【进程篇】04.进程的状态与优先级

一、进程的状态 1.1 进程的状态 1.1.1 并行与并发 • 并行: 多个进程在多个CPU下分别,同时进行运行 • 并发: 多个进程在一个CPU下采用进程切换的方式,在一个时间片内,让多个进程都得以推进 1.1.2 时间片的概念 LInux/windows这些民用级别…...

ElasticSearch 数据聚合与运算

1、数据聚合 聚合(aggregations)可以让我们极其方便的实现数据的统计、分析和运算。实现这些统计功能的比数据库的 SQL 要方便的多,而且查询速度非常快,可以实现近实时搜索效果。 注意: 参加聚合的字段必须是 keywor…...

科研学习|论文解读——智能体最新研究进展

从2024-12-13到2024-12-18的45篇文章中精选出5篇优秀的工作分享 Can Modern LLMs Act as Agent Cores in Radiology~Environments? Achieving Collective Welfare in Multi-Agent Reinforcement Learning via Suggestion Sharing A systematic review of norm emergence in …...

面试小札:Java后端闪电五连鞭_8

1. Kafka消息模型及其组成部分 - 消息(Message):是Kafka中最基本的数据单元。消息包含一个键(key)、一个值(value)和一个时间戳(timestamp)。键可以用于对消息进行分区等…...

java error(2)保存时间带时分秒,回显时分秒变成00:00:00

超简单,顺带记录一下 1.入参实体类上使用注释:JsonFormat(pattern “yyyy-MM-dd”) 导致舍弃了 时分秒的部分。 2.数据库字段对应的类型是 date。date就是日期,日期就不带时分秒。 3.返参实体类使用了JsonFormat(pattern “yyyy-MM-dd”) 导…...

计算机毕业设计python+spark+hive动漫推荐系统 漫画推荐系统 漫画分析可视化大屏 漫画爬虫 漫画推荐系统 漫画爬虫 知识图谱 大数据毕设

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

数字IC后端设计实现篇之TSMC 12nm TCD cell(Dummy TCD Cell)应该怎么加?

TSMC 12nm A72项目我们需要按照foundary的要求提前在floorplan阶段加好TCD Cell。这个cell是用来做工艺校准的。这个dummy TCD Cell也可以等后续Calibre 插dummy自动插。但咱们项目要求提前在floorplan阶段就先预先规划好位置。 TSCM12nm 1P9M的metal stack结构图如下图所示。…...

(8)YOLOv6算法基本原理

一、YOLOv6 模型原理 发布日期:2022年6月 作者:美团技术团队 骨干网络:参考了 RepVGG 的设计,将重参数化能力进行补强,增强了模型结构的重参数化能力。使用了深度可分离卷积和跨阶段连接等技术,旨在提升…...

LNMP+discuz论坛

0.准备 文章目录 0.准备1.nginx2.mysql2.1 mysql82.2 mysql5.7 3.php4.测试php访问mysql5.部署 Discuz6.其他 yum源: # 没有wget,用这个 # curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo[rootlocalhost ~]#…...

在linux系统的docker中安装GitLab

一、安装GitLab: 在安装了docker之后就是下载安装GitLab了,在linux系统中输入命令:docker search gitlab就可以看到很多项目,一般安装第一个,它是英文版的,如果英文不好可以安装twang2218/gitlab-ce-zh。 …...

Python面试常见问题及答案12

问题: 请解释Python中的GIL(全局解释器锁)是什么? ○ 答案: GIL是Python解释器中的一种机制,用于确保任何时候只有一个线程在执行Python字节码。这在多线程场景下可能影响性能优化,但对于单线程…...

从0-1开发一个Vue3前端系统页面-9.博客页面布局

本节主要实现了博客首页界面的基本布局并完善了响应式布局,因为完善了响应式布局故对前面的页面布局有所改动,这里会将改动后的源码同步上传。 1.对页面头部的用户信息进行设计和美化 布局设计参考 :通常初级前端的布局会通过多个div划分区域…...

[手机Linux] 六,ubuntu18.04私有网盘(NextCloud)安装

一,LNMP介绍 LNMP一键安装包是一个用Linux Shell编写的可以为CentOS/RHEL/Fedora/Debian/Ubuntu/Raspbian/Deepin/Alibaba/Amazon/Mint/Oracle/Rocky/Alma/Kali/UOS/银河麒麟/openEuler/Anolis OS Linux VPS或独立主机安装LNMP(Nginx/MySQL/PHP)、LNMPA(Nginx/MySQ…...

白话java设计模式

创建模式 单例模式(Singleton Pattern): 就是一次创建多次使用,它的对象不会重复创建,可以全局来共享状态。 工厂模式(Factory Method Pattern): 可以通过接口来进行实例化创建&a…...

助力 Tuanjie OpenHarmony 开发:如何使用工具包 Hilog 和 SDK Kits Package?

随着团结引擎从 1.0.0 迭代至 1.3.0,越来越多的开发者开始使用团结引擎开发 OpenHarmony 应用。 在开发的过程中,我们也收到了大量反馈,尤其是在日志、堆栈和性能数据方面,这些信息对开发和调试过程至关重要。同时,我…...

NSDT 3DConvert:高效实现大模型文件在线预览与转换

NSDT 3DConvert 作为一个 WebGL 展示平台,能够实现多种模型格式免费在线预览,并支持大于1GB的OBJ、STL、GLTF、点云等模型进行在线查看与交互,这在3D模型展示领域是一个相当强大的功能。 平台特点 多格式支持 NSDT 3DConvert兼容多种3D模型…...

电商数据采集电商,行业数据分析,平台数据获取|稳定的API接口数据

电商数据采集可以通过多种方式完成,其中包括人工采集、使用电商平台提供的API接口、以及利用爬虫技术等自动化工具。以下是一些常用的电商数据采集方法: 人工采集:人工采集主要是通过基本的“复制粘贴”的方式在电商平台上进行数据的收集&am…...

VUE+Node.js+mysq实现响应式个人博客|项目初始化+路由配置+基础组件搭建

Day 1 开发文档:项目初始化与基础架构搭建 一、项目初始化 1. 创建项目 首先,我们使用 Vite 创建一个基于 Vue 3 的项目: # 创建项目 npm create vitelatest my-blog -- --template vue # 这条命令会创建一个名为 my-blog 的新项目&#…...

Python如何正确解决reCaptcha验证码(9)

前言 本文是该专栏的第73篇,后面会持续分享python爬虫干货知识,记得关注。 我们在处理某些国内外平台项目的时候,相信很多同学或多或少都见过,如下图所示的reCaptcha验证码。 而本文,笔者将重点来介绍在实战项目中,遇到上述中的“reCaptcha验证码”,如何正确去处理并解…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

ESP32读取DHT11温湿度数据

芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 ​…...