QT-自定义参数设计框架软件
QT-自定义参数设计框架软件
- Chapter1 QT-自定义参数设计框架软件
- 前言
- 一、演示效果
- 二、使用步骤
- 1.应用进行参数注册
- 2.数据库操作单例对象
- 3.参数操作单例对象
- 三、下载链接
- Chapter2 Qt中管理配置参数(QSettings、单例模式)
- 1 前言
- 2 QSettings类
- ini文件
- 写ini文件
- 读取Ini文件
- 3 单例模式
- 4 参考文献
- Chapter3 基于QT的参数设置界面
- 介绍
- 使用方法
- 1.在你自己的工程中定义一个参数的结构体
- 2.在你自己工程的界面中拖入一个QTableView控件,将其提升为DBTableParamView.h
- 3.Test_TableView.h
- 4.Test_TableView.cpp
- Chapter4 QT实现一个系统参数管理窗口
- 1. 配置参数类
- 2. 管理界面 QWidget
- 3. 主程序
- 代码说明
- 1. ConfigManager 类:
- 2. ConfigWidget 类:
- 3. 主程序:
- Chapter5 Qt平台自动化项目开发基础框架
- 一、程序框架结构
- 二、GUI界面开发
- 1)登录界面
- 2)主界面
- 3)手动控制界面Qwidget
- 三、主控制线程
- 1)多线程之间的数据共享
- 2)主控制逻辑
- 四、外设模块线程
- 五、日志
- 1)头文件:
- 2)调用方法:
Chapter1 QT-自定义参数设计框架软件
原文链接:https://blog.csdn.net/u013083044/article/details/137160536
前言
常用本地数据参数通常使用的是xml等文本的格式,进行本地的数据参数的存储。这种参数的保存方式有个致命的一点,就是可以存在参数的丢失。特别是在软件异常退出的情况下发生。
针对此等情况,我们现在使用的是sqllite的本地存储方式进行设计,并且尽量将参数的格式通用化。
一、演示效果
二、使用步骤
1.应用进行参数注册
代码如下:
void QSettingTools::registerParams()
{if (1){QString strGroupName = u8"用户参数";cParamOperator::getInstance()->registerQString(strGroupName, "User", "sa", QString(u8"用户名称"));cParamOperator::getInstance()->registerQString(strGroupName, "Passord", "123456", QString(u8"用户密码"));cParamOperator::getInstance()->registerInt(strGroupName, "Index", 0, 0, 10000, QString(u8"序号"));cParamOperator::getInstance()->registerFloat(strGroupName, "Time", 1.5, 0, 10000, QString(u8"时间,单位秒"));}if (1){QString strGroupName = u8"系统参数";cParamOperator::getInstance()->registerQString(strGroupName, "FactoryId", "123433ad", QString(u8"工厂ID"));}cParamOperator::getInstance()->autoRemoveInvialParams();}
2.数据库操作单例对象
代码如下:
#include "ParamOperator.h"
#include "SqlLiteDatabase.h"
#include <QFile>
#include <QMutex>
#include <QXmlStreamWriter>
#include <QDebug>
#include <QCoreApplication>
#include <QDomDocument>
#include <QApplication>cParamOperator g_userParam; // 用户参数struct cAppBaserData
{QHash<QString, QString> strScrTranHash;
};cParamOperator::cParamOperator(): d_ptr(new cParamManagerData)
{m_strConfig = "Setting.db";
}cParamOperator::~cParamOperator()
{delete d_ptr;d_ptr = nullptr;
}// 获取一个实例
cParamOperator *cParamOperator::getInstance()
{static cParamOperator manager;return &manager;
}// 设置app安装目录
void cParamOperator::setAppExePath(QString strPath)
{m_strAppExePath = strPath;cSqlLiteDatabase::getInstance()->setAppExePath(strPath);initialParams();
}// 注册整形参数
void cParamOperator::registerInt(QString strGroup, QString strName, int nMin, int nSet, int nMax, QString strDescribe)
{sParamItem_t paramItem; paramItem.strGroupName = strGroup;paramItem.strParamType = PARAM_TYPE_INT; paramItem.strName = strName;paramItem.strDescribe = strDescribe;paramItem.minVal = nMin;paramItem.setVal = nSet;paramItem.maxVal = nMax;registerParam(paramItem);
}// 注册浮点型参数
void cParamOperator::registerFloat(QString strGroup, QString strName, float fMin, float fSet, float fMax, QString strDescribe)
{sParamItem_t paramItem;paramItem.strGroupName = strGroup;paramItem.strParamType = PARAM_TYPE_FLOAT;paramItem.strName = strName;paramItem.strDescribe = strDescribe;paramItem.minVal = fMin;paramItem.setVal = fSet;paramItem.maxVal = fMax;registerParam(paramItem);
}// 注册字符串参数
void cParamOperator::registerQString(QString strGroup, QString strName, QString strSet, QString strDescribe)
{sParamItem_t paramItem;paramItem.strGroupName = strGroup;paramItem.strParamType = PARAM_TYPE_STRING;paramItem.strName = strName;paramItem.strDescribe = strDescribe;paramItem.minVal = "";paramItem.setVal = strSet;paramItem.maxVal = "";registerParam(paramItem);
}// 修改参数值
void cParamOperator::setParam(QString strName, QVariant var)
{auto pParam = getParam(strName);if (pParam != nullptr){pParam->setVal = var;g_userParam.updateParam(*pParam);}
}// 获取整数
int cParamOperator::getParamInt(QString strName)
{auto param = getParam(strName);if (param != nullptr){return param->setVal.toInt();}return -12345;
}// 获取浮点数
float cParamOperator::getParamFloat(QString strName)
{auto param = getParam(strName);if (param != nullptr){return param->setVal.toFloat();}return -12345;
}// 获取字符串
QString cParamOperator::getParamString(QString strName)
{auto param = getParam(strName);if (param != nullptr){return param->setVal.toString();}return "Error";
}// 获取参数组名链表
QStringList cParamOperator::getGroupNameList()
{QStringList strNameList;auto paramList = params();for (size_t i = 0; i < paramList->size(); i++){auto item = paramList->at(i);if (!strNameList.contains(item.strGroupName)){strNameList << item.strGroupName;}}return strNameList;
}// 获取某个组的所有参数
QVector<sParamItem_t> cParamOperator::getGroupParam(QString strGroupName)
{QVector<sParamItem_t> list;auto paramList = params();for (size_t i = 0; i < paramList->size(); i++){auto item = paramList->at(i);if (item.strGroupName == strGroupName){list << item;}}return list;
}// 初始化一个参数,插入内存中
int cParamOperator::initialParamItem(sParamItem_t ¶m)
{// 注册过的就直接返回就行了if (d_ptr->mapName.contains(param.strName))return d_ptr->mapName.value(param.strName);// 链表没有,插入链表记录auto index = d_ptr->vecParam.count();d_ptr->vecParam.append(param);d_ptr->mapName.insert(param.strName, index);// 如果有组名,直接插入到组名对应得链表里面if (d_ptr->mapGroup.contains(param.strGroupName)){for (auto iter = d_ptr->mapGroup.begin(); iter != d_ptr->mapGroup.end(); iter++){if (iter.key() == param.strGroupName){iter.value().insert(param.strName, index);break;}}}else{// 重新建立一个组名链表QHash<QString, int> groupItem;groupItem.insert(param.strName, index);d_ptr->mapGroup.insert(param.strGroupName, groupItem);}return index;
}// 刷新参数
bool cParamOperator::updateParam(sParamItem_t ¶m, bool bUpdateAll)
{bool bRet = false;if (!param.strName.isEmpty()){if (bUpdateAll){// 整个项都更新cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagSetVal, param.setVal.toString());cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagGroupName, param.strGroupName);cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagType, param.strParamType);cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagMinVal, param.minVal.toString());cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagMaxVal, param.maxVal.toString());}else{cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagSetVal, param.setVal.toString());}}return bRet;
}// 通过参数名获取参数
sParamItem_t *cParamOperator::getParam(QString strName)
{return getParam(d_ptr->mapName.value(strName, -1));
}// 通过索引获取参数
sParamItem_t *cParamOperator::getParam(int nIndex)
{if (nIndex >= 0 && nIndex < d_ptr->vecParam.count())return &d_ptr->vecParam[nIndex];return nullptr;
}// 所有的参数
QVector<sParamItem_t>* cParamOperator::params()
{return &d_ptr->vecParam;
}// 删除参数
bool cParamOperator::removeParam(QString strName)
{for (int i = 0; i < d_ptr->vecParam.size(); i++){if (d_ptr->vecParam[i].strName == strName){d_ptr->vecParam.removeAt(i);break;}}return cSqlLiteDatabase::getInstance()->remove(m_strTableName, g_strTagName, strName);
}// 注册参数
int cParamOperator::registerParam(sParamItem_t ¶m)
{// 缓存起来if (!m_strResisterNameList.contains(param.strName))m_strResisterNameList << param.strName;// 注册过,组名不同得话就重新修改所属得组名,如果没有就直接返回if (d_ptr->mapName.contains(param.strName)){auto paramTemp = getParam(param.strName);// 如果组名不同,就换新注册得组名if (paramTemp->strGroupName != param.strGroupName){auto itGroup = d_ptr->mapGroup.find(paramTemp->strGroupName);if (itGroup != d_ptr->mapGroup.end()){// 删掉旧的,再插入新得auto qTempHash = itGroup.value();d_ptr->mapGroup.erase(itGroup);d_ptr->mapGroup.insert(param.strGroupName, qTempHash);}// 更新为最新注册得paramTemp->strGroupName = param.strGroupName;cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagGroupName, param.strGroupName);}cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagMinVal, param.minVal.toString());cSqlLiteDatabase::getInstance()->update(d_ptr->strTableName, g_strTagName, param.strName, g_strTagMaxVal, param.maxVal.toString());auto pItem = getParam(param.strName);if (pItem != nullptr){pItem->strDescribe = param.strDescribe;pItem->minVal = param.minVal;pItem->maxVal = param.maxVal;if (pItem->strDescribe.indexOf("《****")!= -1){param.strRemarks = "";}pItem->strRemarks = param.strRemarks;}return d_ptr->mapName.value(param.strName);}// 链表没有,插入链表记录auto index = d_ptr->vecParam.count();d_ptr->vecParam.append(param);d_ptr->mapName.insert(param.strName, index);QHash<QString, QString> itemHash;// 同时插入数据表itemHash.insert(g_strTagGroupName, param.strGroupName);itemHash.insert(g_strTagType, param.strParamType);itemHash.insert(g_strTagName, param.strName);itemHash.insert(g_strTagSetVal, param.setVal.toString());itemHash.insert(g_strTagMinVal, param.minVal.toString());itemHash.insert(g_strTagMaxVal, param.maxVal.toString());itemHash.insert(g_strTagDescribe, param.strDescribe);cSqlLiteDatabase::getInstance()->insert(d_ptr->strTableName,itemHash);// 如果有组名,直接插入到组名对应得链表里面if (d_ptr->mapGroup.contains(param.strGroupName)){for (auto iter = d_ptr->mapGroup.begin(); iter != d_ptr->mapGroup.end(); iter++){if (iter.key() == param.strGroupName){iter.value().insert(param.strName, index);break;}}}else{// 重新建立一个组名链表QHash<QString, int> groupItem;groupItem.insert(param.strName, index);d_ptr->mapGroup.insert(param.strGroupName, groupItem);}return index;
}// 全部注册后,删除无效参数(注意:所有的参数要全部注册完成后调用)
void cParamOperator::autoRemoveInvialParams()
{for (auto itSqlName:m_strSqlParamNameList){// 如果程序注册的链表不存在就需要删除数据库的参数if (!m_strResisterNameList.contains(itSqlName)){removeParam(itSqlName);}}// 重新排序,按注册顺序显示QVector<sParamItem_t> tempAllParam = d_ptr->vecParam;d_ptr->vecParam.clear();d_ptr->mapName.clear();d_ptr->mapGroup.clear();for (auto itRegister:m_strResisterNameList){for (auto& itFind : tempAllParam){if (itFind.strName == itRegister){initialParamItem(itFind);break;}}}}// 初始化数据参数
int cParamOperator::initialParams(QString strTableName)
{if (strTableName.isEmpty())return -1;// 先打开数据库cSqlLiteDatabase::getInstance()->openDb(m_strConfig);m_strTableName = strTableName;static QMutex mutex;mutex.lock();// 创建数据库表QStringList strList;strList << g_strTagGroupName << g_strTagType<< g_strTagName << g_strTagSetVal<< g_strTagMinVal << g_strTagMaxVal<< g_strTagDescribe << g_strTagIsTemp;d_ptr->strTableName = strTableName;cSqlLiteDatabase::getInstance()->createTable(strTableName, strList);// 查找数据库表的所有参数auto dataHashList = cSqlLiteDatabase::getInstance()->select(strTableName);for (int i = 0; i < dataHashList.size(); i++){auto hash = dataHashList[i];sParamItem_t paramItem;paramItem.strGroupName = hash.find(g_strTagGroupName) != hash.end() ? hash.find(g_strTagGroupName).value() : "";paramItem.strParamType = hash.find(g_strTagType) != hash.end() ? hash.find(g_strTagType).value() : "";paramItem.strName = hash.find(g_strTagName) != hash.end() ? hash.find(g_strTagName).value() : "";paramItem.setVal = hash.find(g_strTagSetVal) != hash.end() ? hash.find(g_strTagSetVal).value() : "";paramItem.minVal = hash.find(g_strTagMinVal) != hash.end() ? hash.find(g_strTagMinVal).value() : "";paramItem.maxVal = hash.find(g_strTagMaxVal) != hash.end() ? hash.find(g_strTagMaxVal).value() : "";paramItem.strDescribe = hash.find(g_strTagDescribe) != hash.end() ? hash.find(g_strTagDescribe).value() : "";// 缓存数据库的参数名称if (!m_strSqlParamNameList.contains(paramItem.strName))m_strSqlParamNameList << paramItem.strName;initialParamItem(paramItem);}mutex.unlock();return 0;
}
3.参数操作单例对象
代码如下:
#include "SqlLiteDatabase.h"
#include <QDir>
#include <QDateTime>
#include <QApplication>cSqlLiteDatabase::cSqlLiteDatabase(QObject *parent): QObject(parent)
{}cSqlLiteDatabase::~cSqlLiteDatabase()
{closeDb();
}// 设置appExe安装目录
void cSqlLiteDatabase::setAppExePath(QString strPath)
{m_strExePath = strPath;createDir();
}// 获取实例
cSqlLiteDatabase *cSqlLiteDatabase::getInstance()
{static cSqlLiteDatabase obj;return &obj;
}// 创建配置文件夹
void cSqlLiteDatabase::createDir()
{QDir dir;m_strConfigPath = m_strExePath + QString("/Config");if (!dir.exists(m_strConfigPath))dir.mkpath(m_strConfigPath);m_strDbDirPath = m_strConfigPath;
}// 打开数据库
QSqlDatabase cSqlLiteDatabase::openDb(QString strDatabaseName, QString strDbDirPath)
{if (!strDbDirPath.isEmpty())m_strDbDirPath = strDbDirPath;m_strDatabaseName = strDatabaseName;QSqlDatabase db;if (QSqlDatabase::contains(m_strDatabaseName))db = QSqlDatabase::database(m_strDatabaseName);else{QString strTempName = m_strDbDirPath + "/"+strDatabaseName;db = QSqlDatabase::addDatabase("QSQLITE", m_strDatabaseName);db.setDatabaseName(strTempName);db.setPassword("8888");db.setHostName("root");db.setUserName("root");}if (!db.open())qDebug() << db.lastError().text();elsem_bConnected = true;return db;}// 关闭数据库
bool cSqlLiteDatabase::closeDb()
{QSqlDatabase::removeDatabase(m_strDatabaseName);return true;
}// 创建表
bool cSqlLiteDatabase::createTable(QString strTableName, QStringList strHeaderNameList)
{bool bRet = true;if (true){if (strTableName.isEmpty() || strHeaderNameList.size() <= 0){bRet = false;return bRet;}auto findItem = m_strTableNameHeaderHash.find(strTableName);if (findItem == m_strTableNameHeaderHash.end())m_strTableNameHeaderHash.insert(strTableName, strHeaderNameList);QString strCreateTable = QString(u8"CREATE TABLE %1(").arg(strTableName);for (int i = 0; i < strHeaderNameList.size(); i++){if (i < (strHeaderNameList.size() - 1))strCreateTable = strCreateTable + strHeaderNameList[i] + QString(u8" VARCHAR(256)") + QString(",");elsestrCreateTable = strCreateTable + strHeaderNameList[i] + QString(u8" VARCHAR(256)") + QString(")");}bRet = excute(strCreateTable);}return bRet;
}// 删除表
bool cSqlLiteDatabase::dropTable(QString strTableName)
{if (strTableName.isEmpty())return false;QString strDrop = QString("DROP TABLE %1").arg(strTableName);return excute(strDrop);
}// 执行sql语句
bool cSqlLiteDatabase::excute(QString strSql)
{static QMutex mutex;mutex.lock();bool bRet = true;QSqlDatabase db = openDb(m_strDatabaseName);QString strConnectionName = db.connectionName();QSqlQuery query(db);query.prepare(strSql);bool success = query.exec(strSql);if (!success){qDebug() << "Error:" << query.lastError();bRet = false;}closeDb();mutex.unlock();return bRet;
}// 查找
QList<QStringList> cSqlLiteDatabase::seach(QString strTableName, QString strSql)
{QList<QStringList> temp;QSqlDatabase db = openDb(m_strDatabaseName);QString strConnectionName = db.connectionName();db.transaction(); // 开启事务查询QSqlQuery query("", db);query.exec(strSql);db.commit(); // 提交事务while (query.next()){QStringList keyValueHash;int nCount = query.record().count();for (size_t i = 0; i < nCount; i++)keyValueHash << query.record().value(i).toString();if (keyValueHash.size() > 0)temp << keyValueHash;}closeDb();return temp;
}// 查找
QList< QHash<QString/*name*/, QString/*value*/> > cSqlLiteDatabase::select(QString strTableName, QString strName, QString strValue)
{QList< QHash<QString, QString> > temp;if (strTableName.isEmpty() || strTableName.isEmpty())return temp;QSqlDatabase db = openDb(m_strDatabaseName);QString strConnectionName = db.connectionName();QString strSelect("");if (!strValue.isEmpty()&& !strName.isEmpty())strSelect = QString("SELECT * FROM %1 WHERE %2 = '%3';").arg(strTableName).arg(strName).arg(strValue);elsestrSelect = QString("SELECT * FROM %1;").arg(strTableName);db.transaction(); // 开启事务查询QSqlQuery query("", db);query.exec(strSelect);db.commit(); // 提交事务while (query.next()){QHash<QString, QString> keyValueHash;int nCount = query.record().count();for (size_t i = 0; i < nCount; i++)keyValueHash.insert(query.record().fieldName(i), query.record().value(i).toString());if (keyValueHash.size() > 0)temp << keyValueHash;}closeDb();return temp;
}// 插入数据
bool cSqlLiteDatabase::insert(QString strTableName, QHash<QString/*name*/, QString/*value*/> dataHash)
{bool bRet = false;if (strTableName.isEmpty())return bRet;QStringList strHeaderList;auto findItem = m_strTableNameHeaderHash.find(strTableName);if (findItem == m_strTableNameHeaderHash.end())return bRet;elsestrHeaderList = m_strTableNameHeaderHash[strTableName];if (true){QString strInsert = QString(u8"INSERT INTO %1 VALUES(").arg(strTableName);for (size_t j = 0; j < strHeaderList.size(); j++){QString strName = strHeaderList[j];QString strValue = "";auto findName = dataHash.find(strName);if (findName != dataHash.end())strValue = findName.value();if (j < (strHeaderList.size() - 1))strInsert = strInsert + QString(u8"'%1'").arg(strValue) + QString(",");elsestrInsert = strInsert + QString(u8"'%1'").arg(strValue) + QString(")");}bRet = excute(strInsert);} return bRet;
}// 更新数据
bool cSqlLiteDatabase::update(QString strTableName, QString strWhereName, QString strWhereValue, QString strUpdateName, QString strUpdateValue)
{bool bRet = false;if (strTableName.isEmpty())return bRet;QStringList strHeaderList;auto findItem = m_strTableNameHeaderHash.find(strTableName);if (findItem == m_strTableNameHeaderHash.end())return bRet;if (true){QString strUpdate = QString(u8"UPDATE %1 SET %2='%3' WHERE %4='%5';").arg(strTableName).arg(strUpdateName).arg(strUpdateValue).arg(strWhereName).arg(strWhereValue);bRet = excute(strUpdate);}return bRet;
}// 删除数据
bool cSqlLiteDatabase::remove(QString strTableName, QString strWhereName, QString strWhereValue)
{bool bRet = false;if (strTableName.isEmpty())return bRet;QStringList strHeaderList;auto findItem = m_strTableNameHeaderHash.find(strTableName);if (findItem == m_strTableNameHeaderHash.end())return bRet;if (true){QString strUpdate = QString(u8"DELETE FROM %1 WHERE %2='%3';").arg(strTableName).arg(strWhereName).arg(strWhereValue);bRet = excute(strUpdate);}return bRet;
}// 模糊查询
QStringList cSqlLiteDatabase::fuzzySearch(QString strTableName, QString strName, QString strLike)
{QStringList temp;if (strTableName.isEmpty() || strTableName.isEmpty())return temp;QSqlDatabase db = openDb(m_strDatabaseName);QString strConnectionName = db.connectionName();QString strSelect("");if (!strLike.isEmpty())strSelect = QString("SELECT %1 FROM %2 WHERE %3 LIKE '%%4%' LIMIT 0,10;").arg(strName).arg(strTableName).arg(strName).arg(strLike);elsereturn temp;db.transaction(); // 开启事务查询QSqlQuery query("", db);query.exec(strSelect);while (query.next()){QHash<QString, QString> keyValueHash;for (size_t i = 0; i < query.record().count(); i++){QString strName = query.record().fieldName(i);QString strValue = query.record().value(i).toString();if (!temp.contains(strValue) && (strLike != strValue))temp << strValue;}}db.commit(); // 提交事务closeDb();return temp;
}
三、下载链接
https://download.csdn.net/download/u013083044/89053778
Chapter2 Qt中管理配置参数(QSettings、单例模式)
原文链接:https://blog.csdn.net/yunshu0_0/article/details/133132431
1 前言
描述为什么需要配置类,以及为什么使用单利
工作过程中总是需要修改用于内网本地测试的版本或者外网测试版本,此时如果将参数直接导出到配置文件config.ini,只修改单个参数即可。当然很多地方也是如此,如修改用户界面主题等,使用配置文件保存配置参数是比较好的做法。在Qt中提供了QSettings类来格式导出和导入配置文件。
另外,我希望能够全局访问配置文件,并且要考虑多线程访问的情况,因此考虑实现为单例,在单例中需要考虑如何避免出现竞态条件?如何析构此单例类?
因此,本篇文章的主要目的是:
1. 创建单例类,可应对多线程情况,且能够正确析构
2. 结合QSettings,生成配置文件和读取配置文件
2 QSettings类
此处只描述本次使用的情景,还有额外的其他使用方法,如设置写入的位置是用户范围还是系统范围,设置公司、机构、软件名等等。
ini文件
.ini 文件是Initialization File的缩写,即初始化文件。INI文件被用来对操作系统或特定程序初始化或进行参数设置,以实现不同用户的要求。一般不用直接编辑这些.ini文件,应用程序的图形界面即可操作以实现相同的功能。
Qt中可使用QSettings存储.ini文件,.ini文件有[section]、key=value组成。其中section表示段,用于区分项目中不同的部分配置参数,key和value为键值对,value为QVariant类型。
[transmitData]
port=9600
station=1[canvas]
gridStyle=true
width=500
height=400
写ini文件
将配置参数写入QSettings对象如下:
//创建此对象,也可创建在堆上
QSettings settings("/home/petra/misc/myapp.ini", QSettings::IniFormat);//此处直接填写的是文件路径
setting.setIniCodec(QTextCodec::codecForName("UTF-8"));//设置文件编码,配置文件中使用中文时,这是必须的,否则乱码//beginGroup和endGroup可设置一级标题,中间的setValue为设置的参数
settings.beginGroup("mainwindow");
settings.setValue("size", win->size());
settings.setValue("fullScreen", win->isFullScreen());
settings.endGroup();
-
QSettings::Format取值为
QSettings::NativeFormat在windows平台可以读写windows注册表;
QSettings::IniFormat可以读写ini格式的配置文件 -
小心使用分号和逗号
分号在ini配置文件中是注释符号
逗号在ini配置文件中被解析为数组分隔符, -
层级结构除了使用上述的写法,也可以使用前缀的方式:
settings.setValue(“mainwindow/size”, win->size()); -
编码
配置文件如果有中文就需要设置为setting.setIniCodec(QTextCodec::codecForName(“UTF-8”))
保存float有问题,将其转换为double就好 -
其他
创建QSettings后,如果指定文件不存在,则创建文件
setValue不会立刻写入本地磁盘,只是修改了缓冲参数,因此在修改参数后,可调用settings->sync()或者syncFlushFileBuffers来同步
QSettings* setting = new QSettings("config.ini", QSettings::IniFormat);
setting->setIniCodec(QTextCodec::codecForName("UTF-8"));
setting->beginGroup("Transmit");
setting->setValue("port", 9600);
setting->setValue("width", 500);
setting->setValue("height", 400);
setting->endGroup();
setting->sync();
读取Ini文件
QSettings settings("/home/petra/misc/myapp.ini", QSettings::IniFormat);//此处直接填写的是文件路径//beginGroup和endGroup可设置一级标题,中间的setValue为设置的参数
settings.beginGroup("mainwindow");
QSize size = settings.value("size").toSize();
QColor = settings.value("color").value<QColor>();
settings.endGroup();
- 也可使用前缀读取方式
QSize size = settings.value("mainwindow\size").toSize();
- 默认值
如果不存在该标签项,则可以在第二个参数处填写默认值,否则就直接返回空QVariant()
对于QGUI的部分类型,如QPixmap、QColor等无法直接toColor,可使用QVariant.value来实现
自定义类型可以使用qRegisterMetaType(MyStruct)注册
3 单例模式
为了在全局使用同一个配置文件,或者日志文件,使用单例模式是比较适合的。使用步骤:
- 私有化构造函数、拷贝构造函数、析构函数、operator=等
- 内部创建静态对象指针,并创建静态函数返回该对象指针
- 注意
如何避免多线程形成竞态条件?
如何更好的析构该单例对象?
class ConfigParam
{
public:static ConfigParam* GetInstance(){if(m_pInstance == nullptr)//采用双判断,是为了减少不断的加锁、解锁的操作{static QMutex mutex;mutex.lock();if(m_pInstance == nullptr)m_pInstance = new ConfigParam;//加锁是为了避免线程检查条件后,开始创建对象还未创建时,切换线程,其他线程也继续创建对象mutex.unlock();}return m_pInstance;}class Deletor{public:~Deletor(){delete m_pInstance;m_pInstance = nullptr;}};private:static ConfigParam* m_pInstance;static Deletor deleter;//创建为静态对象,在清理全局区时,调用该软件的析构函数,会释放单例对象private:ConfigParam(){}~ConfigParam(){}ConfigParam(const ConfigParam&){}ConfigParam& operator=(const ConfigParam&){return *this;}
};ConfigParam* ConfigParam::m_pInstance = nullptr;
ConfigParam::Deletor ConfigParam::deleter;
4 参考文献
【1】总结:Qt读写ini配置文件(QSettings)
【2】QT学习之如何读写配置文件(QSettings)
【3】QSettings
【4】单例模式——讲解比较通透
Chapter3 基于QT的参数设置界面
原文链接:https://blog.csdn.net/db7328537/article/details/127085940
介绍
在一些工业开发的软件中,需要对一些硬件或者算法进行参数的设置,但是QT官方给出的QtTreeProperty控件,在一些程度上,不足以满足我们的需求,比如说,单独的按钮,以及一些选择文件夹和文件路径等。在次基础上,利用QT5.14.2的QTableView控件来制作一款参数设置控件。界面效果如下:
说明:
参数1 代表 int型整数
参数2 代表 double型参数
参数3 代表 QString型参数
参数4 代表 true or false参数
参数5 代表 下拉框型参数
参数6 代表 枚举型参数
参数7 代表 QPoint型参数
参数8 代表 矩形参数(row1, col1, row2, col2)
参数9 代表 旋转矩形参数(row,col,phi,length1,length2)
参数10 代表 QSize型参数
参数11 代表 QPushButton型
参数12 代表 QColor型参数
参数13 代表 QFont型参数
参数14 代表 选择文件路径
参数15 代表 选择文件及路径
使用方法
1.在你自己的工程中定义一个参数的结构体
struct STBaseParam //算法输入的基本参数
{STBaseParam(){nParamClassify = 1;sParamName = "";sParamLabel = "";sParamTip = "";nParamType = 0;sParamDefault = "";sParamValueRange = "";sParamValue = "";sParamRemark = "";}~STBaseParam(){nParamClassify = 1;sParamName = "";sParamLabel = "";sParamTip = "";nParamType = 0;sParamDefault = "";sParamValueRange = "";sParamValue = "";sParamRemark = "";}STBaseParam& operator=(STBaseParam& pInfo){this->nParamClassify = pInfo.nParamClassify;this->sParamName = pInfo.sParamName;this->sParamLabel = pInfo.sParamLabel;this->sParamTip = pInfo.sParamTip;this->nParamType = pInfo.nParamType;this->sParamDefault = pInfo.sParamDefault;this->sParamValueRange = pInfo.sParamValueRange;this->sParamValue = pInfo.sParamValue;this->sParamRemark = pInfo.sParamRemark;return (*this);}friend QDataStream& operator << (QDataStream& output, const STBaseParam& Obj){output << Obj.nParamClassify;if (Obj.sParamName == "") output << QString("NULL");else output << Obj.sParamName;if (Obj.sParamLabel == "") output << QString("NULL");else output << Obj.sParamLabel;if (Obj.sParamTip == "") output << QString("NULL");else output << Obj.sParamTip;output << Obj.nParamType;if (Obj.sParamDefault == "") output << QString("NULL");else output << Obj.sParamDefault;if (Obj.sParamValueRange == "") output << QString("NULL");else output << Obj.sParamValueRange;if (Obj.sParamValue == "") output << QString("NULL");else output << Obj.sParamValue;if (Obj.sParamRemark == "") output << QString("NULL");else output << Obj.sParamRemark;return output;}friend QDataStream& operator >> (QDataStream& input, STBaseParam& Obj){input >> Obj.nParamClassify;input >> Obj.sParamName;if (Obj.sParamName == "NULL") Obj.sParamName = "";input >> Obj.sParamLabel;if (Obj.sParamLabel == "NULL") Obj.sParamLabel = "";input >> Obj.sParamTip;if (Obj.sParamTip == "NULL") Obj.sParamTip = "";input >> Obj.nParamType;input >> Obj.sParamDefault;if (Obj.sParamDefault == "NULL") Obj.sParamDefault = "";input >> Obj.sParamValueRange;if (Obj.sParamValueRange == "NULL") Obj.sParamValueRange = "";input >> Obj.sParamValue;if (Obj.sParamValue == "NULL") Obj.sParamValue = "";input >> Obj.sParamRemark;if (Obj.sParamRemark == "NULL") Obj.sParamRemark = "";return input;}int nParamClassify; //参数类别(1=过程参数;2=结果参数;3=调试参数)QString sParamName; //参数名字QString sParamLabel; //参数在界面显示的中文名字QString sParamTip; //参数在界面的提示的说明文本//参数类型(0=int;1=double;2=QString;3=bool;4=combobox;//5 = flag;6 = point(x, y);7 = 矩形(row1, col1, row2, col2);//8 = 旋转矩形(row, col, phi, lenth1, length2);9 = size(width, height);//10 = 按钮;11 = 颜色选择对话框color(r, g, b, a); 12 = 字体选择对话框font 13 = 选择文件对话框 14 = 选择文件夹对话框)int nParamType;QString sParamDefault; //参数默认值QString sParamValueRange; //参数的取值范围(0%%255%%1:最小值为0;最大值为255;每次增加1)QString sParamValue; //参数值QString sParamRemark; //参数的备注
};
2.在你自己工程的界面中拖入一个QTableView控件,将其提升为DBTableParamView.h
3.Test_TableView.h
#pragma once#include <QtWidgets/QWidget>
#include "ui_Test_TableView.h"#include "DBHeader.h"#pragma execution_character_set("utf-8")
class Test_TableView : public QWidget
{Q_OBJECTpublic:Test_TableView(QWidget *parent = Q_NULLPTR);~Test_TableView();protected:void closeEvent(QCloseEvent* event);private:void InitWidget();void InitConnection();private:void ParamValueChanged(QString sParamName, QString sParamValue);private:QVector<STBaseParam*> Vect_BaseParam;private:Ui::Test_TableViewClass ui;
};
4.Test_TableView.cpp
#include "Test_TableView.h"
#include <QDebug>
#include <QMessageBox>Test_TableView::Test_TableView(QWidget *parent): QWidget(parent)
{ui.setupUi(this);InitWidget();InitConnection();
}Test_TableView::~Test_TableView()
{}void Test_TableView::closeEvent(QCloseEvent* event)
{//exit(0);
}void Test_TableView::InitWidget()
{//int型SpinBox控件STBaseParam* pBase1 = new STBaseParam;pBase1->nParamClassify = 1;pBase1->nParamType = 0;pBase1->sParamName = "Param1";pBase1->sParamLabel = "参数1";pBase1->sParamTip = "这是参数1";pBase1->sParamDefault = "12";pBase1->sParamValueRange = "1%%100%1";pBase1->sParamValue = "15";pBase1->sParamRemark = "最小值是1,最大值是100,默认值是12";Vect_BaseParam.push_back(pBase1);//double型SpinBox控件STBaseParam* pBase2 = new STBaseParam;pBase2->nParamClassify = 1;pBase2->nParamType = 1;pBase2->sParamName = "Param2";pBase2->sParamLabel = "参数2";pBase2->sParamTip = "这是参数2";pBase2->sParamDefault = "2.5";pBase2->sParamValueRange = "1%%100%%0.5";pBase2->sParamValue = "15.6";pBase2->sParamRemark = "最小值是1,最大值是100,默认值是2.5";Vect_BaseParam.push_back(pBase2);//TextEdit型控件STBaseParam* pBase3 = new STBaseParam;pBase3->nParamClassify = 1;pBase3->nParamType = 2;pBase3->sParamName = "Param3";pBase3->sParamLabel = "参数3";pBase3->sParamTip = "这是参数3";pBase3->sParamDefault = "dongbin";pBase3->sParamValueRange = "";pBase3->sParamValue = "dongbin123";pBase3->sParamRemark = "请输入名字,默认值是dongbin";Vect_BaseParam.push_back(pBase3);//CheckBox型控件STBaseParam* pBase4 = new STBaseParam;pBase4->nParamClassify = 1;pBase4->nParamType = 3;pBase4->sParamName = "Param4";pBase4->sParamLabel = "参数4";pBase4->sParamTip = "这是参数4";pBase4->sParamDefault = "1";pBase4->sParamValueRange = "是否选择信息";pBase4->sParamValue = "0";pBase4->sParamRemark = "=true 运行 =false 不运行 默认值:true";Vect_BaseParam.push_back(pBase4);//ComboBox型控件STBaseParam* pBase5 = new STBaseParam;pBase5->nParamClassify = 1;pBase5->nParamType = 4;pBase5->sParamName = "Param5";pBase5->sParamLabel = "参数5";pBase5->sParamTip = "这是参数5";pBase5->sParamDefault = "Name";pBase5->sParamValueRange = "Name,Age,City,Other";pBase5->sParamValue = "City";pBase5->sParamRemark = "选择相关信息,默认值是Name";Vect_BaseParam.push_back(pBase5);//多个CheckBox型控件STBaseParam* pBase6 = new STBaseParam;pBase6->nParamClassify = 1;pBase6->nParamType = 5;pBase6->sParamName = "Param6";pBase6->sParamLabel = "参数6";pBase6->sParamTip = "这是参数6";pBase6->sParamDefault = "0,0,0,0";pBase6->sParamValueRange = "Flag1,Flag2,Flag3,Flag4";pBase6->sParamValue = "0,1,0,1";pBase6->sParamRemark = "设置每个Falg的状态,默认值是Flag1=false,Flag2=false,Flag3=false,Flag4=false";Vect_BaseParam.push_back(pBase6);//点数据控件STBaseParam* pBase7 = new STBaseParam;pBase7->nParamClassify = 1;pBase7->nParamType = 6;pBase7->sParamName = "Param7";pBase7->sParamLabel = "参数7";pBase7->sParamTip = "这是参数7";pBase7->sParamDefault = "10,20";pBase7->sParamValueRange = "";pBase7->sParamValue = "30,40";pBase7->sParamRemark = "设置点的坐标(X,Y),默认值是(10,20)";Vect_BaseParam.push_back(pBase7);//矩形1数据控件STBaseParam* pBase8 = new STBaseParam;pBase8->nParamClassify = 1;pBase8->nParamType = 7;pBase8->sParamName = "Param8";pBase8->sParamLabel = "参数8";pBase8->sParamTip = "这是参数8";pBase8->sParamDefault = "10,20,30,40";pBase8->sParamValueRange = "";pBase8->sParamValue = "50,60,70,80";pBase8->sParamRemark = "设置矩形1的坐标(Row1,Col1,Row2,Col2),默认值是(10,20,30,40)";Vect_BaseParam.push_back(pBase8);//旋转矩形2数据控件STBaseParam* pBase9 = new STBaseParam;pBase9->nParamClassify = 1;pBase9->nParamType = 8;pBase9->sParamName = "Param9";pBase9->sParamLabel = "参数9";pBase9->sParamTip = "这是参数9";pBase9->sParamDefault = "10,20,0,30,40";pBase9->sParamValueRange = "";pBase9->sParamValue = "50,60,1.32007,70,80";pBase9->sParamRemark = "设置矩形1的坐标(Row,Col,Phi,Length1,Length2),默认值是(10,20,0,30,40)";Vect_BaseParam.push_back(pBase9);//尺寸数据控件STBaseParam* pBase10 = new STBaseParam;pBase10->nParamClassify = 1;pBase10->nParamType = 9;pBase10->sParamName = "Param10";pBase10->sParamLabel = "参数10";pBase10->sParamTip = "这是参数10";pBase10->sParamDefault = "10,20";pBase10->sParamValueRange = "";pBase10->sParamValue = "30,40";pBase10->sParamRemark = "设置尺寸的大小(Width,Height),默认值是(10,20)";Vect_BaseParam.push_back(pBase10);//按钮控件STBaseParam* pBase11 = new STBaseParam;pBase11->nParamClassify = 1;pBase11->nParamType = 10;pBase11->sParamName = "Param11";pBase11->sParamLabel = "参数11";pBase11->sParamTip = "这是参数11";pBase11->sParamDefault = "";pBase11->sParamValueRange = "";pBase11->sParamValue = "调试程序";pBase11->sParamRemark = "按下按钮,开始调试程序";Vect_BaseParam.push_back(pBase11);//选择颜色对话框控件STBaseParam* pBase12 = new STBaseParam;pBase12->nParamClassify = 1;pBase12->nParamType = 11;pBase12->sParamName = "Param12";pBase12->sParamLabel = "参数12";pBase12->sParamTip = "这是参数12";pBase12->sParamDefault = "200,200,200,100"; //r,g,b,apBase12->sParamValueRange = "";pBase12->sParamValue = "255,255,255,200";pBase12->sParamRemark = "选择颜色对话框(R,G,B,A),默认值是rgba(200,200,200,100)";Vect_BaseParam.push_back(pBase12);//选择字体对话框控件STBaseParam* pBase13 = new STBaseParam;pBase13->nParamClassify = 1;pBase13->nParamType = 12;pBase13->sParamName = "Param13";pBase13->sParamLabel = "参数13";pBase13->sParamTip = "这是参数13";pBase13->sParamDefault = "宋体,-1,-1,0";pBase13->sParamValueRange = "";pBase13->sParamValue = "宋体,12,12,0";pBase13->sParamRemark = "选择字体对话框(family,pointSize,weight,italic),默认值是rgba(宋体,-1,-1,0)";Vect_BaseParam.push_back(pBase13);//选择文件路径对话框控件STBaseParam* pBase14 = new STBaseParam;pBase14->nParamClassify = 1;pBase14->nParamType = 13;pBase14->sParamName = "Param14";pBase14->sParamLabel = "参数14";pBase14->sParamTip = "这是参数14";pBase14->sParamDefault = "D:\\test.txt";pBase14->sParamValueRange = "";pBase14->sParamValue = "";pBase14->sParamRemark = "选择文件路径对话框(文件路径),默认值是D:\\test.txt";Vect_BaseParam.push_back(pBase14);//选择文件夹路径对话框控件STBaseParam* pBase15 = new STBaseParam;pBase15->nParamClassify = 1;pBase15->nParamType = 14;pBase15->sParamName = "Param15";pBase15->sParamLabel = "参数15";pBase15->sParamTip = "这是参数15";pBase15->sParamDefault = "D:\\test";pBase15->sParamValueRange = "";pBase15->sParamValue = "";pBase15->sParamRemark = "选择文件夹路径对话框(文件夹路径),默认值是D:\\test";Vect_BaseParam.push_back(pBase15);ui.tableView->InitTableParamView(Vect_BaseParam);
}void Test_TableView::InitConnection()
{ui.tableView->RegisterCallbackParamValueChanged(std::bind(&Test_TableView::ParamValueChanged, this, std::placeholders::_1, std::placeholders::_2));
}void Test_TableView::ParamValueChanged(QString sParamName, QString sParamValue)
{qDebug() << "ParamName=" << sParamName << " ParamValue=" << sParamValue;if (sParamName == QString("Param11")){QMessageBox::information(this, QString("提示"), QString("按钮按下!!!!!"));}
}
Chapter4 QT实现一个系统参数管理窗口
原文链接:https://blog.csdn.net/zkmrobot/article/details/140962984
为了实现一个管理系统参数的设计,我们可以创建一个配置参数类来封装配置的读取和写入操作,并使用一个 QWidget 作为用户界面来管理这些参数。以下是如何设计一个这样的系统,包括配置参数类和管理界面。
1. 配置参数类
我们创建一个 ConfigManager 类来处理配置的读取和写入。这将使得参数管理与用户界面分离,方便维护和扩展。
#include <QSettings>
#include <QString>class ConfigManager {
public:ConfigManager(const QString &organization, const QString &application): settings(organization, application) {}QString getServerAddress() const {return settings.value("serverAddress", "").toString();}void setServerAddress(const QString &address) {settings.setValue("serverAddress", address);}QString getPort() const {return settings.value("port", "").toString();}void setPort(const QString &port) {settings.setValue("port", port);}bool isLoggingEnabled() const {return settings.value("enableLogging", false).toBool();}void setLoggingEnabled(bool enabled) {settings.setValue("enableLogging", enabled);}private:QSettings settings;
};
2. 管理界面 QWidget
接下来,我们创建一个 ConfigWidget 类来提供用户界面,使用 ConfigManager 类来管理和存储配置参数。
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QMessageBox>#include "ConfigManager.h"class ConfigWidget : public QWidget {Q_OBJECTpublic:ConfigWidget(ConfigManager *configManager, QWidget *parent = nullptr): QWidget(parent), configManager(configManager) {// 创建控件QLabel *label1 = new QLabel("Server Address:", this);QLabel *label2 = new QLabel("Port:", this);QLabel *label3 = new QLabel("Enable Logging:", this);serverAddressEdit = new QLineEdit(this);portEdit = new QLineEdit(this);enableLoggingCheckBox = new QCheckBox(this);QPushButton *saveButton = new QPushButton("Save", this);QPushButton *cancelButton = new QPushButton("Cancel", this);// 设置布局QFormLayout *formLayout = new QFormLayout;formLayout->addRow(label1, serverAddressEdit);formLayout->addRow(label2, portEdit);formLayout->addRow(label3, enableLoggingCheckBox);QHBoxLayout *buttonLayout = new QHBoxLayout;buttonLayout->addWidget(saveButton);buttonLayout->addWidget(cancelButton);QVBoxLayout *mainLayout = new QVBoxLayout(this);mainLayout->addLayout(formLayout);mainLayout->addLayout(buttonLayout);setLayout(mainLayout);// 连接信号和槽connect(saveButton, &QPushButton::clicked, this, &ConfigWidget::handleSaveButton);connect(cancelButton, &QPushButton::clicked, this, &ConfigWidget::handleCancelButton);// 读取设置readSettings();}private slots:void handleSaveButton() {// 保存设置configManager->setServerAddress(serverAddressEdit->text());configManager->setPort(portEdit->text());configManager->setLoggingEnabled(enableLoggingCheckBox->isChecked());QMessageBox::information(this, "Configuration Saved", "Settings have been saved.");}void handleCancelButton() {// 关闭窗口close();}private:void readSettings() {// 读取设置serverAddressEdit->setText(configManager->getServerAddress());portEdit->setText(configManager->getPort());enableLoggingCheckBox->setChecked(configManager->isLoggingEnabled());}ConfigManager *configManager;QLineEdit *serverAddressEdit;QLineEdit *portEdit;QCheckBox *enableLoggingCheckBox;
};
3. 主程序
最后,我们创建主程序来初始化 ConfigManager 并显示 ConfigWidget。
#include <QApplication>
#include "ConfigWidget.h"
#include "ConfigManager.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);ConfigManager configManager("MyCompany", "MyApp");ConfigWidget configWidget(&configManager);configWidget.setWindowTitle("System Configuration");configWidget.resize(300, 200);configWidget.show();return app.exec();
}
代码说明
1. ConfigManager 类:
-
- 封装了读取和写入配置参数的逻辑。使用 QSettings 来管理配置的持久化。
-
- 提供了方法来获取和设置服务器地址、端口和日志记录状态。
2. ConfigWidget 类:
-
- 提供了一个用户界面来显示和修改配置参数。
-
- 使用 ConfigManager 类来读取和保存设置。
-
- 在窗口加载时从 ConfigManager 读取设置,并在用户点击保存按钮时将设置写回 ConfigManager。
3. 主程序:
-
- 初始化 QApplication 实例和 ConfigManager 实例。
-
- 创建 ConfigWidget 实例,并将 ConfigManager 传递给它。
-
- 显示配置窗口。
Chapter5 Qt平台自动化项目开发基础框架
原文链接:https://blog.csdn.net/lg_cool_/article/details/143451103
一、程序框架结构
主函数代码:
#include "mainwindow.h"
#include "logindialog.h"
#include <QApplication>
#include <QIcon>int main(int argc, char *argv[])
{QApplication a(argc, argv);logindialog loginDialog;loginDialog.setWindowIcon(QIcon("D:/Project/auto_calibrate_device/haptron.ico")); // 使用资源文件中的图标if (loginDialog.exec() == QDialog::Accepted){MainWindow w;w.setWindowIcon(QIcon("D:/Project/auto_calibrate_device/haptron.ico")); // 使用资源文件中的图标w.show();return a.exec();}return 0;
}
二、GUI界面开发
1)登录界面
登录界面代码:
#include "logindialog.h"
#include <QMessageBox>
#include "ui_logindialog.h"logindialog::logindialog(QWidget *parent):QDialog(parent),ui(new Ui::logindialog)
{ui->setupUi(this);
}
logindialog::~logindialog()
{delete ui;
}
void logindialog::on_m_Login_Btn_clicked()
{if (ui -> m_UserName_LineEdit->text() == "admin" && ui -> m_Password_LineEdit->text() == "admin"){this->accept(); // Close the dialog}else{QMessageBox::information(this, "提示", "Invalid username or password");}
}
void logindialog::on_m_Cancel_Btn_clicked()
{this->close();
}
2)主界面
主界面代码:
在主界面中创建了主控制线程对象,pcontrolThread=new controlThread;同时在主控制线程中创建了Qwidget对象,controlCenterWidget[index]=new Widget;以下为大致代码结构:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "widget.h"
#include "message_log.h"
#include <QMessageBox>
#include <QCheckBox>
#include <QVector>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);qInstallMessageHandler(recordMessage); //注册日志函数for (int i = 0; i < 4; ++i){controlCenterWidget[i] = nullptr; //初始化为nullptr}pcontrolThread=new controlThread;connect(pcontrolThread,SIGNAL(send_signal(QString)),this,SLOT(recv_string(QString))); //处理接收到的信号}MainWindow::~MainWindow()
{}
//点击启动按钮
void MainWindow::on_startButton_clicked()
{QVector<QCheckBox*> checkBoxVector; // 使用QVector存储QCheckBox指针checkBoxVector.append(ui->QCheck_calibrate_1);checkBoxVector.append(ui->QCheck_calibrate_2);checkBoxVector.append(ui->QCheck_calibrate_3);checkBoxVector.append(ui->QCheck_calibrate_4);// 读取复选框的状态for(int i=0;i<4;++i){Qt::CheckState state = checkBoxVector[i]->checkState();switch (state){case Qt::Unchecked://qDebug() << "Checkbox is unchecked";break;case Qt::PartiallyChecked://qDebug() << "Checkbox is partially checked";break;case Qt::Checked:pcontrolThread->runflag[i]="stop";pcontrolThread->weightNoLimit_Start[i]=comboBoxVector_Start[i]->currentIndex();pcontrolThread->weightNoLimit_End[i]=comboBoxVector_End[i]->currentIndex();sta_weigth=comboBoxVector_Start[i]->currentIndex();end_weigth=comboBoxVector_End[i]->currentIndex();crr_weigth=sta_weigth;lineEditVector[i]->setText(QString::number(comboBoxVector_Start[i]->currentIndex()*10)+"N");progressVector[i]->setValue(0); // 设置进度条的当前值为 0break;}}//修改状态显示ui->sys_status_label->setText("运行");ui->sys_status_label->setStyleSheet("QLabel { font-size: 28pt; font-weight: 700; color: #008000; text-align: center; }");ui->sys_status_label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);// 读取标定砝码的范围pcontrolThread->start(); //启动子线程//间接调用了run()函数//即接收数据
}//点击停止按钮
void MainWindow::on_stopButton_clicked()
{pcontrolThread->CloseThread(); //停止线程ui->sys_status_label->setText("停止");ui->sys_status_label->setStyleSheet("QLabel { font-size: 28pt; font-weight: 700; color: #aa0000; text-align: center; }");ui->sys_status_label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
}//点击控制中心1按钮
void MainWindow::on_ControlCenterButton_1_clicked()
{OpenControlCenterWindow(0);
}//打开控制中心界面
void MainWindow::OpenControlCenterWindow(int index)
{if (!controlCenterWidget[index]){controlCenterWidget[index]=new Widget;controlCenterWidget[index]->setWindowTitle("控制中心"+QString::number(index+1));controlCenterWidget[index]->setWindowIcon(QIcon("D:/Project/auto_calibrate_device/haptron.ico"));controlCenterWidget[index]->show(); // 显示新窗口}else{controlCenterWidget[index]->close();controlCenterWidget[index]->setWindowIcon(QIcon("D:/Project/auto_calibrate_device/haptron.ico"));controlCenterWidget[index]->show(); // 显示新窗口}controlCenterWidget[index]->widget_Id=index;
}
如何从mainwindow窗口中调用其他窗口中的控件呢?
controlCenterWidget是一个Qwidget类指针。
// 使用findChild来查找QTabWidgetQTabWidget *tabWidget = controlCenterWidget[index]->findChild<QTabWidget*>("tabWidget");if (tabWidget){// 如果找到了QTabWidget,设置当前标签页为第一个tabWidget->setCurrentIndex(0);}else{// 没有找到QTabWidget,可以输出错误信息或者进行其他处理qDebug() << "QTabWidget not found!";}
3)手动控制界面Qwidget
手动控制界面代码:
手动控制界面有可能被打开多个,因此手动控制界面有一个专属的界面id,以区别是被谁打开的。
在mainWindow.cpp中打开Qwidget窗口的部分代码:
//打开控制中心界面
void MainWindow::OpenControlCenterWindow(int index)
{if (!controlCenterWidget[index]){controlCenterWidget[index]=new Widget;controlCenterWidget[index]->show(); // 显示新窗口}else{controlCenterWidget[index]->close();controlCenterWidget[index]->setWindowIcon(QIcon("D:/Project/auto_calibrate_device/haptron.ico"));controlCenterWidget[index]->show(); // 显示新窗口}controlCenterWidget[index]->widget_Id=index;//打开窗口后给窗口设定一个唯一的id值
}
在widget.cpp中,主要完成一些手动控制逻辑,直接调用.dll库中的驱动接口。
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);sharedData= new SharedData; //创建类对象sharedDatamotorControl = nullptr; //初始化为nullptrwidget_Id=0;
}
Widget::~Widget()
{
}
void Widget::on_Button_connect_clicked()
{qDebug()<<widget_Id+1<<"#"<<"点击连接";motorControl = new Thread(sharedData,widget_Id); //创建类对象motorControlThreadif (motorControl->connect()==true) //打开通道{ui->label_mark->setStyleSheet("QLabel {border: 1px solid black; border-radius: 12px;background-color: green;}");qDebug()<<widget_Id+1<<"#"<<"控制中心:标定台电机连接成功";}else{delete motorControl; // 如果连接失败,释放资源motorControl = nullptr;}
}
void Widget::on_Button_home_clicked()
{qDebug()<<widget_Id+1<<"#"<<"点击回零";if (motorControl){QMessageBox::information(this, "提示", "确认安全后点击“确定”开始回零,请等待回零完成!");else{QMessageBox::information(this, "提示", "回零异常,请检查后再次尝试回零!");}}else{QMessageBox::information(this, "提示", "请先建立连接!");delete motorControl;motorControl = nullptr;}
}
void Widget::on_Button_run_clicked()
{qDebug()<<widget_Id+1<<"#"<<"点击运动";if (motorControl){QString loc_str="move1";QMessageBox::information(this, "提示", "确认安全后点击“确定”开始运动中,请等待运动到位!");int loc1=ui->comboBox_loc1->currentIndex();int loc2=ui->comboBox_loc2->currentIndex();}else{QMessageBox::information(this, "提示", "请先建立连接!");delete motorControl;motorControl = nullptr;}
}
void Widget::on_Button_recv_sensor_clicked()
{qDebug()<<widget_Id+1<<"#"<<"采集数据";
}
三、主控制线程
主控制线程主要负责调度外设的运行逻辑。
1)多线程之间的数据共享
共享数据类代码:ShareData.h
#ifndef SHAREDDATA_H
#define SHAREDDATA_H
#include <QMutex>
#include <QMutexLocker>
#include <QWaitCondition>
#include <QQueue>
#include <QDebug>
#include <QThread>
// 共享数据类,用于在线程间共享数据和同步
class SharedData {
public:SharedData() : dataReady(false) {}QMutex mutex; // 互斥锁,用于保护共享数据QWaitCondition condition; // 条件变量,用于线程间的通知bool dataReady; // 标志,指示数据是否准备好QQueue<QString> queue; // 用于存储数据的队列QQueue<QString> queue_1; // 用于存储数据的队列(接收消费者的信息)// 通知消费者线程,生产者已经放入数据void wake(){QMutexLocker locker(&mutex); // 锁定互斥锁if (!queue.isEmpty()){//qDebug() << "Data available, waking consumers!";dataReady = true; // 标记数据准备好condition.wakeAll(); // 唤醒所有等待的消费者线程}else{//qDebug()<<"queue.isEmpty";return;}}// 等待生产者线程放入数据void wait_data(){QMutexLocker locker(&mutex); // 锁定互斥锁while (!dataReady) //如果数据已经准备好,则无需等待{//让当前线程等待(挂起)直到某个条件满足,同时释放与之关联的互斥锁(QMutex),允许其他线程进入临界区并可能改变条件状态condition.wait(&mutex); // 如果数据未准备好,等待生产者线程的通知,由condition.wakeAll()或condition.wakeOne()触发}dataReady = false; // 重置数据准备好标志//qDebug()<<"wait Data available!";}
};
#endif // SHAREDDATA_H
2)主控制逻辑
为每个模块创建一个控制流程类,负责数据的交互与运行状态的记录:
centrial_control.hpp(motorAuto_run类主要负责电机模组模块的运行状态记录以及数据收发)
#ifndef CENTRIAL_CONTROL_H
#define CENTRIAL_CONTROL_H#include <QThread>
class motorAuto_run; //控制流程类
class controlThread : public QThread //用户自定义的类
{Q_OBJECT
public:controlThread(); //构造函数//初始化变量~controlThread();
protected://QThread类的虚函数//就是子线程处理函数void run();//子线程处理函数//不能直接调用该函数//只能通过start间接调用private:bool abb_Enable=false;SharedData *sharedData[4]; // 创建共享数据指针Thread *motorControlThread[4]; //定义类指针数组SharedData *abbsharedData;
signals:void send_calibrateF(const QString &data); // 用于传递数据到MainWindow
private slots:void recv_motor_canF(const QString &data){emit send_calibrateF(data); // 将接收到的数据通过controlThread传递给MainWindow}
};//控制流程类
class motorAuto_run {
public:motorAuto_run(SharedData *sharedData) :sharedData(sharedData){}int sendstep; //发送字符串步骤标志位int recvstep; //接收字符串步骤标志位int weight_No; //砝码编号1~10void send_data(QString str){{QMutexLocker locker(&sharedData->mutex); // 锁定共享数据sharedData->queue.enqueue(str); // 生产数据,放入队列}sharedData->wake(); // 通知消费者线程}QString recv_data(){QString str="";QMutexLocker locker(&sharedData->mutex); //锁定共享数据if (!sharedData->queue_1.isEmpty()){str = sharedData->queue_1.dequeue(); // 取出数据}return str;}
private:SharedData *sharedData; // 创建共享数据指针
};
#endif // CENTRIAL_CONTROL_H
centrial_control.cpp
#include "centrial_control.hpp"
#include <QThread>
#include <QMessageBox>
#include <QWidget>
controlThread::controlThread() //构造函数//初始化变量
{for(int i=0;i<4;i++){sharedData[i]= new SharedData; //创建类对象sharedDatamotorControlThread[i] = new Thread(sharedData[i],i); //创建类对象motorControlThreadconnect(motorControlThread[i], &Thread::send_motor_canF, this, &controlThread::recv_motor_canF); // 连接信号和槽}abbsharedData=new SharedData;abb=new SocketClient(abbsharedData);for(int i=0;i<4;i++){motorAction[i]=new motorAuto_run(sharedData[i]); //创建对象}
}
controlThread::~controlThread()
{ for(int i=0;i<4;i++){motorAction[i]->send_data("stop");delete sharedData[i];delete motorControlThread[i];}abbsend_data("stop");delete abbsharedData;delete abb;for(int i=0;i<4;i++){delete motorAction[i];}
}
void controlThread::run() //controlThread->start 间接调用该函数
{while(!stopped)//死循环//{for(int i=0;i<4;i++){if(runflag[i]=="running"){if(DebugSendstep[i]!=motorAction[i]->sendstep){qDebug()<<i+1<<"#motorAction_sendstep="<<motorAction[i]->sendstep;DebugSendstep[i]=motorAction[i]->sendstep;}//step1 查询当前状态 停止 or 运动中if(motorAction[i]->sendstep==1 && motorAction[i]->recvstep!=1){QString task_name= "status_" + QString::number(i+1);motorAction[i]->send_data(task_name);motorAction[i]->recvstep=1;}//step2 查询当前位置else if(motorAction[i]->sendstep==2 && motorAction[i]->recvstep!=2){QString task_name= "encoderValue_" + QString::number(i+1);motorAction[i]->send_data(task_name);motorAction[i]->recvstep=2;}}}sleep(1);//接收执行结果for(int i=0;i<4;i++){if(runflag[i]=="running"){if(DebugRecvstep[i]!=motorAction[i]->recvstep){qDebug()<<i+1<<"#motorAction_recvstep="<<motorAction[i]->recvstep;DebugRecvstep[i]=motorAction[i]->recvstep;}if(motorAction[i]->recvstep==1) //step1 查询当前状态{QString result=motorAction[i]->recv_data();if(result!=""){qDebug()<<i+1<<"#"<<result;checkStatus(result,i); //检查当前电机状态并跳转流程步骤}}else if(motorAction[i]->recvstep==2) //step2 查询当前位置{QString result=motorAction[i]->recv_data();if(result!=""){qDebug()<<i+1<<"#"<<result;checkEncoderValue(result,i,0,0); //检查当前电机位置并跳转流程步骤}}}}}
}
四、外设模块线程
外设模块主要的任务是实现与主控制线程的数据传输以及自身的任务执行。
motor_can.hpp:(Thread(SharedData *sharedData,int devind);共享数据在主控制线程中创建(new),指向共享数据的指针被传递到外设模块的线程中)
#ifndef MOTOR_CAN_HPP
#define MOTOR_CAN_HPP
#include <QThread>
#include "lib/ECanVci.h"
#include <vector>
#include "SharedData.h"
#include <QDateTime>
#include <QDir>
#include "CLibModBus.h"
#define LibECanVci "ECanVci" //定义宏LibECanVci
class Thread : public QThread //用户自定义的类
{Q_OBJECT
public:QString task_name = "";Thread(SharedData *sharedData,int devind); //构造函数//初始化变量~Thread();
public:typedef DWORD(__stdcall SetReference)(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);typedef DWORD(__stdcall ReadBoardInfo)(DWORD DeviceType,DWORD DeviceInd,P_BOARD_INFO pInfo);int devtype=8; //设备类型号//
protected://QThread类的虚函数//就是子线程处理函数void run();//子线程处理函数//不能直接调用该函数//只能通过start间接调用
private:SharedData *sharedData; // 创建共享数据指针CLibModBus *sensor;QString VoltageList;QString AverageList;
signals:void send_motor_canF(const QString &str); //自定义信号函数用于传递数据到controlThread
};
#endif // MOTOR_CAN_HPP
motor_can.cpp:(采用线程同步的方式接收数据【队列queue】,执行任务后返回执行结果,【队列queue_1】)
#include "motor_can.hpp"
#include <QThread>Thread::Thread(SharedData *sharedData,int devind) //构造函数//初始化变量:devind(devind),sharedData(sharedData) //连接共享数据指针
{//this->sharedData=sharedData; //连接共享数据指针QLibrary lib("ECanVci64.dll");if(true==lib.load()){qDebug()<<devind+1<<"#ECanVci.dll load ok";}else{qDebug()<<devind+1<<"#ECanVci.dll load error";}
}
void Thread::run() //QThread->start 间接调用该函数
{if (Thread::OpenCANThread(mbaud)==true) //打开通道{while(!stopped)//死循环//{sharedData->wait_data(); // 等待生产者线程放入数据//获取共享数据,设定锁的范围{QMutexLocker locker(&sharedData->mutex); //锁定共享数据if (!sharedData->queue.isEmpty()){task_name = sharedData->queue.dequeue(); // 取出数据}}qDebug()<<devind+1<<"#"<<"task_name"<<task_name; //task_name "calibrate_4_0N" calibrate:标定流程 4:第四个工位 0N:空载标定if(this->task_name == "stop"){CloseCANThread(); //关闭设备stop(); //停止子线程}//执行非标定任务else if (this->task_name != "" && !this->task_name.contains("calibrate")){QString task_return=executeTask(task_name);//返回关节模组执行结果给控制逻辑{QMutexLocker locker(&sharedData->mutex); // 锁定共享数据sharedData->queue_1.enqueue(task_return); // 生产数据,放入队列}}//执行标定流程else if(this->task_name.trimmed().section('_',0,0) == "calibrate"){//标定数据采集位置定义std::vector<QString> task_move= {"move1_","move3_","move4_","move5_","move6_","move7_","move8_","move9_","move2_","move10_"};//标定过程中生成的文件名格式:标定台+砝码位置+重量+电机位置QString file_title=task_name.trimmed().section('_',1,1)+"C_"+task_name.trimmed().section('_',2,2);//file_title=4C_10N 4:标定台号 C:砝码在标定台中间位置 10N:当前标定砝码重量calibrateProcess(task_move, file_title);}this->task_name="";sleep(5);}stopped = false;}else{CloseCANThread();stop(); //停止子线程}
}
五、日志
1)头文件:
在recordMessage函数中设置了日志的命名格式,代码中可采用qDebug()输出记录信息,例如
执行 qDebug()<<“motor1#load ok”; 语句,#号前面的字符串将会生成 ”log_motor1+当前日期“ 文件,且#号前面的字符串会生成日志信息为 ”load ok“ 。
#ifndef MESSAGE_LOG_H
#define MESSAGE_LOG_H#include <QApplication>
#include <QtDebug>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QDateTime>
#include <QMutex>
#include <QString>
#include <qapplication.h>
#include <QMessageBox>
#include <QDir>void recordMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{// 加锁static QMutex mutex;mutex.lock();QString text;switch(type){case QtDebugMsg:text = QString("Debug:");break;case QtWarningMsg:text = QString("Warning:");break;case QtCriticalMsg:text = QString("Critical:");break;case QtFatalMsg:text = QString("Fatal:");break;default:break;}// 设置输出信息格式//QString context_info = QString("File:(%1) Line:(%2)").arg(QString(context.file)).arg(context.line);QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ");QString current_date = QString("(%1)").arg(current_date_time);//QString message = QString("%1 %2 %3 %4").arg(text).arg(context_info).arg(msg).arg(current_date);QString message = QString("%1 : %2").arg(current_date).arg(msg);// 提取日志消息中的标识符,假设它位于第一个#前的字符串QString LogIdentifier = msg.section("#", 0, 0).trimmed();if(LogIdentifier.size()>10 || !msg.contains('#')){LogIdentifier="undefine";}// 输出信息至文件中(读写、追加形式)QString logFilePath="D:/Project/auto_calibrate_device/log";QDir logDir(logFilePath); //初始化logdir目录if (!logDir.exists()) //如果"log"文件夹不存在{logDir.mkpath("."); //创建文件夹}QString logFileName="log_"+LogIdentifier+"_"+QDateTime::currentDateTime().toString("yyyy-MM-dd ")+".txt";logFilePath += "/";logFilePath += logFileName;QFile file(logFilePath);file.open(QIODevice::WriteOnly | QIODevice::Append);QTextStream text_stream(&file);text_stream << message << "\r\n";file.flush();file.close();// 解锁mutex.unlock();
}#endif // MESSAGE_LOG_H
2)调用方法:
调用qInstallMessageHandler(recordMessage); //注册日志函数
#include "message_log.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);qInstallMessageHandler(recordMessage); //注册日志函数//输出信息到指定文件,文件名为log_mainWindowlog+日期,日志信息为log load okqDebug()<<"mainWindowlog#log load ok";
}
相关文章:

QT-自定义参数设计框架软件
QT-自定义参数设计框架软件 Chapter1 QT-自定义参数设计框架软件前言一、演示效果二、使用步骤1.应用进行参数注册2.数据库操作单例对象3.参数操作单例对象 三、下载链接 Chapter2 Qt中管理配置参数(QSettings、单例模式)1 前言2 QSettings类ini文件写in…...

VUE集成Live2d
VUE集成Live2d 目前基于大模型,可以实现一个桌面的3D动画小人,个人猜测可以简介这个项目进行实现 1-参考网址 试了很多项目,只有这个项目直观的把问题说清楚了 Live2D Vue3技术应用:https://blog.csdn.net/hh1233321/article/details/1406947…...
【CPP面经】科大讯飞 腾讯后端开发面经分享
文章目录 C 面试问题整理基础问题简答1. 内存对齐2. this 指针3. 在成员函数中删除 this4. 引用占用内存吗?5. C 越界访问场景6. 进程通信方式7. 无锁队列实现8. ping 在哪一层?实现原理?9. HTTPS 流程10. GDB 使用及 CPU 高使用定位11. 智能…...

el-card 结合 el-descriptions 作为信息展示
记录下el-card 组合 el-descriptions 实现动态展示信息 文章结构 实现效果1. el-descriptions 组件使用1.1 结合v-for实现列表渲染1.2 解析 2. 自定义 el-descriptions 样式2.1 修改背景色、字体颜色2.2 调整字体大小2.3 解析 3. el-card 结合 el-descriptions 作为信息展示3.…...
GaussDB自带诊断工具实战指南
一、引言 GaussDB是一种分布式的关系型数据库。在数据库运维中,快速定位性能瓶颈、诊断故障是保障业务连续性的关键。GaussDB内置了多种诊断工具,结合日志分析、执行计划解析和实时监控功能,帮助开发者与运维人员高效解决问题。本文深入讲解…...

LeetCode 链表章节
简单 21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2: 输入:l1 [], l2…...

SSL证书和HTTPS:全面解析它们的功能与重要性
每当我们在互联网上输入个人信息、进行在线交易时,背后是否有一个安全的保障?这时,SSL证书和HTTPS便扮演了至关重要的角色。本文将全面分析SSL证书和HTTPS的含义、功能、重要性以及它们在网络安全中的作用。 一、SSL证书的定义与基本概念 S…...

正交投影与内积空间:机器学习的几何基础
前言 本文隶属于专栏《机器学习数学通关指南》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 🔍 1. 内积空间的…...

Qt中txt文件输出为PDF格式
main.cpp PdfReportGenerator pdfReportGenerator;// 加载中文字体if (QFontDatabase::addApplicationFont(":/new/prefix1/simsun.ttf") -1) {QMessageBox::warning(nullptr, "警告", "无法加载中文字体");}// 解析日志文件QVector<LogEntr…...

《HelloGitHub》第 107 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 Python、…...
Langchain解锁LLM大语言模型的结构化输出能力(多种实现方案)
在 LangChain解锁LLM大语言模型的结构化输出能力:调用 with_structured_output() 方法 这篇博客中,我们了解了格式化LLM输出内容的必要性以及如何通过调用langchain框架中提供的 with_structured_output() 方法对LLM输出进行格式化(三种可选方…...

AI数据分析:deepseek生成SQL
在当今数据驱动的时代,数据分析已成为企业和个人决策的重要工具。随着人工智能技术的快速发展,AI 驱动的数据分析工具正在改变我们处理和分析数据的方式。本文将着重介绍如何使用 DeepSeek 进行自动补全SQL 查询语句。 我们都知道,SQL 查询语…...
力扣-动态规划-115 不同子序列
思路 dp数组定义:0_i-1的字符串中有0_j-1的字符串有dp[i][j]个递推公式: if(s[i-1] t[j-1]){dp[i][j] dp[i-1][j-1] dp[i-1][j]; }else{dp[i][j] dp[i-1][j]; } 在该元素相同时,有两种可能1:使用该元素,所以0_i-2…...

Qt C++ 开发 动态上下页按钮实现
项目开发,想实现动态的显示按钮,考虑使用QStackedWidget做两个页面去切换。 首先,我们使用Qt ui 画出两个QStackedWidget的两个页面 要实现切换,我们只需要调用stackedWidget->setCurrentIndex(index)就行。 那么如何自动调…...

数据结构第五节:排序
1.常见的排序算法 插入排序:直接插入排序、希尔排序 选择排序:直接选择排序、堆排序 交换排序:冒泡排序、快速排序 归并排序:归并排序 排序的接口实现: // 1. 直接插入排序 void InsertSort(int* a, int n); // 2. 希…...

从文件到块: 提高 Hugging Face 存储效率
Hugging Face 在Git LFS 仓库中存储了超过30 PB 的模型、数据集和 Spaces。由于 Git 在文件级别进行存储和版本控制,任何文件的修改都需要重新上传整个文件。这在 Hub 上会产生高昂的成本,因为平均每个 Parquet 和 CSV 文件大小在 200-300 MB 之间&#…...
Android14 串口控制是能wifi adb实现简介
Android14 串口控制是能wifi adb实现简介 一、前言 文章目录 Android14 串口控制是能wifi adb实现简介一、前言二、Android14 串口控制是能wifi adb实现1、设置prop属性命令开启adb(1)相关prop属性设置(2)在设置界面或者 ifconfi…...
vue3中 组合式~测试深入组件:事件 与 $emit()
一、语法(props) 第一步:在组件模板表达式中,可以直接用$emit()方法触发自定义事件, <!-- MyComponent --> <button click"$emit(someEvent)">Click Me</button> 第二步父组件可以通过 v-on (缩写为 ) 来监听…...

SQL-labs13-16闯关记录
http://127.0.0.1/sqli-labs/less-13/ 基于POST单引号双注入变形 1,依然是一个登录框,POST型SQL注入 2,挂上burpsuite,然后抓取请求,构造请求判断漏洞类型和闭合条件 admin 发生了报错,根据提示闭合方式是(…...

基于微信小程序的停车场管理系统的设计与实现
第1章 绪论 1.1 课题背景 随着移动互联形式的不断发展,各行各业都在摸索移动互联对本行业的改变,不断的尝试开发出适合于本行业或者本公司的APP。但是这样一来用户的手机上就需要安装各种软件,但是APP作为一个只为某个公司服务的一个软件&a…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...