本文章适用于,有一定C++基础,大概看过一部分EasyX文档中的函数,的人。
什么是EasyX
按照我的理解:EasyX是一个函数库(当然,里面不止有函数,这么说可能会让初学者更好理解),就像我们的基本输入输出函数库(iosteam)一样,EasyX的所有功能也是在函数库中实现的,官网下载的EasyX安装程序所安装的主要文件就是两个函数库: easyx.h 以及graphics.h ,所有的函数功能均在这两个函数库中实现.
优点:简单,便捷,我们只需要会使用其中的函数画图就行了. 吐槽:内容少,难以开发大项目,难以有沉浸式体验,部分函数可能用起来不是那么符合你的习惯.
关于开发环境
EasyX的官方安装器只支持VS, 本来用的是Codeblocks,至于为什么这两个头文件不能运行,网上说不支持编译器gcc,看兼容教程要改EasyX里的一些东西,为了防止改错(主要是绝对麻烦)干脆用vs2017了
EasyX的使用教程
EasyX的官网 具有详细的参考文档,包含了一切函数的具体用法说明,下面仅介绍我的观看思路,遇到的问题与解决方案
基础原理
三个基本概念:颜色,坐标,设备;
颜色, 采用了RGB颜色方案,即:红,蓝,绿,三种颜色按不同比例混合后的颜色,如果我们不知道某个颜色的取值,可以使用网上的一些取色工具。
坐标,从窗口的左上角开始为0,向右为x方向,向下为y方向,单位为像素
设备,即绘图窗口,也就是用户可以看见的窗口,以后的绘图都在这个窗口上进行
宽字符
UNICODE编码字符,叫宽字节字符。
UNICODE编码,每个字符占二个字节。也叫万国码(一种国际标准字符集,为世界上绝大多数已知的字符集定义了唯一的16位数值)
EasyX所有的字符使用的均为宽字符,也就是 wchar_t ,需要在字符串前面加L来表示这是一个宽字符集 比如 L"我是一个字符串" 就是一个宽字符集的字符串;
关于宽字符集与字符串之间的转化,(该部分 转载至该博客 ),如果会,可跳过
1.MultiByteToWideChar函数
函数功能:该函数将一个字符串到转换成一个宽字符(unicode)的字符串。
函数原型:
int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cchMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
第一个参数CodePage:指定执行转换的字符集,这个参数可以为系统已安装或有效的任何字符集所给定的值。 可选值:
CP_ACP:ASCII字符集;
CP_UTF7:使用UTF-7转换;
CP_UTF8:使用UTF-8转换;
第二个参数dwFlags:一般用 0
第三个参数lpMultiByteStr: ASCII 字符串的地址, 这个字符串是第一个参数指定的语言的 ASCII 字符串
第四个参数cchMultiByte: ASCII 字符串的长度,如果用 -1, 就表示是用 '\0' 作为结束符的字符串
第五个参数 lpWideCharStr,:转化生成的 unicode 字符串 (WideString) 的地址, 如果为 NULL, 就是代表计算生成的字符串的长度
第六个参数cchWideChar:转化生成的 unicode 字符串缓存的容量,也就是有多少个UNICODE字符,若此值为零,函数将返回缓冲区所必需的宽字符数。
因此该函数一般使用两次:
第一次获取转换成unicode所需要的长度
第二次才是真正的把一个字符串转换为unicode,代码如下:
char* mbs = "中国,你好!I Love You!";
int length = MultiByteToWideChar( CP_ACP, 0, mbs, -1, NULL, 0 );
wchar_t* wcs = new wchar_t[ length ];
MultiByteToWideChar( CP_ACP, 0, mbs, -1, wcs, length );
delete wcs;
wcs = NULL;
2.WideCharToMultiByte函数
函数功能:该函数将一个宽字符(unicode)转换为字符串的函数。
函数原形:
int WideCharToMultiByte(
UINT *CodePage*, //指定执行转换的代码页
DWORD *dwFlags*, //允许你进行额外的控制,它会影响使用了读音符号(比如重音)的字符,一般设置为NULL
LPCWSTR *lpWideCharStr*, //指定要转换为宽字节字符串的缓冲区
int *cchWideChar*, //指定由参数lpWideCharStr指向的缓冲区的字符个数
LPSTR *lpMultiByteStr*, //指向接收被转换字符串的缓冲区
int *cchMultiByte*, //指定由参数lpMultiByteStr指向的缓冲区最大值
LPCSTR *lpDefaultChar*, //当WideCharToMultiByte函数遇到一个宽字节字符,而该字符在CodePage参数标识的代码页中并没有它的表示法时,WideCharToMultiByte函数才使用这两个参数。如果宽字节字符不能被转换,该函数便使用lpDefaultChar参数指向的字符。如果该参数是NULL(这是大多数情况下的参数值),那么该函数使用系统的默认字符。该默认字符通常是个问号。
LPBOOL *pfUsedDefaultChar* //至少有一个字符不能转换为其多字节形式,函数就会把这个变量设为TRUE
);
代码如下:
Wchar_t* wcs = _T("中国,你好!I Love You!");
int length = WideCharToMultiByte( CP_ACP, 0, wcs, -1, NULL, 0, NULL, NULL);
char* mbs = new char[ length ];
WideCharToMultiByte( CP_ACP, 0, wcs, -1, mbs, length, NULL, NULL);
delete mbs;
mbs = NULL;
以上方法只适用于char数组与wchar_t数组的转化 ,更多的,如果我们用到了字符串(string),那么我们还需要: 字符串与char数组的转化 :
一、string 转 char*
主要有三种方法可以将 str 转换为 char* 类型,分别是:data(); c_str(); copy;
1、data() 方法:
string str = "hello world";
const char *p = str.data(); //加const 或用 char *p = (char*)str.data(); 的形式
注:若不加 const,编译时会报错:无法从 "const char" 转换为 "char";
2、c_str() 方法:
string str = "hello";
const char *p = str.c_str(); //同上
3、copy() 方法:
string str = "hello";
char buf[50];
str.copy(buf,5,0); //这里 6代表6个字符,0代表复制位置
*(buf+5) = '\0'; //需手动加结束符
二、char* 转 string
可以直接赋值。
string str;
char *p = "hello";
str = p; //注意:当p中含有较多'\0'时,转换的string是截断后的,正确转换如下:
string str2(p,size); //size是p的大小
函数
EasyX的所有功能基于函数来实现. 在这里,我将函数分为几大类: 1.样式函数 2.绘制函数 3.交互函数 4.其他函数
样式函数
即设置各种样式的函数,常见的样式有:填充颜色、图形描边、文字颜色、字体、文字背景、
我们在使用这些函数的时候,需要注意以下几点:
样式的设置生效范围为全局;即我们不需要为某个特定的形状设置样式,当我们设置完样式后,无论我们画什么形状,这个形状会自动按照我们设置的最后一个样式来绘制。
在设置颜色时,请尽量使用RGB(0,0,0) 来获取颜色,如果使用十六进制颜色法 0x00ff00 那么会将本此设置的颜色与上次设置的颜色混合,从而产生一个奇怪的颜色,如果你对颜色的混合不了解,请不要使用此方法.
我们可以将样式放在,划线样式对象,填充样式对象,字体样式对象中,方便我们多次使用。
绘制函数
在设置了样式以后,我们可以使用绘制函数在窗口上画一些东西了,比如一个有描边圆;一个没有描边的矩形;一排红色,背景为透明的文字。 值得注意的是:
后来绘制的图案会覆盖前面的图案,在输出文字时可能出现多个文字出现在统一区域的情况,此时你可以重新在这个区域绘制一个和背景颜色一样的矩形将文字覆盖掉。
输出文字时提前设置文字的背景颜色,不然为黑色
交互函数
有了样式函数和绘制函数,理论上就可以在窗口上画出任何东西了,但我们为了给用户一个良好的体验,我们需要捕获用户输入的以下数据:键盘,鼠标,文本。 为什么把文本和键盘分开呢?文本主要指中文字符串,键盘指键盘上的按键。
键盘输入的捕获和以前一样,使用C/C++中的一些输入函数(比如getch())即可。
既然有了可视化界面,那么鼠标的点击就显得十分重要,我们希望当用户点击某个区域时,我们的程序能知道,并作出反应,Easyx中也提供了相应的的函数和类。
Easyx中将与鼠标有关的数据都放入了一个结构体中,我们只需要判断捕获的数据是否符合我们需要即可
思路如下:创建鼠标类,读取消息赋值给鼠标类,读取鼠标类中的属性,判断刚才的消息是否符合我们需要
文本输入 文本的输入Easyx中只有一个函数inputbox()可以满足文本输入,该函数会弹出一个窗口让用户输入,且返回的值为一个宽字符串指针。
其他函数
并不是那么必要,但合理使用可以使你的程序更加灵活。
附源码
基于C++与EasyX的职工管理系统,封装与逻辑不是那么的完美,基于以前的系统改的. 我们主要用它来观看EasyX函数的使用方法.
开发环境: Visual Studio 2017 ; win10 ; 2021年6月19日
#include<iostream>
#include<string>
#include<fstream> //文件操作的头文件
#include<windows.h>
#include<graphics.h> //EasyX库
#include<conio.h>
using namespace std;
int huanying(); //开始界面
string inputboxpro(const LPCTSTR & prompt); //获得用户输入,返回字符串
wchar_t*& strtowide(const string & str); //将字符串转化为宽字符的指针;
class zhigong //定义职工类,保存职工属性与操作,对职工的操作使用该类来完成
{
public:
//zhigong(string&,string&,string&,string&); //构造函数
int putout(int i); //显示信息
int read(ifstream&, string&, string&); //从文件中读取职工
int setin(ofstream&); //将职工信息保存到文件中
int shuru(); //输入职工信息
int setname(string&);
int setgangwei(string&);
int setnumber(string&);
protected:
string number;
string name;
string gangwei;
string zhize;
};
int zhigong::setname(string& str)
{
name = str;
return 0;
}
int zhigong::setgangwei(string& str)
{
gangwei = str;
return 0;
}
int zhigong::setnumber(string& str) {
number = str;
return 0;
}
int zhigong::read(ifstream& read, string& num, string& iname)
{
name.clear();
gangwei.clear();
number.clear();
read >> name >> gangwei >> number;
if (number.compare(num) == 0 || name.compare(iname) == 0)
{
// cout << "iname=" <<iname<<"num="<<num<< " name=" <<name <<" number="<< number<<endl;
return 1;
}
else
{
return 0;
}
}
int zhigong::setin(ofstream& write)
{
write << "\n" << name << "\n" << gangwei << "\n" << number;
return 0;
}
int zhigong::putout(int i)
{
int height{25};
char in;
if (gangwei.compare("职工") == 0)
{
zhize = "完成经理交给的任务";
}
else if (gangwei.compare("经理") == 0)
{
zhize = "完成老板交给的任务";
}
else
{
zhize = "管理公司所有事务";
}
// cout << "编号:" << number << " 姓名:" << name << " 岗位:" << gangwei << " 职责:" << zhize << endl;
setbkmode(TRANSPARENT); //设置文字填充时背景色为透明
settextcolor(RGB(0, 0, 0)); //设置文字颜色
if (i == 1) { //显示标题栏
setfillcolor(RGB(255, 242, 231));
solidrectangle(230, 50, 650, 50+height); //绘制背景
outtextxy(240, 55, L"编号"); //输出文字
outtextxy(320, 55, L"姓名");
outtextxy(400, 55, L"岗位");
outtextxy(480, 55, L"职责");
}
else if (i % 14 == 1) {
outtextxy(300, 450, L"按‘→键’翻页"); //输出文字
while (true) {
if ((in = _getch()) < 0) { in = _getch(); } //读取输入
if (in == 77) {
setfillcolor(RGB(248, 248, 255));
solidrectangle(230, 50+height, 650, 480); //重新绘制背景
break;
}
}
}
if (i > 14) {
i = i % 14;
}
if (i == 0) {
i = 14;
}
if (i % 2 == 0) //隔行换颜色
{
setfillcolor(RGB(255,255,240)); //淡淡黄色
}
else
{
setfillcolor(RGB(211, 211, 211)); //亮灰色
}
solidrectangle(230, 50+(height*i), 650, 50 + (height*(i+1))); //绘制背景
outtextxy(240, 55 + height * i, strtowide(number)); //输出文字
outtextxy(320, 55 + height * i, strtowide(name));
outtextxy(400, 55 + height * i, strtowide(gangwei));
outtextxy(480, 55 + height * i, strtowide(zhize));
return 0;
}
int zhigong::shuru()
{
cout << "请输入姓名:";
cin >> name;
cout << "请输入岗位(职工/经理/老板):";
cin >> gangwei;
if (gangwei.compare("职工") == 0)
{
zhize = "完成经理交给的任务";
}
else if (gangwei.compare("经理") == 0)
{
zhize = "完成老板交给的任务,下发任务给员工";
}
else
{
zhize = "管理公司";
}
return 0;
}
int main()
{
int pan;
int num;
char num_2;
string linshi;
string gangwei;
string name;
zhigong mem_1, mem_2;
ifstream read; //读取流指针
ofstream write; //输出流指针
MOUSEMSG msg; //鼠标消息结构体
huanying(); //初始化页面
while (1)
{
msg = GetMouseMsg(); //获取鼠标消息
while (msg.uMsg != WM_LBUTTONDOWN) //判断鼠标是否左键单击
{
msg = GetMouseMsg();
}
if (msg.x >= 20 && msg.x<= 180) {
setfillcolor(RGB(248, 248, 255));
solidrectangle(200, 0, 680, 480); //绘制右侧背景
if (msg.y >= 20 && msg.y <= 60) {
exit(0);
}
else if (msg.y >= 80 && msg.y <= 120) //增加职工
{
name = inputboxpro(L"请输入姓名");
gangwei = inputboxpro(L"请输入岗位(职工/经理/老板):");
read.open("./member.txt", ios::in);
write.open("./member.txt", ios::app); //以末尾追加形式打开文件
num = 0;
if (read.is_open() && read.peek() != EOF) // 判断文件是否存在且不为空,
{
while (read.peek() != EOF) //peek() —— 此函数将返回输入流文件的下一个字符,但它不移动内置指针。
{
read >> linshi >> linshi >> num;
}
num++;
write << "\n" << name << "\n" << gangwei << "\n" << num; //输入文件
}
else
{
num = 1;
write << name << "\n" << gangwei << "\n" << num; //输入文件
}
linshi = to_string(num); //设置员工信息
mem_1.setnumber(linshi);
mem_1.setgangwei(gangwei);
mem_1.setname(name);
mem_1.putout(1); //显示员工信息
read.close();
write.close();
}
else if (msg.y >= 140 && msg.y <= 180) //删除职工
{
pan = 0;
linshi = inputboxpro(L"请输入要删除的员工姓名或编号");
//cout << "linshi=" <<linshi<<endl;
read.open("./member.txt");
write.open("./linshi.txt");
while (read.peek() != EOF)
{
if (mem_1.read(read, linshi, linshi) == 1)
{
setbkmode(TRANSPARENT); //设置文字填充时背景色为透明
settextcolor(RGB(255,69,0)); //设置文字颜色
outtextxy(230, 25, L"以下员工被删除");
pan ++;
mem_1.putout(pan);
continue;
}
else
{
mem_1.setin(write);
}
}
read.close();
write.close();
if (pan == 0)
{
setbkmode(TRANSPARENT); //设置文字填充时背景色为透明
settextcolor(RGB(255, 69, 0)); //设置文字颜色
outtextxy(430, 235, L"查无此人");
continue;
}
read.open("./linshi.txt"); //读取回来
write.open("./member.txt");
while (read.peek() != EOF)
{
mem_1.read(read, linshi, linshi);
mem_1.setin(write);
}
read.close();
write.close();
}
else if (msg.y >= 200 && msg.y <= 240)
{
pan = 0;
linshi = inputboxpro(L"请输入要修改的员工姓名或编号");
read.open("./member.txt");
write.open("./linshi.txt");
while (read.peek() != EOF)
{
if (mem_1.read(read, linshi, linshi))
{
pan = 1;
setbkmode(TRANSPARENT); //设置文字填充时背景色为透明
settextcolor(RGB(255, 69, 0)); //设置文字颜色
outtextxy(230,25,L"请选择修改项目:");
mem_1.putout(1);
setfillcolor(RGB(152, 245, 255)); //设置按钮颜色
solidrectangle(250, 350, 350, 390); //生成按钮
solidrectangle(400, 350, 500, 390);
outtextxy(260, 360, L"姓名");
outtextxy(400, 360, L"岗位");
while (true) //更改信息
{
msg = GetMouseMsg(); //获取鼠标消息
while (msg.uMsg != WM_LBUTTONDOWN) //判断鼠标是否左键单击
{
msg = GetMouseMsg();
}
if (msg.y >= 350 && msg.y <= 390) {
if (msg.x >= 250 && msg.x <= 350) {
name = inputboxpro(L"输入姓名");
mem_1.setname(name);
break;
}
else if (msg.x >= 400 && msg.x <= 500)
{
gangwei = inputboxpro(L"输入岗位");
mem_1.setgangwei(gangwei);
break;
}
}
}
mem_1.setin(write);
}
else
{
mem_1.setin(write);
}
}
read.close();
write.close();
if (pan == 0)
{
outtextxy(430, 235, L"查无此人!");
continue;
}
read.open("./linshi.txt");
write.open("./member.txt");
while (!read.eof())
{
mem_1.read(read, linshi, linshi);
mem_1.setin(write);
}
outtextxy(430, 235, L"修改成功");
read.close();
write.close();
}
else if (msg.y >= 260 && msg.y <= 300) {
pan = 0;
linshi = inputboxpro(L"请输入要查找的姓名或编号:");
read.open("./member.txt");
while (read.peek() != EOF)
{
if (mem_1.read(read, linshi, linshi))
{
pan ++;
mem_1.putout(pan);
continue;
}
}
if (pan == 0)
{
outtextxy(430,235,L"查无此人!\n");
}
read.close();
write.close();
}
else if (msg.y >= 320 && msg.y <= 360) {
read.open("./member.txt");
pan = 1;
while (!read.eof())
{
mem_1.read(read, linshi, linshi);
mem_1.putout(pan);
pan++;
}
read.close();
}
else if (msg.y >= 380 && msg.y <= 420) {
linshi = inputboxpro(L"此操作无法撤销!请输入‘我已慎重考虑’以继续操作");
if (linshi.compare("我已慎重考虑") == 0) {
write.open("./member.txt", ios::trunc);
write.close();
settextcolor(RGB(255, 0, 0));
outtextxy(400, 235, L"成功删库");
}
}
}
}
}
int huanying()
{
int height{ 60 }; //保存按钮高度信息
//cout << "欢迎来到职工系统:\n0.退出系统\n1.增加职工信息\n2.删除离职职工\n3.修改职工信息\n4.查找职工信息\n5.显示所有职工\n";
initgraph(680, 480); //设置窗口大小
setfillcolor(RGB(127, 255, 212)); //设置填充颜色
solidrectangle(0, 0, 200, 480); //绘制无边框矩形(按钮栏背景
setfillcolor(RGB(248, 248, 255));
solidrectangle(200, 0, 680, 480); //绘制右侧背景
setfillcolor(RGB(152, 245, 255));
for (int i = 0; i <= 6; i++) {
solidrectangle(20, 20 + (height*i), 180, height*(i + 1)); //绘制无边框矩形
}
setbkmode(TRANSPARENT); //设置文字填充时背景色为透明
settextcolor(RGB(0, 0, 0)); //设置文字颜色
outtextxy(30, height * 1 - 30, L"退出系统"); //输出文字
outtextxy(30, height * 2 - 30, L"增加员工");
outtextxy(30, height * 3 - 30, L"删除员工");
outtextxy(30, height * 4 - 30, L"修改员工");
outtextxy(30, height * 5 - 30, L"查找员工");
outtextxy(30, height * 6 - 30, L"显示所有员工");
outtextxy(30, height * 7 - 30, L"删除所有员工");
return 0;
}
string inputboxpro(const LPCTSTR & prompt) //获得用户输入,返回字符串
{
wchar_t shuru[100];
char * shuru_change;
int length;
string str;
for (int i = 0; i < 100; i++) { //初始化宽字符数组
shuru[i] = L'\0';
}
InputBox(shuru, 100, prompt);
length = WideCharToMultiByte(CP_ACP, 0, shuru, -1, NULL, 0, NULL, NULL); //获得转化后的字符数组长度
shuru_change = new char[length];
WideCharToMultiByte(CP_ACP, 0, shuru, -1, shuru_change, length, NULL, NULL); //将宽字符数组转化为字符数组
str = shuru_change; //字符数组转化为字符串
delete[] shuru_change;
return str; //返回字符串
}
wchar_t*& strtowide(const string & str) //将字符串转化为宽字符的指针;
{
const char* ch = str.data();
int length = MultiByteToWideChar(CP_ACP, 0, ch, -1, NULL, 0); //获得转化后的宽字符数组长度
wchar_t * wch = new wchar_t[length+1];
MultiByteToWideChar(CP_ACP, 0, ch, -1, wch, length); //将字符数组转化为宽字符数组
return wch;