组件库篇 | EUI | 快速上手
导入组件库
仅需三个步骤便可以导入组件库:
-
进入main.cpp所在目录,将EUI文件夹复制到该目录下
-
双击sln文件使用vs打开项目,右键项目名-添加-新建筛选器,命名为EUI
-
将第1步导入的EUI文件夹拖入到第2步创建的筛选器中
算了,文字描述总归不够细致,你们肯定希望我手把手教你们,那好吧,我可太懂读者的心声了,谁让我要打造最适合初学者最通俗易懂的教程呢,来吧。
我们从创建空项目开始,先新建一个空项目,再添加main.cpp文件
然后就是上述的三步:
步1:进入main.cpp所在目录,将EUI文件夹复制到该目录下
步2:双击sln文件使用vs打开项目,右键项目名-添加-新建筛选器,命名为EUI
步3:将第1步导入的EUI文件夹拖入到第2步创建的筛选器中
跟着我的步骤走应该不会有任何问题了吧,嘿嘿。
在使用EUI图形库之前,需要添加头文件
#include "EUI/EUI.h" // 引入EUI组件库
然后就随心所欲的使用这些组件来构建你的软件系统吧!
下面我们来循序渐进地使用每个控件(给出的都是完整的可运行代码,在引入组件库后,直接复制代码到项目中就可以成功运行啦)
按钮
我们来使用EUI组件库创建一个按钮吧。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255)); // 白色
cleardevice();
Button btn(820, 320, 120, 40); // 创建一个宽120,高40的按钮,坐标为(820,320)
btn.setText(L"点击"); // 设置文本
btn.show(); // 显示按钮
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
这样就创建好了一个按钮,但是它还不能点击,因为我们还没有对鼠标进行监测,让我们来继续完善这个按钮,让它可以响应鼠标操作吧。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Button btn(820, 320, 120, 40); // 创建一个宽120,高40的按钮,坐标为(820,320)
btn.setText(L"点击"); // 设置文本
btn.show(); // 显示按钮
// 整个界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 如果鼠标位于按钮区域
if (btn.isOn(msg.x, msg.y))
{
btn.eventLoop(); // 进入按钮的事件循环
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
这次我们定义了一个鼠标消息,加了一段循环,然后它就能响应鼠标操作了,拥有了悬浮和点击效果。其实就是利用一个循环来实时监测鼠标消息,当鼠标位于按钮所在区域时进入按钮的事件循环,事件循环的内容就是按钮对鼠标的操作进行响应,你可以不必了解事件循环的具体内容,只需要知道只有进入控件的事件循环中,控件才能对鼠标和键盘的操作进行响应,明白这一点就够了。
好了,现在我们拥有了一个外观效果非常完善的按钮,但是别忘了,按钮存在的目的是为了让用户点击后实现某个效果,我们目前的按钮还只是一个空按钮,还没有对它绑定点击事件,下面我们继续进行功能上的完善,实现点击后展示一段文本的功能(当然,在实际的业务场景下,可能是点击按钮进行搜索,或是点击进行界面切换,功能由我们自己定制,我们这里为了方便只演示点击按钮展示文本的功能)。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
// 显示文本
void showText()
{
settextcolor(BLACK); // 设置文本颜色为黑色
outtextxy(810, 460, L"用户点击了按钮"); // 在坐标(810,460)下显示文本
}
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Button btn(820, 320, 120, 40); // 创建一个宽120,高40的按钮,坐标为(820,320)
btn.setText(L"点击");
btn.bindOnClick(showText); // 为按钮绑定showText函数作为点击事件
btn.show(); // 显示按钮
// 整个界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 如果鼠标位于按钮区域
if (btn.isOn(msg.x, msg.y))
{
btn.eventLoop();
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
我们定义了一个showText函数,它的功能很简单,就是显示文本(注意,如果不设置文本颜色的话,默认是白色,与背景融为一体,则看不出任何效果,这是一个常见的坑),然后为按钮绑定showText函数作为点击事件。
到现在为止,我们就实现出了一个非常完善且具有现代化风格的按钮,你可以按照项目的需求为按钮绑定相应的点击事件。
注意区分界面的事件循环监听和控件的事件循环监听,界面的事件循环监听是一个大while循环,负责监听整个界面中鼠标和键盘对所有控件的操作,界面中所有的控件判断是否进入事件循环的语句都要放在这个大while循环中。
输入框
创建一个文本输入框。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Input input(800, 320, 170, 30); // 创建一个宽170,高30的按钮,坐标为(800,320)
input.show();
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
这样就创建好了一个输入框,但它还什么都做不了,别着急,让我们为它注入灵魂。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Input input(800, 320, 170, 30); // 创建一个宽170,高30的按钮,坐标为(800,320)
input.show();
// 整个界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 如果鼠标位于输入框所在区域
if (input.isOn(msg.x, msg.y))
{
if (msg.message == WM_LBUTTONDOWN) // 鼠标点击
{
input.eventLoop(); // 进入输入框的事件循环
}
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
与按钮相同,我们用循环实时监听鼠标消息,当鼠标位于输入框所在区域且鼠标进行了点击操作后进入输入框的事件循环,然后就可以响应鼠标和键盘操作了,即用户就可以正常输入了。
那用户输入后怎样获取输入的文本内容呢?我们要是拿不到用户输入的内容,那输入框就没有存在的意义了,所以我们的下一步操作是获取用户的输入内容,请看:
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Input input(800, 320, 170, 30); // 创建一个宽170,高30的按钮,坐标为(800,320)
input.show();
// 整个界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 如果鼠标位于输入框所在区域
if (input.isOn(msg.x, msg.y))
{
if (msg.message == WM_LBUTTONDOWN)
{
input.eventLoop(); // 进入输入框的事件循环
// 获取输入框文本,并进行显示
settextcolor(BLACK);
wchar_t* text = input.text();
outtextxy(800, 380, text);
}
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
我们使用Input的text函数获取到用户输入的文本,该函数的返回值是一个wchar_t类型的指针,如果你认为wchar_t是一个新数据类型那就大错特错了,不过是char在Unicode编码下的新形式而已,我们用一个wchar_t类型的指针接收返回值,然后进行文本展示。为什么要将获取文本的操作放在进入输入框的事件循环结束后呢,因为事件循环结束意味着用户的本轮输入结束,所以在这个时候获取文本再合适不过了,那为什么放在循环中呢,因为用户可能进行多轮输入,要确保每一轮输入都能够获取到,并覆盖之前的旧输入。
现在的输入框是一个普通输入框,用户输入的文本会明文显示,那你可能会说了,我想做一个登录界面,需要用到密码输入框,让用户在输入密码时不明文显示,而是用星号隐藏,这又该怎么做呢?放心,我已经替你们考虑到了,只需要加一行代码就可以搞定了,请看:
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Input input(800, 320, 170, 30); // 创建一个宽170,高30的按钮,坐标为(800,320)
input.setPassword(1); // 设置为密码输入框
input.show();
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 如果鼠标位于输入框所在区域
if (input.isOn(msg.x, msg.y))
{
if (msg.message == WM_LBUTTONDOWN)
{
input.eventLoop(); // 进入输入框的事件循环
// 获取输入框文本,并进行显示
settextcolor(BLACK);
wchar_t* text = input.text();
outtextxy(800, 380, text);
}
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
现在你可能又有话要说了,我不仅需要密码输入框用来做登录界面,我还想在输入框中设置提示文本。好的没问题,只需 一行代码即可设置占位文本,请看:
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Input input(800, 320, 170, 30); // 创建一个宽170,高30的按钮,坐标为(800,320)
input.setHolderText(L"占位文本"); // 设置占位文本
input.show();
// 整个界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 如果鼠标位于输入框所在区域
if (input.isOn(msg.x, msg.y))
{
if (msg.message == WM_LBUTTONDOWN)
{
input.eventLoop(); // 进入输入框的事件循环
// 获取输入框文本,并进行显示
settextcolor(BLACK);
wchar_t* text = input.text();
outtextxy(598, 380, text);
}
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
好了,现在我们既实现了普通输入框,又实现了密码输入框,既可以给输入框设置占位文本,又可以获取到用户输入,可以说是功能非常完善了,快用到自己的项目中吧。
弹框
说起弹框也许你大致有个概念,但并不是完全清楚它到底是什么东西,下面我来展示一下,你一看便知。
这是第一种类型的弹框,我们可以称它为确认消息弹框,确认消息弹框的功能为当用户做出重要操作时,给出一段提示消息,让用户二次确认或取消,例如删除某条数据,为了防止用户误操作,一个合格的软件系统应该给出一个确认消息弹框。再来看看下面这个。
这是第二种类型的弹框,我们可以称它为提交内容弹框,提交内容弹框的功能为让用户在弹框中输入并提交信息,例如添加一条数据,就需要给出一个提交内容弹框。
下面我们来分别演示如何使用上述两种类型的弹框。
第一种:确认消息弹框
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
Message message(WIN_WIDTH, WIN_HEIGHT, 520, 145); // 创建宽520,高145的弹框,并传入窗口的宽和高
Button messageBtn(800, 320, 120, 40); // 创建宽120,高40的按钮,坐标为(800,320)
// 打开弹框
void openMessage()
{
message.open(INFO); // 打开弹框,传入INFO参数表明这是一个确认消息弹框
}
// 关闭弹框
void closeMessage()
{
message.close(); // 关闭弹框
}
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
message.setTitle(L"提示"); // 设置弹框标题
message.setText(L"你打开了确认消息弹框"); // 设置弹框文本
message.enterBtn()->bindOnClick(closeMessage); // 获取到弹框的确认按钮并绑定hideMessage函数为点击事件
message.cancelBtn()->bindOnClick(closeMessage); // 获取到弹框的取消按钮并绑定hideMessage函数为点击事件
messageBtn.bindOnClick(openMessage); // 为按钮绑定showMessage函数为点击事件
messageBtn.show(); // 显示按钮
int messageShow = message.isShow(); // 弹框是否处于显示状态
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 先判断有没有弹框,如果有则优先进入弹框的事件循环
if (messageShow)
{
// 如果鼠标位于弹框所在区域
if (message.isOn(msg.x, msg.y))
{
message.eventLoop(); // 进入弹框的事件循环
messageShow = message.isShow(); // 获取弹框的状态(显示/隐藏)
}
}
else
{
// 如果鼠标位于按钮所在区域
if (messageBtn.isOn(msg.x, msg.y))
{
messageBtn.eventLoop(); // 进入按钮的事件循环
messageShow = message.isShow(); // 获取弹框的状态(显示/隐藏)
}
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
弹框应当是动态显示的,即在用户按下某个按钮后显示。因此除了创建弹框外,还需要创建一个按钮,并且定义两个函数,这两个函数的功能分别为显示、隐藏弹框。由于函数中需要用到message,因此将message作为全局变量,这里我把messageBtn同样也作为全局变量。
为messageBtn绑定显示弹框的点击事件,值得注意的是,弹框右下角有两个按钮,也需要绑定点击事件,我这里绑定的隐藏弹框的点击事件(在实际业务场景中根据需求自行设计)。
我们还需要一个int变量来标识弹框的状态。使用Message的isShow函数可以获取到弹框的状态,1为显示,0为隐藏。在弹框的事件循环结束后和按钮的事件循环结束后分别获取弹框的状态,因为弹框状态的改变取决于弹框事件循环中右下角两个按钮的点击操作和按钮事件循环中的点击操作。在整个界面的事件循环监听中,需要优先判断弹框的状态,如果弹框为显示状态,则根据鼠标的状态判断进入弹框的事件循环中,只有当弹框隐藏时才判断是否进入其他控件的事件循环。
确认消息弹框的核心内容:
- show函数传入参数为INFO
- 使用setText函数设置文本
弹框是所有控件中最复杂的控件,如果有些细节不太理解也没有关系,以项目为驱动,会使用即可。
第二种:提交内容弹框
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
Message message(WIN_WIDTH, WIN_HEIGHT, 520, 345); // 创建宽520,高145的弹框,并传入窗口的宽和高
Button messageBtn(800, 120, 120, 40); // 创建宽120,高40的按钮,坐标为(800,120)
// 弹框内输入框
Input input1(0, 0, 150, 35);
Input* input1Ptr = &input1;
Input input2(0, 0, 150, 35);
Input* input2Ptr = &input2;
Input input3(0, 0, 150, 35);
Input* input3Ptr = &input3;
// 打开弹框
void openMessage()
{
message.open(CONTENT); // 打开弹框,传入CONTENT参数表明这是一个提交内容弹框
}
// 关闭弹框
void closeMessage()
{
message.close(); // 关闭弹框
}
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
message.setTitle(L"提示"); // 设置弹框标题
// 弹框内添加文本
message.addLabel(L"书名");
message.addLabel(L"作者");
message.addLabel(L"出版时间");
// 弹框内添加输入框
message.addInput(input1Ptr);
message.addInput(input2Ptr);
message.addInput(input3Ptr);
message.enterBtn()->bindOnClick(closeMessage); // 获取到弹框的确认按钮并绑定hideMessage函数为点击事件
message.cancelBtn()->bindOnClick(closeMessage); // 获取到弹框的取消按钮并绑定hideMessage函数为点击事件
messageBtn.bindOnClick(openMessage); // 为按钮绑定showMessage函数为点击事件
messageBtn.show(); // 显示按钮
int messageShow = message.isShow(); // 弹框是否处于显示状态
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 先判断有没有弹框,如果有则优先进入弹框的事件循环
if (messageShow)
{
if (message.isOn(msg.x, msg.y))
{
message.eventLoop(); // 进入弹框的事件循环
messageShow = message.isShow(); // 获取弹框的状态(打开/关闭)
// 获取CONTENT弹框中输入框1的文本,并显示
settextcolor(BLACK);
wchar_t* input1Text = input1Ptr->text();
wchar_t* input2Text = input2Ptr->text();
wchar_t* input3Text = input3Ptr->text();
outtextxy(500, 50, input1Text);
outtextxy(500, 100, input2Text);
outtextxy(500, 150, input3Text);
}
}
else
{
// 如果鼠标位于按钮所在区域
if (messageBtn.isOn(msg.x, msg.y))
{
messageBtn.eventLoop(); // 进入按钮的事件循环
messageShow = message.isShow(); // 获取弹框的状态(打开/关闭)
}
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
提交内容弹框与确认消息弹框大都类似。
这里为什么要先创建输入框,再创建输入框指针指向输入框呢,因为addInput函数的参数为Input*,即传参必须传递输入框指针。
提交内容弹框的核心内容:
- open函数参数为CONTENT
- 需要使用addLabel函数添加文本
- 需要使用addInput函数添加输入框
- 在弹框的事件循环结束后获取输入框中的文本
这里给的示例为获取文本后显示,在实际的业务场景中大多需要将获取的文本保存起来,或存入文件或存入数据库中。
侧边导航栏
侧边导航栏也是一个常用的组件,它是由侧边导航栏和侧边导航栏按钮组成的,换句话说,侧边导航栏按钮存在的意义就是为侧边导航栏服务。我们下面来说明如何使用导航栏组件,其中会用到导航栏按钮组件。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
// 导航栏
Navbar nav(0, 0, 200, WIN_HEIGHT); // 创建导航栏,宽200,高与窗口高度一致,坐标为(0,0)
// 导航栏按钮
// 创建3个宽200,高50的导航栏按钮
NavButton navBtn1(200, 50);
NavButton* navBtn1_ptr = &navBtn1;
NavButton navBtn2(200, 50);
NavButton* navBtn2_ptr = &navBtn2;
NavButton navBtn3(200, 50);
NavButton* navBtn3_ptr = &navBtn3;
// 导航栏
nav.setTitleText(L"侧边导航栏"); // 设置导航栏头部文本
// 设置导航栏按钮文本
navBtn1_ptr->setText(L"首页");
navBtn2_ptr->setText(L"账户管理");
navBtn3_ptr->setText(L"图书管理");
// 导航栏内添加按钮
nav.addNavButton(navBtn1_ptr);
nav.addNavButton(navBtn2_ptr);
nav.addNavButton(navBtn3_ptr);
nav.show(); // 显示导航栏
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
这样就创建了一个有三个按钮的侧边导航栏,有三个要点:
- 创建导航栏是高度要与窗口高度一致,坐标定为(0,0)
- 各个导航栏按钮大小一致
- 按钮在导航栏中排列的次序只与添加按钮的先后次序有关
由于还没有加界面的事件循环监听,因此现在导航栏的按钮还不能对鼠标做出响应,我们继续。
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
// 导航栏
Navbar nav(0, 0, 200, WIN_HEIGHT); // 创建导航栏,宽200,高与窗口高度一致,坐标为(0,0)
// 导航栏按钮
// 创建3个宽200,高50的导航栏按钮
NavButton navBtn1(200, 50);
NavButton* navBtn1_ptr = &navBtn1;
NavButton navBtn2(200, 50);
NavButton* navBtn2_ptr = &navBtn2;
NavButton navBtn3(200, 50);
NavButton* navBtn3_ptr = &navBtn3;
// 导航栏
nav.setTitleText(L"侧边导航栏"); // 设置导航栏头部文本
// 设置导航栏按钮文本
navBtn1_ptr->setText(L"首页");
navBtn2_ptr->setText(L"账户管理");
navBtn3_ptr->setText(L"图书管理");
// 导航栏内添加按钮
nav.addNavButton(navBtn1_ptr);
nav.addNavButton(navBtn2_ptr);
nav.addNavButton(navBtn3_ptr);
nav.show(); // 显示导航栏
// 界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 鼠标位于导航栏所在区域
if (nav.isOn(msg.x, msg.y))
{
nav.eventLoop(); // 进入导航栏的事件循环监听
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
值得注意的是,只需要对导航栏判断是否进入事件循环监听即可,并不需要一一对按钮做判断,因为导航栏按钮是包含在导航栏中的。
这里创建导航栏按钮指针是因为 addNavButton 函数需要接收NavButton*类型的参数。
除此之外,我们需要对每个导航栏按钮绑定点击事件,这与普通按钮的绑定操作完全一致,因此不再演示,如果你不太清楚这部分内容可以去看按钮的部分,在实际业务场景中一般绑定的点击事件为切换界面,自行实现即可。
列表
如果你开发一个软件系统,尤其是管理系统,那列表是绝对无法避免的组件,很多初学者都会卡在这一块,后来可能干脆表格不打了,直接一行一行输出文本,其实这还真不是犯懒,因为这对初学者而言确实是个难题。不过,用了EUI组件库,这种问题就不叫问题了,下面请看示例:
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
// 表格数据结构体
typedef struct {
wchar_t id[MAX_TEXT_LEN];
wchar_t bookName[MAX_TEXT_LEN];
wchar_t author[MAX_TEXT_LEN];
wchar_t time[MAX_TEXT_LEN];
}list;
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
Table tb(230, 230, 1430, 6, 4); // 创建表格,宽1430,6行4列,坐标为(230,230)
headerList hl[MAX_COL]; // 表头
int width[] = { 10,20,20,50 }; // 设定表格宽度比例,单位为%,和为100
tb.setGridWidth(width);
// 设定表头,4列
wcscpy_s(hl[0].header, L"序号");
wcscpy_s(hl[1].header, L"书名");
wcscpy_s(hl[2].header, L"作者");
wcscpy_s(hl[3].header, L"时间");
list data[4]; // 表格数据
int listCnt = 2; // 表格数据条数
// 设定表格数据
wcscpy_s(data[0].id, L"01");
wcscpy_s(data[0].bookName, L"C程序设计语言");
wcscpy_s(data[0].author, L"布莱恩");
wcscpy_s(data[0].time, L"2023-01-23");
wcscpy_s(data[1].id, L"02");
wcscpy_s(data[1].bookName, L"深入理解计算机系统");
wcscpy_s(data[1].author, L"兰德尔");
wcscpy_s(data[1].time, L"2023-06-02");
tableList* tl = tb.tableListCont(); // 获取tableList指针
// 将表格数据一对一映射到tableList中
for (int i = 0; i < listCnt; i++)
{
wcscpy_s(tl->col[0], data[i].id);
wcscpy_s(tl->col[1], data[i].bookName);
wcscpy_s(tl->col[2], data[i].author);
wcscpy_s(tl->col[3], data[i].time);
tl++;
}
tb.show(hl);
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
在C语言开发的软件系统中,要在表格中展示的数据通常都存放在结构体数组中,这里我定义了一个结构体数组,并使用wcscpy_s函数为其赋初值,如果你是第一次见到这个函数心里可能还有一点畏惧,看起来好难的样子,其实不用担心,我想你一定用过strcpy这个函数,就是字符数组的拷贝函数,即把一个字符数组的内容拷贝到另一个字符数组中,这个函数也是一样的,它可以把字符串常量拷贝到wchar_t数组中,或是wchar_t数组之间的拷贝。
你需要定义一个元素个数和表格列数相同的int型数组,设定表格宽度比例,单位为%,和为100,将该数组作为参数传入setGridWidth函数中,这样就得到了自定义列宽的表格,下面就要为其填入数据了。
使用tableListCont函数获取到tableList类型的指针,再将表格数据一对一映射到tableList中,这段代码是一套固定的写法,大家可以直接照搬,再根据你项目中表格数据的不同微微做调整。由于这块的原理稍微有些复杂,所以并不要求大家掌握,只要会使用即可。
注意,表格是一个用于展示的组件,并不需要与鼠标交互,因此没有事件循环监听。
弱提示框
也许你是第一次听到这个词语:弱提示框,不免心生疑惑,什么是弱提示框。弱提示框顾名思义就是不含任何选择性、强制性的提示框,只是给你一个弱弱的提示,告诉你发生了什么事,这是一个交互优秀的软件系统所必需的,下面来看一个示例:
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
Toast toast(WIN_WIDTH, 30, 350, 50); // 创建一个宽350,高50的弱提示框,y坐标为30,传入窗口宽度
Button toastBtn(820, 180, 120, 40); // 创建一个宽120,高40的按钮,坐标为(820,180)
void showToast()
{
toast.show(SUCCESS); // 显示弱提示框,类型为SUCCESS
}
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
toast.setText(L"这是一段成功消息"); // 设置文本
// 弱提示框按钮
toastBtn.setText(L"按钮");
toastBtn.bindOnClick(showToast); // 绑定点击事件
toastBtn.show();
// 界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 鼠标位于按钮所在区域
if (toastBtn.isOn(msg.x, msg.y))
{
toastBtn.eventLoop(); // 进入导航栏的事件循环监听
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
弱提示框与弹框类似,也是一个动态显示的组件,因此需要一个按钮来触发,定义一个显示弱提示框的函数与按钮进行绑定,由于函数中要使用toast,因此我将弱提示框作为全局变量,同样把按钮也放在了全局,然后,我们需要进行界面的事件循环监听,判断是否进入按钮的事件循环中。
弱提示框有两种类型,成功类型SUCCESS和警告类型WARNING,分别用来展示成功消息和警告消息,类型需要在show函数中体现,即给show函数传参SUCCESS或WARNNING。当然,你还需要为弱提示框设置文本作为你的消息内容。
上面的示例是SUCCESS类型的成功提示框,下面来演示WARNING类型的警告提示框,它们在使用上仅仅只有传参的区别,当然,警告框的文本肯定与成功框有所不同,请看:
#include "EUI/EUI.h" // 引入EUI组件库
#define WIN_WIDTH 1800 // 窗口宽度
#define WIN_HEIGHT 900 // 窗口高度
Toast toast(WIN_WIDTH, 30, 350, 50); // 创建一个宽350,高50的弱提示框,y坐标为30,传入窗口宽度
Button toastBtn(820, 180, 120, 40); // 创建一个宽120,高40的按钮,坐标为(820,180)
void showToast()
{
toast.show(WARNING); // 显示弱提示框,类型为WARNNING
}
int main()
{
initgraph(WIN_WIDTH, WIN_HEIGHT); // 创建绘图窗口
// 设置背景色
setbkcolor(RGB(255, 255, 255));
cleardevice();
toast.setText(L"这是一段警告消息"); // 设置文本
// 弱提示框按钮
toastBtn.setText(L"按钮");
toastBtn.bindOnClick(showToast); // 绑定点击事件
toastBtn.show();
// 界面的事件循环监听
ExMessage msg; // 鼠标消息
while (1)
{
msg = getmessage(EX_MOUSE); // 获取消息输入
// 鼠标位于按钮所在区域
if (toastBtn.isOn(msg.x, msg.y))
{
toastBtn.eventLoop(); // 进入导航栏的事件循环监听
}
}
_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}
注意,弱提示框是一个用于展示的组件,并不需要与鼠标进行交互,因此没有事件循环监听,这里的事件循环监听是按钮的,一定要区分清楚。
小结
好了,这就是快速上手部分的全部内容了,我带着大家挨个组件循序渐进地进行了使用方法的演示和讲解,虽然我认为自己讲的已经足够详细且通俗易懂了,但可能对于初学者来说难免会有些不太理解的地方,现阶段不必过于纠结,核心是会用、会用、会用,重要的事情说三遍。图形库、组件库只是工具,对于工具我们大多数时候只要会使用就足够了,并不需要深入它的原理和底层,切勿本末倒置。当然,如果你想做一个新的工具,那你就需要研究各种各样工具的设计思想、底层原理,这个时候对现有工具的要求可就不是会用这么简单了。所以说呢,学习这件事是一件非常灵活的事,如何学习、学到什么程度完全取决于你学习的目的,要把有限的精力花在刀刃上。希望EUI组件库带给你的不仅仅是帮助你开发软件系统这一件事,希望你能从中体会到合理高效的学习方法和面对新工具、新事物的处理办法,在你日后的软件开发生涯中帮助你最大程度的提高效率。
当然,如果你并不满足于会使用EUI组件库,而非常好奇我是如何实现出各种各样美观的组件的,那么在你基本掌握了C++后,完全可以学习EUI的设计思想和实现方法,这是一个非常轻量级的库,总计代码不超过2000行,非常适合C++初学者学习,这样你不仅会使用它,也可以改造它,并且可以融入自己的思想和理念,在这期间你的能力一定会得到相当大的提升,而这也正是软件开发的魅力所在。