diff --git a/README.md b/README.md index ffb720e..cc6195d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ # SUSTech_HowManyGPA -SUSTech's GPA Viewer +SUSTech GPA Viewer + +## 运行环境 + +- 64位Windows操作系统 + +## 运行 + +### 介绍 + +项目是MFC应用程序,使用Visual Studio 2019编译,项目编码为多字节。 + +程序需要访问网络。 + +程序不会保存,或向除CAS系统以外的地方发送学生个人CAS账号密码。 + +程序先向CAS系统post账号密码登录,然后前往教务系统读取成绩信息。 + +### GPA计算 + +暂未找到同一节课多次F的案例,未知多次F时,GPA计入多次F,还是只计入一个F,程序代码暂时按仅计入1个F计算。 + +### 挖坑 + +- 加上根据课程编号前缀筛选功能 +- 加上根据课程编号排序功能 +- 减少画面卡顿和闪烁 +- 迁移到Qt +- 开发小程序 + +## 注 + +本程序内图片来源网络。 \ No newline at end of file diff --git a/src/DIALOG_LOGIN.cpp b/src/DIALOG_LOGIN.cpp new file mode 100644 index 0000000..b2551f6 --- /dev/null +++ b/src/DIALOG_LOGIN.cpp @@ -0,0 +1,208 @@ +// DIALOG_LOGIN.cpp: 实现文件 +// + +#include "pch.h" +#include "SUSTech_HowManyGPA.h" +#include "DIALOG_LOGIN.h" +#include "afxinet.h" +#include + +// DIALOG_LOGIN 对话框 + +IMPLEMENT_DYNAMIC(DIALOG_LOGIN, CDialogEx) + +DIALOG_LOGIN::DIALOG_LOGIN(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_DIALOG_LOGIN, pParent) + , inputAccount(_T("")) + , inputPassword(_T("")) +{ + +} + +DIALOG_LOGIN::~DIALOG_LOGIN() +{ +} + +void DIALOG_LOGIN::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Text(pDX, IDC_EDIT_ACCOUNT, inputAccount); + DDX_Text(pDX, IDC_EDIT_PASSWORD, inputPassword); +} + + +BEGIN_MESSAGE_MAP(DIALOG_LOGIN, CDialogEx) + ON_BN_CLICKED(IDOK, &DIALOG_LOGIN::OnBnClickedOk) + ON_BN_CLICKED(IDCANCEL, &DIALOG_LOGIN::OnBnClickedCancel) + ON_WM_PAINT() + ON_WM_CTLCOLOR() + ON_WM_DRAWITEM() +END_MESSAGE_MAP() + + +// DIALOG_LOGIN 消息处理程序 +BOOL DIALOG_LOGIN::OnInitDialog() +{ + newFont.CreatePointFont(120, "等线"); + GetDlgItem(IDC_STATIC_ACCOUNT)->SetFont(&newFont); + GetDlgItem(IDC_STATIC_PASSWORD)->SetFont(&newFont); + GetDlgItem(IDOK)->SetFont(&newFont); + GetDlgItem(IDCANCEL)->SetFont(&newFont); + return TRUE; +} + + +wchar_t* DIALOG_LOGIN::UTF8toWide(CString& str) +{ + char* pStr = (char*)str.GetBuffer(str.GetLength()); + int nBufferSize = MultiByteToWideChar(CP_UTF8, 0, pStr, -1, NULL, 0); + wchar_t* pBuffer = (wchar_t*)malloc(nBufferSize * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, pStr, -1, pBuffer, nBufferSize); + return pBuffer; +} + + +char* DIALOG_LOGIN::WidetoUTF8(CString& str) +{ + // CString to Unicode + char* pStr = (char*)str.GetBuffer(str.GetLength()); + int nBufferSize = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, pStr, -1, NULL, 0); + wchar_t* wszUTF8 = new wchar_t[nBufferSize + 1]; + memset(wszUTF8, 0, nBufferSize * 2 + 2); + MultiByteToWideChar(CP_ACP, 0, pStr, -1, wszUTF8, nBufferSize); + + // Unicode to UTF-8 + nBufferSize = WideCharToMultiByte(CP_UTF8, 0, wszUTF8, -1, NULL, 0, NULL, NULL); + char* szUTF8 = new char[nBufferSize + 1]; + memset(szUTF8, 0, nBufferSize + 1); + WideCharToMultiByte(CP_UTF8, 0, wszUTF8, -1, szUTF8, nBufferSize, NULL, NULL); + delete[] wszUTF8; + return szUTF8; +} + + +void DIALOG_LOGIN::OnBnClickedOk() +{ + // TODO: 在此添加控件通知处理程序代码 + UpdateData(TRUE); + if (inputAccount.GetLength() != 8) { + AfxMessageBox("请输入正确长度的学号!"); + return; + } + + CString loginURL = "cas.sustech.edu.cn"; + CHttpConnection* pServer = session.GetHttpConnection(loginURL, INTERNET_FLAG_SECURE, (INTERNET_PORT)443, NULL, NULL); + CHttpFile* pfile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET, "/cas/login", NULL, 1, + NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_DONT_CACHE); + pfile->SendRequest(); + DWORD dwRet; + pfile->QueryInfoStatusCode(dwRet); + if (dwRet != HTTP_STATUS_OK) { + AfxMessageBox("网络异常!"); + return; + } + + CString getCAS = "", strLine; + while (pfile->ReadString(strLine)) + getCAS += strLine; + wchar_t* pBuffer = UTF8toWide(getCAS); + std::wstring wstr = pBuffer; + + CString postInformation; + std::wregex regCASInformation(L"on\" value=\"(.+?)\""); + std::wsmatch wsm; + std::regex_search(wstr, wsm, regCASInformation); + postInformation = (CString)("username=" + inputAccount + "&password=" + inputPassword + "&_eventId=submit&execution="); + postInformation += ((std::wstring)wsm[1]).c_str(); + free(pBuffer); + + pfile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, "/cas/login?service=https%3A%2F%2Fjwxt.sustech.edu.cn%2Fjsxsd%2F", NULL, 1, + NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_DONT_CACHE); + char* wait_send = WidetoUTF8(postInformation); + CString strHeaders = "Content-Type: application/x-www-form-urlencoded\r\n"; + pfile->SendRequest(strHeaders, wait_send, strlen(wait_send)); + pfile->QueryInfoStatusCode(dwRet); + delete[] wait_send; + + getCAS = "", strLine; + while (pfile->ReadString(strLine)) + getCAS += strLine; + pBuffer = UTF8toWide(getCAS); + wstr = pBuffer; + + if (dwRet == HTTP_STATUS_DENIED || (dwRet == HTTP_STATUS_OK && wstr.find(L"登录成功") == wstr.npos)) { + AfxMessageBox("密码错误!"); + return; + } + else if (dwRet != HTTP_STATUS_OK) { + AfxMessageBox("网络异常!"); + return; + } + CDialogEx::OnOK(); + ::SendMessage(GetParent()->m_hWnd, WM_USER_FRESH_SCORE, (WPARAM)0, (LPARAM)0); +} + + +void DIALOG_LOGIN::OnBnClickedCancel() +{ + // TODO: 在此添加控件通知处理程序代码 + CDialogEx::OnCancel(); + ::SendMessage(GetParent()->m_hWnd, WM_CLOSE, 0, 0); +} + + +void DIALOG_LOGIN::OnPaint() +{ + CPaintDC dc(this); // device context for painting + // TODO: 在此处添加消息处理程序代码 + // 不为绘图消息调用 CDialogEx::OnPaint() + CRect myrect; + GetClientRect(&myrect); + CDC dcMem; + dcMem.CreateCompatibleDC(&dc); + CBitmap bmpBackground; + bmpBackground.LoadBitmap(IDB_BITMAP_LOGIN); + BITMAP bitmap; + bmpBackground.GetBitmap(&bitmap); + CBitmap* pbmpOld = dcMem.SelectObject(&bmpBackground); + dc.StretchBlt(0, 0, myrect.Width(), myrect.Height(), &dcMem, 0, 0, + bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); + + CDialogEx::OnPaint(); +} + + +HBRUSH DIALOG_LOGIN::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor); + + // TODO: 在此更改 DC 的任何特性 + if (pWnd->GetDlgCtrlID() == IDC_STATIC_ACCOUNT || pWnd->GetDlgCtrlID() == IDC_STATIC_PASSWORD) + { + pDC->SetTextColor(RGB(20, 60, 80)); + pDC->SetBkMode(TRANSPARENT); + hbr = (HBRUSH)GetStockObject(NULL_BRUSH); + } + // TODO: 如果默认的不是所需画笔,则返回另一个画笔 + return hbr; +} + + +void DIALOG_LOGIN::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + // TODO: 在此添加消息处理程序代码和/或调用默认值 + if (nIDCtl == IDOK || nIDCtl == IDCANCEL) { + CDC dc; + dc.Attach(lpDrawItemStruct->hDC); + + CString strText; + ((CButton*)GetDlgItem(nIDCtl))->GetWindowText(strText); + dc.Draw3dRect(&(lpDrawItemStruct->rcItem), RGB(0, 0, 0), RGB(255, 255, 255)); + dc.FillSolidRect(&(lpDrawItemStruct->rcItem), RGB(50, 170, 165)); + DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), + &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER); + dc.Detach(); + } + else + CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct); +} diff --git a/src/DIALOG_LOGIN.h b/src/DIALOG_LOGIN.h new file mode 100644 index 0000000..e768e86 --- /dev/null +++ b/src/DIALOG_LOGIN.h @@ -0,0 +1,36 @@ +#pragma once +#include "afxinet.h" +#define WM_USER_FRESH_SCORE WM_USER+1 +// DIALOG_LOGIN 对话框 +class DIALOG_LOGIN : public CDialogEx +{ + DECLARE_DYNAMIC(DIALOG_LOGIN) + +public: + DIALOG_LOGIN(CWnd* pParent = nullptr); // 标准构造函数 + virtual ~DIALOG_LOGIN(); + +// 对话框数据 +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_DIALOG_LOGIN }; +#endif + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 + + DECLARE_MESSAGE_MAP() +public: + CString inputAccount; + CString inputPassword; + CInternetSession session; + CFont newFont; + + static wchar_t* DIALOG_LOGIN::UTF8toWide(CString& str); + static char* DIALOG_LOGIN::WidetoUTF8(CString& str); + virtual BOOL OnInitDialog(); + afx_msg void OnBnClickedOk(); + afx_msg void OnBnClickedCancel(); + afx_msg void OnPaint(); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct); +}; diff --git a/src/SUSTechHowManyGPA.rc b/src/SUSTechHowManyGPA.rc new file mode 100644 index 0000000..8e0979c Binary files /dev/null and b/src/SUSTechHowManyGPA.rc differ diff --git a/src/SUSTech_HowManyGPA.cpp b/src/SUSTech_HowManyGPA.cpp new file mode 100644 index 0000000..1058d38 --- /dev/null +++ b/src/SUSTech_HowManyGPA.cpp @@ -0,0 +1,107 @@ + +// SUSTech_HowManyGPA.cpp: 定义应用程序的类行为。 +// + +#include "pch.h" +#include "framework.h" +#include "SUSTech_HowManyGPA.h" +#include "SUSTech_HowManyGPADlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CSUSTechHowManyGPAApp + +BEGIN_MESSAGE_MAP(CSUSTechHowManyGPAApp, CWinApp) + ON_COMMAND(ID_HELP, &CWinApp::OnHelp) +END_MESSAGE_MAP() + + +// CSUSTechHowManyGPAApp 构造 + +CSUSTechHowManyGPAApp::CSUSTechHowManyGPAApp() +{ + // 支持重新启动管理器 + m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; + + // TODO: 在此处添加构造代码, + // 将所有重要的初始化放置在 InitInstance 中 +} + + +// 唯一的 CSUSTechHowManyGPAApp 对象 + +CSUSTechHowManyGPAApp theApp; + + +// CSUSTechHowManyGPAApp 初始化 + +BOOL CSUSTechHowManyGPAApp::InitInstance() +{ + // 如果一个运行在 Windows XP 上的应用程序清单指定要 + // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式, + //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。 + INITCOMMONCONTROLSEX InitCtrls; + InitCtrls.dwSize = sizeof(InitCtrls); + // 将它设置为包括所有要在应用程序中使用的 + // 公共控件类。 + InitCtrls.dwICC = ICC_WIN95_CLASSES; + InitCommonControlsEx(&InitCtrls); + + CWinApp::InitInstance(); + + + AfxEnableControlContainer(); + + // 创建 shell 管理器,以防对话框包含 + // 任何 shell 树视图控件或 shell 列表视图控件。 + CShellManager *pShellManager = new CShellManager; + + // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题 + CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); + + // 标准初始化 + // 如果未使用这些功能并希望减小 + // 最终可执行文件的大小,则应移除下列 + // 不需要的特定初始化例程 + // 更改用于存储设置的注册表项 + // TODO: 应适当修改该字符串, + // 例如修改为公司或组织名 + SetRegistryKey(_T("应用程序向导生成的本地应用程序")); + + CSUSTechHowManyGPADlg dlg; + m_pMainWnd = &dlg; + INT_PTR nResponse = dlg.DoModal(); + if (nResponse == IDOK) + { + // TODO: 在此放置处理何时用 + // “确定”来关闭对话框的代码 + } + else if (nResponse == IDCANCEL) + { + // TODO: 在此放置处理何时用 + // “取消”来关闭对话框的代码 + } + else if (nResponse == -1) + { + TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n"); + TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n"); + } + + // 删除上面创建的 shell 管理器。 + if (pShellManager != nullptr) + { + delete pShellManager; + } + +#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS) + ControlBarCleanUp(); +#endif + + // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序, + // 而不是启动应用程序的消息泵。 + return FALSE; +} + diff --git a/src/SUSTech_HowManyGPA.h b/src/SUSTech_HowManyGPA.h new file mode 100644 index 0000000..38e5c72 --- /dev/null +++ b/src/SUSTech_HowManyGPA.h @@ -0,0 +1,32 @@ + +// SUSTech_HowManyGPA.h: PROJECT_NAME 应用程序的主头文件 +// + +#pragma once + +#ifndef __AFXWIN_H__ + #error "在包含此文件之前包含 'pch.h' 以生成 PCH" +#endif + +#include "resource.h" // 主符号 + + +// CSUSTechHowManyGPAApp: +// 有关此类的实现,请参阅 SUSTech_HowManyGPA.cpp +// + +class CSUSTechHowManyGPAApp : public CWinApp +{ +public: + CSUSTechHowManyGPAApp(); + +// 重写 +public: + virtual BOOL InitInstance(); + +// 实现 + + DECLARE_MESSAGE_MAP() +}; + +extern CSUSTechHowManyGPAApp theApp; diff --git a/src/SUSTech_HowManyGPA.vcxproj b/src/SUSTech_HowManyGPA.vcxproj new file mode 100644 index 0000000..524a95e --- /dev/null +++ b/src/SUSTech_HowManyGPA.vcxproj @@ -0,0 +1,220 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {40CBE926-D3E9-4A36-8EFA-CF00ADE87092} + MFCProj + SUSTechHowManyGPA + 10.0 + + + + Application + true + v142 + MultiByte + Static + + + Application + false + v142 + true + Unicode + Static + + + Application + true + v142 + MultiByte + Static + + + Application + false + v142 + true + MultiByte + Static + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + true + WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) + pch.h + + + Windows + + + false + true + _DEBUG;%(PreprocessorDefinitions) + + + 0x0804 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + _WINDOWS;_DEBUG;%(PreprocessorDefinitions) + pch.h + + + Windows + + + false + true + _DEBUG;%(PreprocessorDefinitions) + + + 0x0804 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + true + true + WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) + pch.h + + + Windows + true + true + + + false + true + NDEBUG;%(PreprocessorDefinitions) + + + 0x0804 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + true + true + true + _WINDOWS;NDEBUG;%(PreprocessorDefinitions) + pch.h + + + Windows + true + true + UseLinkTimeCodeGeneration + + + false + true + NDEBUG;%(PreprocessorDefinitions) + + + 0x0804 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/SUSTech_HowManyGPADlg.cpp b/src/SUSTech_HowManyGPADlg.cpp new file mode 100644 index 0000000..9c3bb8a --- /dev/null +++ b/src/SUSTech_HowManyGPADlg.cpp @@ -0,0 +1,395 @@ +// SUSTech_HowManyGPADlg.cpp: 实现文件 +// + +#include "pch.h" +#include "framework.h" +#include "SUSTech_HowManyGPA.h" +#include "SUSTech_HowManyGPADlg.h" +#include "afxdialogex.h" +#include "afxinet.h" +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CSUSTechHowManyGPADlg 对话框 +CSUSTechHowManyGPADlg::CSUSTechHowManyGPADlg(CWnd* pParent /*=nullptr*/) + : CDialogEx(IDD_SUSTECH_HOWMANYGPA_DIALOG, pParent) +{ + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); +} + +void CSUSTechHowManyGPADlg::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_LIST_Score, studentScore); +} + +BEGIN_MESSAGE_MAP(CSUSTechHowManyGPADlg, CDialogEx) + ON_WM_SYSCOMMAND() + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_MESSAGE(WM_USER_FRESH_SCORE, FreshScore) + ON_BN_CLICKED(IDC_BUTTON_CHOOSEALL, &CSUSTechHowManyGPADlg::OnBnClickedButtonChooseall) + ON_BN_CLICKED(IDC_BUTTON_CLEARALL, &CSUSTechHowManyGPADlg::OnBnClickedButtonClearall) + ON_BN_CLICKED(IDC_BUTTON_DEFAULT, &CSUSTechHowManyGPADlg::OnBnClickedButtonDefault) + ON_NOTIFY(NM_CLICK, IDC_LIST_Score, &CSUSTechHowManyGPADlg::OnNMClickListScore) + ON_NOTIFY(NM_DBLCLK, IDC_LIST_Score, &CSUSTechHowManyGPADlg::OnNMClickListScore) + ON_WM_CTLCOLOR() + ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST_Score, &CSUSTechHowManyGPADlg::OnCustomdrawListScore) + ON_WM_DRAWITEM() + ON_NOTIFY(LVN_KEYDOWN, IDC_LIST_Score, &CSUSTechHowManyGPADlg::OnLvnKeydownListScore) +END_MESSAGE_MAP() + + +// CSUSTechHowManyGPADlg 消息处理程序 +BOOL CSUSTechHowManyGPADlg::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 + // 执行此操作 + SetIcon(m_hIcon, TRUE); // 设置大图标 + SetIcon(m_hIcon, TRUE); // 设置小图标 + + // TODO: 在此添加额外的初始化代码 + dLogin.Create(IDD_DIALOG_LOGIN, this); + + CRect rect; + studentScore.GetClientRect(&rect); + studentScore.SetExtendedStyle(studentScore.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER); + studentScore.InsertColumn(0, "开课学期", LVCFMT_LEFT, rect.Width() * 1 / 5, 0); + studentScore.InsertColumn(1, "课程编号", LVCFMT_LEFT, rect.Width() * 1 / 5, 1); + studentScore.InsertColumn(2, "课程名称", LVCFMT_LEFT, rect.Width() * 1 / 5, 2); + studentScore.InsertColumn(3, "等级制", LVCFMT_LEFT, rect.Width() * 2 / 15, 3); + studentScore.InsertColumn(4, "百分制", LVCFMT_LEFT, rect.Width() * 2 / 15, 4); + studentScore.InsertColumn(5, "学分", LVCFMT_LEFT, rect.Width() * 2 / 15, 5); + + char cListCtrlBackgroundPath[MAX_PATH] = { 0 }; + ::GetModuleFileName(AfxGetInstanceHandle(), cListCtrlBackgroundPath, MAX_PATH); + CString sPath; + sPath.Format("res://%s/#2/#139", cListCtrlBackgroundPath); + studentScore.SetBkImage(sPath.GetBuffer(sPath.GetLength()), TRUE); + + GetDlgItem(IDC_EDIT_GPA)->SetWindowText("0.00"); + + dLogin.ShowWindow(SW_SHOW); + return TRUE; // 除非将焦点设置到控件,否则返回 TRUE +} + +void CSUSTechHowManyGPADlg::OnSysCommand(UINT nID, LPARAM lParam) +{ + CDialogEx::OnSysCommand(nID, lParam); +} + +// 如果向对话框添加最小化按钮,则需要下面的代码 +// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, +// 这将由框架自动完成。 + +void CSUSTechHowManyGPADlg::OnPaint() +{ + if (IsIconic()) + { + CPaintDC dc(this); // 用于绘制的设备上下文 + + SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); + + // 使图标在工作区矩形中居中 + int cxIcon = GetSystemMetrics(SM_CXICON); + int cyIcon = GetSystemMetrics(SM_CYICON); + CRect rect; + GetClientRect(&rect); + int x = (rect.Width() - cxIcon + 1) / 2; + int y = (rect.Height() - cyIcon + 1) / 2; + + // 绘制图标 + dc.DrawIcon(x, y, m_hIcon); + } + else + { + CPaintDC dc(this); + CRect myrect; + GetClientRect(&myrect); + CDC dcMem; + dcMem.CreateCompatibleDC(&dc); + CBitmap bmpBackground; + bmpBackground.LoadBitmap(IDB_BITMAP_BACKGROUND); + BITMAP bitmap; + bmpBackground.GetBitmap(&bitmap); + CBitmap* pbmpOld = dcMem.SelectObject(&bmpBackground); + dc.StretchBlt(0, 0, myrect.Width(), myrect.Height(), &dcMem, 0, 0, + bitmap.bmWidth, bitmap.bmHeight, SRCCOPY); + + CDialogEx::OnPaint(); + } +} + +//当用户拖动最小化窗口时系统调用此函数取得光标 +//显示。 +HCURSOR CSUSTechHowManyGPADlg::OnQueryDragIcon() +{ + return static_cast(m_hIcon); +} + +HBRUSH CSUSTechHowManyGPADlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor); + + // TODO: 在此更改 DC 的任何特性 + if (pWnd->GetDlgCtrlID() == IDC_STATIC_GPA) + { + pDC->SetTextColor(RGB(0, 0, 0)); + pDC->SetBkMode(TRANSPARENT); + hbr = (HBRUSH)GetStockObject(NULL_BRUSH); + } + // TODO: 如果默认的不是所需画笔,则返回另一个画笔 + return hbr; +} + +void CSUSTechHowManyGPADlg::OnCustomdrawListScore(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMLVCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); + // TODO: 在此添加控件通知处理程序代码 + *pResult = 0; + + + if (CDDS_PREPAINT == pNMCD->nmcd.dwDrawStage) + { + *pResult = CDRF_NOTIFYITEMDRAW; + } + else if (CDDS_ITEMPREPAINT == pNMCD->nmcd.dwDrawStage) + { + *pResult = CDRF_NOTIFYSUBITEMDRAW; + } + else if ((CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pNMCD->nmcd.dwDrawStage) + { + COLORREF clrNewBkColor; + int nItem = static_cast(pNMCD->nmcd.dwItemSpec); + if (nItem % 2) + { + clrNewBkColor = RGB(225, 225, 225); + } + else + { + clrNewBkColor = RGB(255, 255, 255); + } + + pNMCD->clrTextBk = clrNewBkColor; + *pResult = CDRF_DODEFAULT; + } +} + +LRESULT CSUSTechHowManyGPADlg::FreshScore(WPARAM iParam1, LPARAM iParam2) +{ + CString jwxtURL = "jwxt.sustech.edu.cn"; + CHttpConnection* pServer = dLogin.session.GetHttpConnection(jwxtURL, INTERNET_FLAG_SECURE, (INTERNET_PORT)443, NULL, NULL); + CHttpFile* pfile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, "/jsxsd/kscj/cjcx_query", NULL, 1, + NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_DONT_CACHE); + pfile->SendRequest(); + + std::wregex regScore(L"(.+?).*?(.+?).*?(.+?).*?(.+?).*?(.+?)"); + std::wsmatch wsm; + std::wstring::const_iterator iter; + std::wstring::const_iterator iterEnd; + CTime now = CTime::GetCurrentTime(); + + for (int i = 2010 + (dLogin.inputAccount[3] - '0'), j = i + 1; j <= now.GetYear() + 1; i++, j++) + { + for (int k = 1; k <= 3; k++) + { + CString termNo; + termNo.Format("%d-%d-%d", i, j, k); + pfile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST, "/jsxsd/kscj/cjcx_list?kksj=" + termNo, NULL, 1, + NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_TRANSFER_ASCII | INTERNET_FLAG_DONT_CACHE); + pfile->SendRequest(); + + CString getCAS = "", strLine; + while (pfile->ReadString(strLine)) + getCAS += strLine; + wchar_t* pBuffer = dLogin.UTF8toWide(getCAS); + std::wstring wstr = pBuffer; + iter = wstr.begin(); + iterEnd = wstr.end(); + + if (wstr.find(L"未查询到数据") != wstr.npos) + continue; + else + { + while (std::regex_search(iter, iterEnd, wsm, regScore)) + { + studentScore.SetRedraw(FALSE); + studentScore.InsertItem(0, termNo); + studentScore.SetItemText(0, 1, (CString)((std::wstring)wsm[1]).c_str()); + studentScore.SetItemText(0, 2, (CString)((std::wstring)wsm[2]).c_str()); + studentScore.SetItemText(0, 3, (CString)((std::wstring)wsm[3]).c_str()); + studentScore.SetItemText(0, 4, (CString)((std::wstring)wsm[4]).c_str()); + studentScore.SetItemText(0, 5, (CString)((std::wstring)wsm[5]).c_str()); + studentScore.SetRedraw(TRUE); + iter = wsm[0].second; + } + } + } + } + return 0; +} + +void CSUSTechHowManyGPADlg::SetStatus(int nItem, bool addOrSub) +{ + CString str = studentScore.GetItemText(nItem, 2); + int aCredit = _ttoi(studentScore.GetItemText(nItem, 5)); + CString aRank = studentScore.GetItemText(nItem, 3); + if (aRank != "P" && aRank != "W") + { + for (int i = 0; i < studentScore.GetItemCount(); i++) + { + if (str == studentScore.GetItemText(i, 2) && i != nItem && studentScore.GetCheck(i) && + studentScore.GetItemText(i, 3) != "P" && studentScore.GetItemText(i, 3) != "W") + { + credit += aCredit * (addOrSub ? -1 : 1); + break; + } + } + credit += aCredit * (addOrSub ? 1 : -1); + GPASUM += aCredit * mapGPA[aRank] * (addOrSub ? 1 : -1); + if (credit == 0) + GetDlgItem(IDC_EDIT_GPA)->SetWindowText("0.00"); + else { + CString str; + str.Format("%.2lf", GPASUM / credit); + GetDlgItem(IDC_EDIT_GPA)->SetWindowText(str); + } + UpdateData(FALSE); + } +} + +void CSUSTechHowManyGPADlg::OnNMClickListScore(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR); + // TODO: 在此添加控件通知处理程序代码 + + DWORD dwPos = GetMessagePos(); + CPoint point(LOWORD(dwPos), HIWORD(dwPos)); + studentScore.ScreenToClient(&point); + + UINT* nFlag = new UINT; + int nItem = studentScore.HitTest(point, nFlag); + if (*nFlag == LVHT_ONITEMSTATEICON) { + BOOL bCheckState = studentScore.GetCheck(nItem); + /*old state : checked && new state : unchecked */ + if (bCheckState) + { + SetStatus(nItem, FALSE); + } + /* old state : unchecked && new state : checked*/ + else + { + SetStatus(nItem, TRUE); + } + } + delete nFlag; + + *pResult = 0; +} + +void CSUSTechHowManyGPADlg::OnLvnKeydownListScore(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMLVKEYDOWN pLVKeyDow = reinterpret_cast(pNMHDR); + // TODO: 在此添加控件通知处理程序代码 + if (pLVKeyDow->wVKey == VK_SPACE) + { + POSITION p = studentScore.GetFirstSelectedItemPosition(); + for (unsigned int i = 0; i < studentScore.GetSelectedCount(); i++) + { + int nItem = studentScore.GetNextSelectedItem(p); + + BOOL bCheckState = studentScore.GetCheck(nItem); + /*old state : checked && new state : unchecked */ + if (bCheckState) + { + studentScore.SetCheck(nItem, FALSE); + SetStatus(nItem, FALSE); + } + /* old state : unchecked && new state : checked*/ + else + { + studentScore.SetCheck(nItem, TRUE); + SetStatus(nItem, TRUE); + } + } + *pResult = 1; + } + else + *pResult = 0; +} + +void CSUSTechHowManyGPADlg::OnBnClickedButtonChooseall() +{ + // TODO: 在此添加控件通知处理程序代码 + for (int i = 0; i < studentScore.GetItemCount(); i++) + { + if (!studentScore.GetCheck(i)) { + studentScore.SetCheck(i, TRUE); + SetStatus(i, TRUE); + } + } +} + +void CSUSTechHowManyGPADlg::OnBnClickedButtonClearall() +{ + // TODO: 在此添加控件通知处理程序代码 + for (int i = 0; i < studentScore.GetItemCount(); i++) + { + if (studentScore.GetCheck(i)) { + studentScore.SetCheck(i, FALSE); + SetStatus(i, FALSE); + } + } +} + +void CSUSTechHowManyGPADlg::OnBnClickedButtonDefault() +{ + // TODO: 在此添加控件通知处理程序代码 + POSITION p = studentScore.GetFirstSelectedItemPosition(); + BOOL allChecked = TRUE; + for (unsigned int i = 0; i < studentScore.GetSelectedCount(); i++) + { + int nItem = studentScore.GetNextSelectedItem(p); + allChecked = allChecked && studentScore.GetCheck(nItem); + } + p = studentScore.GetFirstSelectedItemPosition(); + for (unsigned int i = 0; i < studentScore.GetSelectedCount(); i++) + { + int nItem = studentScore.GetNextSelectedItem(p); + if (allChecked) { + studentScore.SetCheck(nItem, FALSE); + SetStatus(nItem, FALSE); + } + else if (!studentScore.GetCheck(nItem)) + { + studentScore.SetCheck(nItem, TRUE); + SetStatus(nItem, TRUE); + } + } + +} + +void CSUSTechHowManyGPADlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + // TODO: 在此添加消息处理程序代码和/或调用默认值 + if (nIDCtl == IDC_BUTTON_CHOOSEALL || nIDCtl == IDC_BUTTON_CLEARALL) { + CDC dc; + dc.Attach(lpDrawItemStruct->hDC); + + CString strText; + ((CButton*)GetDlgItem(nIDCtl))->GetWindowText(strText); + dc.Draw3dRect(&(lpDrawItemStruct->rcItem), RGB(0, 0, 0), RGB(255, 255, 255)); + dc.FillSolidRect(&(lpDrawItemStruct->rcItem), RGB(50, 170, 165)); + DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), + &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER); + dc.Detach(); + } + else + CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct); +} diff --git a/src/SUSTech_HowManyGPADlg.h b/src/SUSTech_HowManyGPADlg.h new file mode 100644 index 0000000..1ebd022 --- /dev/null +++ b/src/SUSTech_HowManyGPADlg.h @@ -0,0 +1,55 @@ + +// SUSTech_HowManyGPADlg.h: 头文件 +// +#pragma once +#include "DIALOG_LOGIN.h" +#include +#define WM_USER_FRESH_SCORE WM_USER+1 + +// CSUSTechHowManyGPADlg 对话框 +class CSUSTechHowManyGPADlg : public CDialogEx +{ + // 构造 +public: + CSUSTechHowManyGPADlg(CWnd* pParent = nullptr); // 标准构造函数 + +// 对话框数据 +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_SUSTECH_HOWMANYGPA_DIALOG }; +#endif + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 + + +// 实现 +protected: + HICON m_hIcon; + + // 生成的消息映射函数 + virtual BOOL OnInitDialog(); + afx_msg void OnSysCommand(UINT nID, LPARAM lParam); + afx_msg void OnPaint(); + afx_msg HCURSOR OnQueryDragIcon(); + DECLARE_MESSAGE_MAP() + +public: + DIALOG_LOGIN dLogin; + CListCtrl studentScore; + int credit = 0; + double GPASUM = 0.0; + std::map mapGPA = { {"A+",4.00}, {"A",3.94}, {"A-",3.85}, + {"B+",3.73}, {"B",3.55}, {"B-",3.32}, + {"C+",3.09}, {"C",2.78}, {"C-",2.42}, + {"D+",2.08}, {"D",1.63}, {"D-",1.15}, {"F",0.0} }; + afx_msg LRESULT FreshScore(WPARAM iParam1, LPARAM iParam2); + afx_msg void OnBnClickedButtonChooseall(); + afx_msg void OnBnClickedButtonClearall(); + void CSUSTechHowManyGPADlg::SetStatus(int nItem, bool addOrSub); + afx_msg void OnBnClickedButtonDefault(); + afx_msg void OnNMClickListScore(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + afx_msg void OnCustomdrawListScore(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct); + afx_msg void OnLvnKeydownListScore(NMHDR* pNMHDR, LRESULT* pResult); +}; diff --git a/src/res/1.bmp b/src/res/1.bmp new file mode 100644 index 0000000..0679dda Binary files /dev/null and b/src/res/1.bmp differ diff --git a/src/res/2.bmp b/src/res/2.bmp new file mode 100644 index 0000000..bf7fb74 Binary files /dev/null and b/src/res/2.bmp differ diff --git a/src/res/SUSTechHowManyGPA.rc2 b/src/res/SUSTechHowManyGPA.rc2 new file mode 100644 index 0000000..c47ff4c Binary files /dev/null and b/src/res/SUSTechHowManyGPA.rc2 differ diff --git a/src/res/SUSTech_HowManyGPA.ico b/src/res/SUSTech_HowManyGPA.ico new file mode 100644 index 0000000..95a91b9 Binary files /dev/null and b/src/res/SUSTech_HowManyGPA.ico differ diff --git a/src/resource.h b/src/resource.h new file mode 100644 index 0000000..b011809 --- /dev/null +++ b/src/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ 生成的包含文件。 +// 供 SUSTechHowManyGPA.rc 使用 +// +#define IDD_SUSTECH_HOWMANYGPA_DIALOG 102 +#define IDR_MAINFRAME 128 +#define IDD_DIALOG_LOGIN 130 +#define IDB_BITMAP_BACKGROUND 139 +#define IDB_BITMAP_LOGIN 143 +#define IDC_EDIT_ACCOUNT 1001 +#define IDC_EDIT_PASSWORD 1002 +#define IDC_STATIC_ACCOUNT 1004 +#define IDC_STATIC_PASSWORD 1005 +#define IDC_LIST_Score 1006 +#define IDC_EDIT_GPA 1007 +#define IDC_STATIC_GPA 1010 +#define IDC_BUTTON_CHOOSEALL 1011 +#define IDC_BUTTON_CLEARALL 1012 +#define IDC_BUTTON_DEFAULT 1014 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 145 +#define _APS_NEXT_COMMAND_VALUE 32775 +#define _APS_NEXT_CONTROL_VALUE 1018 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif