基于Windows系统用C++做一个点名工具

news2024/11/22 19:51:40

目录

一、前言        

二、主要技术点

三、准备工作

四、主界面

1.绘制背景图

2、实现读取花名册功能

3.实现遍历花名册功能

4.实现储存功能

4.1创建数据库

4.2存储数据到数据库表

4.3读取数据库表数据


一、前言        

        人总是喜欢回忆过去,突然回忆起大学时光来,我的计算机老师是用自己做的点名工具,来抽人回答问题的。那么本次的主题就是做一个点名工具,咱们在点名的基础上加一个存储功能,在多次抽取的情况下进行存储。

二、主要技术点

Sqlite数据的增删改查、GDI/GDI+绘制、COM接口、文件数据读取、定时器。

三、准备工作

1.SQLite数据库操作工具

适用于 SQLite 的 DB 浏览器

2.SqLiteCpp第三方库

GitHub - SRombauts/SQLiteCpp:SQLiteC++ (SQLiteCpp) 是一个智能且易于使用的 C++ SQLite3 包装器。

3.一个.txt格式的花名册

四、主界面

主界面包含三个控件:静态文本控件、按钮控件、编辑框控件。分别用于响应选择本地花名册文件、遍历花名册名单、显示花名册名单。

1.绘制背景图

映射WM_PAINT消息进行绘制,可以使用GDI或者GDI+方法。

MESSAGE_HANDLER(WM_PAINT, OnPaint)

GDI的方法:

    LRESULT CRandomDlg::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(&ps);
        HDC hdcMem = CreateCompatibleDC(hdc);
        if (lstrlen(m_picPath) != 0)
        {
            HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, m_picPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
            HBITMAP oldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
            BITMAP bitmap;
            GetObject(hBitmap, sizeof(bitmap), &bitmap);
            SetStretchBltMode(hdc, STRETCH_HALFTONE);   //设置位图拉伸模式,解决模糊问题
            StretchBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
                ps.rcPaint.bottom - ps.rcPaint.top, hdcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
            SelectObject(hdcMem, oldBitmap);
            DeleteObject(hBitmap);
        }
        EndPaint(&ps);
        DeleteDC(hdcMem);
        return 0;
    }

GDI+方法:

#include <gdiplus.h>
using namespace Gdiplus;
    
LRESULT CRandomDlg::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(&ps);
        if (lstrlen(m_picPath) != 0)
        {
            Graphics gh(hdc);
            RectF rect(ps.rcPaint.left, ps.rcPaint.top,
                ps.rcPaint.right - ps.rcPaint.left,
                ps.rcPaint.bottom - ps.rcPaint.top);
           Image* image = Image::FromFile(m_picPath);
            gh.DrawImage(image, rect);
        }

        EndPaint(&ps);
        return 0;
    }

如果你写代码所使用的框架没有初始化GDI+,那么需要我们手动初始化和释放:

//初始化GDI+
ULONG_PTR gdiplusToken;   
GdiplusStartupInput gdiplusStartupInput;

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//释放GDI+
GdiplusShutdown(gdiplusToken);

2、实现读取花名册功能

映射WM_COMMAND消息。

MESSAGE_HANDLER(WM_COMMAND, OnCommand)

处理静态文本控件被点击时的通知消息STN_CLICKED,以此来弹出选择窗口选择花名册文件。我们使用IFileDialog接口来打开一个shell窗口进行文件选择,获取选择文件的路径之后通过CreateFile()函数打开文件,再通过ReadFile()函数读取其内容:

 LRESULT CRandomDlg::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        int wmId = LOWORD(wParam);
        int messageId = HIWORD(wParam);
        switch (messageId)
        {
        case STN_CLICKED:
        {
            if (wmId == IDC_CHOOSESTATIC)
            {
                
            IFileDialog* pFileDialog = NULL;
                //创建IFileDialog接口实例
                HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileDialog));
                if (SUCCEEDED(hr))
                {
                    IShellItem* pItem = nullptr;
                    DWORD dwOptions;
                    hr = pFileDialog->GetOptions(&dwOptions);
                    //设置窗口选项
                    pFileDialog->SetOptions(dwOptions | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST);
                    //设置窗口标题
                    pFileDialog->SetTitle(L"请选择文件:");
                    //设置筛选器
                    COMDLG_FILTERSPEC fileSpec[] =
                    {
                        { L"文本文件", L"*.txt"},
                    };
                    pFileDialog->SetFileTypes(1, fileSpec);
                    hr = pFileDialog->Show(GetWindow(IDD_RANDOMDIALOG));
                    //获取用户选择
                    if (SUCCEEDED(hr))
                    {
                        hr = pFileDialog->GetResult(&pItem);
                        if (SUCCEEDED(hr))
                        {
                            //获取选择文件的路径
                            hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &m_pszFilePath);
                            HANDLE hFile = CreateFile(m_pszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                            if (hFile != INVALID_HANDLE_VALUE)
                            {
                                //打开文件读取内容
                                DWORD size = GetFileSize(hFile, NULL);
                                if (size != INVALID_FILE_SIZE)
                                {
                                    nameVec.clear();
                                    char* pBuffer = new char[size + 1];
                                    memset(pBuffer, 0, size + 1);
                                    DWORD dwBytesRead = 0;
                                    WCHAR tmp[100] = { 0 };
                                    if (ReadFile(hFile, pBuffer, size, &dwBytesRead, NULL))
                                    {
                                        splitString(pBuffer);
                                        if (nameVec.empty())
                                            MessageBox(L"所选文件没有内容!", L"Warning", MB_OK | MB_ICONWARNING);
                                        else
                                        {
                                            stringToLPCWSTR(*nameVec.begin(), tmp);
                                            SetDlgItemText(IDC_NAMESTATIC, tmp);
                                        }
                                    }
                                }
                                CloseHandle(hFile);
                            }
                            else
                                MessageBox(L"CreateFile Error!", L"Error", MB_OK | MB_ICONERROR);
                        }
                        else
                        {
                            MessageBox(L"GetResult Error!", L"Error", MB_OK | MB_ICONERROR);
                        }
                    }

                    // 释放资源
                    pFileDialog->Release();
                    CoTaskMemFree(m_pszFilePath);
                    m_pszFilePath = nullptr;
                }
                else
                {
                    MessageBox(L"CoCreateInstance Error!", L"Error", MB_OK | MB_ICONERROR);
                }
}

效果图:

3.实现遍历花名册功能

我们通过响应按钮被按下的通知消息BN_CLICKED,来开始遍历花名册。将选中的名字显示在主窗口上,再用定时器来延迟3秒之后清除显示,通过AnimateWindow()函数将显示和清除动作加上动画效果。

LRESULT CRandomDlg::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        int wmId = LOWORD(wParam);
        int messageId = HIWORD(wParam);
        switch (messageId)
        {
        case BN_CLICKED:
        {
            if (wmId == IDC_STOPBUTTON)
            {
                //响应开始/停止按钮
                isStop = !isStop;
                BOOL errorFlag = FALSE;
                HANDLE handlePro = GetCurrentProcess();
                SetUserObjectInformation(handlePro, UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &errorFlag, sizeof(BOOL));
                if (isStop)
                {
                    SetDlgItemText(IDC_STOPBUTTON, L"开始");
                    KillTimer(ID_SHOWNAME);
                    WCHAR tmpStr[100] = { 0 };
                    stringToLPCWSTR(nameVec.at(index), tmpStr);
                    choosedVec.push_back(tmpStr);
                    HWND hwnd = GetDlgItem(IDC_POPSTATIC);
                    ::SetWindowText(hwnd, tmpStr);
                    AnimateWindow(hwnd, 300, AW_ACTIVATE | AW_HOR_POSITIVE);  //动态显示窗口
                    SetTimer(ID_POPNAME, 3000, PopNameProc);
                }
                else
                {
                    if (nameVec.empty())
                    {
                        MessageBox(L"未选择抽奖名单或者名单为空", L"Warning", MB_OK | MB_ICONWARNING);
                        isStop = TRUE;
                        return FALSE;
                    }
                    ::ShowWindow(GetDlgItem(IDC_POPSTATIC), SW_HIDE);
                    SetDlgItemText(IDC_STOPBUTTON, L"停止");
                    SetTimer(ID_SHOWNAME, 50, ShowNameProc);
                    InvalidateRect(0, 1);
                    UpdateWindow();
                }

            }
}

效果图:

4.实现储存功能

存储方式有很多种,我们本次是使用储存到数据库的方法。使用第三方库SqliteCPP操作sqlite数据库。

4.1创建数据库

我们需要在程序初始化时创建一个数据库文件,并且创建储存数据的表。

创建表指令:

CREATE TABLE 表名 (列名1 列1对应值的类型,……, 列名n 列n对应值的类型)

try
    {
        //创建数据库文件
        if (!PathFileExists(L"C\\ToolBox"))
            CreateDirectory(L"C:\\ToolBox", NULL);

        SQLite::Database db("C:\\ToolBox\\TB.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
        //创建表
        string sql = R"(
            CREATE TABLE IF NOT EXISTS RANDOM (
                id INTEGER PRIMARY KEY NOT NULL,
                name TEXT NOT NULL,
                week INTEGER NOT NULL,
                todayDate TEXT NOT NULL
            )
        )";
        db.exec(sql);
    }
    catch (const exception& e)
    {
        MessageBoxA(NULL, e.what(), "Create table failed!", MB_OK | MB_ICONERROR);
    }

4.2存储数据到数据库表

我们在第3点中响应按钮消息,将抽中的名单暂时存储到一个vector容器中,再统一保存到本地数据库。

插入数据指令 :

 INSERT INTO 表名 (列名1,……,列名n)VALUES (列名1 对应的值,……,列名n对应的值)

try 
        {
            SQLite::Database db("C:\\ToolBox\\TB.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
            string sql;
            for (int i = 0; i < choosedVec.size(); i++)
            {
                sql = "INSERT INTO RANDOM (name, week, todayDate) VALUES (?,?,?)";
                SQLite::Statement query(db, sql);
                query.bind(1, UnicodeToUtf8(choosedVec.at(i)));
                query.bind(2, m_curWeek);
                query.bind(3, UnicodeToUtf8(m_curDate));
                query.exec();
            }

            nameVec.clear();
            choosedVec.clear();
            if (m_staticFont != NULL)
                DeleteObject(m_staticFont);
            if (m_titleFont != NULL)
                DeleteObject(m_titleFont);
            if (m_bkBrush != NULL)
                DeleteObject(m_bkBrush);
        }
        catch (const exception& e)
        {
            MessageBoxA(NULL, e.what(), "Store to DB failed!", MB_OK | MB_ICONERROR);
            return 0;
        }

注意:sqlite数据库默认的编码模式是UTF-8,所以当我们上传的数据是字符串格式时且编译器设置的字符集非UTF8格式,那么需要先转换为UTF-8格式再上传,否则数据库存储的数据可能就是乱码了。

再储存完成之后我们可以使用事先准备的数据库工具来打开创建的数据库,以此查看数据:

4.3读取数据库表数据

我们可以在程序中读取数据库中储存的数据,读取数据指令:

SELECT 列名 FROM 表名        读取表中某一列的数据

SELECT * FROM 表名                读取整个表的数据

                    WCHAR date[50] = { 0 };
                    GetNowDateString(date);
                    wstring total;
                    try
                    {
                        SQLite::Database db(L"C:\\ToolBox\\TB.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
                        SQLite::Statement query2(db, "SELECT name FROM RANDOM WHERE todayDate = ?");
                        query2.bind(1, UnicodeToUtf8(date));

                        while (query2.executeStep())
                        {
                            wstring data;
                            UTF8ToUniocde(query2.getColumn(0).getString().c_str(), data);
                            total += data + L";";
                        }

                        if (!total.empty())
                        {
                            MessageBox(hWnd, total.c_str(), L"上期中奖名单", MB_OK);
                        }
                        else
                        {
                            MessageBox(hWnd, L"暂无数据", L"Tip", MB_OK | MB_ICONINFORMATION);
                        }
                    }
                    catch (const exception& e)
                    {
                        MessageBoxA(NULL, e.what(), "GetData failed!", MB_OK | MB_ICONERROR);
                        return FALSE;
                    }

效果图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端监控之sourcemap精准定位和还原错误源码

一、概述 在前端开发中&#xff0c;监控和错误追踪是确保应用稳定性和用户体验的重要环节。 随着前端应用的复杂性增加&#xff0c;JavaScript错误监控变得尤为重要。在生产环境中&#xff0c;为了优化加载速度和性能&#xff0c;前端代码通常会被压缩和混淆。这虽然提升了性…

算法编程题-排序

算法编程题-排序 比较型排序算法冒泡排序选择排序插入排序希尔排序堆排序快速排序归并排序 非比较型排序算法计数排序基数排序 本文将对七中经典比较型排序算法进行介绍&#xff0c;并且给出golang语言的实现&#xff0c;还包括基数排序、计数排序等非比较型的算法的介绍和实现…

Jenkins修改LOGO

重启看的LOGO和登录页面左上角的LOGO 进入LOGO存在的目录 [roottest-server01 svgs]# pwd /opt/jenkins_data/war/images/svgs [roottest-server01 svgs]# ll logo.svg -rw-r--r-- 1 jenkins jenkins 29819 Oct 21 10:58 logo.svg #jenkins_data目录是我挂载到了/opt目录&…

【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段

文章目录 一、MyBatis-Plus简介二、快速入门1、环境准备2、将mybatis项目改造成mybatis-plus项目&#xff08;1&#xff09;引入MybatisPlus依赖&#xff0c;代替MyBatis依赖&#xff08;2&#xff09;配置Mapper包扫描路径&#xff08;3&#xff09;定义Mapper接口并继承BaseM…

云讷科技Kerloud无人飞车专利发布

云讷科技Kerloud无人飞车获得了“一种室内外两用的四旋翼无人飞车”的实用新型专利证书&#xff0c;作为科教社区第一款四旋翼飞车&#xff0c;这项技术结合了无人机和无人车的优势&#xff0c;提供了一种能够在多种环境下使用的多功能飞行器。 这项设计的优势如下&#xff…

Applied Intelligence投稿

一、关于手稿格式&#xff1a; 1、该期刊是一个二区的&#xff0c;模板使用Springer nature格式&#xff0c; 期刊投稿要求&#xff0c;详细期刊投稿指南&#xff0c;大部分按Soringernature模板即可&#xff0c;图片表格声明参考文献命名要求需注意。 2、参考文献&#xff…

Spark SQL大数据分析快速上手-完全分布模式安装

【图书介绍】《Spark SQL大数据分析快速上手》-CSDN博客 《Spark SQL大数据分析快速上手》【摘要 书评 试读】- 京东图书 大数据与数据分析_夏天又到了的博客-CSDN博客 Hadoop完全分布式环境搭建步骤-CSDN博客,前置环境安装参看此博文 完全分布模式也叫集群模式。将Spark目…

零基础上手WebGIS+智慧校园实例(1)【html by js】

请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01; 等下再更新一下1. WebGIS矢量图形的绘制&#xff08;超级详细&#xff01;&#xff01;&#xff09;&#xff0c;2. WebGIS计算距离&#xff0c; 以及智慧校园实例 with 3个例子&#xff01;&#xff01;…

[开源] 告别黑苹果!用docker安装MacOS体验苹果系统

没用过苹果电脑的朋友可能会对苹果系统好奇&#xff0c;有人甚至会为了尝鲜MacOS去折腾黑苹果。如果你只是想体验一下MacOS&#xff0c;这里有个更简单更优雅的解决方案&#xff0c;用docker安装MacOS来体验苹果系统。 一、项目简介 项目描述 Docker 容器内的 OSX&#xff08…

IDEA:2023版远程服务器debug

很简单&#xff0c;但是很多文档没有写清楚&#xff0c;wocao 一、首先新建一个远程jvm 二、配置 三、把上面的参数复制出来 -agentlib:jdwptransportdt_socket,servery,suspendn,address5005 四、然后把这串代码放到服务器中&#xff08;这里的0.0.0.0意思是所有IP都能访问&a…

卷积神经网络的padding是什么?如何计算?

文章目录 为什么需要padding&#xff1f;1.Valid Padding&#xff08;有效填充&#xff09;2.Same Padding&#xff08;相同填充&#xff09;2.1.如何计算padding&#xff1f;1. 计算总 padding2. 分配 padding&#xff1a; 2.2.举例子1. 步幅为 1 的 Same Padding2. 步幅不为 …

介绍一下strncmp(c基础)

strncmp是strcmp的进阶版 链接介绍一下strcmp(c基础)-CSDN博客 作用 比较两个字符串的前n位 格式 #include <string.h> strncmp (arr1,arr2,n); 工作原理&#xff1a;strcmp函数按照ACII&#xff08;字符编码顺序&#xff09;比较两个字符串。它从两个字符串的第一…

列出D3的所有交互方法,并给出示例

D3.js 提供了丰富的交互方法&#xff0c;可以用来增强图表的用户交互体验。以下是一些常用的交互方法及其示例&#xff1a; 1. 鼠标事件 on("mouseover", function) 用途: 当鼠标悬停在元素上时触发。示例:svg.selectAll(".bar").on("mouseover&qu…

丹摩征文活动 | AI创新之路,DAMODEL助你一臂之力GPU

目录 前言—— DAMODEL&#xff08;丹摩智算&#xff09; 算力服务 直观的感受算力提供商的强大​ 平台功能介绍​ 镜像选择 云磁盘创建 总结 前言—— 只需轻点鼠标,开发者便可拥有属于自己的AI计算王国 - 从丰富的GPU实例选择,到高性能的云磁盘,再到预配置的深度学习…

基于大数据爬虫数据挖掘技术+Python的网络用户购物行为分析与可视化平台(源码+论文+PPT+部署文档教程等)

#1024程序员节&#xff5c;征文# 博主介绍&#xff1a;CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老…

六、卷积神经网络(CNN)基础

卷积神经网络&#xff08;CNN&#xff09;基础 前言一、CNN概述二、卷积层2.1 卷积2.2 步幅(Stride)2.3 填充(Padding)2.4 多通道卷积2.5 多卷积计算2.6 特征图大小计算2.7 代码演示 三、池化层3.1 池化层计算3.1.1 最大池化层3.1.2 平均池化层 3.2 填充(Padding)3.3 步幅(Stri…

国标GB28181视频平台EasyCVR视频融合平台H.265/H.264转码业务流程

在当今数字化、网络化的视频监控领域&#xff0c;大中型项目对于视频监控管理平台的需求日益增长&#xff0c;特别是在跨区域、多设备、高并发的复杂环境中。EasyCVR视频监控汇聚管理平台正是为了满足这些需求而设计的&#xff0c;它不仅提供了全面的管理功能&#xff0c;还支持…

Jmeter中的断言(四)

13--XPath断言 功能特点 数据验证&#xff1a;验证 XML 响应数据是否包含或不包含特定的字段或值。支持 XPath 表达式&#xff1a;使用 XPath 表达式定位和验证 XML 数据中的字段。灵活配置&#xff1a;可以设置多个断言条件&#xff0c;满足复杂的测试需求。 配置步骤 添加…

实验室管理解决方案:Spring Boot技术

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

【STM32】USART串口数据包

数据包的作用是将一个个单独的数据打包起来&#xff0c;方便进行多字节的数据通信 数据包格式 HEX数据包 文本数据包 数据包接收 HEX数据包接收&#xff08;固定包长&#xff09; 文本数据包接收&#xff08;可变包长&#xff09; 串口收发HEX数据包 接线图 Serial模块 se…