QT项目五:简易计算器

1,简介

这是用QT帮朋友做的一个定制化的计算器程序,他们对于计算器使用频率非常高,用系统自带的或网上找的软件也有很多地方不是很满意。

提出了下列定制需求:

(1)支持显示输入的历史记录,并拷贝复制。
(2)保持置顶。
(3)支持热键唤醒,并自动激活输入焦点。

另外我加入了一项功能(此功能朋友大赞!):

(4)支持常规四则表达式求值。

2,效果

3,思路与实现

(1)设计为采用全键盘操作,保证操作效率。因此没有图形按键,建议使用小键盘上按键基本可以完成所有计算操作。

(2) 输完表达式按回车,即计算出结果,并把当前完整表达式与结果记录在下方TextEdit内,供查看和复制操作。

(3)置顶: setWindowFlags(Qt::WindowStaysOnTopHint) 

(4)热键:installNativeEventFilter ,MainWindow::nativeEventFilter中处理windows WM_HOTKEY热键消息

(5)唤醒、激活:setWindowState(Qt::WindowActive);activateWindow();  setFocus();

主要代码:

main.cpp

/************************************************************************/
/*           
工程名: Calc.exe
说明:   QT实战项目之五--计算器
作者:   逆风微光
qq:     787701109
blog:   http://blog.csdn.net/dpsying

特性:
1,支持常规四则表达式求值。
2,支持显示输入的历史记录,并拷贝复制。
3,保持置顶。
4,支持热键唤醒。
*/
/************************************************************************/


#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow w;
	a.installNativeEventFilter(&w);
    w.show();

    return a.exec();
}

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QStack>
#include <QAbstractNativeEventFilter>
#include <windows.h>

namespace Ui {
class MainWindow;
}

#define MY_HOT_KEY 4009

class MainWindow : public QMainWindow,public QAbstractNativeEventFilter
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
	~MainWindow();

	QString AnlysisExpr(QString sInputExpr);
	QString GetWholeWord(const QString& sInput, const int nStartPos, int& nOutEndPos);

	virtual bool MainWindow::nativeEventFilter( const QByteArray &eventType, void *message, long *result );

private:
	QString AddZeroBefore(QString str);			//如果表达式以-开头,就在最前面加个0
	void DeleteZero(QString& s);				//去掉小数点后无效的0

private slots:
	void on_lineEdit_returnPressed();
	
private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H


mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QStack>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

	ui->lineEdit->setFont(QFont( "Timers" , 28 ,  QFont::Bold) );
	ui->textEdit->setFont(QFont( "Timers" , 14 ,  QFont::Normal) );

	setWindowFlags(Qt::WindowStaysOnTopHint);

	//注册热键 CTRL+X
	//热键功能要求:当在其他程序时,直接通过热键唤醒本计算器并自动显示焦点在输入框
	::RegisterHotKey((HWND)winId(),MY_HOT_KEY,MOD_ALT,'X');

}

MainWindow::~MainWindow()
{
	//反注册热键
	::UnregisterHotKey((HWND)winId(),MY_HOT_KEY);
    delete ui;
}

QString MainWindow::GetWholeWord(const QString& sInput, const int nStartPos, int& nOutEndPos)  
{  
	QString sWord;  
	nOutEndPos = nStartPos;  
	for (int i = nStartPos; i < sInput.length(); i++)  
	{  
		QChar ch = sInput.at(i);  
		if ((ch >= '0' && ch <= '9')  
			|| (ch >= 'a' && ch <= 'z')  
			|| (ch >= 'A' && ch <= 'Z')  
			|| (ch == '.'))  
		{  
			sWord += ch;  
			nOutEndPos = i;  
		}  
		else  
		{  
			break;  
		}  
	}  
	return sWord;  
};  

QString MainWindow::AnlysisExpr(QString sInputExpr)  
{  
	// 获取逆波兰表达式    
	QStack<QString> stackMain;  
	QStack<QString> stackOper;  
	for (int i = 0; i < sInputExpr.length(); i++)  
	{  
		QChar ch = sInputExpr[i];  
		if (ch == ' ')  
		{ // 空格    
			continue;  
		}  
		else if (ch == '(')  
		{ // 左括号    
			stackOper.push(QString(ch));  
		}  
		else if (ch == ')')  
		{ // 右括号    
			while (!stackOper.empty())  
			{  
				QString chPop = stackOper.pop();  
				if (chPop != "(")  
				{  
					stackMain.push(chPop);  
				}  
				else  
				{  
					break;  
				}  
			}  
		}  
		else if ((ch >= '0' && ch <= '9')  
			|| (ch >= 'a' && ch <= 'z')  
			|| (ch >= 'A' && ch <= 'Z')  
			|| (ch == '.'))  
		{ // 数字或字符    
			stackMain.push(GetWholeWord(sInputExpr, i, i));  
		}  
		else if (ch == '+' || ch == '-' || ch == '*' || ch == '/')  
		{ // 操作符    
			if (stackOper.empty())  
			{ //     
				stackOper.push(QString(ch));  
			}  
			else  
			{ // 出栈直到遇到优先小于等于的    
				QString chTemp = stackOper.top();  
				while (((ch == '+' || ch == '-') && (chTemp == "*" || chTemp == "/"))  
					|| ((ch == '+' || ch == '-') && (chTemp == "+" || chTemp == "-"))  
					|| ((ch == '*' || ch == '/') && (chTemp == "*" || chTemp == "/")))  
				{  
					stackMain.push(stackOper.pop());  
					if (stackOper.size() > 0)  
						chTemp = stackOper.top();  
					else  
						break;  
				}  
				stackOper.push(QString(ch));  
			}  
		}  
	}  
	while (!stackOper.empty())  
	{  
		QString chPop = stackOper.pop();  
		stackMain.push(chPop);  
	}  

	QString sText;  
	QStack<QString> stackCalc;  
	while (!stackMain.empty())  
	{  
		QString chPop = stackMain.pop();  
		stackCalc.push(chPop);  
		sText.push_back(chPop);  
	}  

	// 表达式还原计算  
	QString sNewExpr;  
	double dResultValue = sInputExpr.toDouble();  
	{  
		QString sOper;  

		// 计算逆波兰表达式:重新生成表达式    
		QStack<QString> stackTemp;  
		QStack<double> stackTempValue;  
		while (!stackCalc.empty())  
		{  
			QString sPop = stackCalc.pop();  
			if (sPop.length() == 0)  
				continue;  

			QChar ch = sPop.at(0);  
			if (ch == '+' || ch == '-' || ch == '*' || ch == '/')  
			{  
				if (stackTemp.size() >= 2)  
				{  
					sOper = sPop;  
					QString s1 = stackTemp.pop();  
					QString s2 = stackTemp.pop();  
					if ((sOper == "*" || sOper == "/") && (s2.contains('+') || s2.contains('-')))  
					{  
						s2 = "(" + s2 + ")";  
					}  
					sNewExpr = QString("%1 %2 %3").arg(s2).arg(sOper).arg(s1);  
					stackTemp.push(sNewExpr);  

					double d1 = stackTempValue.pop();  
					double d2 = stackTempValue.pop();  
					switch (ch.cell())  
					{  
					case '+':dResultValue = d2 + d1; break;  
					case '-':dResultValue = d2 - d1; break;  
					case '*':dResultValue = d2 * d1; break;  
					case '/':dResultValue = d2 / d1; break;  
					}  
					stackTempValue.push(dResultValue);  
				}  
			}  
			else  
			{  
				stackTemp.push(sPop);  
				stackTempValue.push(sPop.toDouble());  
			}  
		}  
	}  
	//return QString("%1").arg(dResultValue);		//使用QString的%1方式用浮点数来初始化时,会出现科学计数法的表示结果
	return QString::number(dResultValue,'f',10);	//最多保留小数点后10位
}  

//如果表达式以-开头,就在最前面加个0,防止负数开头的情况下,他认为是减号没有左操作数而忽略掉了
QString MainWindow::AddZeroBefore(QString str)
{
	QString s = str;
	if (s.isEmpty())
	{
		return s;
	}
	if (s[0] == '-')
	{
		s = QString("0") + s;
	}
	return s;
}

//去掉小数点后无效的0
void MainWindow::DeleteZero(QString& s)
{
	int nPos = s.indexOf(".");
	if (nPos != -1)
	{
		int nNum = 0;
		for (int i=s.length()-1; i>=nPos; i--)
		{
			if (s[i] == '0' || s[i] == '.')
			{
				nNum++;
			}
			else
			{
				break;
			}
		}
		if (nNum>0)
		{
			s = s.left(s.length() - nNum);
		}
	}
}

void MainWindow::on_lineEdit_returnPressed()
{
	QString strInput = ui->lineEdit->text();
	if (strInput.isEmpty())
	{
		return ;
	}
	//如果最开始是减号,就在减号前加一个0,因为这个算法会忽略一开始就是运算符的情况,认为没有左操作数 比如导致-1+3=4忽略-号,加上0改成0-1+3=2就正常
	QString strNewInput = AddZeroBefore(strInput);
	QString strResult = AnlysisExpr(strNewInput);
	if (strResult.isEmpty())
	{
		return;
	}
	//去掉小数点后无效的0
	DeleteZero(strResult);

	QString strRecord = strInput;
	if (strRecord.right(1) != "=")
	{
		strRecord += "=";
	}
	strRecord += strResult;

	ui->lineEdit->setText(strResult);
	ui->textEdit->setText(strRecord + "\n" + ui->textEdit->toPlainText());
}

//响应热键
bool MainWindow::nativeEventFilter( const QByteArray &eventType, void *message, long *result )
{
	if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG")
	{
		MSG * pMsg = reinterpret_cast<MSG *>(message);

		if (pMsg->message == WM_HOTKEY)
		{
			if (pMsg->wParam == MY_HOT_KEY)
			{
				//setWindowFlags(Qt::WindowStaysOnTopHint);
				this->showNormal();
				//以下两句缺一均不能有效将窗口置顶
				this->setWindowState(Qt::WindowActive);
				this->activateWindow();
				//焦点
				ui->lineEdit->setFocus();
				
				return true;
			}
		}
	}
	return false;
}

4,下载地址

链接:https://pan.baidu.com/s/1VGlGg2X67TvQwOxkOLnd-A

提取码:1ypz

若链接失效,本专栏代码都可在学习群群文件免费下载!

群号码:1149411109 (若满加2群,群文件完全同步)

2群:917341904

群内提供文章源码,及各种优质Qt项目源码、学习资料。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逆枫゛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值