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

Qt u盘自动升级软件

Qt u盘自动升级软件

  • Chapter1 Qt u盘自动升级软件
    • u盘自动升级软件思路:
    • step1. 获取U盘 判断U盘名字是否正确, 升级文件是否存在。
    • step2. 升级
    • step3. 升级界面
  • Chapter2 Qt 嵌入式设备应用程序,通过U盘升级的一种思路
  • Chapter3 在开发板上运行的QT应用程序,如何拷贝文件到U盘
  • Chapter4 嵌入式Qt,U盘升级程序
    • 前言
    • 一、实现过程
      • 1.检测U盘中的更新程序
      • 2. 数字验证
      • 3.升级程序
      • 4.主函数
    • 二、总结


Chapter1 Qt u盘自动升级软件

原文链接

u盘自动升级软件思路:

1.检测U盘是否存在

2.检测升级文件是否存在,升级文件版本是否比当前软件版本新

3.软件升级

step1. 获取U盘 判断U盘名字是否正确, 升级文件是否存在。

//获取U盘  判断U盘名字是否正确, 升级文件是否存在。
void MainWindow::getUDisk()
{QMap<QString,QString> namePath;foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes()){if (storage.isValid() && storage.isReady()){UDiskPath = storage.rootPath();namePath.insert(storage.displayName(),storage.rootPath());}}QString path = namePath.value("LADYBUG");QFile file(path+"/"+app::updateFile);if(file.exists()){//存在 弹窗是否升级}else{//不存在}
}

step2. 升级

    OperateThread *thread = new OperateThread;thread->setUpgradeFile(strSrcFile);ShowProgressDialog dialog(this);connect(thread, SIGNAL(emitFinish(int)), &dialog, SLOT(onThreadFinished(int)));thread->start();dialog.exec();

step3. 升级界面

class ShowProgressDialog : public QDialog
{Q_OBJECT
public:explicit ShowProgressDialog(QWidget *parent = 0);~ShowProgressDialog();void setTitleText(const QString &strText);void setMessageText(const QString &strMsg);public slots:void on_btnOk_clicked();void onThreadFinished(int nExitCode);private:QLabel          *m_labelTitle;QLabel          *m_labelMsg;QPushButton     *m_btnOk;QLabel          *m_labelWait;QMovie          *m_pMovie;
};ShowProgressDialog::ShowProgressDialog(QWidget *parent) : QDialog(parent)
{setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::Dialog);setAttribute(Qt::WA_X11DoNotAcceptFocus);setFocusPolicy(Qt::NoFocus);//更改背景色QPalette palette = this->palette();QPixmap pix(":/res/dialog_bg.png");palette.setBrush(QPalette::Background,QBrush(pix));setAutoFillBackground(true);this->setPalette(palette);resize(410, 260);setMinimumSize(QSize(410, 260));setMaximumSize(QSize(410, 260));m_labelTitle = new QLabel(this);m_labelMsg = new QLabel(this);m_btnOk = new QPushButton(this);m_labelWait = new QLabel(this);m_pMovie = new QMovie(":/res/loading.gif");m_labelTitle->setGeometry(QRect(10, 1, 220, 30));m_labelMsg->setGeometry(QRect(30, 60, 350, 60));m_labelMsg->setWordWrap(true);m_btnOk->setGeometry(QRect(320, 180, 58, 58));m_labelWait->setGeometry(QRect(30, 120, 322, 18));m_labelWait->setMovie(m_pMovie);m_pMovie->start();m_labelTitle->setStyleSheet("font: bold 17px; color: white;");m_labelMsg->setStyleSheet("font: bold 17px ; color: black; ");m_btnOk->setStyleSheet("QPushButton{background-image: url(:/res/dialog_ok.png);border: 0px;}""QPushButton:pressed{background-image: url(:/res/dialog_ok_p.png);border: 0px;}");connect(m_btnOk , SIGNAL(clicked()) , this , SLOT(on_btnOk_clicked()));setTitleText("升级");setMessageText("正在升级,请勿插拔U盘!");m_btnOk->hide();
}ShowProgressDialog::~ShowProgressDialog()
{delete m_labelTitle;delete m_labelMsg;delete m_btnOk;
}void ShowProgressDialog::setTitleText(const QString &strText)
{m_labelTitle->setText(strText);
}void ShowProgressDialog::setMessageText(const QString &strMsg)
{m_labelMsg->setText(strMsg);
}void ShowProgressDialog::on_btnOk_clicked()
{QDialog::accept();
}void ShowProgressDialog::onThreadFinished(int nExitCode)
{qDebug() << "nExitCode" << nExitCode;if (nExitCode == 0){setMessageText("升级成功,请重新上电。");m_btnOk->show();m_pMovie->stop();}else{setMessageText("升级出错!请断电以恢复!");m_btnOk->show();m_pMovie->stop();}
}

注意:线程,继承自QThread,完成复制工作。使用QProcess来完成Linux下的解压和删除的工作。关于Qt的多线程,可以去查找一下其他的教程,这里就不做过多的解释。

class OperateThread : public QThread
{Q_OBJECT
public:explicit OperateThread(QObject *parent = 0);public:void setUpgradeFile(QString strUpgradeFile){m_UpgradeFile = strUpgradeFile;}protected:void run();signals:void emitFinish(int);public slots:private:void OprUpgradeUp();private:QString  m_UpgradeFile;
};OperateThread::OperateThread(QObject *parent) : QThread(parent)
{}void OperateThread::run()
{//很复杂的数据处理OprUpgradeUp();
}void OperateThread::OprUpgradeUp()
{
#define DEST_DIR    "../"QString destDir = QString("%1/update.tar.gz").arg(DEST_DIR);if( QFile::copy(m_UpgradeFile, destDir) ){qDebug()<<"-------成功-----------";}else{qDebug()<<"-------出错-----------";//升级失败emit emitFinish(1);return;}#ifdef Q_OS_LINUXQString exe = QString("tar -zxvf %1 -C %2").arg(destDir).arg(DEST_DIR);QProcess::execute(exe);//升级成功,自动同步磁盘并删除ARM上的升级包exe = QString("sync");QProcess::execute(exe);exe = QString("rm -rf %1").arg(destDir);QProcess::execute(exe);
#endif//升级成功emit emitFinish(0);
}

Chapter2 Qt 嵌入式设备应用程序,通过U盘升级的一种思路

原文链接:https://blog.csdn.net/qq1113231395/article/details/81867153

最近在做一个通过U盘升级的功能,程序是运行在ARM Linux Qt平台上的。这个应该是很多嵌入式设备必备的一个功能了,所以把这部分的实现抽出来,做成一个例子供需要的人参考。这只是U盘升级的一种思路,如果有更好的方法,也可以提供相应的意见。

源码下载:softwareupgrade.tar.gz

升级文件的格式是通过tar压缩后的文件以gz结尾的, 可以通过tar命令生成相应的升级文件如update.tar.gz:

tar -czvf update.tar.gz demo

主要思路就是,点击相应的功能后从U盘中选中需要升级的文件update.tar.gz,之后开启一个线程和一个提示正在升级的对话框,以免让用户觉得假死。在线程中完成的事情是:将选中的升级文件复制到应用程序的目录中,然后将update.tar.gz 解压覆盖原来的程序。最后将update.tar.gz删除。发送一个线程结束的信号。

下面就介绍一下实现过程。

MainWindow很简单,只有一个按钮。 点击按钮开启线程,显示提示框,绑定线程结束后的信号。提示框根据信号的参数值来确定升级是否成功。

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{QString strSrcFile = QFileDialog::getOpenFileName(this, "选择升级文件", ".", "tar (*.gz)");qDebug() << "strSrcFile" << strSrcFile;OperateThread *thread = new OperateThread;thread->setUpgradeFile(strSrcFile);ShowProgressDialog dialog(this);connect(thread, SIGNAL(emitFinish(int)), &dialog, SLOT(onThreadFinished(int)));thread->start();dialog.exec();
}

运行的效果图:
在这里插入图片描述

提示框: void onThreadFinished(int nExitCode) 线程结束后更新提示框。里面还使用到了一些qss,来设置背景。

class ShowProgressDialog : public QDialog
{Q_OBJECT
public:explicit ShowProgressDialog(QWidget *parent = 0);~ShowProgressDialog();void setTitleText(const QString &strText);void setMessageText(const QString &strMsg);public slots:void on_btnOk_clicked();void onThreadFinished(int nExitCode);private:QLabel          *m_labelTitle;QLabel          *m_labelMsg;QPushButton     *m_btnOk;QLabel          *m_labelWait;QMovie          *m_pMovie;
};ShowProgressDialog::ShowProgressDialog(QWidget *parent) : QDialog(parent)
{setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::Dialog);setAttribute(Qt::WA_X11DoNotAcceptFocus);setFocusPolicy(Qt::NoFocus);//更改背景色QPalette palette = this->palette();QPixmap pix(":/res/dialog_bg.png");palette.setBrush(QPalette::Background,QBrush(pix));setAutoFillBackground(true);this->setPalette(palette);resize(410, 260);setMinimumSize(QSize(410, 260));setMaximumSize(QSize(410, 260));m_labelTitle = new QLabel(this);m_labelMsg = new QLabel(this);m_btnOk = new QPushButton(this);m_labelWait = new QLabel(this);m_pMovie = new QMovie(":/res/loading.gif");m_labelTitle->setGeometry(QRect(10, 1, 220, 30));m_labelMsg->setGeometry(QRect(30, 60, 350, 60));m_labelMsg->setWordWrap(true);m_btnOk->setGeometry(QRect(320, 180, 58, 58));m_labelWait->setGeometry(QRect(30, 120, 322, 18));m_labelWait->setMovie(m_pMovie);m_pMovie->start();m_labelTitle->setStyleSheet("font: bold 17px; color: white;");m_labelMsg->setStyleSheet("font: bold 17px ; color: black; ");m_btnOk->setStyleSheet("QPushButton{background-image: url(:/res/dialog_ok.png);border: 0px;}""QPushButton:pressed{background-image: url(:/res/dialog_ok_p.png);border: 0px;}");connect(m_btnOk , SIGNAL(clicked()) , this , SLOT(on_btnOk_clicked()));setTitleText("升级");setMessageText("正在升级,请勿插拔U盘!");m_btnOk->hide();
}ShowProgressDialog::~ShowProgressDialog()
{delete m_labelTitle;delete m_labelMsg;delete m_btnOk;
}void ShowProgressDialog::setTitleText(const QString &strText)
{m_labelTitle->setText(strText);
}void ShowProgressDialog::setMessageText(const QString &strMsg)
{m_labelMsg->setText(strMsg);
}void ShowProgressDialog::on_btnOk_clicked()
{QDialog::accept();
}void ShowProgressDialog::onThreadFinished(int nExitCode)
{qDebug() << "nExitCode" << nExitCode;if (nExitCode == 0){setMessageText("升级成功,请重新上电。");m_btnOk->show();m_pMovie->stop();}else{setMessageText("升级出错!请断电以恢复!");m_btnOk->show();m_pMovie->stop();}
}

线程,继承自QThread,完成复制工作。使用QProcess来完成Linux下的解压和删除的工作。关于Qt的多线程,可以去查找一下其他的教程,这里就不做过多的解释。

class OperateThread : public QThread
{Q_OBJECT
public:explicit OperateThread(QObject *parent = 0);public:void setUpgradeFile(QString strUpgradeFile){m_UpgradeFile = strUpgradeFile;}protected:void run();signals:void emitFinish(int);public slots:private:void OprUpgradeUp();private:QString  m_UpgradeFile;
};OperateThread::OperateThread(QObject *parent) : QThread(parent)
{}void OperateThread::run()
{//很复杂的数据处理OprUpgradeUp();
}void OperateThread::OprUpgradeUp()
{
#define DEST_DIR    "../"QString destDir = QString("%1/update.tar.gz").arg(DEST_DIR);if( QFile::copy(m_UpgradeFile, destDir) ){qDebug()<<"-------成功-----------";}else{qDebug()<<"-------出错-----------";//升级失败emit emitFinish(1);return;}#ifdef Q_OS_LINUXQString exe = QString("tar -zxvf %1 -C %2").arg(destDir).arg(DEST_DIR);QProcess::execute(exe);//升级成功,自动同步磁盘并删除ARM上的升级包exe = QString("sync");QProcess::execute(exe);exe = QString("rm -rf %1").arg(destDir);QProcess::execute(exe);
#endif//升级成功emit emitFinish(0);
}

Chapter3 在开发板上运行的QT应用程序,如何拷贝文件到U盘

原文链接:https://blog.csdn.net/pang_fighting/article/details/139114780

笔者最近一直被这个问题所困惑,好在今天已经解决,之前找了很多资料,试了其他家的使用QProcess类来实现QT应用下的命令行实现,但很遗憾还是不行,找到新的解决方法如下:

system("cp -r /home/root/test_data /run/media/sda1");
system("sync");
system("umount /run/media/sda1");

解释一下,这需要你的开发板移植的系统能够插入U盘直接挂载,否则需要先检测U盘插入再挂载U盘,才可以使用,笔者用的正点原子157开发板,使用的是他们的官方系统,这个系统是支持插入U盘直接挂载的,挂载目录是“/run/media/sda1”。

U盘挂载完成之后,使用C函数system来执行命令行拷贝文件,拷贝完成之后执行sync命令,最后直接取消挂载拔出U盘即可!

Chapter4 嵌入式Qt,U盘升级程序

原文链接:https://blog.csdn.net/sjd753/article/details/135284869

前言

近期在完成一个U盘升级功能,对于Arm嵌入式设备来说应该算是必备的了。

参考链接:

https://blog.csdn.net/newnewman80/article/details/8766657
https://blog.csdn.net/newnewman80/article/details/8766657

一、实现过程

1.检测U盘中的更新程序

结合netlink捕获USB的热拔插,我们需要去检测U盘的挂载点,通常情况下都是挂载在/media下。

bool checkConsoleAppExists() 
{DIR *dir = opendir("/run/media"); // 打开 /run/media 目录 具体的挂载点根据设备决定if (dir) {struct dirent *entry;while ((entry = readdir(dir)) != NULL) {if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {devpath = "/run/media/" + std::string(entry->d_name);path = "/run/media/" + std::string(entry->d_name) + "/updateFileName";std::string signatureFilePath = devpath +"/sigFileName.sig"; std::string publicKeyPath = devpath + "/key.pub" ;if (access(path.c_str(), F_OK) != -1) {//检测到更新程序,可以做一些校验,我这里的话是验证数字签名,根据具体情况决定return checkUpdate(signatureFilePath,publicKeyPath,path);}}}closedir(dir);}return false;
}

2. 数字验证

/*检测是否需要更新,是否为正确的更新程序*/
bool checkUpdate(const std::string& signatureFile, const std::string& publicKeyFile, const std::string& dataFile) {// 打开更新文件// 这里是想要去检测版本号,去判断是否需要升级的,后面由于使用了数字签名就没有实现这部分。FILE *updateFile = fopen(dataFile.c_str(), "rb");if (!updateFile) {perror("Error opening update file\n");return false;}// 验证数字签名return verifySignature(signatureFile,publicKeyFile,dataFile);
}
// 函数用于验证数字签名是否有效
bool verifySignature(const std::string& signatureFile, const std::string& publicKeyFile, const std::string& dataFile) 
{// 打开公钥文件以读取公钥信息FILE* fp = fopen(publicKeyFile.c_str(), "r");if (!fp) {std::cerr << "Error opening public key file." << std::endl;return false;}// 从公钥文件中读取 RSA 公钥信息RSA* rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);fclose(fp);// 检查是否成功读取公钥信息if (!rsa) {std::cerr << "Error reading public key." << std::endl;return false;}// 创建一个用于存储 RSA 公钥的 EVP_PKEY 对象EVP_PKEY* evpKey = EVP_PKEY_new();// 将 RSA 公钥赋值给 EVP_PKEY 对象if (!EVP_PKEY_assign_RSA(evpKey, rsa)) {std::cerr << "Error assigning RSA key." << std::endl;RSA_free(rsa);return false;}// 创建一个用于消息摘要计算的 EVP_MD_CTX 对象EVP_MD_CTX* ctx = EVP_MD_CTX_new();if (!ctx) {std::cerr << "Error creating context." << std::endl;RSA_free(rsa);return false;}// 打开数据文件以进行签名验证std::ifstream fileStream(dataFile, std::ios::binary | std::ios::ate);if (!fileStream.is_open()) {std::cerr << "Error opening data file." << std::endl;EVP_MD_CTX_free(ctx);RSA_free(rsa);return false;}// 读取数据文件的大小和内容std::streamsize fileSize = fileStream.tellg();fileStream.seekg(0, std::ios::beg);std::vector<unsigned char> fileData(fileSize);if (!fileStream.read(reinterpret_cast<char*>(fileData.data()), fileSize)) {std::cerr << "Error reading data file." << std::endl;fileStream.close();EVP_MD_CTX_free(ctx);RSA_free(rsa);return false;}fileStream.close();// 打开签名文件以进行签名验证std::ifstream signatureFileStream(signatureFile, std::ios::binary);if (!signatureFileStream.is_open()) {std::cerr << "Error opening signature file." << std::endl;EVP_MD_CTX_free(ctx);RSA_free(rsa);return false;}// 读取签名文件的大小和内容signatureFileStream.seekg(0, std::ios::end);size_t signatureFileSize = signatureFileStream.tellg();signatureFileStream.seekg(0, std::ios::beg);std::vector<unsigned char> signatureData(signatureFileSize);if (!signatureFileStream.read(reinterpret_cast<char*>(signatureData.data()), signatureFileSize)) {std::cerr << "Error reading signature file." << std::endl;signatureFileStream.close();EVP_MD_CTX_free(ctx);RSA_free(rsa);return false;}signatureFileStream.close();// 更新消息摘要的内容,计算数据文件的哈希值if (!EVP_VerifyUpdate(ctx, fileData.data(), fileSize)) {std::cerr << "Error updating verification." << std::endl;EVP_MD_CTX_free(ctx);RSA_free(rsa);return false;}// 验证签名的有效性int result = EVP_VerifyFinal(ctx, signatureData.data(), signatureFileSize, evpKey);EVP_MD_CTX_free(ctx);RSA_free(rsa);// 根据验证结果返回相应的布尔值if (result != 1) {std::cerr << "Signature verification failed." << std::endl;return false;}std::cout << "Signature verification successful." << std::endl;return true;
}

3.升级程序

升级程序的话,实际上就是一个拷贝的过程。将设备上的备份,U盘中的更新程序拷贝出来。

/* 更新程序 */
bool copyFile(const char *sourcePath, const char *destinationPath) {//system("mv updateFileName  updateFileName.back"); //这里是一个备份,后续可以进行一个升级失败,备份还原。FILE *sourceFile = fopen(sourcePath, "rb");FILE *destinationFile = fopen(destinationPath, "wb");if (sourceFile == NULL) {printf("Error opening sourceFile files.\n");return false;}if(destinationFile == NULL){printf("Error opening  destinationFile files.\n");return false;}char buffer[1024];size_t bytesRead;while ((bytesRead = fread(buffer, 1, sizeof(buffer), sourceFile)) > 0) {printf("start copy file........................\n");fwrite(buffer, 1, bytesRead, destinationFile);}/*更简单一点就是用system命令,直接拷贝不过有一点需要注意,程序在运行的时候,有时候会拷贝失败。可以先用system命令删除或者备份,然后再进行拷贝,再用system给个权限。*/fclose(sourceFile);fclose(destinationFile);return true;
}

4.主函数

int main(void)
{struct sockaddr_nl client;struct timeval tv;int CppLive, rcvlen, ret;fd_set fds;int buffersize = 1024;CppLive = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);memset(&client, 0, sizeof(client));client.nl_family = AF_NETLINK;client.nl_pid = getpid();client.nl_groups = 1; /* receive broadcast message*/setsockopt(CppLive, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));bind(CppLive, (struct sockaddr*)&client, sizeof(client));while (1) {char buf[UEVENT_BUFFER_SIZE] = { 0 };FD_ZERO(&fds);FD_SET(CppLive, &fds);tv.tv_sec = 0;tv.tv_usec = 100 * 1000;ret = select(CppLive + 1, &fds, NULL, NULL, &tv);if(ret < 0)continue;if(!(ret > 0 && FD_ISSET(CppLive, &fds)))continue;/* receive data */rcvlen = recv(CppLive, &buf, sizeof(buf), 0);if (rcvlen > 0) { printf("%s\n", buf);//检测更新程序//升级程序//重启....}}close(CppLive);return 0;
}

二、总结

以上就是今天要讲的内容,本文仅仅简单介绍了结合netlink的u盘升级,后期可以通过Qt实现U盘升级,实现一个程序,通过界面可以判断更新状态。

相关文章:

Qt u盘自动升级软件

Qt u盘自动升级软件 Chapter1 Qt u盘自动升级软件u盘自动升级软件思路&#xff1a;step1. 获取U盘 判断U盘名字是否正确&#xff0c; 升级文件是否存在。step2. 升级step3. 升级界面 Chapter2 Qt 嵌入式设备应用程序&#xff0c;通过U盘升级的一种思路Chapter3 在开发板上运行的…...

【Conda 和 虚拟环境详细指南】

Conda 和 虚拟环境的详细指南 什么是 Conda&#xff1f; Conda 是一个开源的包管理和环境管理系统&#xff0c;支持多种编程语言&#xff08;如Python、R等&#xff09;&#xff0c;最初由Continuum Analytics开发。 主要功能&#xff1a; 包管理&#xff1a;安装、更新、删…...

Python递归函数深度解析:从原理到实战

Python递归函数深度解析&#xff1a;从原理到实战 递归是计算机科学中重要的编程范式&#xff0c;也是算法设计的核心思想之一。本文将通过20实战案例&#xff0c;带你深入理解Python递归函数的精髓&#xff0c;掌握递归算法的实现技巧。 一、递归函数核心原理 1.1 递归三要…...

OpenGL学习笔记(五):Textures 纹理

文章目录 纹理坐标纹理环绕方式纹理过滤——处理纹理分辨率低的情况多级渐远纹理Mipmap——处理纹理分辨率高的情况加载与创建纹理 &#xff08; <stb_image.h> &#xff09;生成纹理应用纹理纹理单元练习1练习2练习3练习4 通过上一篇着色部分的学习&#xff0c;我们可以…...

【TypeScript】基础:数据类型

文章目录 TypeScript一、简介二、类型声明三、数据类型anyunknownnervervoidobjecttupleenumType一些特殊情况 TypeScript 是JavaScript的超集&#xff0c;代码量比JavaScript复杂、繁多&#xff1b;但是结构更清晰 一、简介 为什么需要TypeScript&#xff1f; JavaScript的…...

Notepad++消除生成bak文件

设置(T) ⇒ 首选项... ⇒ 备份 ⇒ 勾选 "禁用" 勾选禁用 就不会再生成bak文件了 notepad怎么修改字符集编码格式为gbk 如图所示...

Android NDK

Android NDK环境 D:\Android SDK\ndk\25.2.9519653 使用clang而不用gcc D:\Android SDK\ndk\25.1.8937393\toolchains\llvm\prebuilt\windows-x86_64\bin\clang --version 查看是否安装成功clang ptrace 在 C 语言中&#xff0c;ptrace 已经被 Linux 内核实现&#xff0…...

内部知识库助力组织智力激发与信息共享实现业绩增长

内容概要 内部知识库是企业知识管理的核心组件&#xff0c;具有不可估量的重要性。通过构建有效的知识库&#xff0c;组织能够将孤立的知识和信息整合成为一个系统性的体&#xff0c;极大提高员工访问和利用这些信息的能力。这不仅简化了决策过程&#xff0c;还通过减少重复劳…...

通过F12收集的信息

按 F12 键打开浏览器的开发者工具&#xff08;DevTools&#xff09;可以获取部分操作系统和中间件信息&#xff0c;但能力有限。以下是具体说明&#xff1a; 一、通过 F12 收集的信息 1. 客户端操作系统信息 - Console 控制台 通过 JavaScript 直接获取客户端操作系统信息&am…...

用Python替代OpenMV IDE显示openmv USB 图像

原理是利用openmv的usb模仿串口&#xff0c;然后用Python代码打开串口接收 能替代openmv ide 跑48帧图像 Python端需要的依赖&#xff1a; 需要的是&#xff1a; from ultralytics import YOLO import cv2 import numpy as np from serial import Serial import time from co…...

c语言:编译和链接(详解)

前言 要将编译和链接&#xff0c;就不得不提及编译器是如何运作的&#xff0c;虽然这部分知识是针对于要创造编译器和创作语言的人所需要清楚的&#xff0c;但作为c语言的学习者也需要了解一下&#xff0c;修炼内功&#xff0c;尤其是对于想学习c的人而言。 编译器的运作过程…...

数据结构【单链表操作大全详解】【c语言版】(只有输入输出为了方便用的c++)

单链表操作的C/C实现详解 在数据结构中&#xff0c;单链表是一种非常基础且重要的数据结构。它由一系列节点组成&#xff0c;每个节点包含数据和指向下一个节点的指针。今天我们就来深入探讨用C/C实现的单链表及其各种操作。 一、单链表的定义 const int N 1e5; //单链表 t…...

leetcode27.删除有序数组中的重复项

目录 问题描述判题标准示例提示 具体思路思路一思路二 代码实现 问题描述 给你一个非严格递增排列的数组nums&#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素只出现一次&#xff0c;返回删除后数组的新长度。元素的相对顺序应该保持一致 。然后返回nums中唯一元…...

[c语言日寄]越界访问:意外的死循环

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…...

【c++11】包装器

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 包装器&#xff08;Wrapper&#xff09; 是一个常见的编程设计模式&#xff0c;通常用于封装或“包装”某个现有的对象、函数、数据结构或者操作&#xff0c;以提供额外的功能或简化接口。…...

信息学奥赛一本通 1422:【例题1】活动安排

【题目链接】 ybt 1422&#xff1a;【例题1】活动安排 【题目考点】 1. 贪心 【解题思路】 该题属于区间选点问题&#xff0c;ybt 1324&#xff1a;【例6.6】整数区间 是给定一些区间&#xff0c;选择一些点使得每个区间范围内至少有1个点。 本题为&#xff1a;给定一些区…...

数据库、数据仓库、数据湖有什么不同

数据库、数据仓库和数据湖是三种不同的数据存储和管理技术&#xff0c;它们在用途、设计目标、数据处理方式以及适用场景上存在显著差异。以下将从多个角度详细说明它们之间的区别&#xff1a; 1. 数据结构与存储方式 数据库&#xff1a; 数据库主要用于存储结构化的数据&…...

llama.cpp LLM_CHAT_TEMPLATE_DEEPSEEK_3

llama.cpp LLM_CHAT_TEMPLATE_DEEPSEEK_3 1. LLAMA_VOCAB_PRE_TYPE_DEEPSEEK3_LLM2. static const std::map<std::string, llm_chat_template> LLM_CHAT_TEMPLATES3. LLM_CHAT_TEMPLATE_DEEPSEEK_3References 不宜吹捧中国大语言模型的同时&#xff0c;又去贬低美国大语言…...

深度学习的应用场景及常用技术

深度学习作为机器学习的一个重要分支&#xff0c;在众多领域都有广泛的应用&#xff0c;以下是一些主要的应用场景及常用技术。 1.应用场景 1. 计算机视觉 图像分类 描述&#xff1a;对图像中的内容进行分类&#xff0c;识别出图像中物体所属的类别。例如&#xff0c;在安防领…...

小程序项目-购物-首页与准备

前言 这一节讲一个购物项目 1. 项目介绍与项目文档 我们这里可以打开一个网址 https://applet-base-api-t.itheima.net/docs-uni-shop/index.htm 就可以查看对应的文档 2. 配置uni-app的开发环境 可以先打开这个的官网 https://uniapp.dcloud.net.cn/ 使用这个就可以发布到…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...