基于QT实现的词典(附源码)

简介: 主要用到的技术:JSON字符串,堆栈窗体(方便以后加新功能),QTcpSocket,LcdNumber,时间戳的转换。通信用的是TCP通信,将TCP封在了一个头文件中,哪个文件想去通过TCP链接发送消息就可以使用头文件QT端写的是客户端,服务器还是在我笔记上次的Linux系统下写的服务器,做了一些小修改

 目录

词典功能:

技术栈和通信协议:

项目演示效果:

技术简单讲解:

1. QT JSON字符串处理

2. 堆栈窗体(StackedWidget) - 方便扩展新功能

3. QTcpSocket - 网络通信

服务器代码:

dictionary.h

cJSON.h


词典功能:

1. 查询单词

2. 查询历史记录

3. 注册

4. 登陆

5. 删除历史记录。

技术栈和通信协议:

主要用到的技术:JSON字符串,堆栈窗体(方便以后加新功能),QTcpSocket,LcdNumber,时间戳的转换。

通信用的是TCP通信,将TCP封在了一个头文件中,哪个文件想去通过TCP链接发送消息就可以使用头文件

QT端写的是客户端,服务器还是在我笔记上次的Linux系统下写的服务器,做了一些小修改

项目演示效果:

image.gif 编辑

image.gif 编辑 image.gif 编辑

技术简单讲解:

1. QT JSON字符串处理

Qt提供了一个强大的类`QJsonDocument`和`QJsonObject`来处理JSON数据。简单来说:

// **QJsonObject**:代表一个JSON对象,你可以添加键值对到这个对象中。
  QJsonObject obj;
  obj["name"] = "Alice";
  obj["age"] = 30;
- **QJsonDocument**:可以将`QJsonObject`或`QJsonArray`转换为JSON字符串或从JSON字符串解析出对象。
  // 从对象生成JSON字符串
  QJsonDocument doc(obj);
  QByteArray json = doc.toJson(QJsonDocument::Compact); // 缩进形式或Compact形式
  
  // 从JSON字符串解析
  QJsonDocument document = QJsonDocument::fromJson(json);
  QJsonObject jsonObject = document.object();

image.gif

2. 堆栈窗体(StackedWidget) - 方便扩展新功能

`QStackedWidget`是Qt中用于实现多个页面切换的容器控件,非常适合构建需要多个界面且能够轻松切换的应用程序,比如设置页面、主界面与详情页等。

- **基本使用**:将不同的页面(QWidget的子类)添加到`QStackedWidget`中,然后通过索引或指针切换显示的页面。

 

QStackedWidget *stack = new QStackedWidget;
  QWidget *page1 = new QWidget;
  QWidget *page2 = new QWidget;
  
  stack->addWidget(page1);
  stack->addWidget(page2);
  
  // 切换到第二个页面
  stack->setCurrentIndex(1);

image.gif

3. QTcpSocket - 网络通信

`QTcpSocket`类提供了TCP网络通信的能力,允许你的应用程序作为客户端或服务器与其他网络实体交换数据。

- **作为客户端连接**:
  
  QTcpSocket socket;
  socket.connectToHost("example.com", 80);
  
  if(socket.waitForConnected(3000)) {
      qDebug() << "Connected!";
      socket.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
  } else {
      qDebug() << "Connection failed!";
  }
- **读取和写入数据**:使用`readAll()`、`write()`等方法处理数据。

image.gif

### 4. QLCDNumber - 模拟LCD数字显示

`QLCDNumber`类提供了一个模拟液晶显示器风格的数字显示控件,常用于显示计数器、温度等数值信息。

- **作为客户端连接**:
  QTcpSocket socket;
  socket.connectToHost("example.com", 80);
  
  if(socket.waitForConnected(3000)) {
      qDebug() << "Connected!";
      socket.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
  } else {
      qDebug() << "Connection failed!";
  }
- **读取和写入数据**:使用`readAll()`、`write()`等方法处理数据。

image.gif

服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "dictionary.h"
#include <sqlite3.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include "cJSON.h"
int acceptfd;
int search_word(int acceptfd);
int num = 10;
time_t t;
struct tm *tm;
char data[1024];
// 注册函数
void register_sev(int acceptfd, char *p, sqlite3 *db)
{
    char sql[128] = " "; //定义数组来装insert语句
    sprintf(sql, "insert into user values('%s','%s');", dic.name, dic.password);
    //如果插入不成功则给dic.text赋值失败反之OK
    if (sqlite3_exec(db, sql, NULL, NULL, &p) != SQLITE_OK)
    {
        strcpy(dic.text, "already exits!");
    }
    else
    {
        strcpy(dic.text, "OK");
    }
    dic.history_id = 1;
    cJSON *data = cJSON_CreateObject();
    cJSON_AddStringToObject(data, "text", dic.text);
    cJSON_AddNumberToObject(data, "history", dic.history_id);
    char *buf;
    buf = cJSON_PrintUnformatted(data);
    send(acceptfd, buf, strlen(buf), 0); //发送
}
// 登录函数
void login_sev(int acceptfd, char *p, char **result, int row, int line, sqlite3 *db)
{
    // 定义一个输入去接收这个查询用户名和密码的语句
    char buf[128];
    dic.history_id = 1;
    sprintf(buf, "select * from user where name = '%s' and password = '%s';", dic.name, dic.password);
    // 先把输入的数据查询一下在表里,如果表里有数据则代表数据库有密码
    if (sqlite3_get_table(db, buf, &result, &row, &line, &p) != SQLITE_OK)
    {
        perror("sev sqlite3_table error\n");
        return;
    }
    // 如果行大于零则代表有数据成功
    if (row > 0)
    {
        strcpy(dic.text, "login successs");
    }
    else
    {
        strcpy(dic.text, "login loose,please check your name or password!");
    }
    cJSON *data = cJSON_CreateObject();
    cJSON_AddStringToObject(data, "text", dic.text);
    cJSON_AddNumberToObject(data, "history", dic.history_id);
    char *str;
    str = cJSON_PrintUnformatted(data);
    send(acceptfd, str, strlen(str), 0); //发送
    return;
}
// 查询单词
int query_sev(int acceptfd, sqlite3 *db, char *p)
{
    time_t t;
    struct tm *tm;
    int a = 0;
    //定义一个语句去接受查询的语句
    char sql2[128] = "";
    if (!strcmp(dic.password, "#")) //如果在查找单词中输入#就会退出
    {
        return -1;
    }
    int found = search_word(acceptfd); //去接受查询单词这个函数的返回值 不为1则代表失败 返回1则代表成功
    if (found != 1)
    {   
        dic.history_id = 1;
        strcpy(dic.text, "can't find \n");
        cJSON *data = cJSON_CreateObject();
        cJSON_AddStringToObject(data, "text", dic.text);
        cJSON_AddNumberToObject(data, "history", dic.history_id);
        char *buf;
        buf = cJSON_PrintUnformatted(data);
        send(acceptfd, buf, strlen(buf), 0); //发送
    }
    else
    {
        time(&t);
        tm = localtime(&t);
        sprintf(dic.time_sev, "%d-%d-%d %d:%d:%d", tm->tm_year + 1900,
                tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
        sprintf(sql2, "insert into record values('%s','%s');", dic.password, dic.time_sev); // 成功后就把查询成功的单词插入到record这个表里
        if (sqlite3_exec(db, sql2, NULL, NULL, &p) != SQLITE_OK)
        {
            perror("record insert error\n");
            return -1;
        }
    }
    return 0;
}
// 查询单词函数
int search_word(int acceptfd)
{
    //定义每次接受来的密码的长度
    int len = strlen(dic.password);
    //打开文件用标准IO
    FILE *sp = fopen("dic.txt", "r");
    if (NULL == sp)
    {
        perror("sp error\n");
        return -1;
    }
    //如果dic.txt文件不为空时继续执行
    while (fgets(dic.text, sizeof(dic.text), sp) != NULL)
    {
        char buf[32] = {};
        for (int i = 0; i < 32; i++)
        {
            if (dic.text[i] != ' ')
            {
                buf[i] = dic.text[i];
            }
            else
            {
                break;
            }
        }
        //拿密码去比较文件中单词 如果成功发送dic 并且返回1
        if (!strcmp(buf, dic.password))
        {
            printf("%s\n", dic.text);
            cJSON *data = cJSON_CreateObject();
            cJSON_AddStringToObject(data, "text", dic.text);
            cJSON_AddNumberToObject(data, "history", dic.history_id);
            char *buf;
            buf = cJSON_PrintUnformatted(data);
            send(acceptfd, buf, strlen(buf), 0); //发送
            return 1;
        }
        else
        {
            continue;
        }
    }
    return -1;
}
//历史函数
int history_sev(char *p, sqlite3 *db)
{
    char buf[64] = "select * from record ;";
    char **pResult;
    int nRow;
    int nCol;
    strcpy(dic.text,"");
    if (sqlite3_get_table(db, buf, &pResult, &nRow, &nCol, &p) != SQLITE_OK)
    {
        fprintf(stderr, "select is error: %s\n", p); //如果不等于OK责代表没成功,报错
        return -1;
    }
    else
    {
        for (size_t i = 2; i < nRow * nCol + 2; i++)
        {
            strcat(dic.text, pResult[i]);
            strcat(dic.text, "\r\n");
        }
        printf("%s\n",dic.text);
        dic.history_id = 2;
        cJSON *data = cJSON_CreateObject();
        cJSON_AddStringToObject(data, "text", dic.text);
        cJSON_AddNumberToObject(data, "history", dic.history_id);
        char *buf;
        buf = cJSON_PrintUnformatted(data);
        send(acceptfd, buf, strlen(buf), 0); //发送
    }
    return 0;
}
//删除历史记录函数
void delete_history(char *p, sqlite3 *db)
{
    if (sqlite3_exec(db, "delete from record;", NULL, NULL, &p) != SQLITE_OK)
    {
        fprintf(stderr, "delete is error: %s\n", p); //如果不等于OK责代表没成功,报错
    }
    else
    {
        strcpy(dic.text, "删除成功!");
        cJSON *data = cJSON_CreateObject();
        cJSON_AddStringToObject(data, "text", dic.text);
        cJSON_AddNumberToObject(data, "history", dic.history_id);
        char *buf;
        buf = cJSON_PrintUnformatted(data);
        send(acceptfd, buf, strlen(buf), 0); //发送
    }
    return;
}
//处理僵尸进程
void hanlder(int arg)
{
    waitpid(-1, NULL, WNOHANG);
}
int main(int argc, char const *argv[])
{
    //1. 创建sql表
    sqlite3 *db = NULL;
    if (sqlite3_open("./dictionary.db", &db) != 0)
    {
        fprintf(stderr, "sqlite3 is error%s\n", sqlite3_errmsg(db));
        return -1;
    }
    // 创建两个表
    char *p = NULL;
    if (sqlite3_exec(db, "create table user(name char primary key, password char);", NULL, NULL, &p) != SQLITE_OK)
    {
        fprintf(stderr, "create stu1 is error %s\n", p);
        return -1;
    }
    if (sqlite3_exec(db, "create table record(password char,time char);", NULL, NULL, &p) != SQLITE_OK)
    {
        fprintf(stderr, "create record is error %s\n", p);
        return -1;
    }
    // 创建指针以及行和列的变量
    char **result = NULL;
    int row = 0;
    int line = 0;
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("sev sockfd error\n");
        return -1;
    }
    //填充结构体
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("192.168.50.248");
    int len = sizeof(caddr);
    // 绑定 和 监听
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind error\n");
        return -1;
    }
    if (listen(sockfd, 5) < 0)
    {
        perror("sev listen error\n");
        return -1;
    }
    // 处理僵尸进程
    signal(SIGCHLD, hanlder);
    // 循环去接受
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("sev acceptfd is error\n");
            return -1;
        }
        // 创建进程
        pid_t pid = fork();
        if (pid < 0)
        {
            perror("sev pid error\n");
            return -1;
        }
        else if (pid == 0) // 子进程
        {
            while (recv(acceptfd, data, sizeof(data), 0))
            {
                sleep(1);
                printf("%s\n", data);
                cJSON *root = cJSON_Parse(data);
                dic.type = cJSON_GetObjectItem(root, "type")->valueint;
                strcpy(dic.password, cJSON_GetObjectItem(root, "password")->valuestring);
                strcpy(dic.name, cJSON_GetObjectItem(root, "name")->valuestring);
                strcpy(dic.text, cJSON_GetObjectItem(root, "text")->valuestring);
                strcpy(dic.time_sev, cJSON_GetObjectItem(root, "time_sev")->valuestring);
                dic.history_id = 0;
                printf("type = %c\n", dic.type);
                printf("password = %s\n", dic.password);
                printf("name = %s\n", dic.name);
                printf("text = %s\n", dic.text);
                printf("time_sev = %s\n", dic.time_sev);
                printf("history_id = %d\n", dic.history_id);
                switch (dic.type)
                {
                case 'r':
                    register_sev(acceptfd, p, db); // 注册函数
                    break;
                case 'l':
                    login_sev(acceptfd, p, result, row, line, db); // 登录函数
                    break;
                case 'q':
                    query_sev(acceptfd, db, p); //查询函数
                    break;
                case 'h':
                    history_sev(p, db); //历史查询函数
                    break;
                case 'd':
                    delete_history(p, db); // 历史记录删除函数
                    break;
                default:
                    break;
                }
            }
        }
        else // 父进程
        {
            close(acceptfd);
        }
    }
    return 0;
}

image.gif

dictionary.h

struct dictionary
{
    char type;          // r代表注册 l 代表登录 q代表查询 h代表历史
    char name[128];     // 用户名
    char password[128]; // 密码
    char text[128];     // 通知内容或者查找内容
    char time_sev[32];  // 时间
    int history_id;
} dic;

image.gif

cJSON.h

/*
  Copyright (c) 2009 Dave Gamble
 
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
 
  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.
 
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
  
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* The cJSON structure: */
typedef struct cJSON {
  struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
  struct cJSON *child;    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
  int type;         /* The type of the item, as above. */
  char *valuestring;      /* The item's string, if type==cJSON_String */
  int valueint;       /* The item's number, if type==cJSON_Number */
  double valuedouble;     /* The item's number, if type==cJSON_Number */
  char *string;       /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
      void *(*malloc_fn)(size_t sz);
      void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char  *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char  *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* Delete a cJSON entity and all subentities. */
extern void   cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int    cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
extern int cJSON_HasObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
  
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);  /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
  
/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name)    cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)    cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)   cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)  cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)  cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)  cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val)     ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val)    ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
/* Macro for iterating over an array */
#define cJSON_ArrayForEach(pos, head)     for(pos = (head)->child; pos != NULL; pos = pos->next)
#ifdef __cplusplus
}
#endif
#endif

image.gif

感谢大家的观看~,QT源码在最上方哦

相关文章
|
C++ Python
Qt+C++桌面计算器源码
Qt+C++桌面计算器源码
159 0
Qt+C++桌面计算器源码
|
2月前
|
编解码 开发工具 UED
QT Widgets模块源码解析与实践
【9月更文挑战第20天】Qt Widgets 模块是 Qt 开发中至关重要的部分,提供了丰富的 GUI 组件,如按钮、文本框等,并支持布局管理、事件处理和窗口管理。这些组件基于信号与槽机制,实现灵活交互。通过对源码的解析及实践应用,可深入了解其类结构、布局管理和事件处理机制,掌握创建复杂 UI 界面的方法,提升开发效率和用户体验。
154 12
|
2月前
|
Windows
QT源码拾贝6-11(qwindowswindow)
这篇文章深入探讨了Qt源码中与窗口激活相关的函数,QDebug运算符重载,vscode的变量提示,Windows常用类型名,获取所有窗体的方法,以及QSharedPointer智能指针的使用。
QT源码拾贝6-11(qwindowswindow)
|
2月前
|
存储 Java C++
QT源码拾贝0-5(qimage和qpainter)
这篇文章介绍了在Qt源码中qimage和qpainter的使用,包括线程池的使用、智能指针的存储、std::exchange函数的应用、获取类对象的方法以及QChar字节操作。
QT源码拾贝0-5(qimage和qpainter)
|
3月前
|
存储 C++
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
|
3月前
|
存储 算法 C++
【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】
【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】
|
3月前
从源码角度分析Qt元对象系统2
从源码角度分析Qt元对象系统
55 0
|
3月前
|
存储
从源码角度分析Qt元对象系统1
从源码角度分析Qt元对象系统
78 0
|
3月前
|
存储 C++
【C++】C++ QT实现 学生信息管理系统(QT源码)【独一无二】
【C++】C++ QT实现 学生信息管理系统(QT源码)【独一无二】
|
6月前
|
人工智能 Ubuntu Shell
【Ubuntu工具】安装教程:Ubuntu系统上源码编译安装QT5.15.13(有坑)
【Ubuntu工具】安装教程:Ubuntu系统上源码编译安装QT5.15.13(有坑)
524 0