Qt之QPluginLoader使用插件子项目及插件间通信(简易框架)(含部分源码+注释)

文章目录

  • 一、项目示例
    • 1.导航栏操作页面操作示例图
    • 2.打开所有页面操作示例图
    • 3.打开指定界面操作示例图
    • 3.插件重载操作演示
  • 二、插件逻辑个人理解
    • 1.QPluginLoader的简单使用
    • 2.子插件的基本要素
  • 三、项目结构(思路)简述
    • 1.定义插件接口类
    • 2.定义插件类别
      • 一个主项目
      • 若干子插件
    • 3.主项目及子插件的关联
  • 四、源码(此处列举主项目和一个子插件源码为例)
    • 1.主项目相关文件
      • iplugindatabusinterface.h
      • commondefins
      • mainwindow
      • main.cpp
    • 2.子插件(页面1)相关文件
      • pagefirstplugin
  • 总结

一、项目示例

1.导航栏操作页面操作示例图

下图演示了通过导航栏打开和关闭页面并在主页插件显示的操作。
在这里插入图片描述

2.打开所有页面操作示例图

下图演示了一键打开所有界面并同事更新导航来页面状态的操作。

在这里插入图片描述

3.打开指定界面操作示例图

下图演示了根据不同项按钮打开指定界面的操作,并且同时更新对应导航栏状态。
在这里插入图片描述

3.插件重载操作演示

下图演示了指定页面重载指定页面的逻辑演示。
注:本文代码此处重载并非同一插件的重载,而是将当前使用插件卸载,然后通过更新插件路径从而加载一个全新的插件逻辑。
在这里插入图片描述

二、插件逻辑个人理解

1.QPluginLoader的简单使用

使用步骤如下:

  1. **设置库文件:**创建一个QPluginLoader对象,可通过构造传参或setFileName函数设置库文件;
  2. **加载库文件:**使用QPluginLoader对象的load函数加载库文件;
  3. **获取插件指针:**通过QPluginLoader对象的instance函数获取一个QObject指针,然后通过转换获取,再使用插件指针获取控件对象等操作;
  4. 卸载插件:. 通过QPluginLoader对象的unload函数卸载插件,该函数会自动释放插件指针对象。

2.子插件的基本要素

  1. 继承自定义接口并根据功能实现对应的接口函数。
  2. 使用Q_DECLARE_INTERFACE:将接口对象声明给元对象,保证可使用Q_INTERFACES添加该接口。
  3. 使用Q_INTERFACES:添加后可使用qobject_cast转换为定义接口类(不添加该标识符,使用dynamic_cast强制转换也可使用,但不推荐)。
  4. 使用Q_PLUGIN_METADATA:添加后可使得QPluginLoader的instance函数获取到接口(类)指针。

三、项目结构(思路)简述

1.定义插件接口类

插件与插件之间无法直接通信,此时就需要一个接口作为中间类建立通信的桥梁(提供保障插件正常工作的函数,如收、发数据的函数),并且要求进行通信的子插件都需要继承接口类并实现对应的通信函数;以及在该类文件中定义唯一标识符(使得Qt能通过标识符识别该接口类),。

插件唯一标识符

// 通过宏定义插件标识符
#define InterfaceIdent "plugin.plugindatabusinterface"

插件相关必要函数

signals:// 数据通信信号void sigPluginCommTriggered(const StCommData &data);
public:// 数据接收处理函数virtual void recvPluginCommData(const StCommData &data) = 0;// 初始化函数virtual void initialize() = 0;// 初始化状态函数virtual bool isInitialized() const = 0;// 获取插件名称virtual QString name() const = 0;// 创建插件对象virtual QWidget *createWidget() = 0;
public slots:// 所有插件初始化完成函数virtual void slotInitialized() = 0;

2.定义插件类别

一个主项目

管理子插件集合以及负责各个子插件的互相通信数据转发;主项目负责加载、管理、卸载子插件并处理各个插件间的消息转发,并且主项目包含main.cpp,使得程序从该入口运行。

主项目的目录结构
在这里插入图片描述

若干子插件

某一功能的集合,负责该功能的消息发送及消息处理;要求继承接口类并个性化实现接口类虚函数,且子插件中的插件类中需添加指定的插件宏。

子插件接口宏使用

    // 添加后可使用qobject_cast转换为定义接口类(不添加该标识符,使用dynamic_cast强制转换也可使用,但不推荐)Q_INTERFACES(IPluginDataBusInterface)
#if QT_VERSION >= 0x050000// 添加后可使得QPluginLoader的instance函数获取到接口(类)指针Q_PLUGIN_METADATA(IID InterfaceIdent)
#endif // QT_VERSION >= 0x050000

子插件目录结构
在这里插入图片描述
其他子插件
除开初始化显示的导航栏插件和主页插件固定外,子页插件是通过导航栏插件解析配置文件获取,因此导航栏插件目录下添加其他插件信息配置文件。
如下图:
在这里插入图片描述

3.主项目及子插件的关联

以本文项目举例(详情请看源码
在主项目头文件中添加插件信息容器集合处理。
在这里插入图片描述
插件初始化时将对应插件存储至相关容器,并关联数据信号处理消息。
在这里插入图片描述

四、源码(此处列举主项目和一个子插件源码为例)

1.主项目相关文件

iplugindatabusinterface.h

#ifndef IPLUGINDATABUSINTERFACE_H
#define IPLUGINDATABUSINTERFACE_H#include <QObject>
#include "commondefins.h"// 通过宏定义插件标识符
#define InterfaceIdent "plugin.plugindatabusinterface"class IPluginDataBusInterface : public QObject
{Q_OBJECT
public:IPluginDataBusInterface(QObject *parent = Q_NULLPTR):QObject(parent){}signals:/*** @brief sigPluginCommTriggered 插件通信信号触发* @param data 插件数据*/void sigPluginCommTriggered(const StCommData &data);public:/*** @brief recvPluginCommData 接收插件数据信息* @param data 插件数据*/virtual void recvPluginCommData(const StCommData &data) = 0;/*** @brief initialize 初始化函数*/virtual void initialize() = 0;/*** @brief isInitialized 是否初始化* @return 初始化状态*/virtual bool isInitialized() const = 0;/*** @brief name 获取插件名* @return 插件名*/virtual QString name() const = 0;/*** @brief createWidget 创建插件控件* @param parent 插件控件父对象* @return 插件控件指针*/virtual QWidget *createWidget() = 0;public slots:/*** @brief slotInitialized 初始化完成槽函数*/virtual void slotInitialized() = 0;protected:bool m_initialized; // 是否初始化变量
};// 将接口对象声明给元对象
Q_DECLARE_INTERFACE(IPluginDataBusInterface, InterfaceIdent)#endif // IPLUGINDATABUSINTERFACE_H

commondefins

commondefins.h

#ifndef COMMONDEFINS_H
#define COMMONDEFINS_H#include <QHash>
#include <QPair>
#include <QString>
#include <QJsonObject>
#include <QMessageBox>#define DefaultPluginLoadPath QString("./") // 默认插件路径enum EmDataCode{PAGE_COMM = 0,PAGE_OPEN,PAGE_CLOSE,PAGE_RELOAD,PAGE_RELOAD_FAILED,PAGE_OPEN_ALL,PAGE_INIT,PAGE_INIT_FAILED,
};typedef struct StCommData {EmDataCode  code;       // 数据码QString     pageName;   // 页面名称QJsonObject commData;   // 通信数据StCommData(){}StCommData(EmDataCode code, QString pageName, QJsonObject commData) {this->code = code;this->pageName = pageName;this->commData = commData;}
}StCommData;// 初始化加载插件信息
typedef struct StInitPluginLoaderInfo {// 当运行模式为Debug时使用的区分使用
#ifdef QT_DEBUGconst QString navigationBar = DefaultPluginLoadPath + "navigationbarplugind.dll";const QString homePage = DefaultPluginLoadPath + "homepageplugind.dll";
#elseconst QString navigationBar = DefaultPluginLoadPath + "navigationbarplugin.dll";const QString homePage = DefaultPluginLoadPath + "homepageplugin.dll";
#endif
//    const QString homePage = DefaultPluginLoadPath + "pagefirstplugin.dll";
}StInitPluginLoaderInfo;// 主页加载插件信息
typedef struct StPluginCommInfo {const QStringList listNavigationBarInfo = {"PageFirst", "PageSecond"};  // 导航栏列表信息}StHomePagePluginLoaderInfo;#endif // COMMONDEFINS_H

commondefins.cpp

#include "commondefins.h"// 创建变量,保证项目可以全局使用
StInitPluginLoaderInfo g_stInitPluginLoaderInfo;

mainwindow

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QPluginLoader>
#include "iplugindatabusinterface.h"namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = Q_NULLPTR);~MainWindow();/*** @brief initial 初始化函数*/void initial();signals:/*** @brief sigInitialized 初始化完成信号*/void sigInitialized();private slots:/*** @brief slotPluginCommTriggered 插件通信槽函数* @param data 通信数据*/void slotPluginCommTriggered(const StCommData &data);private:Ui::MainWindow *ui;QPair<QPluginLoader *, IPluginDataBusInterface *>                   m_pairNavigateInfo;         // 导航栏指针信息QPair<QPluginLoader *, IPluginDataBusInterface *>                   m_pairHomePageInfo;         // 主页指针信息QHash<QString, QPair<QPluginLoader *, IPluginDataBusInterface *>>   m_hashPageLoaderIPlugin;    // 子页指针信息};#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"#include "commondefins.h"
#include <QDebug>
#include <QDateTime>
#include <QFile>extern StInitPluginLoaderInfo g_stInitPluginLoaderInfo;MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);initial();
}MainWindow::~MainWindow()
{// 卸载所有库m_pairNavigateInfo.first->unload();m_pairHomePageInfo.first->unload();foreach(auto info,m_hashPageLoaderIPlugin) {info.first->unload();}delete ui;
}void MainWindow::initial()
{// 初始化导航栏插件m_pairNavigateInfo.first = new QPluginLoader(g_stInitPluginLoaderInfo.navigationBar, this);m_pairNavigateInfo.second = qobject_cast<IPluginDataBusInterface *>(m_pairNavigateInfo.first->instance());connect(m_pairNavigateInfo.second, &IPluginDataBusInterface::sigPluginCommTriggered, this, &MainWindow::slotPluginCommTriggered);connect(this, &MainWindow::sigInitialized, m_pairNavigateInfo.second, &IPluginDataBusInterface::slotInitialized);ui->layoutNavigation->addWidget(m_pairNavigateInfo.second->createWidget());// 初始化主页插件m_pairHomePageInfo.first = new QPluginLoader(g_stInitPluginLoaderInfo.homePage, this);m_pairHomePageInfo.second = qobject_cast<IPluginDataBusInterface *>(m_pairHomePageInfo.first->instance());connect(m_pairHomePageInfo.second, &IPluginDataBusInterface::sigPluginCommTriggered, this, &MainWindow::slotPluginCommTriggered);connect(this, &MainWindow::sigInitialized, m_pairHomePageInfo.second, &IPluginDataBusInterface::slotInitialized);ui->tabWidget->addTab(m_pairHomePageInfo.second->createWidget(), u8"主页");emit sigInitialized();
}void MainWindow::slotPluginCommTriggered(const StCommData &data)
{switch (data.code) {case PAGE_INIT: {// 不同运行模式使用的库名不一样
#ifdef QT_DEBUGQString pluginName = data.commData.value("pluginName").toString() + "d.dll";
#elseQString pluginName = data.commData.value("pluginName").toString() + ".dll";
#endif// 指定加载库路径QPluginLoader *pageLoader = new QPluginLoader(DefaultPluginLoadPath + pluginName, this);// 链接页面信号IPluginDataBusInterface *pageInteface = qobject_cast<IPluginDataBusInterface *>(pageLoader->instance());// 直接判断接口对象,加载失败时指针为0x00,相当于falseif(pageInteface) {// 判断页面是否存在if(m_hashPageLoaderIPlugin.contains(data.pageName)) {QMessageBox::information(this, u8"提示", data.pageName + u8"页面名称已存在");break;}connect(pageInteface, &IPluginDataBusInterface::sigPluginCommTriggered, this, &MainWindow::slotPluginCommTriggered);// 添加页面指针信息m_hashPageLoaderIPlugin[data.pageName] = QPair<QPluginLoader *, IPluginDataBusInterface *>(pageLoader, pageInteface);m_pairHomePageInfo.second->recvPluginCommData(data);}else {StCommData reply = data;reply.code = PAGE_INIT_FAILED;QMessageBox::information(this, u8"提示", data.pageName + u8"页面信息初始化失败" + pageLoader->errorString());m_pairNavigateInfo.second->recvPluginCommData(reply);}break;}case PAGE_OPEN: {// 判断页面是否存在if(!m_hashPageLoaderIPlugin.contains(data.pageName)) {QMessageBox::information(this, u8"提示", data.pageName + u8"页面不存在");}// 判断页面打开状态else if (-1 != ui->tabWidget->indexOf(m_hashPageLoaderIPlugin[data.pageName].second->createWidget())) {QMessageBox::information(this, u8"提示", data.pageName + u8"页面已打开");}// 获取页面指针并打开页面else {QWidget *page = m_hashPageLoaderIPlugin[data.pageName].second->createWidget();ui->tabWidget->addTab(page, data.pageName);ui->tabWidget->setCurrentWidget(page);// 页面打开回执(原信息返回)m_pairNavigateInfo.second->recvPluginCommData(data);}break;}case PAGE_OPEN_ALL: {// 遍历容器,打开所有页面foreach(auto pageInfo, m_hashPageLoaderIPlugin) {ui->tabWidget->addTab(pageInfo.second->createWidget(), m_hashPageLoaderIPlugin.key(pageInfo));}// 页面关闭回执(原信息返回)m_pairNavigateInfo.second->recvPluginCommData(data);break;}case PAGE_CLOSE: {// 判断页面是否存在if (!m_hashPageLoaderIPlugin.contains(data.pageName)) {QMessageBox::information(this, u8"提示", data.pageName + u8"页面不存在");}// 判断页面打开状态else if (-1 == ui->tabWidget->indexOf(m_hashPageLoaderIPlugin[data.pageName].second->createWidget())) {QMessageBox::information(this, u8"提示", data.pageName + u8"页面暂未打开/已关闭");}// 关闭页面else{ui->tabWidget->removeTab(ui->tabWidget->indexOf(m_hashPageLoaderIPlugin[data.pageName].second->createWidget()));// 页面关闭回执(原信息返回)m_pairNavigateInfo.second->recvPluginCommData(data);}break;}case PAGE_RELOAD: {// 判断页面是否存在if (!m_hashPageLoaderIPlugin.contains(data.pageName)) {QMessageBox::information(this, u8"提示", data.pageName + u8"重载页面不存在");}else if(m_hashPageLoaderIPlugin[data.pageName].first->unload()) {QString fileName =  m_hashPageLoaderIPlugin[data.pageName].first->fileName().replace(DefaultPluginLoadPath, DefaultPluginLoadPath + "backup/");// 判断新文件名是否存在,存在即更新库名if (QFile::exists(fileName)) {m_hashPageLoaderIPlugin[data.pageName].first->setFileName(fileName);}// 加载库if (m_hashPageLoaderIPlugin[data.pageName].first->load()) {m_hashPageLoaderIPlugin[data.pageName].second = qobject_cast<IPluginDataBusInterface *>(m_hashPageLoaderIPlugin[data.pageName].first->instance());m_pairNavigateInfo.second->recvPluginCommData(data);m_hashPageLoaderIPlugin[data.pageName].second->createWidget();}else {StCommData tmpData = data;tmpData.code = PAGE_RELOAD_FAILED;m_pairNavigateInfo.second->recvPluginCommData(tmpData);QMessageBox::information(this, u8"提示", data.pageName + u8"页面重载失败 " + m_hashPageLoaderIPlugin[data.pageName].first->errorString());}}else {QMessageBox::information(this, u8"提示", data.pageName + u8"页面重载失败 " + m_hashPageLoaderIPlugin[data.pageName].first->errorString());}break;}default:break;}
}

main.cpp

#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}

2.子插件(页面1)相关文件

注:该插件控件仅ui添加标识label,故仅展示插件文件

pagefirstplugin

pagefirstplugin.h

#ifndef PAGEFIRSTPLUGIN_H
#define PAGEFIRSTPLUGIN_H#include "iplugindatabusinterface.h"#include <QDesignerCustomWidgetInterface>class FormPageFirst;
class PageFirstPlugin : public IPluginDataBusInterface
{Q_OBJECTQ_INTERFACES(IPluginDataBusInterface)
#if QT_VERSION >= 0x050000Q_PLUGIN_METADATA(IID InterfaceIdent)
#endif // QT_VERSION >= 0x050000public:PageFirstPlugin(QObject *parent = Q_NULLPTR);~PageFirstPlugin();// IPluginDataBusInterface interface
public:void initialize();bool isInitialized() const;QString name() const;QWidget *createWidget();void recvPluginCommData(const StCommData &data);// IPluginDataBusInterface interface
public slots:void slotInitialized();private:FormPageFirst *m_formPageFirst = Q_NULLPTR;
};#endif // PAGEFIRSTPLUGIN_H

pagefirstplugin.cpp

#include "formpagefirst.h"
#include "pagefirstplugin.h"#include <QtPlugin>PageFirstPlugin::PageFirstPlugin(QObject *parent): IPluginDataBusInterface(parent)
{m_initialized = false;
}PageFirstPlugin::~PageFirstPlugin()
{if(Q_NULLPTR != m_formPageFirst) {delete m_formPageFirst;m_formPageFirst = Q_NULLPTR;}
}void PageFirstPlugin::initialize()
{if (m_initialized)return;// Add extension registrations, etc. herem_initialized = true;
}void PageFirstPlugin::slotInitialized()
{}void PageFirstPlugin::recvPluginCommData(const StCommData &data)
{}bool PageFirstPlugin::isInitialized() const
{return m_initialized;
}QWidget *PageFirstPlugin::createWidget()
{if(Q_NULLPTR == m_formPageFirst) {m_formPageFirst = new FormPageFirst();}return m_formPageFirst;
}QString PageFirstPlugin::name() const
{return QLatin1String("PageFirstPlugin");
}#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(pagefirstplugin, PageFirstPlugin)
#endif // QT_VERSION < 0x050000

总结

本文简易介绍插件使用、部分通信逻辑及插件构建逻辑,下一篇出插件创建详细步骤。(有需要可私源码)


友情提示——哪里看不懂可私哦,让我们一起互相进步吧
(创作不易,请留下一个免费的赞叭 谢谢 ^o^/)

注:文章为作者编程过程中所遇到的问题和总结,内容仅供参考,若有错误欢迎指出。
注:如有侵权,请联系作者删除


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

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

相关文章

提取阿里国际站商家电话的爬虫软件

引言: 随着电子商务的快速发展&#xff0c;越来越多的商家选择在阿里国际站上开设店铺。然而&#xff0c;对于想要联系某些商家或者进行商务合作的人来说&#xff0c;商家的联系电话往往是非常重要的信息。在这篇文章中&#xff0c;我们将介绍如何使用爬虫软件提取阿里国际站商…

装箱问题(贪婪策略:首次适应递减(First Fit Decreasing, FFD))

装箱问题&#xff08;贪婪策略:首次适应递减&#xff08;First Fit Decreasing, FFD&#xff09;&#xff09; 装箱问题是一种典型的组合优化问题&#xff0c;它可以用多种贪婪&#xff08;greedy&#xff09;策略来解决。贪婪算法通过在每一步选择当前最优的解决方案&#xf…

IDEA推荐使用十大插件

在本文中&#xff0c;我们将介绍 10 多个最好的 IntelliJ IDEA 插件&#xff0c;以提高工作效率并在更短的时间内完成更多工作。如果将这些插件合并到您的工作流程中&#xff0c;您将能够更有效地应对开发挑战。 1、TabNine TabNine 是一个 IntelliJ IDEA 插件&#xff0c;可…

c# 获取oracle 表及表内容

1、 /// <summary> /// 获取表名列 /// </summary> /// <param name"owner"></param> private void GetTableNameList(string owner) { TableNameGridList.Clear(); GetT…

YOLOv5语义分割7.0推理代码封装

YOLOv5语义分割7.0推理代码封装 YOLOv5语义分割7.0推理代码封装 YOLOv5语义分割7.0推理代码封装 import argparse import os import numpy as np import re import sys from pathlib import Path import torchFILE = Path(__file__).resolve() ROOT = FILE.parents[1

C/C++ 纸张尺寸问题(蓝桥杯)

题目描述&#xff1a; 在 ISO 国际标准中定义了 A 0 A0A0 纸张的大小为 1189 m m 841 m m 1189mm841mm1189mm841mm&#xff0c;将 A 0 A0A0 纸沿长边对折后为 A 1 A1A1 纸&#xff0c;大小为 841 m m 594 m m 841mm594mm841mm594mm&#xff0c;在对折的过程中长度直接取下整…

CSS常用选择器(通配符选择器,标签选择器,类选择器,id选择器……),你知道了多少?

目录 CSS常用选择器 一、什么是选择器 二、通配符选择器 基本语法格式&#xff1a; 三、标签选择器 基本语法格式&#xff1a; 四、类选择器 基本语法格式&#xff1a; 五、id选择器 基本语法格式&#xff1a; 六、类选择器还是 ID 选择器&#xff1f; 区别 1&…

芯片设计后端遇到的各种文件类型和文件后缀

芯片设计后端遇到的各种文件类型和文件后缀 文件类型 描述 文件后缀 netlist网表文件 verilog文件格式&#xff0c;记录了芯片里各个instance的逻辑连接关系 .v (for Verilog netlists) Lib&#xff0c;liberty timing file 记录了cell的timing信息及一定power信息。有的…

Python自动化测试:API接口自动化——requests、webSocket

接口自动化测试1 一、requests二、简单示例1.导入/引入库2.请求与响应示例1>简单访问百度主页-GET请求2>简单的登录请求-POST请求3>保存cookies至头信息headers4>其他接口请求时携带headers 三、webSocketwebSocket连接与数据收发示例 本文介绍了借助Python的reque…

leetcode-重复的子字符串

459. 重复的子字符串 题解&#xff1a; 首先&#xff0c;我们需要找到字符串s的所有子串。然后&#xff0c;我们需要检查这些子串是否可以通过重复多次构成原字符串s。如果找到了这样的子串&#xff0c;返回True&#xff0c;否则返回False。 class Solution:def repeatedSub…

什么是同源策略?如何检测跨站点 WebSocket 劫持漏洞?post 表单跳转跨域问题、Ajax跨域请求、浏览器特性和安全策略、WebSocket 协议连接

什么是同源策略?如何检测跨站点 WebSocket 劫持漏洞?post 表单跳转跨域问题、Ajax跨域请求、浏览器特性和安全策略、WebSocket 协议连接。 同源策略(Same Origin Policy)是一种浏览器安全机制,用于保护用户的信息和数据安全。它限制了来自不同源(协议、域名、端口)的网页…

华为手环 8:返校季新宠,助力高效学习与健康生活

随着春节假期的结束&#xff0c;学生们也纷纷踏上了返校的旅途。新的学期&#xff0c;新的气象&#xff0c;让华为手环8为你的带来全新的智能生活体验。它不仅仅是一款风格多变的时尚手环&#xff0c;还拥有了智能消息提醒、100多种运动模式和睡眠监测等强大功能&#xff0c;让…

计算机设计大赛 深度学习疲劳驾驶检测 opencv python

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 &#x1f525; 优…

基于springboot实现粮食仓库管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现粮食仓库管理系统演示 摘要 粮食作为人类生活的重要物质来源&#xff0c;在粮食流通过程中对于粮食仓库的管理不容忽视&#xff0c;随着我国粮食生产能力的提升以粮食存储管理的不断革新&#xff0c;粮食产量的增加为粮食仓储管理带来了挑战也带来了机遇&am…

蜂窝物联:物联网大数据云平台功能模块简介

蜂窝云平台可远程获取现场环境&#xff08;如温室大棚、稻田&#xff09;的空气温湿度、土壤水分温度、二氧化碳浓度、光照强度及视频图像&#xff0c;通过数据模型分析&#xff0c;可以自动控制湿帘、风机、喷淋滴灌、内外遮阳、顶窗侧窗、加温补光、增氧机等设备&#xff1b;…

Java零基础-包机制

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

实践课项目化

程序设计实践 项目内容&#xff1a; 这是一门物联网工程专业的必修课程&#xff0c;基于大学计算机基础和C编程课程&#xff0c;要求使用C语言设计和实现一个小型信息管理系统。课程提供两个小项目供学生选择&#xff0c;学生分组完成其中一个项目&#xff0c;每组5人。 成果…

MySQL面试题-锁(答案版)

锁 1、MySQL 有哪些锁&#xff1f; &#xff08;1&#xff09;全局锁 加了全局锁之后&#xff0c;整个数据库就处于只读状态了&#xff0c;这时其他线程执行以下操作&#xff0c;都会被阻塞&#xff1a; 对数据的增删改操作&#xff0c;比如 insert、delete、update等语句&…

C# 异步操作汇总

在C#中&#xff0c;异步操作&#xff08;Asynchronous Operations&#xff09;可以提高程序的性能和响应能力。通常情况下&#xff0c;程序会等待某个操作完成之后才会继续执行下一个操作&#xff0c;这会导致程序的运行速度变慢。而异步操作可以让程序在等待某个操作完成的同时…

【深度学习笔记】计算机视觉——R-CNN

区域卷积神经网络&#xff08;R-CNN&#xff09;系列 sec_rcnn 除了 sec_ssd中描述的单发多框检测之外&#xff0c; 区域卷积神经网络&#xff08;region-based CNN或regions with CNN features&#xff0c;R-CNN&#xff09; Girshick.Donahue.Darrell.ea.2014也是将深度模型…