使用OpenGL的简单语句来制作一个简单的计算器-加减乘除

2 篇文章 0 订阅

OpenGL采用glut,虽然版本太老了,但是容易入门。计算器很粗糙,但已经可以实现比较小的数字加减乘除。

IDE使用VS2019.

效果如下:

 

只需要在调用main()中调用calculator()就可以了,头文件和源文件在下面:

#include"calculator.h"

int main(int argc,char * argv[])
{
    calculator(argc,argv);
    return 0;
}

头文件<calculator.h>如下:

#pragma once		//告诉编译器仅包含一次本头文件
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")//隐藏控制台
#include<gl\glut.h>
#include<iostream>
#include<string>
#include<Windows.h>
#include<math.h>

//主函数
int calculator(int argc, char* argv[]);
//初始化
void init();
//初始窗口绘制
void display1();
//窗口控制函数
void winctrl(int x, int y);
//打印窗口信息
void print(GLfloat x, GLfloat y);
//由glRasterPos2f(GLfloat x,GLfloat Y);指定位置,然后drawString绘制文字
void drawString(const char* str);
//设置字体和大小
void selectFont(int size, int charset, const char* face);
//窗口大小发生改变时实时修改全局变量
void windowChange();
//在指定位置绘制文本串
void printTextBox(double x, double y, std::string str);
//绘制按键
void key(GLfloat x, GLfloat y, GLfloat width, GLfloat height, const GLfloat* color, std::string str);
//键盘函数
void keyboard(unsigned char key, int x, int y);
//鼠标函数
void mouse(int button, int state, int x, int y);
//将string类型字符串转换为整型数字,mirror:=0说明字符从左到右排列,否则从右到左排列,str:待转换字符串
double str_to_int(const char mirror, std::string str);
//将str中的等式运算出结果
double opernum(std::string str);
//处理按键输入的字符
void processInput(char keyValue);
//令指定位置的按键变色
void keyDown(int x, int y);
//恢复所有按键
void keyUp(int a, int b);

//保护宏,如果没有#pragma once	,则需要启用下面的宏
#ifndef CALCULATOR_H
#define CALCULATOR_H

#define WIN_COLOR 0.8,0.8,0.8,0.8	//背景颜色
#define MAXNUMBER 9			//每个数字字符串最大长度

//参数:从左下角计算,POS(0,0)即表示左下角第一个按键,指定按键绘制的位置
#define POS(x,y) (((x)*(keyWidth+interval)+interval)*2/winWidth-1.0),(((y)*(keyHeight+interval)+interval)*2/winHeight-1.0)

#endif

 源文件如下:

#include"calculator.h"

const GLfloat winWidth = 500, winHeight = 600;//初始化窗口大小
GLfloat ww = winWidth, wh = winHeight;//ww储存实时窗口的宽度,wh储存高度
const GLfloat keyWidth = 120, keyHeight = 108;//初始化按键
GLfloat keyW = 120, keyH = 108;//实时按键大小
const GLfloat keyposw = (GLfloat)(keyW * 2.0 / ww), keyposh = (GLfloat)(keyH * 2.0 / wh);//按键占比
GLfloat _keyposw = keyposw, _keyposh = keyposh;//实时按键占比大小

const GLfloat textBoxW=winWidth*0.92,textBoxH=120;//初始显示区位置
GLfloat tbw = textBoxW, tbh=textBoxH;//实时显示区
double tbwp =tbw/ww,tbhp= 0.6;

const GLfloat interval = 4.0;//按键之间的间隔
GLfloat val = interval;//储存实时间隔

const GLfloat KEY_COLOR[] = { 1,1,1 };//按键颜色
const GLfloat KEY_DOWN_COLOR[] = { 0.75,0.75,0.75 };//按键按下颜色
static char mouseIsDown = 0;//鼠标按下,置1,弹起后置0

static int mx, my;//保存按下的键
static double numberNum = 0;//储存字符串字符数
static int divNum = 0;
static int addNum = 0;
static int subNum = 0;
static int mulNum = 0;

const double CInterval[5] = { 0.1164,0.12,0.068,0.104,0.056 };//初始化默认占比
static double charInter=0.1164;//数字占比
static double addInter = 0.12;//"+"
static double subInter = 0.068;//"-"
static double mulInter = 0.104;//"*"
static double divInter = 0.056;//"/"

static GLfloat printx = tbwp, printy = tbhp;//时刻指向字符串加载的位置

std::string formula;//实时储存显示在界面上的算式字符串

const std::string keystr[4][4] = {//显示数组
	{"C","0","=","/"},
	{"1","2","3","x"},
	{"4","5","6","-"},
	{"7","8","9","+"}
};

int mouseClickBuf[4][4][2];//根据窗口变化实时储存按键点击范围,

using namespace std;

int calculator(int argc, char* argv[])
{
	glutInit(&argc, argv);//初始化
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);//单缓存,reg颜色,深度缓冲区
	glutInitWindowPosition(100, 100);//窗口在桌面的位置
	glutInitWindowSize(winWidth, winHeight);//窗口大小
	glutCreateWindow("calculator");//窗口标题

	init();
	glutDisplayFunc(display1);//窗口内容绘制
	glutReshapeFunc(winctrl);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	print(1000, 1000);
	glutMainLoop();
	return 0;
}

void selectFont(int size, int charset, const char* face)
{
	//createfonta创建字体
	HFONT hFont = CreateFontA(size, 0, 0, 0, FW_MEDIUM, 0, 0, 0,
		charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, face);
	//设置字体对象
	HFONT hOldFont = (HFONT)SelectObject(wglGetCurrentDC(), hFont);
	//删除对象
	DeleteObject(hOldFont);
}

void winctrl(int x, int y)
{
	glViewport(0, 0, x, y);
	ww = (GLfloat)x;
	wh = (GLfloat)y;
	windowChange();
}

//初始化窗口界面
void init()
{
	glClearColor(WIN_COLOR);//将窗口背景设置为白色,透明度80%
}

#define STROFFX 0.18
#define STROFFY 0.14
//绘制按键
void key(GLfloat x, GLfloat y, GLfloat width, GLfloat height, const GLfloat * color, std::string str)
{
	//绘制按键
	glColor3fv(color);
	glBegin(GL_POLYGON);
	glVertex2f(x, y);
	glVertex2f(width + x, y);
	glVertex2f(width + x, height + y);
	glVertex2f(x, height + y);
	glEnd();

	//绘制文字
	glColor3f(0, 0, 0);
	//设定文字的显示位置
	glRasterPos2f(x + STROFFX, y + STROFFY);
	//打印文字
	drawString(str.c_str());
	glutSwapBuffers();
}

void display1()
{
	glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲区
	glClearColor(WIN_COLOR);

	selectFont(60, ANSI_CHARSET, "");
	int i, j;
	for (i = 0; i < 4; ++i) {
		for (j = 0; j < 4; ++j) {
			key(POS(j, i), keyposw, keyposh, KEY_COLOR, keystr[i][j]);
		}
	}
	glRasterPos2f(printx, printy);
	drawString(formula.c_str());
	glFlush();
}

void print(GLfloat x, GLfloat y)
{
	cout << "=====================================" << endl;
	cout << "x=" << x << endl;
	cout << "y=" << y << endl;
	cout << "按键占比大小:" << _keyposw << "," << _keyposh << endl;
	cout << "按键大小" << keyW << "," << keyH << endl;
	cout << "窗口大小:" << ww << "," << wh << endl;
	cout << "鼠标状态:" << mouseIsDown << endl;
	cout << "间隔:" << val << endl;
	cout << "字符占比:" << charInter << endl;
	cout << "=====================================" << endl;
}

#define MAX_CHAR 128	//128个ascii字符

void drawString(const char* str)
{
	static int isFirstCall = 1;
	static GLuint lists;

	if (isFirstCall) { // 如果是第一次调用,执行初始化
						 // 为每一个ASCII字符产生一个显示列表
		isFirstCall = 0;

		// 申请MAX_CHAR个连续的显示列表编号
		lists = glGenLists(MAX_CHAR);

		// 把每个字符的绘制命令都装到对应的显示列表中
		wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);
	}
	// 调用每个字符对应的显示列表,绘制每个字符
	for (; *str != '\0'; ++str)
		glCallList(lists + *str);
}
//键盘处理
void keyboard(unsigned char key, int x, int y)
{
	int i, j;
	for (i = 0; i < 4; ++i) {
		for (j = 0; j < 4; ++j) {
			if (keystr[i][j][0] == key) {
				keyDown(i, j);
				mx = i; my = j;
				break;
			}
			else if (key == 8) {//退格键
				mx = 0; my = 0;
				break;
			}
			else if (key == 0x0d) {//回车键
				mx = 0; my = 2;
				break;
			}
		}
	}
	//读取key并处理
	processInput(key);
	//延时,展示效果
	Sleep(100);
	//还原窗口
	keyUp(mx, my);
}

//窗口大小改变,ww和wh值将会被修改,这个函数负责实现按键范围的取值工作,同时修改按键大小keyW,keyH,间隔interval
void windowChange()
{
	//从ww,wh读取数据,首先修改interval
	GLfloat tw = (ww) / winWidth,th=(wh)/winHeight;//获得增加倍数

	cout << "增加倍数:" << tw <<","<<th<< endl;
	val = (GLfloat)(int)(interval * (sqrt(tw*tw+th*th)));
	cout << "修正间隔:interval=" << val << endl;
	//修改按键大小
	keyW = (GLfloat)(int)(keyWidth * tw);
	keyH = (GLfloat)(int)(keyHeight * th);
	cout << "修改按键宽:" << keyW << ",修改按键高:" << keyH << endl;
	_keyposw = (GLfloat)(keyW * 2 / ww);
	_keyposh = (GLfloat)(keyH * 2 / wh);
	//修改文本框大小
	tbw = (GLfloat)(int)(tbw * tw);
	tbh = (GLfloat)(int)(tbw* th);
	//修改字符占比
	double s = 0.1164 * 250;
	charInter = (double)CInterval[0] * winWidth / ww;
	addInter = (double)CInterval[1] * winWidth / ww;
	subInter = (double)CInterval[2] * winWidth / ww;
	mulInter = (double)CInterval[3] * winWidth / ww;
	divInter = (double)CInterval[4] * winWidth / ww;
	
	//填充按键点击数组,数组共有16个坐标,分别对应每个按键左上角的顶点
	int i, j;
	for (i = 0; i < 4; ++i) {
		for (j = 0; j < 4; ++j) {
			mouseClickBuf[i][j][0] = val + j * (val + keyW);
			mouseClickBuf[i][j][1] = wh - (i + 1) * (val + keyH);
			cout << "[" << i << "][" << j << "]=" << mouseClickBuf[i][j][0] << "," << mouseClickBuf[i][j][1] << endl;
		}
	}
	print(ww, wh);
	keyUp(mx,my);
}

//button:三个按键=GLUT_LEFT_BUTTON,GLUT_MIDDLE_BUTTON,GLUT_RIGHT_BUTTON
//state:按下或弹起=GLUT_DOWN,GLUT_UP
//x,y:坐标,原点是窗口左上角
void mouse(int button, int state, int x, int y)
{
	int i = 0, j = 0, a = 0, b = 0;
	char keyValue = 0;//保存按下的键值,与mouseIsDown一起使用
	if (button == GLUT_LEFT_BUTTON) {//左键按下
		std::cout << "鼠标左键按下" << endl;
		std::cout << "mousex=" << x << ",mousey" << y << endl;
		if (state == GLUT_DOWN) {//鼠标左键按下,令按键变色,调用key
			//判断按键,根据mouseClickBuf查找合适的位置
			for (i = 0; i < 4; ++i)
				for (j = 0; j < 4; ++j) {
					//找到合适位置
					if (x > mouseClickBuf[i][j][0] && y > mouseClickBuf[i][j][1] && x < mouseClickBuf[i][j][0] + keyW && y < mouseClickBuf[i][j][1] + keyH) {
						keyValue = keystr[i][j][0];
						keyDown(i, j);
						break;
					}
				}

		}
		else if (state == GLUT_UP) {
			if (mouseIsDown == 1) {
				keyUp(my,my);
				//print(mx, my);
				std::cout << "鼠标弹起" << std::endl;
				mouseIsDown = 0;
			}
		}
	}
	//按下键的位置存入mx,my
	if (mouseIsDown == 1) {
		processInput(keyValue);
	}
}

void printTextBox(double x,double y,std::string str)
{
	//打印字符串到文本框
	int a = 0, b = 0;
	printx = x; printy = y;
	glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲区
	glClearColor(WIN_COLOR);
	selectFont(60, ANSI_CHARSET, "");
	glRasterPos2f(x,y);
	cout << "文字绘制位置:" <<x<<","<<y<< endl;
	if(str.length()>0)drawString(str.c_str());
	if (mouseIsDown == 1 && mx < 4 && my < 4) {
		for (a = 0; a < 4; ++a) {
			for (b = 0; b < 4; ++b) {
				if (a == mx && b == my) {//令指定位置变色
					key(POS(b, a), _keyposw, _keyposh, KEY_DOWN_COLOR, keystr[mx][my]);
					continue;
				}
				key(POS(b, a), keyposw, keyposh, KEY_COLOR, keystr[a][b]);
			}
		}
		glFlush();
	}
	
}


//运算出结果
double opernum(string str)
{
	int temp = 0, i = 0, j = 0;
	string a="";
	//首先转换出所有数字
	double num[50] = { 0 };
	int firstpos[20] = { 0 };//储存第一优先级符号在pos的位置
	int firstnum = 0;
	int secondpos[20]={0};//储存第二优先级符号早pos的位置
	int secondnum = 0;
	char minus = 0,recip=0;//判断负数

	for (i = 0; i < str.length()+1;++i) {
		if (str[i] >= 0x30 && str[i] <= 0x39) {
			a.push_back(str[i]);
		}
		else {
			if (minus > 0) {
				num[j] = -str_to_int(0, a); minus = 0;
			}
			else if (recip > 0) {
				num[j] = (double)1 / str_to_int(0, a); recip = 0;
			}
			else {
				num[j] = str_to_int(0, a);
			}
			a.clear();
			switch (str[i]) {

			case '-': //之后的一个数为负数
				minus = 1;
			case '+':
				secondpos[secondnum++] = j; break;
			case '/':
				recip = 1;
			case 'x':
				firstpos[firstnum++] = j; break;
			}
			j++;
		}
	}
	for (i = 0; i < firstnum; ++i) {
		num[firstpos[i]] *= num[firstpos[i] + 1];
		//将num[firstpos[i] + 1]向前平移一段
		for (temp=firstpos[i]+1; temp < j; ++temp) {
			num[temp] = num[temp + 1];
		}
		--j; firstpos[i + 1]= firstpos[i + 1]-1-i;
	}
	for (i = 0; i < secondnum; ++i) {
		num[0] += num[i+1];
	}
	cout << "计算结果:"<<num[0] << endl;
	return num[0];
}
//将string类型字符串转换为整型数字,mirror:=0说明字符从左到右排列,否则从右到左排列,str:待转换字符串
double str_to_int(const char mirror,std::string str)
{
	double num = 0;
	int i,t=0;
	for (i = 0; i < str.length(); ++i) {
		t = (mirror > 0) ? (str.length() - 1 - i) : i;
		num += (str[t]-'0')*(pow(10, str.length()-1-i));
	}
	return num;
}

//处理输入的字符
void processInput(char keyValue)
{
#define TEXTX tbwp- divInter * divNum-addInter * addNum-subInter * subNum-mulInter*mulNum-(++numberNum*charInter)
#define TEXTY tbhp
#define ADDOPERX tbwp- divInter * divNum-addInter * (++addNum)-subInter * subNum-mulInter*mulNum-(numberNum*charInter)
#define SUBOPERX tbwp- divInter * divNum-addInter * addNum-subInter * (++subNum)-mulInter*mulNum-(numberNum*charInter)
#define MULOPERX tbwp- divInter * divNum-addInter * addNum-subInter * subNum-mulInter*(++mulNum)-(numberNum*charInter)
#define DIVOPERX tbwp- divInter * (++divNum)-addInter * addNum-subInter * subNum-mulInter*mulNum-(numberNum*charInter)

	//cout << "divNum:" << divNum << ",addNum:" << addNum << ",subNum:" << subNum << ",mulNum:"<< mulNum<<",numberNum:"<< numberNum << endl;
	int strsize = formula.length() - 1;
	strsize = (strsize > 0) ? strsize : 0;
	switch (keyValue) {
	case '1': {	
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '2': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '3': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '4': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '5': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '6': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '7': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '8': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '9': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '0': {
		formula.push_back(keyValue);
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	case '+': {
		if (strsize+1 <= 0)break;
		if (formula[strsize] < 0x30 || formula[strsize]>0x39) {
			switch (formula[strsize]) {
			case '/':divNum--; break;
			case '-':subNum--; break;
			case 'x':mulNum--; break;
			case '+':return;
			}
			formula[strsize] = '+';
		}
		else {
			formula.push_back(keyValue);
		}
		mx = 3; my = 3;
		printTextBox(ADDOPERX, TEXTY, formula);
		break;
	}
	case '-': {
		if (strsize + 1 <= 0)break;
		if (formula[strsize] < 0x30 || formula[strsize]>0x39) {
			switch (formula[strsize]) {
			case '/':divNum--; break;
			case '-':return;
			case 'x':mulNum--; break;
			case '+':addNum--; break;
			}
			formula[strsize] = '-';
		}
		else {
			formula.push_back(keyValue);
		}
		mx = 2; my = 3;
		printTextBox(SUBOPERX, TEXTY, formula);
		break;
	}
	case 42://'*'键值
	case 'X':
	case 'x': {
		if (strsize+1 <= 0)break;
		if (formula[strsize] < 0x30 || formula[strsize]>0x39) {
			switch (formula[strsize]) {
			case '/':divNum--; break;
			case '-':subNum--; break;
			case 'x':return;
			case '+':addNum--; break;
			}
			formula[strsize] = 'x';
		}
		else {
			formula.push_back('x');
		}
		mx = 1; my = 3;
		printTextBox(MULOPERX, TEXTY, formula);
		break;
	}
	case '/': {
		if (strsize + 1 <= 0)break;
		if (formula[strsize] < 0x30 || formula[strsize]>0x39) {
			switch (formula[strsize]) {
			case '/':return;
			case '-':subNum--; break;
			case 'x':mulNum--; break;
			case '+':addNum--; break;
			}
			formula[strsize] = '/';
		}
		else {
			formula.push_back(keyValue);
		}
		mx = 0; my = 3;
		printTextBox(DIVOPERX, TEXTY, formula);
		break;
	}
	case 0x0d:
	case '=': {//处理等号,对算式处理,生成结果
		double result = opernum(formula);
		if (result > pow(10, MAXNUMBER * 2)) {
			result = 0;
		}
		char resultstr[MAXNUMBER*2] = { 0 };
		//将结果打印在窗口上
		_itoa_s((int)result, resultstr, 10);
		resultstr[MAXNUMBER*2 - 1] = '\0';
		//打印
		formula = resultstr;
		numberNum = formula.length()-1;
		divNum = 0;
		addNum = 0;
		subNum = 0;
		mulNum = 0;
		printTextBox(TEXTX, TEXTY, resultstr);
		break;
	}
	case 8:
	case 'c':
	case 'C': {
		formula.clear();
		numberNum = 0;
		divNum = 0;
		addNum = 0;
		subNum = 0;
		mulNum = 0;
		printTextBox(TEXTX, TEXTY, formula);
		break;
	}
	}
}

//令指定位置的按键变色
void keyDown(int x,int y)
{
	if (x > 3 || y > 3)return;
	int i, j;
	mx = x; my = y;
	//glClear(GL_COLOR_BUFFER_BIT);//清空颜色缓冲区
	glClearColor(WIN_COLOR);
	selectFont(60, ANSI_CHARSET, "");
	for (i = 0; i < 4; ++i) {
		for (j = 0; j < 4; ++j) {
			if (i == x && j == y) {
				key(POS(j, i), _keyposw, _keyposh, KEY_DOWN_COLOR, keystr[x][y]);
				continue;
			}
			key(POS(j, i), keyposw, keyposh, KEY_COLOR, keystr[i][j]);
		}
	}
	glFlush();
	//print(mx, my);
	mouseIsDown = 1;//置1,说明按下
}

//恢复所有按键
void keyUp(int a,int b)
{
	glClearColor(WIN_COLOR);
	selectFont(60, ANSI_CHARSET, "");
	//全部重绘,变成原始状态
	if (mx < 4 && my < 4) {
		for (a = 0; a < 4; ++a) {
			for (b = 0; b < 4; ++b) {
				key(POS(b, a), keyposw, keyposh, KEY_COLOR, keystr[a][b]);
			}
		}
		glFlush();
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值