Last active
June 21, 2017 22:18
-
-
Save Noble-Mushtak/b55552e0e6e288b5834143e518aa7b40 to your computer and use it in GitHub Desktop.
Qt Widgets Application for Four-Function Calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
calculator.pro.user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TARGET = calculator | |
TEMPLATE = app | |
target.path = /data/user/qt/$$TARGET | |
INSTALLS += target | |
QT += widgets | |
SOURCES += \ | |
main.cpp \ | |
calcwindow.cpp | |
HEADERS += \ | |
calcwindow.h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "calcwindow.h" | |
#include <QtWidgets> | |
#include <cmath> | |
CalcWindow::CalcWindow(QWidget *parent) : QMainWindow(parent) | |
{ | |
//Set the layout as a grid layout using a central widget: | |
QWidget *centralWidget = new QWidget(this); | |
QGridLayout *layout = new QGridLayout(centralWidget); | |
setCentralWidget(centralWidget); | |
setWindowTitle("Calculator"); | |
//This size policy makes QWidget ignore the size hint, so the buttons and labels will adjust themselves accordingly. | |
QSizePolicy ignore(QSizePolicy::Ignored, QSizePolicy::Ignored); | |
//The following are the stretch factors for the different rows in the grid layout: | |
int firstRowStretch = 7, otherRowsStretch = 5; | |
//This label will show the calculator's result or the number the user is currently typing in: | |
resultLabel = new QLabel(QString::number(curResult), this); | |
resultLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); | |
//Add the label to a horizontal scrollbar: | |
QScrollArea *scrollBar = new QScrollArea(this); | |
scrollBar->setSizePolicy(ignore); | |
scrollBar->setWidget(resultLabel); | |
scrollBar->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | |
//This size policy and setting resizable allows resultLabel to take all the space in the scroll area: | |
resultLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored); | |
scrollBar->setWidgetResizable(true); | |
//This stretch factor makes the scroll bar slightly bigger in height than the other rows: | |
layout->setRowStretch(0, firstRowStretch); | |
//Finally, add the scrollbar to layout so it takes up a full row: | |
layout->addWidget(scrollBar, 0, 0, 1, 4); | |
//This string holds all buttons that will be displayed on the calculated: | |
char buttonIcons[] = "123+456-789*0.=/"; | |
//Add a button for all 16 icons: | |
for (int i = 0; i < NUM_BUTTON_ICONS; i++) { | |
//Create the push button: | |
buttons[i] = new QPushButton(QChar(buttonIcons[i]), this); | |
buttons[i]->setSizePolicy(ignore); | |
//We set size policy to ignore, but we still want to start with the size hint to begin with: | |
buttons[i]->setMinimumSize(buttons[i]->sizeHint()); | |
//Add button to layout and bind handleButton as clicked event handler: | |
layout->addWidget(buttons[i], i/4+1, i % 4); | |
QObject::connect(buttons[i], SIGNAL(clicked()), this, SLOT(handleButton())); | |
//This gives each row of buttons the same height because they have the same stretch factor: | |
layout->setRowStretch(i/4+1, otherRowsStretch); | |
} | |
//Make the scroll area row have slightly bigger height of the button rows: | |
//Simply setting the row stretch factors does not do this because the button's all have minimum height, | |
//which has priority over stretch factors. | |
layout->setRowMinimumHeight(0, (((double)firstRowStretch)/otherRowsStretch)*buttons[0]->sizeHint().height()); | |
} | |
void CalcWindow::resizeEvent(QResizeEvent *event) { | |
//Create font that takes up whole height except for margins: | |
QFont buttonFont = buttons[0]->font(), labelFont = resultLabel->font(); | |
buttonFont.setPixelSize(buttons[0]->size().height()); | |
labelFont.setPixelSize(resultLabel->size().height()); | |
//Set font on label and all buttons: | |
for (int i = 0; i < NUM_BUTTON_ICONS; i++) buttons[i]->setFont(buttonFont); | |
resultLabel->setFont(labelFont); | |
} | |
void CalcWindow::displayNum(double num) { | |
//If num is NaN or inf, show error: | |
if (std::isnan(num) || std::isinf(num)) resultLabel->setText("Error: Press number to reset."); | |
//Otherwise, show num: | |
else resultLabel->setText(QString::number(num)); | |
} | |
void CalcWindow::finishOp() { | |
switch (curOp) { | |
//If there was no operation, simply set the result to the number being typed in: | |
case NULL_OP: | |
curResult = curOperand; | |
break; | |
case PLUS: | |
curResult += curOperand; | |
break; | |
case MINUS: | |
curResult -= curOperand; | |
break; | |
case MULT: | |
curResult *= curOperand; | |
break; | |
default: //Divide | |
curResult /= curOperand; | |
break; | |
} | |
//Show the result: | |
displayNum(curResult); | |
} | |
void CalcWindow::handleButton() { | |
//Get the index of where the button was pressed in the array of buttons: | |
QObject *button = sender(); | |
int indexOf = -1; | |
for (int i = 0; i < NUM_BUTTON_ICONS; i++) { | |
if (button == buttons[i]) indexOf = i; | |
} | |
//If result is isNaN, then there is an error: | |
bool error = std::isnan(curResult) || std::isinf(curResult); | |
//If there is an error, then reset curOp: | |
if (error) curOp = NULL_OP; | |
//If this was +, -, *, or / (ignore in case of error): | |
if ((indexOf % 4) == 3) { | |
//If the user has not started any number and this is the minus sign | |
//and the user did not just click the equals sign: | |
//then the minus is the beginning of a negative number: | |
if (!startedNumber && (indexOf == 7) && (curOp != NULL_OP)) { | |
//Make curSign negative: | |
curSign = -1; | |
//Set startedNumber: | |
startedNumber = true; | |
//Update resultLabel to signal to the user that this starts a negative number: | |
resultLabel->setText("-0"); | |
} | |
//If this is not the start of a negative number: | |
else { | |
//If the user was previously typing in a number, finish the operation: | |
if (startedNumber) finishOp(); | |
//Reset properties: | |
curOperand = 0, curSign = 1, decimalPlace = 0, startedNumber = false; | |
//Set curOp as appropriate: (i.e. PLUS if indexOf == 3, MINUS if indexOf == 7, ...) | |
curOp = (Operation)((indexOf+1)/4); | |
} | |
} | |
//If this was = and the user has started typing in a number: (ignore in case of error) | |
else if (indexOf == 14) { | |
if (startedNumber) { | |
//Finish operation: | |
finishOp(); | |
//Reset properties: | |
curOperand = 0, curSign = 1, curOp = NULL_OP, decimalPlace = 0, startedNumber = false; | |
} | |
} | |
//If this is . and the user has not pressed . for this number before: | |
else if (indexOf == 13) { | |
if (decimalPlace == 0) { | |
//Start setting the tenths place: | |
decimalPlace = 1; | |
//Update resultLabel: | |
/* | |
Even though curOperand does not change here, | |
if the user enters a decimal place before anything else, this will show 0 and | |
thus signal to the user that their number has started. | |
*/ | |
displayNum(curOperand); | |
//Set startedNumber: | |
startedNumber = true; | |
} | |
} | |
//Otherwise, if this is a number: | |
else { | |
//This formula calculates the number on the button from indexOf: | |
int num = ((indexOf/4)*3+(indexOf % 4)+1) % 10; | |
//If . has not been pressed, append num to curOperand: | |
if (decimalPlace == 0) curOperand = curOperand*10+curSign*num; | |
//Otherwise, add num with the appropriate decimal value and increment decimalPlace: | |
else { | |
curOperand += curSign*num*pow(10, -decimalPlace); | |
decimalPlace++; | |
} | |
//Update resultLabel: | |
displayNum(curOperand); | |
//Set startedNumber: | |
startedNumber = true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CALCWINDOW_H | |
#define CALCWINDOW_H | |
#include <QMainWindow> | |
#include <QtWidgets> | |
class CalcWindow : public QMainWindow | |
{ | |
Q_OBJECT | |
public: | |
explicit CalcWindow(QWidget *parent = nullptr); | |
//There are 16 buttons on this calculator: | |
const static int NUM_BUTTON_ICONS = 16; | |
//Enum to keep track of what operation is being computed: | |
enum Operation { NULL_OP, PLUS, MINUS, MULT, DIVIDE }; | |
signals: | |
public: | |
virtual void resizeEvent(QResizeEvent *event); | |
public slots: | |
//Button handler: | |
void handleButton(); | |
private: | |
//Label for number at top: | |
QLabel *resultLabel; | |
//Array of pointers to buttons: | |
QPushButton *buttons[NUM_BUTTON_ICONS]; | |
//Last operation that user pressed (NULL if user presses equals): | |
Operation curOp = NULL_OP; | |
//The current result: | |
double curResult = 0; | |
//The current number that the user is typing in: | |
double curOperand = 0; | |
//Has the user started typing in a number? | |
bool startedNumber = true; | |
//Is the number that the user is typing in negative? (1 if yes, -1 if no) | |
int curSign = 1; | |
//The decimal place the user is currently entering (0 if the user has not pressed . yet): | |
int decimalPlace = 0; | |
//Method to display number on calculator: | |
void displayNum(double num); | |
//Method to finish the calculation the user has typed in; | |
void finishOp(); | |
}; | |
#endif // CALCWINDOW_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <QApplication> | |
#include <QtWidgets> | |
#include "calcwindow.cpp" | |
int main(int numArgs, char *argv[]) { | |
QApplication app(numArgs, argv); | |
CalcWindow w; | |
w.show(); | |
//Call resizeEvent() at the beginning to fix the fonts: | |
w.resizeEvent(NULL); | |
return app.exec(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment