当前位置:网站首页>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
边栏推荐
- JS implementation of new
- IOTOS物联中台对接海康安防平台(iSecure Center)门禁系统
- 2022G2电站锅炉司炉考试题库及在线模拟考试
- 12. < tag linked list and common test site synthesis > - lt.234 palindrome linked list
- New ORM framework -- Introduction to beetlsql
- Find the number of leaf nodes of binary tree
- Why is bi so important to enterprises?
- MySQL installation pit
- Using swagger in. Net5
- “如何实现集中管理、灵活高效的CI/CD”在线研讨会精彩内容分享
猜你喜欢
The most easy to understand service container and scope of dependency injection
A comprehensive understanding of static code analysis
TCP three handshakes and four waves
Supersocket is Use in net5 - concept
2022年度Top9的任务管理系统
Build websocket server in. Net5 webapi
New ORM framework -- Introduction to beetlsql
C语言实现通讯录----(静态版本)
手机连接电脑后,QT的QDIR怎么读取手机文件路径
General testing technology [1] classification of testing
随机推荐
Explanation keyword of MySQL
xutils3修改了我提报的一个bug,开心
Docker pulls MySQL and connects
socket编程 send()与 recv()函数详解
The most easy to understand service container and scope of dependency injection
Quartz. Www. 18fu Used in net core
TCP three handshakes and four waves
Cefsharp stores cookies and reads cookies
【VS Code】解决jupyter文件在vs code中显示异常的问题
2022 Shandong Province safety officer C certificate work certificate question bank and online simulation examination
Supersocket is Use in net5 - concept
队列的存储和循环队列
IDEA查看历史记录【文件历史和项目历史】
Preview of converting doc and PDF to SWF file
Drawing polygons with < polygon / > circular array in SVG tag
超好用的【通用Excel导入功能】
The most understandable life cycle of dependency injection
POI create and export Excel based on data
Fiddler use
ThreadLocal test multithreaded variable instance