1.简介
实现一份不错的课程设计,往往需要利于进行实现良好交互的图形化界面。
在这学期的课程设计中,我发现了一套可以通用的图形化界面,适合需要界面,但不需要多高级的界面的,并且像我一样图形化知识基础为0的C/C++小白。
现记录如下:
(1)运行环境:VS2019
(2)实现效果:
①在编辑框内可以输入内容(能实现改变字号、字体、字色、背景颜色,设置只读等基础设置),将输入的内容写入到文件中
②调用其他程序的exe(想优化输入输出的那个程序)
③可点击按钮,查看运行结果
(3)待继续优化的方向:
①目前输入、输出无法支持中文,只能全英。
②打开初始界面后,若点开了其他如浏览器等覆盖该界面后,编辑框有一定几率会直接不见。
2.完成过程
(1)发现需要实现简单的图形化界面问题,主要满足大学课设的一般需求。
(2)考虑过QT和C#,没使用的原因很简单,就是对这两个完全没什么了解,并且希望只凭借自己浅薄的C/C++的知识就能实现,不需要多么高级的效果。
(3)了解发现easyx该文件可以实现简单的图形化界面,于是着手开始实践。但是等到实现了后,发现当前的效果只能是:
输入我要输入的文本后,点击按钮后可以换个新界面并能弹出一些文本信息。因为其可以插入背景图片,这个的背景图就是我用PPT完成的。其余界面类似如此格式的PPT风。
由于我对easyx也没了解,不希望系统学习其相关的函数等,只希望学当前需要用到的部分,所以直接找的完整项目,从中直接找的需要部分。
我学习的项目是GitHub上的c-easyx-TypingGame-main,这是个小萝卜打字游戏,实现得非常完善有趣。从中能直接学到如何使用easyx实现输出界面、设置背景图片、实现按钮点击变色的效果、如何实现鼠标交互、返回菜单、背景音乐等等可以应用的知识。
完成后,发现其最大的弊端是,我需要的输出的内容行数很多,使用简单的弹窗或者直接输出在界面上根本无法放下,也没有右边的下拉条等。
但如果你的项目输出输入简单,不需要输出一个界面放不下的内容,这个会是这个不错的选择。
虽然界面蛮不错,但这里无奈弃用。
(4)在搜索解决以上问题的过程中,发现了GitHub上扩展的HiEasyX库,能够实现有下拉框的编辑框(可进行长文本输入输出)、多窗口等。因此果断换成使用这个实现。
传送门:HiEasyX库
(5)实现具体效果如下图所示,满足我的长文本输入输出基本需求。
3.具体实现
以上图片的源码如下,就是在那篇GitHub上的示例项目上多改一改就能实现。
简言之,画界面,while判断鼠标点击按钮,并弹出新的编辑框输出相应内容,可以多个窗口同时存在进行对比分析等。
以下代码并不是全有用,因当时时间有限,能达到目标效果后就没再优化了,若参考的话可自行删减一些无用代码。
#include"HiEasyX.h"#include<fstream>#include<bits/stdc++.h>#include<conio.h>#include<stdlib.h>#include<Windows.h>usingnamespace std;
hiex::Canvas canvas_main;
hiex::SysEdit edit;
hiex::SysButton btn;
hiex::SysCheckBox checkbox;
hiex::SysStatic text;#defineWND_W640#defineWND_H480#defineEDIT_W480#defineEDIT_H400#defineBTN_X20#defineBTN_Y430#defineCHECKBOX_X140#defineCHECKBOX_Y435#defineSTATIC_X260#defineSTATIC_Y440#defineWINDOW_LONG1000// 显示窗口的长#defineWINDOW_WIDE600// 显示窗口的宽#defineEDIT_LONG800// 编辑窗口的长#defineEDIT_WIDE480// 编辑窗口的宽voidOutInfo(hiex::Canvas& canvas){static LPCTSTR lpszText = L"Created by HiEasyX " _HIEASYX_VER_STR_;
canvas.SetTextEscapement(0);
canvas.SetTextOrientation(0);
canvas.SetTextStyle(16,0, L"Arial");
canvas.OutTextXY(
canvas.GetWidth()- canvas.TextWidth(lpszText),
canvas.GetHeight()- canvas.TextHeight(lpszText),
lpszText,true, GRAY
);}// 连续输出垂直文字// 调用前需要自行设置文字垂直属性voidVerticalText(LPCTSTR lpsz,bool text_c, COLORREF cText,bool bk_c, COLORREF cBk,bool set =false,int x =0,int y =0){staticint sx =0, sy =0;if(set){
sx = x;
sy = y;}if(text_c)settextcolor(cText);if(bk_c)setbkcolor(cBk);outtextxy(sx, sy, lpsz);
sy +=textwidth(lpsz);}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){static COLORREF bk = CLASSICGRAY;switch(msg){case WM_PAINT:
canvas_main.SetTextEscapement(-900);
canvas_main.SetTextOrientation(-900);
canvas_main.SetTextStyle(140,0, L"微软雅黑");
canvas_main.BeginBatchDrawing();VerticalText(L"Hi",true, LIGHTRED,true, SKYBLUE,true,getwidth()+10,20);VerticalText(L" ",false,0,true, bk);VerticalText(L"E",true, WHITE,true, RED);VerticalText(L"a",false,0,true, ORANGE);VerticalText(L"s",false,0,true, GREEN);VerticalText(L"y",false,0,true, CYAN);VerticalText(L"X",false,0,true, BLUE);
canvas_main.EndBatchDrawing();
canvas_main.SetBkColor(bk);OutInfo(canvas_main);break;case WM_SIZE:{
canvas_main.Clear(true, bk);int dx = canvas_main.GetWidth()- WND_W;int dy = canvas_main.GetHeight()- WND_H;
edit.Resize(
EDIT_W + dx,
EDIT_H + dy
);
btn.Move(BTN_X, BTN_Y + dy);
checkbox.Move(CHECKBOX_X, CHECKBOX_Y + dy);
text.Move(STATIC_X, STATIC_Y + dy);break;}default:return HIWINDOW_DEFAULT_PROC;break;}return0;}voidOptionsWnd(bool* running){static std::map<std::wstring, COLORREF> color_map ={{L"black", BLACK},{L"white", WHITE},{L"gray", GRAY},{L"red", RED},{L"purple", PURPLE},{L"orange", ORANGE},{L"yellow", YELLOW},{L"green", GREEN},{L"blue", BLUE},{L"cyan", CYAN}};static std::wstring typeface[]={
L"system", L"Arial", L"Consolas", L"微软雅黑", L"宋体", L"仿宋", L"黑体"};
hiex::Window wnd_option(300,300);SetWindowText(wnd_option.GetHandle(), L"Option");
hiex::Canvas canvas;
wnd_option.BindCanvas(&canvas);OutInfo(canvas);
canvas.SetTypeface(L"System");DisableResizing(wnd_option.GetHandle(),true);
hiex::SysComboBox combobox_ctext;
hiex::SysComboBox combobox_cbk;
hiex::SysComboBox combobox_typeface;
hiex::SysRadioButton radio[2];
canvas.SetTextColor(GRAY);
canvas.OutTextXY(20,20, L"Select text color");
combobox_ctext.PreSetStyle({false,false,true});
combobox_ctext.Create(wnd_option.GetHandle(),20,40,260,200);
canvas.OutTextXY(20,80, L"Select background color");
combobox_cbk.PreSetStyle({false,false,true});
combobox_cbk.Create(wnd_option.GetHandle(),20,100,260,200);
canvas.OutTextXY(20,140, L"Select typeface");
combobox_typeface.PreSetStyle({false,true,true});
combobox_typeface.Create(wnd_option.GetHandle(),20,160,260,200);for(auto& color : color_map){
combobox_ctext.AddString(color.first);
combobox_cbk.AddString(color.first);}for(auto& name : typeface){
combobox_typeface.AddString(name);}
combobox_ctext.SetSel(0);
combobox_ctext.RegisterSelMessage([](int nSel, std::wstring wstrSelText){
edit.SetTextColor(color_map[wstrSelText]);});
combobox_cbk.SelectString(L"white");// 通过字符串选择项
combobox_cbk.RegisterSelMessage([](int nSel, std::wstring wstrSelText){
edit.SetTextBkColor(color_map[wstrSelText]);
edit.SetBkColor(color_map[wstrSelText]);});
combobox_typeface.SelectString(L"微软雅黑");
combobox_typeface.RegisterSelMessage([](int nSel, std::wstring wstrSelText){
edit.SetFont(26,0, wstrSelText);});
combobox_typeface.RegisterEditMessage([](std::wstring wstrText){
edit.SetFont(26,0, wstrText);});
radio[0].Create(wnd_option.GetHandle(),20,200,100,30, L"Left align");
radio[1].Create(wnd_option.GetHandle(),20,230,100,30, L"Right align");
radio[0].Check(true);
radio[0].RegisterMessage([](bool checked){if(checked)
edit.RightAlign(false);});
radio[1].RegisterMessage([](bool checked){if(checked)
edit.RightAlign(true);});
wnd_option.Redraw();
hiex::init_end(wnd_option.GetHandle());*running =false;}voidOnClick(){staticbool running =false;if(!running){
running =true;
std::thread(OptionsWnd,&running).detach();}}voidOnCheck(bool checked){
edit.ReadOnly(checked);}
string to_string(const wstring& str,const locale& loc =locale()){
vector<char>buf(str.size());use_facet<ctype<wchar_t>>(loc).narrow(str.data(), str.data()+ str.size(),'*', buf.data());returnstring(buf.data(), buf.size());}intfile_xcopy_stod(constchar* source_dir,constchar* destination_dir){
ifstream infile(source_dir, ios::in | ios::binary);//二进制形式打开if(infile.is_open()==0){//出错处理
cout <<"文件"<< source_dir <<"打开失败"<< endl;return-1;}
ofstream outfile(destination_dir, ios::out | ios::binary);//二进制形式打开if(outfile.is_open()==0){//出错处理
cout <<"文件"<< destination_dir <<"打开失败"<< endl;
infile.close();//记得关闭
infile.clear();return-1;}//开始读写constint FLUSH_NUM =1024*1024;//缓冲区大小设置为1Mchar* ch =new(nothrow)char[FLUSH_NUM];if(ch ==NULL){//出错处理
cout <<"动态申请内存失败"<< endl;
infile.close();//记得关闭
infile.clear();
outfile.close();//记得关闭
outfile.clear();return-1;}while(!infile.eof()){
infile.read(ch, FLUSH_NUM);
outfile.write(ch, infile.gcount());//写入读入的成功个数}delete[]ch;//记得释放
infile.close();//记得关闭
infile.clear();
outfile.close();//记得关闭
outfile.clear();return0;}// 输出 1 词法分析 的窗口voiddraw_1(){
hiex::Window wnd1(WINDOW_LONG, WINDOW_WIDE);
wnd1.SetProcFunc(WndProc);
hiex::AutoExit();
hiex::SysEdit edit1;
HWND hwnd1 = wnd1.GetHandle();
ifstream in("lexical_out.txt", ios::in);if(in.fail())MessageBox(wnd1.GetHandle(), L"文件打开失败", L"Submit", MB_OK);
istreambuf_iterator<char>beg(in), end;
wstring strdata(beg, end);
edit1.PreSetStyle({true,false,true});
edit1.Create(hwnd1,100,60, EDIT_LONG, EDIT_WIDE, strdata);
edit1.SetFont(20,0, L"黑体");
in.close();
in.clear();//hiex::init_end(hwnd1);}// 输出 2 语法分析 递归下降 过程 的窗口voiddraw_2(){
hiex::Window wnd2(WINDOW_LONG, WINDOW_WIDE);
wnd2.SetProcFunc(WndProc);
hiex::AutoExit();
hiex::SysEdit edit2;
HWND hwnd2 = wnd2.GetHandle();
ifstream in("rec_out.txt", ios::in);if(in.fail())MessageBox(wnd2.GetHandle(), L"文件打开失败", L"Submit", MB_OK);
istreambuf_iterator<char>beg(in), end;
wstring strdata(beg, end);
edit2.PreSetStyle({true,false,true});
edit2.Create(hwnd2,100,60, EDIT_LONG, EDIT_WIDE, strdata);
edit2.SetFont(20,0, L"黑体");
in.close();
in.clear();//hiex::init_end(hwnd2);}// 输出 3 语法分析 递归下降 tree 的窗口voiddraw_3(){
hiex::Window wnd3(WINDOW_LONG, WINDOW_WIDE);
wnd3.SetProcFunc(WndProc);
hiex::AutoExit();
hiex::SysEdit edit3;
HWND hwnd3 = wnd3.GetHandle();
ifstream in("rec_des_out.txt", ios::in);if(in.fail())MessageBox(wnd3.GetHandle(), L"文件打开失败", L"Submit", MB_OK);
istreambuf_iterator<char>beg(in), end;
wstring strdata(beg, end);
edit3.PreSetStyle({true,false,true});
edit3.Create(hwnd3,100,60, EDIT_LONG, EDIT_WIDE, strdata);
edit3.SetFont(20,0, L"黑体");
in.close();
in.clear();//hiex::init_end(hwnd3);}// 输出 4 语法分析 LL1 过程 的窗口voiddraw_4(){
hiex::Window wnd4(WINDOW_LONG, WINDOW_WIDE);
wnd4.SetProcFunc(WndProc);
hiex::AutoExit();
hiex::SysEdit edit4;
HWND hwnd4 = wnd4.GetHandle();
ifstream in("LL_1_process.txt", ios::in);if(in.fail())MessageBox(wnd4.GetHandle(), L"文件打开失败", L"Submit", MB_OK);
istreambuf_iterator<char>beg(in), end;
wstring strdata(beg, end);
edit4.PreSetStyle({true,false,true});
edit4.Create(hwnd4,100,60, EDIT_LONG, EDIT_WIDE, strdata);
edit4.SetFont(20,0, L"微软雅黑");
in.close();
in.clear();//hiex::init_end(hwnd4);}// 输出 5 语法分析 LL1 tree 的窗口voiddraw_5(){
hiex::Window wnd5(WINDOW_LONG, WINDOW_WIDE);
wnd5.SetProcFunc(WndProc);
hiex::AutoExit();
hiex::SysEdit edit5;
HWND hwnd5 = wnd5.GetHandle();
ifstream in("LL_1_out.txt", ios::in);if(in.fail())MessageBox(wnd5.GetHandle(), L"文件打开失败", L"Submit", MB_OK);
istreambuf_iterator<char>beg(in), end;
wstring strdata(beg, end);
edit5.PreSetStyle({true,false,true});
edit5.Create(hwnd5,100,60, EDIT_LONG, EDIT_WIDE, strdata);
edit5.SetFont(20,0, L"黑体");
in.close();
in.clear();//hiex::init_end(hwnd5);}// 输出 6 语义分析 的窗口voiddraw_6(){
hiex::Window wnd6(WINDOW_LONG, WINDOW_WIDE);
wnd6.SetProcFunc(WndProc);
hiex::AutoExit();
hiex::SysEdit edit6;
HWND hwnd6 = wnd6.GetHandle();
ifstream in("yuyi.txt", ios::in);if(in.fail())MessageBox(wnd6.GetHandle(), L"文件打开失败", L"Submit", MB_OK);
istreambuf_iterator<char>beg(in), end;
wstring strdata(beg, end);
edit6.PreSetStyle({true,false,true});
edit6.Create(hwnd6,100,60, EDIT_LONG, EDIT_WIDE, strdata);
edit6.SetFont(20,0, L"黑体");
in.close();
in.clear();//hiex::init_end(hwnd6);}voidinit(){
string file_name ="lexical_out.txt";
ofstream file_writer1(file_name, ios_base::out);
file_name ="rec_out.txt";
ofstream file_writer2(file_name, ios_base::out);
file_name ="rec_des_out.txt";
ofstream file_writer3(file_name, ios_base::out);
file_name ="LL_1_process.txt";
ofstream file_writer4(file_name, ios_base::out);
file_name ="LL_1_out.txt";
ofstream file_writer5(file_name, ios_base::out);
file_name ="yuyi.txt";
ofstream file_writer6(file_name, ios_base::out);return;}char*wideCharToMultiByte(wchar_t* pWCStrKey){//第一次调用确认转换后单字节字符串的长度,用于开辟空间int pSize =WideCharToMultiByte(CP_OEMCP,0, pWCStrKey,wcslen(pWCStrKey),NULL,0,NULL,NULL);char* pCStrKey =newchar[pSize +1];//第二次调用将双字节字符串转换成单字节字符串WideCharToMultiByte(CP_OEMCP,0, pWCStrKey,wcslen(pWCStrKey), pCStrKey, pSize,NULL,NULL);
pCStrKey[pSize]='\0';return pCStrKey;//如果想要转换成string,直接赋值即可//string pKey = pCStrKey;}// 绘制菜单界面voiddrawMenu(){
hiex::Window wnd(WINDOW_LONG, WINDOW_WIDE);
wnd.SetProcFunc(WndProc);// hiex::AutoExit();
HWND hwnd = wnd.GetHandle();
edit.PreSetStyle({true,false,true});
edit.Create(hwnd,30,30, EDIT_LONG, EDIT_WIDE,
L"Welcome to SNL Compiler !\r\n"
L"\r\n"
L"Let's start it from the simple sample.\r\n");
edit.SetFont(26,0, L"微软雅黑");
hiex::SysButton btn_o;
btn_o.Create(hwnd,100,520,100,50, L" Options...");
btn_o.RegisterMessage(OnClick);
checkbox.Create(hwnd,230,550,100,20, L"Read only");
checkbox.RegisterMessage(OnCheck);// 提交 按钮
hiex::SysButton btn_s;
btn_s.Create(wnd.GetHandle(),450,520,100,50, L"Submit");// 1.词法 按钮
hiex::SysButton btn_1;
btn_1.Create(wnd.GetHandle(),860,30,90,50, L"词法");// 2.递归 按钮
hiex::SysButton btn_2;
btn_2.Create(wnd.GetHandle(),860,100,90,50, L"递归过程");// 3.语法 - 递归 - 树 按钮
hiex::SysButton btn_3;
btn_3.Create(wnd.GetHandle(),860,170,90,50, L"递归tree");// 4.语法 - ll 1 按钮
hiex::SysButton btn_4;
btn_4.Create(wnd.GetHandle(),860,240,90,50, L"LL1过程");// 5.语法 - tree 按钮
hiex::SysButton btn_5;
btn_5.Create(wnd.GetHandle(),860,310,90,50, L"LL1tree");// 6.语义 - 按钮
hiex::SysButton btn_6;
btn_6.Create(wnd.GetHandle(),860,380,90,50, L"语义");/*
// 7.错误 - 按钮
hiex::SysButton btn_7;
btn_7.Create(wnd.GetHandle(), 860, 450, 90, 50, L"error");
*/while(wnd.IsAlive()){// 按下 提交 按钮时,弹窗if(btn_s.IsClicked()){MessageBox(wnd.GetHandle(), L"SNL源代码已提交", L"Submit", MB_OK);// 将输入内容写到文件中//ofstream outfile_snl;
ofstream outfile_snl("C:\\lexical_in.txt", ios::app);// 将输入内容写入的文件绝对路径
stringstream ss;
string temp =to_string(edit.GetText().c_str());
ss << temp;
outfile_snl << ss.str();
outfile_snl.close();
outfile_snl.clear();ShellExecute(NULL,NULL,_T("C:\\Con.exe"),NULL,NULL, SW_NORMAL);// 调用的exe文件绝对路径}// 按下按钮 1 时,弹窗显示文本if(btn_1.IsClicked()){draw_1();}// 按下按钮 2 时,弹窗显示文本if(btn_2.IsClicked()){draw_2();}// 按下按钮 3 时,弹窗显示文本if(btn_3.IsClicked()){draw_3();}// 按下按钮 4 时,弹窗显示文本if(btn_4.IsClicked()){draw_4();}// 按下按钮 5 时,弹窗显示文本if(btn_5.IsClicked()){draw_5();}// 按下按钮 6 时,弹窗显示文本if(btn_6.IsClicked()){draw_6();}}
hiex::init_end(hwnd);closegraph();}intmain(){init();// 清空文件drawMenu();// SNL菜单界面return0;}
4.注意事项
(1)使用x86或x64问题
编译此界面的vs设置,与需要调用的程序编译时的设置必须相同。
当时就因为这个问题我没注意,一个x64,一个x86,结果明明我把界面输入的内容写入了文件,但被调用的程序一直显示无法打开文件并且无法识别。
暴改好久,真的哭死。
前车之鉴,后车之师!铭记如此惨痛的教训!
(2)输出文件的路径
由于界面显示时需要读新产生的文件,它的位置在你的具体项目程序中设定的位置。由于我在项目程序中用的相对路径,所以新生成的文件位置在界面文件夹里。
当时我在项目文件夹中没发现输出文件更新,还以为没调用运行成功,换了很多调用函数,之后不小心打开界面文件夹才发现。可以说是非常愚蠢了,这里也耽误了巨久,就是没第一个问题那么久hhhhh。
(3)其他问题
其余都是可以多查查、速览定义等等就能解决的问题。
5.总结
综上所示,可以看出对编程水平不高的小白,实现简单的图形化界面其实并没有想象中的难,熟练掌握后半天就能做出来。只是由于此处基础知识的匮乏,所以实际处理过程中会遇到很多简单但很非常耽误进展的问题。
希望这篇记录,让自己不要忘记此过程中遇到的种种问题与困难,同时希望帮助到像我一样零基础的人。
版权归原作者 今晚吃奥利奥炒酸奶 所有, 如有侵权,请联系我们删除。