当前位置:网站首页>QT learning summary
QT learning summary
2022-04-23 03:20:00 【Happinessคิดถึง】
QT Learning summary
knowledge has no limit , Consider the past you shall know the future .– Update time 2021-04-10
One 、pro What's in the file ? It's made up of those things ? How to use pro file ?
Look at the picture below :

When you create a new project ,pro He will automatically generate files for you . Other things need to be added by yourself .
For example, the icon of the program , Path is generated , generation , Resource file , Third party Library lib etc. .
Here are some common items :
1. Set up program icons .
Prepare one ico Picture file ( My is 32*32) Put it in your project .
And then in pro Set the file directory .
Here is the effect :

2. Add and use resource files .
First, right-click to add the resource class

Add the resource files you need 
You can take a look at the generated qrc What does your code look like :

Then set a prefix , Add the resource file you need to add .
Code :
QImage img(":/pic/images/myico.ico"); // Create a new one image object
ui->label_2->setPixmap(QPixmap::fromImage(img));
The following is the use effect :

3. Add a third-party library to use . Here we use opencv As an example .

The first is the directory containing his references . And then there's his lib File directory .
The due debug and release The version of the library uses .
Two 、 What configuration files are used ? How to use configuration files ?
A program , What is essential is its configuration file , With different scenes , Or the situation , Make changes . You can't recompile a project every time you modify it , So the configuration file is generated .
1.xml
Extensible Markup Language Extensible markup language .
xml Format :
<?xml version="1.0" encoding="UTF-8"?>
<doc>
<config id="5"/>
<courses institution=" Institutions " teacher=" Lao Wang " count="4">
<lesson id="1" url="https://blog.csdn.net/weixin_44353958" fee=" free ">QT summary </lesson>
</courses>
</doc>
The following is a sample code for reading and writing :
Need to add header information
QT += xml
#include <QDebug>
#include <QDomDocument>
#include <QFile>
#include <QTextStream>
void MainWindow::fun_xml()
{
QString strPath = QCoreApplication::applicationDirPath();
qDebug()<<strPath;
if (!strPath.endsWith("/")) {
strPath += "/";
}
//QString strFileName = strPath + "test.xml"; // Mode one
//QString strFileName = ":/config/xml/test.xml"; // Program running directory xml file --
// Calling the resource file doesn't work , I don't know why , I'm here for convenience , The absolute path used . Generally, it can be used in way 1
QFile file("F:/qtdemo/demo/untitled1/xml/test.xml");
if (!file.open(QFile::WriteOnly | QFile::Text | QFile::Truncate))
{
// QFile::Truncate, Empty the original content
qDebug()<<"clean";
return ;
}else
{
qDebug()<<"open success";
}
QTextStream out(&file);
out.setCodec("UTF-8");
// Specify version
QDomDocument document; //xml object
QDomProcessingInstruction instruction; // Add processing instructions
instruction = document.createProcessingInstruction("xml",
"version=\"1.0\" encoding=\"UTF-8\""); // Please note that the code here is consistent with the code of the source code file .
document.appendChild(instruction);
// doc -- Elements 1
QDomElement rootDoc = document.createElement("doc");
document.appendChild(rootDoc);
//config -- Elements 2
QDomElement eleConfig = document.createElement("config");
eleConfig.setAttribute("id", 5);
rootDoc.appendChild(eleConfig);
QString strName;
QString strValue;
// courses -- Elements 3 2 and 3 It's a peer element
QDomElement eleCourses = document.createElement("courses");
rootDoc.appendChild(eleCourses); // Add subclasses
strName = "institution";
strValue = QString::fromUtf8(" Institutions ");
eleCourses.setAttribute(strName, strValue);// Set property name , Property value
strName = "teacher";
strValue = QString::fromUtf8(" Lao Wang ");
eleCourses.setAttribute(strName, strValue);
strName = "count";
strValue = QString("%1").arg(4);
eleCourses.setAttribute(strName, strValue);
// lesson -- Elements 4
QDomElement eleLesson = document.createElement("lesson");
strName = "id";
strValue = QString("%1").arg(1);
eleLesson.setAttribute(strName, strValue);
strName = "url";
strValue = "https://blog.csdn.net/weixin_44353958";
eleLesson.setAttribute(strName, strValue);
strName = "fee";
strValue = QString::fromUtf8(" free ");
eleLesson.setAttribute(strName, strValue);
// Set text nodes
QDomText dtText = document.createTextNode(QString::fromUtf8("QT summary "));
eleLesson.appendChild(dtText);
eleCourses.appendChild(eleLesson);
// 4: Indent value
document.save(out, 4, QDomNode::EncodingFromTextStream);
qDebug()<<document.toString();
file.close();
// <?xml version="1.0" encoding="UTF-8"?>
// <doc>
// <config id="5"/>
// <courses institution=" Institutions " teacher=" Lao Wang " count="4">
// <lesson id="1" url="https://blog.csdn.net/weixin_44353958" fee=" free ">QT summary </lesson>
// </courses>
// </doc>
}
2.ini
Initialization File( Initialization file )
I've written about using... In other articles QSetting To read ini Profile information .
The class files used are given below — The following is written by someone else .
The header file :
#pragma once
#include <QFile>
#include <QString>
#include <QVector>
namespace ini {
// ini Format configuration file processing class
//-----------------------------------------
class CIniConfig: public QObject
{
Q_OBJECT
public:
CIniConfig();
~CIniConfig();
/** * @brief Set the file name of the configuration file , Use full path , Environment variables are not supported . * @param[in] strFileName file name * @return true: success ,false: Failure */
bool setFileName(const QString& strFileName);
/** * @brief Read bool The key value of the type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] i_nDefault The default value is * @param[out] o_bRet true: success , false: Failure * @return data */
bool getBoolean(const QString& strKey, const QString& strSubKey, bool i_nDefault, bool* o_bRet = NULL);
/** * @brief Read int The key value of the type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] i_nDefault The default value is * @param[out] o_bRet true: success , false: Failure * @return data */
int getInteger(const QString& strKey, const QString& strSubKey, int i_nDefault, bool* o_bRet = NULL);
/** * @brief Read the key value of floating-point type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] i_fDefault The default value is * @param[out] o_bRet true: success , false: Failure * @return data */
double getDouble(const QString& strKey, const QString& strSubKey, double i_fDefault, bool* o_bRet = NULL);
/** * @brief Read string The key value of the type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] strDefault The default value is * @param[out] o_bRet true: success , false: Failure * @return data */
QString getString(const QString& strKey, const QString& strSubKey, const QString& strDefault, bool* o_bRet = NULL);
/** * @brief Read string The key value of the type ( Extension interface ), Support multi line reading * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] strDefault The default value is * @param[out] o_bRet true: success , false: Failure * @return data */
QString getStringMultiline(const QString& strKey, const QString& strSubKey, const QString& strDefault, bool* o_bRet = NULL);
/** * @brief Set up bool The key value of the type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] i_nValue Value of subkey * @return true: success , false: Failure */
bool setBoolean(const QString& strKey, const QString& strSubKey, bool i_nValue);
/** * @brief Set up int The key value of the type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] i_nValue Value of subkey * @return true: success , false: Failure */
bool setInteger(const QString& strKey, const QString& strSubKey, int i_nValue);
/** * @brief Set the key value of floating point type * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @param[in] i_fValue Value of subkey * @return true: success , false: Failure */
bool setDouble(const QString& strKey, const QString& strSubKey, double i_fValue);
/** * @brief Set the key value of string type , such as , The contents of the configuration file are as follows : * [config] * x=xx * y=yy * z=zz * @param[in] strKey Primary key =config * @param[in] strSubKey Subkey =x * @param[in] strValue Value of subkey , by "" Indicates that the subkey is deleted . * @return true: success , false: Failure */
bool setString(const QString& strKey, const QString& strSubKey, const QString& strValue);
/** * @brief Delete all key values * @return true: success ,false: Failure */
bool deleteAllKeys();
/** * @brief Delete the specified primary key * @param[in] strKey Primary key * @return true: success ,false: Failure */
bool removeKey(const QString& strKey);
/** * @brief Delete the specified subkey * @param[in] strKey Primary key * @param[in] strSubKey Subkey * @return true: success ,false: Failure */
bool removeSubKey(const QString& strKey, const QString& strSubKey);
/** * @brief Read the list of key values , , For example, incoming ("config""), obtain , "x=xx\ny=yy\nz=zz" * @param[in] strKey Primary key * @return List of key values */
QString getAllKeys(const QString& strKey);
/** * @brief Set key value list , , For example, incoming ("config", "x=xx\ny=yy\nz=zz") * The result of execution : * [config] * x=xx * y=yy * z=zz * @param[in] strKey Primary key * @param[in] str List of sub key values ,\n Separate . such as : "x=xx\ny=yy\nz=zz" * @return result ,true: success , false: Failure */
bool setAllKeys(const QString& strKey, const QString& Str);
private:
int findSubKey(const QString& strKey, const QString& strSubKey);
bool save2File();
bool getValue(int i_nIndex, QString& o_pValue);
bool setValue(int i_nIndex, const QString& strValue);
void addValue(const QString& strKey, const QString& strSubKey, const QString& strValue, int i_nRet);
QFile m_file; // File object
QVector<QString> m_vecString;
bool m_bOpened; // Whether the configuration file is open
};
}
cpp file :
#include "iniconfig.h"
#include <QTextStream>
namespace ini {
static const int c_bufLen = 10240;
CIniConfig::CIniConfig() :m_bOpened(false) {
}
CIniConfig::~CIniConfig() {
}
bool CIniConfig::setFileName(const QString& strFileName) {
if (strFileName.length() == 0)
return false;
QString tStr;
m_file.setFileName(strFileName);
if (!m_file.open(QFile::ReadOnly)) // If the file does not exist, return false
{
m_file.close();
return false;
}
// file length =0 Then return to false
int t_nFileLen = m_file.size();
if (t_nFileLen <= 0) {
m_file.close();
return false;
}
m_bOpened = true;
m_vecString.clear();
QTextStream in(&m_file);
in.setCodec("UTF-8");
// Read the contents of the file into the row linked list
while (!in.atEnd()) {
tStr = in.readLine();
tStr = tStr.trimmed();
m_vecString.push_back(tStr);
}
m_file.close();
return true;
}
bool CIniConfig::removeSubKey(const QString& strKey, const QString& strSubKey) {
if (strKey.length() == 0 || strSubKey.length() == 0)
return false;
int nIndex = findSubKey(strKey, strSubKey);
if (nIndex > 0)
m_vecString.removeAt(nIndex);
return save2File();
}
bool CIniConfig::removeKey(const QString& strKey) {
if (strKey == NULL)
return false;
int nBegin = -1;
int nEnd = -1;
bool bFindKey = false;
QString strTmp;
strTmp = "[";
strTmp += strKey;
strTmp += "]";
for (int i = 0; i < m_vecString.size(); ++i) {
if (m_vecString[i].left(1) == ";") // notes
continue;
if (!bFindKey) {
// Primary key not found
if (m_vecString[i].indexOf(strTmp, Qt::CaseInsensitive) == 0) {
bFindKey = true;
nEnd = nBegin = i; // If the primary key has no subkeys , The deletion of the primary key is unsuccessful .
}
}
else {
// The primary key has been found
if (m_vecString[i].indexOf("[") == 0) {
// Subkeys do not exist
nEnd = i;
break;
}
if (i == m_vecString.size() - 1)
nEnd = i + 1;
}
}
if (nBegin >= 0 && nEnd >= 0) {
for (int j = nBegin; j < nEnd; j++)
m_vecString.removeAt(nBegin);
return save2File();
}
return false;
}
bool CIniConfig::deleteAllKeys() {
if (m_bOpened) {
m_file.open(QFile::WriteOnly | QFile::Truncate);
m_file.close();
m_vecString.clear();
return true;
}
else
return false;
}
bool CIniConfig::getBoolean(const QString& strKey, const QString& strSubKey, bool i_bDefault, bool* o_bRet) {
QString t_strResult;
if (o_bRet != NULL)
*o_bRet = true;
// Extract the result string
if (!getValue(findSubKey(strKey, strSubKey), t_strResult)) {
if (o_bRet != NULL)
*o_bRet = false;
}
// Convert the result string to an integer number
if (t_strResult == "Y" || t_strResult == "y")
return true;
else if (t_strResult == "N" || t_strResult == "n")
return false;
else {
if (o_bRet != NULL)
*o_bRet = false;
return i_bDefault;
}
}
bool CIniConfig::setBoolean(const QString& strKey, const QString& strSubKey, bool i_bValue) {
if (strKey == NULL || strSubKey == NULL)
return false;
int nLine = findSubKey(strKey, strSubKey);
char t_szBool[2];
if (i_bValue)
sprintf(t_szBool, "%s", "Y");
else
sprintf(t_szBool, "%s", "N");
if (nLine > 0) {
// There are primary keys and subkeys
if (!setValue(nLine, t_szBool))
return false;
}
else {
if (nLine == (-m_vecString.size() - 1))
nLine = 0;
addValue(strKey, strSubKey, t_szBool, nLine);
}
return save2File();
}
int CIniConfig::getInteger(const QString& strKey, const QString& strSubKey, int i_nDefault, bool* o_bRet) {
QString t_strResult;
if (o_bRet != NULL)
*o_bRet = true;
// Extract the result string
if (!getValue(findSubKey(strKey, strSubKey), t_strResult)) {
if (o_bRet != NULL)
*o_bRet = false;
return i_nDefault;
}
// Convert the result string to an integer number
return atoi(t_strResult.toLocal8Bit().data());
}
bool CIniConfig::setInteger(const QString& strKey, const QString& strSubKey, int i_nValue) {
if (strKey == NULL || strSubKey == NULL)
return false;
char t_szInt[64];
sprintf(t_szInt, "%d", i_nValue);
int nLine = findSubKey(strKey, strSubKey);
if (nLine > 0) {
// There are primary keys and subkeys
if (!setValue(nLine, t_szInt))
return false;
}
else {
if (nLine == (-m_vecString.size() - 1))
nLine = 0;
addValue(strKey, strSubKey, t_szInt, nLine);
}
return save2File();
}
double CIniConfig::getDouble(const QString& strKey, const QString& strSubKey, double i_fDefault, bool* o_bRet) {
QString t_strResult;
if (o_bRet != NULL)
*o_bRet = true;
// Extract the result string
if (!getValue(findSubKey(strKey, strSubKey), t_strResult)) {
if (o_bRet != NULL)
*o_bRet = false;
return i_fDefault;
}
// Convert the result string to a floating point number
return atof(t_strResult.toLocal8Bit().data());
}
bool CIniConfig::setDouble(const QString& strKey, const QString& strSubKey, double i_fValue) {
if (strKey == NULL || strSubKey == NULL)
return false;
char t_szReal[64];
sprintf(t_szReal, "%.3f", i_fValue);
int nLine = findSubKey(strKey, strSubKey);
if (nLine > 0) {
// There are primary keys and subkeys
if (!setValue(nLine, t_szReal))
return false;
}
else {
if (nLine == (-m_vecString.size() - 1))
nLine = 0;
addValue(strKey, strSubKey, t_szReal, nLine);
}
return save2File();
}
QString CIniConfig::getString(const QString& strKey, const QString& strSubKey, const QString& strDefault, bool* o_bRet) {
QString tStr;
if (o_bRet != NULL)
*o_bRet = true;
// Extract the result string
if (!getValue(findSubKey(strKey, strSubKey), tStr)) {
if (o_bRet != NULL)
*o_bRet = false;
return strDefault;
}
return tStr;
}
QString CIniConfig::getStringMultiline(const QString& strKey, const QString& strSubKey, const QString& strDefault, bool* o_bRet/*=NULL*/) {
QString tStr;
if (o_bRet != NULL)
*o_bRet = true;
int nIndex = findSubKey(strKey, strSubKey);
// Extract the result string
if (!getValue(nIndex, tStr)) {
if (o_bRet != NULL)
*o_bRet = false;
return strDefault;
}
// Read another line at full value
QString tNextLine = m_vecString[nIndex];
while (tNextLine.length() == (c_bufLen - 1)) {
tNextLine = m_vecString[++nIndex];
tStr += tNextLine;
}
return tStr;
}
bool CIniConfig::setString(const QString& strKey, const QString& strSubKey, const QString& strValue) {
if (strKey == NULL || strSubKey == NULL)
return false;
if (strValue.length() == 0)
return removeSubKey(strKey, strSubKey);
int nLine = findSubKey(strKey, strSubKey);
if (nLine > 0) {
// There are primary keys and subkeys
if (!setValue(nLine, strValue))
return false;
}
else {
if (nLine == (-m_vecString.size() - 1))
nLine = 0;
addValue(strKey, strSubKey, strValue, nLine);
}
return save2File();
}
QString CIniConfig::getAllKeys(const QString& strKey) {
bool bFindKey = false;
QString strTmp;
strTmp = "[";
strTmp += strKey;
strTmp += "]";
bool bFirstTime = true;
QString tStr;
for (int i = 0; i < m_vecString.size(); ++i) {
if (m_vecString[i][0] == ';') // notes
continue;
if (!bFindKey) {
// Primary key not found
if (m_vecString[i].indexOf(strTmp, Qt::CaseInsensitive) == 0)
bFindKey = true;
}
else {
// The primary key has been found
if (m_vecString[i].indexOf("[") == 0) // The search of subkeys ends
return tStr;
if (bFirstTime) {
tStr = m_vecString[i];
bFirstTime = false;
}
else {
tStr += "\r\n";
tStr += m_vecString[i];
}
}
}
return tStr;
}
bool CIniConfig::setAllKeys(const QString& strKey, const QString& i_pStr) {
QString strTmp;
strTmp = "[";
strTmp += strKey;
strTmp += "]";
int tBeginPos = -1;
QString tStr = i_pStr;
for (int i = 0; i < m_vecString.size(); ++i) {
if (m_vecString[i][0] == ';') // notes
continue;
if (m_vecString[i].indexOf(strTmp, Qt::CaseInsensitive) == 0) {
tBeginPos = i;
break;
}
}
if (tBeginPos < 0) {
// Primary key not found
m_vecString.push_front(strTmp);
tBeginPos = 1;
}
else {
tBeginPos += 1;
while (tBeginPos < m_vecString.size() && m_vecString[tBeginPos].indexOf("[") != 0) {
m_vecString.removeAt(tBeginPos);
}
}
// Insert processing ,
QStringList strList = tStr.split("\n");
QStringList::iterator iteStr = strList.begin();
while (iteStr != strList.end()) {
tStr = *iteStr;
m_vecString.insert(tBeginPos, tStr);
iteStr++;
}
return save2File();
}
//=======================================================
// Private functions
//=======================================================
int CIniConfig::findSubKey(const QString& strKey, const QString& strSubKey) {
int nRet = -m_vecString.size() - 1;
bool bFindKey = false;
int nTmpPos = 0;
QString tStr;
QString strTmp;
strTmp = "[";
strTmp += strKey;
strTmp += "]";
for (int i = 0; i < m_vecString.size(); ++i) {
if (m_vecString[i][0] == ';') // notes
continue;
if (!bFindKey) {
// Primary key not found
if (m_vecString[i].indexOf(strTmp, Qt::CaseInsensitive) == 0)
{
bFindKey = true;
nRet = -i - 1; // There is a primary key
}
}
else {
// The primary key has been found
nTmpPos = m_vecString[i].indexOf("=");
tStr = m_vecString[i].left(nTmpPos);
tStr = tStr.trimmed();
if (tStr == strSubKey) {
// Find the subkey
nRet = i;
break;
}
if (m_vecString[i][0] == '[') // Subkeys do not exist
break;
}
}
return nRet;
}
bool CIniConfig::save2File() {
if (!m_file.open(QFile::WriteOnly))
return false;
// Write cache to file
m_file.seek(0);
QTextStream out(&m_file);
out.setCodec("UTF-8");
QString str;
for (int i = 0; i < m_vecString.size(); ++i) {
str = m_vecString[i];
if ((str.right(1) != '\r') && (str.right(1) != '\n')) {
str += "\r\n";
}
out << str;
// m_file.write(str.toUtf8());
}
m_file.close();
return true;
}
bool CIniConfig::getValue(int i_nIndex, QString& o_pValue) {
if (i_nIndex < 0 || i_nIndex >= m_vecString.size())
return false;
int nBegin = m_vecString[i_nIndex].indexOf("=") + 1;
if (nBegin < 0) // No, "=" The number is wrong
return false;
o_pValue = m_vecString[i_nIndex].mid(nBegin);
o_pValue = o_pValue.trimmed();
return true;
}
bool CIniConfig::setValue(int i_nIndex, const QString& strValue) {
if (i_nIndex < 0 || i_nIndex >= m_vecString.size())
return false;
int nBegin = m_vecString[i_nIndex].indexOf("=") + 1;
if (nBegin < 0) // No, "=" The number is wrong
return false;
QString tStr = m_vecString[i_nIndex].mid(nBegin);
m_vecString[i_nIndex].replace(nBegin, tStr.length(), strValue);
return true;
}
void CIniConfig::addValue(const QString& strKey, const QString& strSubKey, const QString& strValue, int i_nRet) {
QString strTmp;
if (i_nRet == 0) {
// No primary key
// Add primary key
strTmp = "[";
strTmp += strKey;
strTmp += "]";
m_vecString.push_back("");
m_vecString.push_back(strTmp);
// Add subkeys
strTmp = strSubKey;
strTmp += "=";
strTmp += strValue;
m_vecString.push_back(strTmp);
}
else {
// There is a primary key but no subkey
// Add subkeys
strTmp = strSubKey;
strTmp += "=";
strTmp += strValue;
m_vecString.insert(-i_nRet, strTmp);
}
}
}
Examples of use :
void MainWindow::fun_setIni()
{
QString strFileName = "F:/qtdemo/demo/untitled1/config.ini";
ini::CIniConfig cfg;
cfg.setFileName(strFileName);
cfg.setAllKeys("config", "x=xx\ny=yy\nz=zz");
cfg.setBoolean("datatype", "bool-1", true);
cfg.setInteger("datatype", "int-1", 0);
cfg.setDouble("datatype", "real-1", 0.03);
cfg.setString("datatype", "string", QString::fromUtf8(" Hello "));
}
void MainWindow::fun_readIni()
{
QString strFileName = "F:/qtdemo/demo/untitled1/config.ini";
ini::CIniConfig cfg;
cfg.setFileName(strFileName);
QString str;
str = cfg.getString("datatype", "string", " La la la ");
qDebug()<<str;
}
3、 ... and 、 How to design common windowed programs ?
Four 、 How to use charts ?
5、 ... and 、 How to draw with a brush ?
6、 ... and 、 Developing a plug-in
The above is my summary of vegetable chicken .
Welcome to communicate with each other .
QQ:1107097641
版权声明
本文为[Happinessคิดถึง]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204220623156582.html
边栏推荐
猜你喜欢

Ide-idea-problem

Aspnetcore configuration multi environment log4net configuration file

Comprehensive calculation of employee information

LoadRunner - performance testing tool

PID debugging of coding motor (speed loop | position loop | follow)

2022山东省安全员C证上岗证题库及在线模拟考试
![Detailed description of MySQL index [B + tree index, hash index, full-text index, overlay index]](/img/1a/a22b4a35d3c083438d0f766a5ecb08.png)
Detailed description of MySQL index [B + tree index, hash index, full-text index, overlay index]

二进制文件版本控制工具选择难?看完这篇你会找到答案

2022 P cylinder filling training test questions and simulation test
![Eight elder brothers chronicle [4]](/img/87/f695d0275f8a66b9def48a75668d15.png)
Eight elder brothers chronicle [4]
随机推荐
Supersocket is Use in net5 - startup
Web Course Design - his system
This new feature of C 11, I would like to call it the strongest!
Supersocket is Use in net5 - concept
研讨会回放视频:如何提升Jenkins能力,使其成为真正的DevOps平台
Fundamentals of software testing and development
幂等性实践操作,基于业务讲解幂等性
Preview of converting doc and PDF to SWF file
Student achievement management
Can you answer the questions that cannot be answered with a monthly salary of 10k-20k?
Mysql database
oracle 查询外键含有逗号分隔的数据
[untitled]
Build websocket server in. Net5 webapi
be based on. NETCORE development blog project starblog - (2) environment preparation and creation project
Configure automatic implementation of curd projects
Ide-idea-problem
[MySQL] left Function | Right Function
Chapter 7 of C language programming (fifth edition of Tan Haoqiang) analysis and answer of modular programming exercises with functions
Super easy to use asynchronous export function of Excel