一、初想
1、QT?
学了快两年的编程了,一直都是在黑窗口控制台进行操作,于是我突发奇想,想要做出一个带界面的,可以用鼠标进行人机交互的通讯录程序作为实训的内容,于是我开始了在网上搜索‘如何用c++做出界面’。
关于这方面的回答有很多,其中大部分都是推荐使用一个名为‘QT’的应用程序,我简要的看了一下,这是一个基于c++的编程工具,其主要途径就是用于桌面应用的开发,其语法自然也和c++差不多。于是我就尝试着下载这款app来进行编程,但是在下载的过程中,我又遇到了各种各样的问题——首先是在版本的选择上QT的版本五花八门,各个版本又都有不一样的地方,我当时好像就选了最新的版本,但是在安装的过程中,需要选择搭载什么样的环境,几十个选项让我无从下手,在网上查询安装过程又发现网上的那个安装页面与我下载的版本又不一样了,折腾了好久,最后,我就一顿乱操作还是把他安装下来了,紧跟着,我打开QT,想试着编写一个程序,但是它的各种一大串的英文定义方式让我感受特别的不舒服。
2、Easyx ?
第二天,我继续在网上搜索着‘如何用c++做出界面’。看到了除了Qt之外的另一个回答——easyx。我顺着回答里面的链接来到easyx的官网,浏览了一下easyx的功能——相当于一个图形库,这个库作为一个头文件可以在编译器里面运行,这无疑是摆脱下载安装一个新app路线的好选择,并且easyx可以和我在用的编译器(vs2021),有很好的联动性,不容易出现各种报错。
当然啦,我下载的easyx的头文件,按着下载完的步骤很顺利的导入vs,然后我就开始在网上学着,如何进行easyx的图形化编程。起初,我在csdn上面学着一些easyx的基础部分,((8条消息) EasyX---快速入门---(一)基本绘图与文字绘制_由崎的博客-CSDN博客_easyx画图)例如怎么建立一个图形窗口,怎么绘制图(矩形,圆形,画线等等),怎么设置窗口的背景色,以及怎么在图形窗口上显示文字等等基础知识。仅仅学习这些内容,对于完成带界面的鼠标交互的通讯录系统,还是远远不够的。
我又在B站上发现一个非常契合我需要的easyx教学视频——c++鼠标实战_搜索_哔哩哔哩-bilibili看完了这个视频,我学习了很多,让我感觉到已经可以着手来进行实操了!
b站的教学视频
二、实操
1、练习
看完了上面的基础知识,使用起来的还是比较简单的,有一种照猫画虎的感觉,创建窗口,设置背景色,刷新屏幕,设置字体格式,放置字体,贴图片等等编写出来都挺简单的,包括鼠标来制作一个点击功能也不在话下,看了那两个知识点,我感觉我已经把easyx玩明白了,于是我开始制作我的通讯录系统,然后再来把图形化应用上去。折腾了好几个小时,我倒是把通讯录系统给整出来了,总体的架构就是一个联系人结构体,一个通讯录功能的类,然后主函数里进行调用,文件处理也包含在通讯录类的里面,然后也用了c++的vector容器来存放数据,整个代码下来两百多行就搞定了(当然啦,健壮性不是很强)。
做完之后,我肯定是迫不及待的想把easyx应用到这个通讯录系统上去!
2.1尝试应用到系统
前面的搭建基本界面应用下来还是没有问题的。但是,突然来到一个然后很头疼的地方,就是easyx的outtextxy()函数来输出容器里的数据时,也就是输出字符到界面上的函数,它的构造并不支持c++的string字符串类型,一直会报错,说识别不了。我一开始也是感觉很奇怪,然后在网上摸爬跪打了几个小时(从csdn到贴吧,再到easyx官网各个网站反复横跳——感觉网上对于这方面的内容并不是很多,不好找……),才得出它并不支持string类型。
#include
#include
#include
#include
#include
#include
using namespace std;
class person {
public:
string name;
string sex;
string number;
string diqu;
person() {
name = "";
sex = "";
number = "";
diqu = "";
}
};
class txl {
private:
vector peo;//用vector容器来存储数据
public:
txl() {
person temp;
temp.name = "";
temp.sex = "";
temp.number = "";
temp.diqu = "";
peo.push_back(temp);
//读取文件操作
ifstream du("通讯录数据.txt", ios::in);
if (du.is_open()) {//打开成功的话
string name_, sex_, number_, diqu_;
while (du >> name_ >> sex_ >> number_ >> diqu_) {
person temp_;
temp_.name = name_;
temp_.sex = sex_;
temp_.number = number_;
temp_.diqu = diqu_;
peo.push_back(temp_);//传入容器 peo
} du.close();
}
else cout << "打开失败,不存在该文件" << endl;
}
bool Save() {
ofstream xie("通讯录数据.txt", ios::out );
for (vector::iterator it = peo.begin(); it != peo.end(); it++)
{
xie<< (*it).name << " " << (*it).sex << " " << (*it).number << " " << (*it).diqu << endl;
}
xie.close();
return true;
}
bool Add(string a, string b, string c, string d) {
if (peo.size() == 100) {
return false;
}
person temp;
temp.name = a;
temp.sex = b;
temp.number = c;
temp.diqu = d;
peo.push_back(temp);
/*o.open("通讯录数据.txt", ios::out | ios::app);
o << a <<" " << b << " " << c << " " << d << " " << endl;
o.close();*/
if (Save()) {
cout << "OK";
}
else cout << "False";
return true;
}
bool Delete() {
cout << "输入删除的姓名" << endl;
string z;
cin >> z;
for (vector::iterator it = peo.begin(); it != peo.end(); )
{
if (z == (*it).name) {
//peo.erase(it);
it = peo.erase(it);
cout << "删除成功" << endl;
Save();
return true;
}
else {
++it;
}
}
return false;
}
bool Find() {
string z;
cout << "请输入姓名" << endl;
cin >> z;
for (vector::iterator it = peo.begin(); it != peo.end(); )
{
if (z == (*it).name) {
cout << "查找成功" << endl;
cout << " 姓名: " << (*it).name << endl;
cout << " 性别: " << (*it).sex << endl;
cout << "电话号码: " << (*it).number << endl;
cout<< " 地区:"<< (*it).diqu << endl;
Save();
return true;
}
else {
++it;
}
}
return false;
}
bool Updata() {
cout << "输入修改的姓名" << endl;
string z;
cin >> z;
for (vector::iterator it = peo.begin(); it != peo.end(); )
{
if (z == (*it).name) {
cout << "重新填写该联系人的基本信息:" << endl;
string z, x, c, v;
cin >> z >> x >> c >> v;
(*it).name = z;
(*it).sex = x;
(*it).number = c;
(*it).diqu = v;
cout << "修改成功" << endl;
Save();
return true;
}
else {
++it;
}
}
return false;
}
friend ostream& operator <<(ostream & cout,txl &x);
};
ostream& operator <<(ostream& cout, txl& x) {
cout << "|姓名||性别| |电话号码| |地区|" ;
for (vector::iterator it = x.peo.begin(); it != x.peo.end(); it++)
{
cout << (*it).name << " " << (*it).sex << " " << (*it).number << " " << (*it).diqu<> xuan;
switch (xuan)
{
case 0:cout << A; break;
case 1: {
cout << "请输入联系人的基本信息(姓名 性别 号码 地区):" << endl;
string z, x, c, v;
cin >> z >> x >> c >> v;
if (A.Add(z, x, c, v)) {
cout << "添加成功" << endl;
}
else cout << "添加失败"<< endl << "正在为您返回主菜单" << endl;
Sleep(2000);
break;
}
case 2: {
if (!A.Delete()) {
cout << "无此人" << endl << "正在为您返回主菜单" << endl;
Sleep(2000);
}
else cout << "删除成功" << endl;
break;
}
case 3: {
if (!A.Find()) {
cout << "无此人" << endl << "正在为您返回主菜单" << endl;
Sleep(2000);
}
else {
}
break;
}
case 4: {
if (!A.Updata()) {
cout << "查无此人" << endl<<"正在为您返回主菜单" << endl;
Sleep(2000);
}
else {
}
break;
}
default:
break;
}
}
return 0;
}
(第一版的通讯录系统)
所以第二天我就把我的通讯录系统再重新改了一遍……改完之后,我就尝试调用outtextxy函数来输出容器里面的数据,发现之前那个让人心肌梗塞的红曲线不见了,我就知道,这次没有问题了。
2.2尝试Easyx的界面切换
紧接着,我就自然的完成了通讯录的一系列界面操作——接受数据,输出数据。但是呢,我又遇到一个问题,就是当我输出当前通讯录系统所有联系人之后,我应该怎么编码然它可以返回主菜单呢?简而言之,就是我应该怎么实现在两个界面之间来回切换这个操作。看着这个系统我感受无从下手,所以我重开了一个文档,想要尝试琢磨出这个界面切换的算法。
#include
#include
using namespace std;
void mywindow1() {
initgraph(600, 400);
setbkcolor(WHITE);//白色画面是窗口1
cleardevice();
}
void mywindow2() {
initgraph(600, 400);
setbkcolor(BLACK);//黑色画面是窗口2
cleardevice();
while (1) {
MOUSEMSG mm = GetMouseMsg();
if (mm.uMsg == WM_RBUTTONDOWN && mm.x > 0 && mm.x < 100 ) {//右击 窗口的左侧
return;
}
}
}
void hit1() {
while (1) {
MOUSEMSG m = GetMouseMsg();
if (m.uMsg == WM_LBUTTONDOWN&&m.x>400&&m.x<600) {//左击 窗口的右侧
mywindow2();//内部附带 右键return的功能
mywindow1();//return 之后返回次处,依次执行
}
}
}
int main() {
mywindow1();
hit1();
system("pause");
return 0;
}
自己折腾了将近一个小时,才折腾出这个界面切换的逻辑(一开始想直接网上找,可是怎么搜都一直找不到*-*)。
2.3真正应用于通讯录系统
我把这个界面切换的逻辑应用到通讯录系统里面,终于——我看到我想看到的画面了,一个人通讯录系统,可以完成鼠标和人之间的交互,可以进行页面切换~
话不多说,直接上代码
#include
#include
#include
#include
#include
#include
using namespace std;
bool mystrcmp(char a[], char b[]) {//自定义字符串比较函数————c++的strcmp()函数在增删查改使用时,总是会莫名其妙度错行,迫不得已我自己定义一个
if (strlen(a) == strlen(b)) {
for (int i = 0; i < strlen(a); i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
else return false;
}
class person {//联系人的的基本信息数据
public:
char name[15];
char sex[5];
char number[20];
char diqu[15];
};
class txl {
private:
vector peo;//用vector容器来存储数据
public:
txl() {
//读取文件操作
ifstream du("通讯录数据.txt", ios::in);//只读
if (du.is_open()) {//打开成功的话
person temp_;//定义一个临时的 person 类型来存储文件中一行的信息
while (du >>temp_.name>> temp_.sex >> temp_.number >> temp_.diqu) {//依次读取每一行的四个字符串,直到读到空就停止
peo.push_back(temp_);//传入容器 peo
}
du.close();
}
else {
MessageBox(NULL, "当前通讯录数据为空,请添加联系人", "False", MB_SYSTEMMODAL);
}
}
bool Save() {
ofstream xie("通讯录数据.txt", ios::out);//只写
for (vector::iterator it = peo.begin(); it != peo.end(); it++)//vector 的遍历方法,用迭代器
{
xie << (*it).name << " " << (*it).sex << " " << (*it).number << " " << (*it).diqu << endl;
}
xie.close();
return true;
}
bool Add() {
if (peo.size() == 100) {
return false;
}
char name_[20], sex_[5], number_[20], diqu_[10];
InputBox(name_, 20, "请输入联系人姓名", NULL, NULL, 0, 0, true);// EaxyX的 InputBox()函数 弹出一个窗口来用于获取用户的输入
InputBox(sex_, 5, "请输入联系人性别", NULL, NULL, 0, 0, true);//InputBox(字符串,最大字符长度,标题,窗口标题,编辑框提示串,弹窗的宽度,高度,确定的形式【true,false】)
InputBox(number_, 20, "请输入联系人号码", NULL, NULL, 0, 0, true);
InputBox(diqu_, 20, "请输入联系人地区", NULL, NULL, 0, 0, true);
if (strlen(name_) == 0||strlen(sex_)==0||strlen(number_)==0|| strlen(diqu_) == 0) {//提高程序的健壮性——1.输入空串
MessageBox(NULL, "错误的空输入", "False", MB_SYSTEMMODAL);//EasyX 的MessageBox()函数 弹出一个窗口用于提醒用户
return false; // MessageBox(窗口的变量,正文字符串,标题字符串,弹出的形式【 MB_APPLMODA,MB_SYSTEMMODAL, MB_TASKMODAL】)
}
if (strlen(name_) > 20 || strlen(sex_) > 5 || strlen(number_) >20 || strlen(diqu_) >20) {//2.输入太长
MessageBox(NULL, "输入的字符太长!", "False", MB_SYSTEMMODAL);
return false;
}
person temp_;
strcpy_s(temp_.name, name_);
strcpy_s(temp_.sex, sex_);
strcpy_s(temp_.number, number_);
strcpy_s(temp_.diqu, diqu_);
peo.push_back(temp_);
if (Save()) {
MessageBox(NULL, "添加成功", "OK", MB_SYSTEMMODAL);
}
else {
}
return true;
}
bool Delete() {
char z[20];
InputBox(z, 20, "请输入要删除的联系人姓名",NULL, NULL, 0, 0, false);
for (vector::iterator it = peo.begin(); it != peo.end(); )
{
if (mystrcmp(z, (*it).name) == 1) {
it = peo.erase(it);
MessageBox(NULL, "删除成功", "OK", MB_TASKMODAL);
Save();
return true;
}
else {
++it;
}
}
MessageBox(NULL, "无此联系人", "错误!", MB_SYSTEMMODAL);
return false;
}
bool Find() {
char z[20];
InputBox(z, 20, "请输入要查找的联系人姓名", NULL, NULL, 0, 0, false);
for (vector::iterator it = peo.begin(); it != peo.end(); )
{
if (mystrcmp(z, (*it).name) == 1) {
MessageBox(NULL, (*it).sex, "性别为", MB_SYSTEMMODAL);
MessageBox(NULL, (*it).number, "号码为", MB_SYSTEMMODAL);
MessageBox(NULL, (*it).diqu, "地区为", MB_SYSTEMMODAL);
return true;
}
else {
++it;
}
}
MessageBox(NULL, "无此联系人","错误!" , MB_SYSTEMMODAL);
return false;
}
bool Updata() {
char z[20];
InputBox(z, 20, "请输入要修改的联系人姓名", NULL, NULL, 0, 0, false);
for (vector::iterator it = peo.begin(); it != peo.end(); )
{
if (mystrcmp(z, (*it).name) == 1) {
char name_[20], sex_[5], number_[20], diqu_[10];
InputBox(name_, 20, "请重新输入联系人姓名", NULL, NULL, 0, 0, true);
InputBox(sex_, 20, "请重新输入联系人性别", NULL, NULL, 0, 0, true);
InputBox(number_, 20, "请重新输入联系人号码", NULL, NULL, 0, 0, true);
InputBox(diqu_, 20, "请重新输入联系人地区", NULL, NULL, 0, 0, true);
if (strlen(name_) == 0 || strlen(sex_) == 0 || strlen(number_) == 0 || strlen(diqu_) == 0) {
MessageBox(NULL, "错误的空输入", "False", MB_SYSTEMMODAL);
return false;
}
person temp_;
strcpy_s((*it).name, name_);
strcpy_s((*it).sex, sex_);
strcpy_s((*it).number, number_);
strcpy_s((*it).diqu, diqu_);
MessageBox(NULL, "修改成功", "OK", MB_SYSTEMMODAL);
Save();
return true;
}
else {
++it;
}
}
MessageBox(NULL, "无此联系人", "错误!", MB_SYSTEMMODAL);
return false;
}
friend ostream& operator <<(ostream& cout, txl& x);
};
void mymainmeau() {
//closegraph();
initgraph(400, 650);//画一个宽400,高650的窗口
IMAGE photo;//定义图片变量
loadimage(&photo, "通讯录主界面2.jpg", 400, 650);//导入图片
putimage(0, 0, &photo);//把图片贴出来
}
void Returnmain() {//定义一个可以从Show界面跳回主界面的函数
while (1) {
MOUSEMSG mm = GetMouseMsg();
if (mm.uMsg == WM_LBUTTONDOWN && mm.y>595 && mm.y < 650&&mm.x>170&&mm.x<250){
return;
}
}
}
ostream& operator <<(ostream& cout, txl& x) {//重载<<运算符
//initgraph(400, 650);
IMAGE photo;
loadimage(&photo, "展示联系人.jpg", 400, 650);
putimage(0, 0, &photo);
//cleardevice();
settextstyle(20, 0, "宋体");//设置文字的大小,形式,颜色,透明
settextcolor(BLACK);
setbkmode(TRANSPARENT);
outtextxy(8, 8, "|姓名||性别| |电话号码| |地区| ");//输出文字到窗口 outtextxy(坐标,字符串)
int i = 10, j = 50;
settextcolor(RGB(0,119,220));
settextstyle(20, 0, "楷体");
for (vector::iterator it = x.peo.begin(); it != x.peo.end(); it++)
{
cout << (*it).name << " " << (*it).sex << " " << (*it).number << " " << (*it).diqu << endl;
outtextxy(i , j, (*it).name);
outtextxy(i + 70, j, (*it).sex);
outtextxy(i + 130, j,(*it).number );
outtextxy(i + 250, j, (*it).diqu);
j = j + 30;
}
return cout;
}
int main() {
txl A;
mymainmeau();
while (1) {//进入无限循环,不然这个界面点击一次就失效了
MOUSEMSG m = GetMouseMsg();//定义鼠标变量
switch (m.uMsg) {//分配鼠标的消息————比如 鼠标移动,左击,右击,左双击,右双击等等
case WM_LBUTTONDOWN: {//左击鼠标操作
if (m.x > 0 && m.x < 100 && m.y>0 && m.y < 100) {// SHOW 的坐标位置
cout << A;
Returnmain();
closegraph();
mymainmeau();
break;
}
if (m.x > 325 && m.x < 400 && m.y>100 && m.y < 175) {// + 的位置
A.Add();
break;
}
if (m.x > 325 && m.x < 400 && m.y>230 && m.y < 290) {// - 的位置
A.Delete();
break;
}
if (m.x > 325 && m.x < 400 && m.y>330 && m.y < 400) {// chazhao 的位置
A.Find();
break;
}
if (m.x > 325 && m.x < 400 && m.y>450 && m.y < 515) {// xiugai 的位置
A.Updata();
break;
}
}
}
}
system("pause");
return 0;
}
(需要添加这两张图片到运行代码的文件夹中——“并且命名为展示联系人和通讯录主界面2”)
2.4优化界面效果
这个通讯录系统基本上已经完成了我起初想要的效果了。但是嘛,在操作起来的时候,我又想到如果可以设置一个鼠标移动到按钮上,显示出这个按钮的功能的文字的话,那岂不是体验感更佳。有了这个想法,我又突然动力满满。
于是就有以下这个功能了
case WM_MOUSEMOVE: {//鼠标移动操作 制造鼠标停留在按钮上会有提示文字的效果,鼠标移开,文字效果消失
settextstyle(15, 0, "宋体");
setbkmode(TRANSPARENT);
if (m.x > 0 && m.x < 100 && m.y>0 && m.y < 100) {// SHOW 的位置
settextcolor(BLACK);
outtextxy(100, 50, "显示所有联系人");
}
else {//如果 离开 SHOW 的位置
settextcolor(WHITE);
outtextxy(100, 50, "显示所有联系人");
}
if (m.x > 325 && m.x < 400 && m.y>100 && m.y < 175) {// + 的位置
settextcolor(BLACK);
outtextxy(240, 150, "添加联系人");
}
else {
settextcolor(WHITE);
outtextxy(240,150, "添加联系人");
}
if (m.x > 325 && m.x < 400 && m.y>230 && m.y < 290) {// - 的位置
settextcolor(BLACK);
outtextxy(240, 260, "删除联系人");
}
else {
settextcolor(WHITE);
outtextxy(240, 260, "删除联系人");
}
if (m.x > 325 && m.x < 400 && m.y>330 && m.y < 400) {// chazhao 的位置
settextcolor(BLACK);
outtextxy(240, 368, "查找联系人");
}
else {
settextcolor(WHITE);
outtextxy(240,368, "查找联系人");
}
if (m.x > 325 && m.x < 400 && m.y>450 && m.y < 515) {// xiugai 的位置
settextcolor(BLACK);
outtextxy(240, 480, "修改联系人");
}
else {
settextcolor(WHITE);
outtextxy(240,480, "修改联系人");
}
break;
}
说白了就是打印出于背景色相同的颜色的字符串,制造种
看不见的效果,当鼠标移动到按钮上面,就把字体变成黑
三、总结
这次的图形化编程经历(前前后后大概一个星期)让我学习到非常多的在学校学不到的知识,也一定程度的激发了我的兴趣(谁喜欢一直对着黑窗口编程呢~)。然后我也深刻的了解到了日常使用电脑软件这些鼠标操作背后的逻辑,当然了,学完之后也会感觉自己不满于一直Easyx库里进行编程,应该也会深入其他界面编程,希望这次的学习带来的后劲可以持续的更久。
(初次写博客,如果这篇文章里面有什么错漏的地方,还望各位大佬多多指点~~)