Skip to content

Instantly share code, notes, and snippets.

@Noble-Mushtak
Last active June 21, 2017 22:18
Show Gist options
  • Save Noble-Mushtak/b55552e0e6e288b5834143e518aa7b40 to your computer and use it in GitHub Desktop.
Save Noble-Mushtak/b55552e0e6e288b5834143e518aa7b40 to your computer and use it in GitHub Desktop.
Qt Widgets Application for Four-Function Calculator
calculator.pro.user
TARGET = calculator
TEMPLATE = app
target.path = /data/user/qt/$$TARGET
INSTALLS += target
QT += widgets
SOURCES += \
main.cpp \
calcwindow.cpp
HEADERS += \
calcwindow.h
#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;
}
}
#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
#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