基于B树的文献管理系统(图形化界面和源码公开)


一、功能介绍

  1. 入库:(管理员)购入新文献,确定文献号录入到系统中,如果文献存在,则将该文献的总库存量增加。
  2. 清除:(管理员)按文献号从系统中删除文献。
  3. 借阅:(登录状态)如果文献的现存量大于零,则登记借阅者证件号和归还日期后借出。
  4. 归还:(登录状态)注销借阅者登记,增加现存量。
  5. 检索:(登录状态)根据文献号给出某文献的相关信息。
  6. 预约:(登录状态)为预约者预留需要借阅的文献。
  7. 检索作者的文献:给出某作者的所有著作。
  8. 显示:基于文献号生成B树的存储结构图片。
  9. 日志:记录程序每次运行的操作到log.txt。
  10. 保存:以二进制保存文献信息到data.dat,每执行一次改变文献信息的操作就会执行一次。
  11. 加载:启动程序就会从data.dat以二进制加载进文献信息到B树。
  12. 注册:用户注册账号,保存账号密码到user_data.txt。
  13. 登录:用户登录(读取user_data.txt验证身份)或管理员登录(管理员密码: aa123+).
  14. 退出:退出登录状态或退出程序。

(管理员)和(登录状态)是使用相应功能的条件。

二、B树及其记录信息结构体的定义

// 文献信息
typedef struct Literature {
    int literatureNumber;         // 文献编号
    char title[MAX_LEN_TITLE];    // 文献名
    char author[MAX_LEN_AUTHOR];  // 著者
    int currentStock;             // 现存量
    int totalStock;               // 总库存
    int appointNum;         // 已预约数量
    int borrowNum;         // 已借出数量
    char borIdNumber[50][50];  // 证件号,假设最大长度为50
    char appIdNumber[50][50];  // 证件号,假设最大长度为50
    char returnDate[50][50];  // 归还日期,假设最大长度为50
} Literature, *Liter;

// B树结点
typedef struct BTNode{
    int keyNum;             // 结点当前关键字个数
    KeyType key[m + 1];     // 关键字数组,key[0]未用
    struct BTNode *parent;  // 双亲结点
    struct BTNode *ptr[m + 1];     // 孩子结点指针数组, 0 号有使用
    Liter data[m + 1];     // 数据数组,data[0]未用
} BTNode, *BTree;

// 用户信息
typedef struct {
    char username[200]; // 用户名
    char account[200];  // 账号
    char password[200]; // 密码
} User;

三、B树的插入删除等相关代码

这些代码过于繁琐,不在此展示。
完整代码见:SourceCode/BTree

四、图形化界面实现

这里的图形化借助了EasyX图形库,使用了EasyX的图形化界面实现。

4.1 实现代码

// 可视化界面
void interfaces(){
    int i;
    short win_width,win_height;//定义窗口的宽度和高度
    win_width = 600;win_height = 400;
    initgraph(win_width,win_height);//初始化窗口(黑屏)

    HWND hnd = GetHWnd();
    SetWindowText(hnd, "文献管理系统");
    setbkcolor(RGB(255,255,255));//设置背景色,原来默认黑色
    cleardevice();//清屏(取决于背景色)

    IMAGE background;//定义一个图片名.
    loadimage(&background,"..\\bg.png",600,400,1);//从图片文件获取图像
    setbkmode(TRANSPARENT);
    putimage(0, 0, &background);//绘制图像到屏幕,图片左上角坐标为(0,0)

    RECT R1={reg[0][0],reg[0][1],reg[0][2],reg[0][3]};
    RECT R2={reg[1][0],reg[1][1],reg[1][2],reg[1][3]};
    RECT R3={reg[2][0],reg[2][1],reg[2][2],reg[2][3]};
    RECT R4={reg[3][0],reg[3][1],reg[3][2],reg[3][3]};
    RECT R5={reg[4][0],reg[4][1],reg[4][2],reg[4][3]};
    RECT R6={reg[5][0],reg[5][1],reg[5][2],reg[5][3]};
    RECT R7={reg[6][0],reg[6][1],reg[6][2],reg[6][3]};
    RECT R8={reg[7][0],reg[7][1],reg[7][2],reg[7][3]};
    RECT R9={reg[8][0],reg[8][1],reg[8][2],reg[8][3]};
    RECT R10={reg[9][0],reg[9][1],reg[9][2],reg[9][3]};
    RECT R11={reg[10][0],reg[10][1],reg[10][2],reg[10][3]};
    RECT R12={reg[11][0],reg[11][1],reg[11][2],reg[11][3]};
    RECT R13={reg[12][0],reg[12][1],reg[12][2],reg[12][3]};
    RECT R14={reg[13][0],reg[13][1],reg[13][2],reg[13][3]};
    LOGFONT f;//字体样式指针
    gettextstyle(&f);					//获取字体样式
    _tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体
    f.lfQuality = ANTIALIASED_QUALITY;    // 设置输出效果为抗锯齿
    settextstyle(&f);                     // 设置字体样式
    settextcolor(GREEN);				//BLACK在graphic.h头文件里面被定义为黑色的颜色常量

    if(caretakers){
        drawtext("入库文献",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示
        drawtext("删除文献",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示
        drawtext("显示B树图",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R8内输入文字,水平居中,垂直居中,单行显示
        drawtext("检索文献",&R4,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
        drawtext("借阅文献",&R5,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R4内输入文字,水平居中,垂直居中,单行显示
        drawtext("归还文献",&R6,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R5内输入文字,水平居中,垂直居中,单行显示
        drawtext("搜作者文献",&R7,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R6内输入文字,水平居中,垂直居中,单行显示
        drawtext("预约借阅文献",&R8,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R7内输入文字,水平居中,垂直居中,单行显示
    } else{
        drawtext("检索文献",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示
        drawtext("借阅文献",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R4内输入文字,水平居中,垂直居中,单行显示
        drawtext("归还文献",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R5内输入文字,水平居中,垂直居中,单行显示
        drawtext("搜作者文献",&R4,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R6内输入文字,水平居中,垂直居中,单行显示
        drawtext("预约借阅文献",&R5,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R7内输入文字,水平居中,垂直居中,单行显示
    }

    if (login){
        drawtext(userName,&R12,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R12内输入文字,水平居中,垂直居中,单行显示
        drawtext("退出登录",&R13,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R13内输入文字,水平居中,垂直居中,单行显示
    }else{
        drawtext("注册",&R10,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R10内输入文字,水平居中,垂直居中,单行显示
        drawtext("登录",&R11,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R11内输入文字,水平居中,垂直居中,单行显示
        drawtext("管理员登录",&R14,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R14内输入文字,水平居中,垂直居中,单行显示
    }
    drawtext("退出",&R9,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R9内输入文字,水平居中,垂直居中,单行显示

    setlinecolor(RED);

    if(caretakers){
        rectangle(reg[0][0],reg[0][1],reg[0][2],reg[0][3]);
        rectangle(reg[1][0],reg[1][1],reg[1][2],reg[1][3]);
        rectangle(reg[2][0],reg[2][1],reg[2][2],reg[2][3]);
        rectangle(reg[3][0],reg[3][1],reg[3][2],reg[3][3]);
        rectangle(reg[4][0],reg[4][1],reg[4][2],reg[4][3]);
        rectangle(reg[5][0],reg[5][1],reg[5][2],reg[5][3]);
        rectangle(reg[6][0],reg[6][1],reg[6][2],reg[6][3]);
        rectangle(reg[7][0],reg[7][1],reg[7][2],reg[7][3]);
    }else{
        rectangle(reg[0][0],reg[0][1],reg[0][2],reg[0][3]);
        rectangle(reg[1][0],reg[1][1],reg[1][2],reg[1][3]);
        rectangle(reg[2][0],reg[2][1],reg[2][2],reg[2][3]);
        rectangle(reg[3][0],reg[3][1],reg[3][2],reg[3][3]);
        rectangle(reg[4][0],reg[4][1],reg[4][2],reg[4][3]);
    }

    if (login){
        rectangle(reg[11][0],reg[11][1],reg[11][2],reg[11][3]);
        rectangle(reg[12][0],reg[12][1],reg[12][2],reg[12][3]);
    }else{
        rectangle(reg[9][0],reg[9][1],reg[9][2],reg[9][3]);
        rectangle(reg[10][0],reg[10][1],reg[10][2],reg[10][3]);
        rectangle(reg[13][0],reg[13][1],reg[13][2],reg[13][3]);
    }

    rectangle(reg[8][0],reg[8][1],reg[8][2],reg[8][3]);


    char title[] = "文献管理系统";
    outtextxy(80, 10, title);
}

上述代码实现了图形化界面的绘制,其中reg是一个二维数组,用于存储各个按钮的位置信息,logincaretakers是两个布尔值,用于判断当前是否登录和是否为管理员。

4.2 按钮的判断函数

int reg[14][4]={{20,80,120,110},
             {140,80,240,110},
             {20,130,120,160},
             {140,130,240,160},
             {20,180,120,210},
             {140,180,240,210},
             {20,230,120,260},
             {140,230,240,260},
             {500,350,560,380},
             {450,10,550,40},
             {450,50,550,80},
             {450,10,550,40},
             {450,50,550,80},
             {450,90,550,120}};//12个按钮的二维数组

//按钮判断函数
int button_judge(int x,int y)
{
    if (caretakers){
        if(x>reg[0][0] && x<reg[0][2] && y>reg[0][1] && y<reg[0][3])return 1;
        if(x>reg[1][0] && x<reg[1][2] && y>reg[1][1] && y<reg[1][3])return 2;
        if(x>reg[2][0] && x<reg[2][2] && y>reg[2][1] && y<reg[2][3])return 3;
        if(x>reg[3][0] && x<reg[3][2] && y>reg[3][1] && y<reg[3][3])return 4;
        if(x>reg[4][0] && x<reg[4][2] && y>reg[4][1] && y<reg[4][3])return 5;
        if(x>reg[5][0] && x<reg[5][2] && y>reg[5][1] && y<reg[5][3])return 6;
        if(x>reg[6][0] && x<reg[6][2] && y>reg[6][1] && y<reg[6][3])return 7;
        if(x>reg[7][0] && x<reg[7][2] && y>reg[7][1] && y<reg[7][3])return 8;
    }else{
        if(x>reg[0][0] && x<reg[0][2] && y>reg[0][1] && y<reg[0][3])return 4;
        if(x>reg[1][0] && x<reg[1][2] && y>reg[1][1] && y<reg[1][3])return 5;
        if(x>reg[2][0] && x<reg[2][2] && y>reg[2][1] && y<reg[2][3])return 6;
        if(x>reg[3][0] && x<reg[3][2] && y>reg[3][1] && y<reg[3][3])return 7;
        if(x>reg[4][0] && x<reg[4][2] && y>reg[4][1] && y<reg[4][3])return 8;
    }

    if (login){
        if(x>reg[11][0] && x<reg[11][2] && y>reg[11][1] && y<reg[11][3])return 12;
        if(x>reg[12][0] && x<reg[12][2] && y>reg[12][1] && y<reg[12][3])return 13;
    }else{
        if(x>reg[9][0] && x<reg[9][2] && y>reg[9][1] && y<reg[9][3])return 10;
        if(x>reg[10][0] && x<reg[10][2] && y>reg[10][1] && y<reg[10][3])return 11;
        if(x>reg[13][0] && x<reg[13][2] && y>reg[13][1] && y<reg[13][3])return 14;
    }
    if(x>reg[8][0] && x<reg[8][2] && y>reg[8][1] && y<reg[8][3])return 9;

    return 0;
}

上述代码中的二维数组是按钮位置的信息。
button_judge函数 实现了按钮的判断,返回值为按钮的编号。

(x1, y1, x2, y2) 分别为按钮的左上角和右下角的坐标。

4.3 效果展示

未登录
用户登录

管理员登录

五、B树的可视化

在编写B树的代码时,为了方便调试,B树的可视化是必不可少的。
这里的可视化借助了Graphviz工具,使用了Graphviz的DOT语言来实现。
(其他树形结构也可以使用这个工具)

5.1 实现代码

// 辅助函数:递归打印B树的结点
void printNode(BTree node, FILE *out) {
    if (node != NULL) {
        fprintf(out, "%d [label=\"", node->key[1]);

        for (int i = 1; i <= node->keyNum; ++i) {
            fprintf(out, "%d", node->key[i]);
            if (i < node->keyNum) {
                fprintf(out, ",");
            }
        }

        fprintf(out, "\"];\n");

        for (int i = 0; i <= node->keyNum; ++i) {
            if (node->ptr[i] != NULL) {
                fprintf(out, "%d -> %d;\n", node->key[1], node->ptr[i]->key[1]);
            }
        }

        for (int i = 0; i <= node->keyNum; ++i) {
            printNode(node->ptr[i], out);
        }
    }
}

// 打开文件并写入B树的DOT格式
void treePrint(BTree root) {
    FILE *out = fopen("./Btree.dot", "w");
    if (out == NULL) {
        perror("Error opening file");
        exit(EXIT_FAILURE);
    }

    fprintf(out, "digraph {\nnode[shape=record];\n");
    printNode(root, out);
    fprintf(out, "}\n");

    fclose(out);

    // 执行 dot 命令来生成图像
    system(".\\Graphviz\\bin\\dot -Tpng ./Btree.dot -o ./Btree.png");
    sprintf(mes, "在当前文件夹生成tree.png文件");
}

5.2效果展示

B树可视化

六、说明

  1. 大概能看懂就能套用图形化界面使用,大概懂得InputBox(小窗口输入),MessageBoxA(弹窗提示就可以完成简单的图形化了(当然按钮功能更能体现图形化)
  2. 学有余力可以去了解.dot语法,这个可以帮助生成树形抽象数据结构的可视化图片。
  3. 该文献系统的全部实现代码在github可以找到: sourceCode
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值