当前位置: 首页 > 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 在开发板上运行的…...

关于低代码技术架构的思考

我们经常会看到很多低代码系统的技术架构图&#xff0c;而且经常看不懂。是因为技术架构图没有画好&#xff0c;还是因为技术不够先进&#xff0c;有时候往往都不是。 比如下图&#xff1a; 一个开发者&#xff0c;看到的视角往往都是技术层面&#xff0c;你给用户讲React18、M…...

如何使用 ChatBox AI 简化本地模型对话操作

部署模型请看上一篇帖子&#xff1a;本地部署DeepSeek教程&#xff08;Mac版本&#xff09;-CSDN博客 使用 ChatBox AI 简化本地模型对话操作&#xff1a; 打开 ChatBox AI 官网&#xff1a;Chatbox AI官网&#xff1a;办公学习的AI好助手&#xff0c;全平台AI客户端&#xf…...

缩位求和——蓝桥杯

1.题目描述 在电子计算机普及以前&#xff0c;人们经常用一个粗略的方法来验算四则运算是否正确。 比如&#xff1a;248153720248153720 把乘数和被乘数分别逐位求和&#xff0c;如果是多位数再逐位求和&#xff0c;直到是 1 位数&#xff0c;得 24814>145 156 56 而…...

hexo部署到github page时,hexo d后page里面绑定的个人域名消失的问题

Hexo 部署博客到 GitHub page 后&#xff0c;可以在 setting 中的 page 中绑定自己的域名&#xff0c;但是我发现更新博客后绑定的域名消失&#xff0c;恢复原始的 githubio 的域名。 后面搜索发现需要在 repo 里面添加 CNAME 文件&#xff0c;内容为 page 里面绑定的域名&…...

neo4j入门

文章目录 neo4j版本说明部署安装Mac部署docker部署 neo4j web工具使用数据结构图数据库VS关系数据库 neo4j neo4j官网Neo4j是用ava实现的开源NoSQL图数据库。Neo4作为图数据库中的代表产品&#xff0c;已经在众多的行业项目中进行了应用&#xff0c;如&#xff1a;网络管理&am…...

代码随想录——回溯

文章目录 组合组合总数电话号码的字母组合组合总数组合总数Ⅱ分割回文串复原IP地址子集子集Ⅱ非递减子序列去重的实现方法方法 1&#xff1a;**排序 跳过重复元素**方法 2&#xff1a;**使用哈希表或数组记录已使用的数字** 去重的完整示例总结本题代码 全排列全排列Ⅱ重新安排…...

独立游戏RPG回顾:高成本

刚看了某纪录片&#xff0c; 内容是rpg项目的回顾。也是这个以钱为核心话题的系列的最后一集。 对这期特别有代入感&#xff0c;因为主角是曾经的同事&#xff0c;曾经在某天晚上听过其项目组的争论。 对其这些年的起伏特别的能体会。 主角是制作人&#xff0c;在访谈中透露这…...

SQLModel入门

目录 概述快速开始官方教程简单使用样例 概述 SQLModel 是一个 ORM 框架&#xff0c;其基于 SQLAlchemy 和 Pydantic&#xff0c;其中 SQLALchemy 提供底层 ORM 能力&#xff0c;Pydantic 提供类型校验能力&#xff0c;SQLModel 中&#xff0c;一个 SQLModel model 既是一个 S…...

关于MySQL InnoDB存储引擎的一些认识

文章目录 一、存储引擎1.MySQL中执行一条SQL语句的过程是怎样的&#xff1f;1.1 MySQL的存储引擎有哪些&#xff1f;1.2 MyIsam和InnoDB有什么区别&#xff1f; 2.MySQL表的结构是什么&#xff1f;2.1 行结构是什么样呢&#xff1f;2.1.1 NULL列表&#xff1f;2.1.2 char和varc…...

【学习笔记】深度学习网络-正则化方法

作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 在之前的文章中介绍了深度学习中用…...

NVIDIA (英伟达)的 GPU 产品应用领域

游戏娱乐领域 PC 游戏&#xff1a;NVIDIA 的 GeForce 系列 GPU 是 PC 游戏玩家的首选之一。能实现实时光线追踪、高分辨率渲染等&#xff0c;使游戏画面更加逼真&#xff0c;如《赛博朋克 2077》等支持光线追踪的游戏&#xff0c;在 NVIDIA GPU 的加持下&#xff0c;可呈现出真…...

Docker快速部署高效照片管理系统LibrePhotos搭建私有云相册

文章目录 前言1.关于LibrePhotos2.本地部署LibrePhotos3.LibrePhotos简单使用4. 安装内网穿透5.配置LibrePhotos公网地址6. 配置固定公网地址 前言 想象一下这样的场景&#xff1a;你有一大堆珍贵的回忆照片&#xff0c;但又不想使用各种网盘来管理。怎么办&#xff1f;别担心…...

goframe 多语言国际化解决方案

项目背景 本项目采用基于JSON配置的多语言国际化&#xff08;i18n&#xff09;解决方案&#xff0c;支持多种语言的无缝切换和本地化。 目录结构 manifest/ └── i18n/├── zh.json # 简体中文├── zh-tw.json # 繁体中文├── en.json # 英语├…...

mysql如何修改密码

在MySQL中修改密码可以通过多种方式完成&#xff0c;具体取决于你的MySQL版本和你是否有足够的权限。以下是一些常用的方法来修改MySQL用户的密码&#xff1a; 方法1: 使用ALTER USER命令 这是最常用的方法&#xff0c;适用于MySQL 5.7及以上版本。 ALTER USER usernameloca…...

17.2 图形绘制8

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 17.2.10 重绘 先看以下例子&#xff1a; 【例 17.28】【项目&#xff1a;code17-028】绘制填充矩形。 private void button1_Clic…...

Java基础知识总结(三十八)--读取数据

使用Reader体系&#xff0c;读取一个文本文件中的数据。返回 -1 &#xff0c;标志读到结尾。 import java.io.*; class { public static void main(String[] args) throws IOException { /* 创建可以读取文本文件的流对象&#xff0c;让创建好的流对象和指定的文件相关联。…...

【并查集】

并查集&#xff08;Disjoint Set Union&#xff0c;DSU&#xff09;是一种用于处理不相交集合的数据结构&#xff0c;主要支持两种操作&#xff1a;查找&#xff08;Find&#xff09;和合并&#xff08;Union&#xff09;。它在解决连通性问题、图论问题以及动态连通性等问题时…...

SQL NOW() 函数详解

SQL NOW() 函数详解 引言 在SQL数据库中&#xff0c;NOW() 函数是一个常用的日期和时间函数&#xff0c;用于获取当前的时间戳。本文将详细介绍 NOW() 函数的用法、参数、返回值以及在实际应用中的注意事项。 函数概述 NOW() 函数返回当前的日期和时间&#xff0c;格式为 Y…...

[EAI-023] FAST,机器人动作专用的Tokenizer,提高VLA模型的能力和训练效率

Paper Card 论文标题&#xff1a;FAST: Efficient Action Tokenization for Vision-Language-Action Models 论文作者&#xff1a;Karl Pertsch, Kyle Stachowicz, Brian Ichter, Danny Driess, Suraj Nair, Quan Vuong, Oier Mees, Chelsea Finn, Sergey Levine 论文链接&…...

Rust 条件语句

Rust 条件语句 在编程语言中&#xff0c;条件语句是进行决策和实现分支逻辑的关键。Rust 语言作为一门系统编程语言&#xff0c;其条件语句的使用同样至关重要。本文将详细介绍 Rust 中的条件语句&#xff0c;包括其基本用法、常见场景以及如何避免常见错误。 基本用法 Rust…...

Windows 上安装 PostgreSQL

Windows 上安装 PostgreSQL PostgreSQL 是一款功能强大的开源对象-关系型数据库系统,它具有出色的扩展性和稳定性。本文将详细介绍在 Windows 操作系统上安装 PostgreSQL 的步骤和注意事项。 1. 准备工作 在开始安装 PostgreSQL 之前,请确保您的计算机满足以下要求: 操作…...

UE 5.3 C++ 对垃圾回收的初步认识

一.UObject的创建 UObject 不支持构造参数。 所有的C UObject都会在引擎启动的时候初始化&#xff0c;然后引擎会调用其默认构造器。如果没有默认的构造器&#xff0c;那么 UObject 将不会编译。 有修改父类参数的需求&#xff0c;就使用指定带参构造 // Sets default value…...

解码,蓝桥杯2020G

a2b 解码后&#xff1a;aab ​ #include<iostream> using namespace std; typedef struct Node {char data;int size;Node* next; }Node,*Linklist; char* scan(char str[],int size) {int i 0;Linklist head new Node;Linklist rear head;while (i<size-1) {Lin…...

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(一)

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;贪心算法篇–CSDN博客 文章目录 一.贪心算法1.什么是贪心算法2.贪心算法的特点 二.例题1.柠…...

Python3 + Qt5:实现AJAX异步更新UI

使用 Python 和 Qt5 开发时异步加载数据的方法 在开发使用 Python 和 Qt5 的应用程序时&#xff0c;为了避免在加载数据时界面卡顿&#xff0c;可以采用异步加载的方式。以下是几种实现异步加载的方法&#xff1a; 1. 使用多线程&#xff08;QThread&#xff09; 通过将数据…...

Windows系统中Docker可视化工具对比分析,Docker Desktop,Portainer,Rancher

Docker可视化工具对比分析&#xff0c;Docker Desktop&#xff0c;Portainer&#xff0c;Rancher Windows系统中Docker可视化工具对比分析1. 工具概览2. Docker Desktop官网链接&#xff1a;主要优点&#xff1a;主要缺点&#xff1a;版本更新频率&#xff1a; 3. Portainer官网…...

从ai产品推荐到利用cursor快速掌握一个开源项目再到langchain手搓一个Text2Sql agent

目录 0. 经验分享&#xff1a;产品推荐 1. 经验分享&#xff1a;提示词优化 2. 经验分享&#xff1a;使用cursor 阅读一篇文章 3. 经验分享&#xff1a;使用cursor 阅读一个完全陌生的开源项目 4. 经验分享&#xff1a;手搓一个text2sql agent &#xff08;使用langchain l…...

curope python安装

目录 curope安装 测试: 报错:libc10.so: cannot open shared object file: No such file or directory 解决方法: curope安装 git clone : GitHub - Junyi42/croco at bd6f4e07d5c4f13ae5388efc052dadf142aff754 cd models/curope/ python setup.py build_ext --inplac…...

低代码产品插件功能一览

下图是统计的目前市面上流行的低代码、零代码产品的插件功能。 产品名称 产品类型 官方插件数量 支持拓展 官方插件功能 宜搭 零代码 3 暂不支持 云打印、CAD看图、打印表单详情 微搭 低代码 1 暂不支持 小程序 明道云 低代码 2 支持 视图、工作流节点 简道…...

流浪 Linux: 外置 USB SSD 安装 ArchLinux

注: ArchLinux 系统为滚动更新, 变化很快, 所以本文中的安装方法可能很快就过时了, 仅供参考. 实际安装时建议去阅读官方文档. 最近, 突然 (也没有那么突然) 有了一大堆 PC: 4 个笔记本, 2 个台式主机 (M-ATX 主板), 1 个小主机 (迷你主机). 嗯, 多到用不过来. 但是, 窝又不能…...

开启 AI 学习之旅:从入门到精通

最近 AI 真的超火,不管是工作还是生活里,到处都能看到它的身影。好多小伙伴都跑来问我,到底该怎么学 AI 呢?今天我就把自己学习 AI 的经验和心得分享出来,希望能帮到想踏入 AI 领域的朋友们! 一、学习内容有哪些 (一)编程语言 Python 绝对是首选!它在 AI 领域的生态…...

笔记:使用ST-LINK烧录STM32程序怎么样最方便?

一般板子在插件上&#xff0c; 8脚 3.3V;9脚 CLK;10脚 DIO;4脚GND ST_Link 19脚 3.3V;9脚 CLK;7脚 DIO;20脚 GND 烧录软件&#xff1a;ST-LINK Utility&#xff0c;Keil_5; ST_Link 接口针脚定义&#xff1a; 按定义连接ST_Link与电路板&#xff1b; 打开STM32 ST-LINK Uti…...

开发环境搭建-4:WSL 配置 docker 运行环境

在 WSL 环境中构建&#xff1a;WSL2 (2.3.26.0) Oracle Linux 8.7 官方镜像 基本概念说明 容器技术 利用 Linux 系统的 文件系统&#xff08;UnionFS&#xff09;、命名空间&#xff08;namespace&#xff09;、权限管理&#xff08;cgroup&#xff09;&#xff0c;虚拟出一…...

925.长按键入

目录 一、题目二、思路三、解法四、收获 一、题目 你的朋友正在使用键盘输入他的名字 name。偶尔&#xff0c;在键入字符 c 时&#xff0c;按键可能会被长按&#xff0c;而字符可能被输入 1 次或多次。 你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字&am…...

【数据采集】案例01:基于Scrapy采集豆瓣电影Top250的详细数据

基于Scrapy采集豆瓣电影Top250的详细数据 Scrapy 官方文档:https://docs.scrapy.org/en/latest/豆瓣电影Top250官网:https://movie.douban.com/top250写在前面 实验目的:基于Scrapy框架采集豆瓣电影Top250的详细数据。 电脑系统:Windows 使用软件:PyCharm、Navicat Python…...

doris:主键模型的导入更新

这篇文档主要介绍 Doris 主键模型基于导入的更新。 整行更新​ 使用 Doris 支持的 Stream Load、Broker Load、Routine Load、Insert Into 等导入方式&#xff0c;向主键模型&#xff08;Unique 模型&#xff09;导入数据时&#xff0c;如果没有相应主键的数据行&#xff0c;…...

日志2025.2.1

日志2025.2.1 1.做了敌人状态机 public class EnermyStateMachine { public EnermyState currentState { get; private set; } public void InitializeState(EnermyState startState) { currentState startState; currentState.Enter(); } public void Change…...

RK3568使用QT操作LED灯

文章目录 一、QT中操作硬件设备思路Linux 中的设备文件操作硬件设备的思路1. 打开设备文件2. 写入数据到设备3. 从设备读取数据4. 设备控制5. 异常处理在 Qt 中操作设备的典型步骤实际应用中的例子:控制 LED总结二、QT实战操作LED灯设备1. `mainwindow.h` 头文件2. `mainwindo…...

Rank-analysis-1.2——一款基于LCU API的排位分析工具,大四学生独立开发

LOL Rank Record Analysis&#xff1a;一款基于LCU API的排位分析工具&#xff0c;大四学生独立开发&#xff01; 大家好&#xff01;我是河南科技学院的大四学生&#xff0c;今天给大家分享一个我自己开发的软件——LOL Rank Record Analysis。这是一个基于 Riot 提供的 LCU …...

关于系统重构实践的一些思考与总结

文章目录 一、前言二、系统重构的范式1.明确目标和背景2.兼容屏蔽对上层的影响3.设计灰度迁移方案3.1 灰度策略3.2 灰度过程设计3.2.1 case1 业务逻辑变更3.2.2 case2 底层数据变更&#xff08;数据平滑迁移&#xff09;3.2.3 case3 在途新旧流程兼容3.2.4 case4 接口变更3.2.5…...

代码随想录-训练营-day17

235. 二叉搜索树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/class S…...

C++,STL,【目录篇】

文章目录 一、简介二、内容提纲第一部分&#xff1a;STL 概述第二部分&#xff1a;STL 容器第三部分&#xff1a;STL 迭代器第四部分&#xff1a;STL 算法第五部分&#xff1a;STL 函数对象第六部分&#xff1a;STL 高级主题第七部分&#xff1a;STL 实战应用 三、写作风格四、…...

《数据可视化新高度:Graphy的AI协作变革》

在数据洪流奔涌的时代&#xff0c;企业面临的挑战不再仅仅是数据的收集&#xff0c;更在于如何高效地将数据转化为洞察&#xff0c;助力决策。Graphy作为一款前沿的数据可视化工具&#xff0c;凭借AI赋能的团队协作功能&#xff0c;为企业打开了数据协作新局面&#xff0c;重新…...

abc 390 D(暴搜 复杂度用 bell数 证明 n 的集合的划分方法的数目)

D题意&#xff1a; 将 长度为 N 的数组 划分为集合 有多少种不同的 异或和 这道题做出来和bell 数没什么关系&#xff0c;如果要证明复杂度那么就需要bell 数 #include <bits/stdc.h> using namespace std; typedef pair<int, int> PII; #define int long long i…...

AJAX综合案例——图书管理

黑马程序员视频地址&#xff1a; AJAX-Day02-10.案例_图书管理AJAX-Day02-10.案例_图书管理_总结_V1.0是黑马程序员前端AJAX入门到实战全套教程&#xff0c;包含学前端框架必会的&#xff08;ajaxnode.jswebpackgit&#xff09;&#xff0c;一套全覆盖的第25集视频&#xff0c…...

计算机网络 笔记 网络层 3

IPv6 IPv6 是互联网协议第 6 版&#xff08;Internet Protocol Version 6&#xff09;的缩写&#xff0c;它是下一代互联网协议&#xff0c;旨在解决 IPv4 面临的一些问题&#xff0c;以下是关于 IPv6 的详细介绍&#xff1a; 产生背景&#xff1a; 随着互联网的迅速发展&…...

Web服务器启动难题:Spring Boot框架下的异常处理解析

摘要 在尝试启动Web服务器时遇到了无法启动的问题&#xff0c;具体错误为org.springframework.boot.web.server.WebServerException。这一异常表明Spring Boot框架在初始化Web服务器过程中出现了故障。通常此类问题源于配置文件错误、端口冲突或依赖项缺失等。排查时应首先检查…...

在LINUX上安装英伟达CUDA Toolkit

下载安装包 wget https://developer.download.nvidia.com/compute/cuda/12.8.0/local_installers/cuda-repo-rhel8-12-8-local-12.8.0_570.86.10-1.x86_64.rpm 安装RPM包 sudo rpm -i cuda-repo-rhel8-12-8-local-12.8.0_570.86.10-1.x86_64.rpm sudo dnf clean all sudo dnf…...

计算机视觉和图像处理

计算机视觉与图像处理的最新进展 随着人工智能技术的飞速发展&#xff0c;计算机视觉和图像处理作为其中的重要分支&#xff0c;正逐步成为推动科技进步和产业升级的关键力量。 一、计算机视觉的最新进展 计算机视觉&#xff0c;作为人工智能的重要分支&#xff0c;主要研究如…...