Skip to main content
Code Review

Return to Question

Commonmark migration
Source Link

#Premise

Premise

#The code

The code

#Restricting user input

Restricting user input

#Premise

#The code

#Restricting user input

Premise

The code

Restricting user input

deleted 4 characters in body; edited title
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

C++ Calculator Simple postfix calculator app: efficiently keeping track of user input history

The calculator has a BackBack button (with a corresponding keyboard shortcut) that allows users to erase the previous input.

#The code All of the code works properly, but only parts of it are relevant to this question. I will post relevant snippets here to make things easier to understand.

All of the code works properly, but only parts of it are relevant to this question. I will post relevant snippets here to make things easier to understand.

If a user enters an operator, such as ++, they may enter a digit or opening parenthesis but may not enter another operator or closing parenthesis.

Notice that my mainwindow then has a private stack of States:QStack<State> history;

As the user provides input, the code creates a new State object and pushes it onto the stack. Here's the .cpp file (There are TODO statements for things I want to do in the future; please ignore those):

QStack<State> history;

As the user provides input, the code creates a new State object and pushes it onto the stack. Here's the .cpp file (there are TODO statements for things I want to do in the future; please ignore those):


##Edit 1

One idea that comes to mind is having each of the slot methods (e.g., on_digit_released() and on_binary_button_released()) check what character came before and proceeding accordingly. I think this would eliminate some of the memory workload, but it would also be a bit messier. Would the tradeofftrade-off be worth it?

C++ Calculator app: efficiently keeping track of user input history

The calculator has a Back button (with a corresponding keyboard shortcut) that allows users to erase the previous input.

#The code All of the code works properly, but only parts of it are relevant to this question. I will post relevant snippets here to make things easier to understand.

If a user enters an operator, such as +, they may enter a digit or opening parenthesis but may not enter another operator or closing parenthesis.

Notice that my mainwindow then has a private stack of States:QStack<State> history;

As the user provides input, the code creates a new State object and pushes it onto the stack. Here's the .cpp file (There are TODO statements for things I want to do in the future; please ignore those):


##Edit 1

One idea that comes to mind is having each of the slot methods (e.g., on_digit_released() and on_binary_button_released()) check what character came before and proceeding accordingly. I think this would eliminate some of the memory workload, but it would also be a bit messier. Would the tradeoff be worth it?

Simple postfix calculator app

The calculator has a Back button (with a corresponding keyboard shortcut) that allows users to erase the previous input.

#The code

All of the code works properly, but only parts of it are relevant to this question. I will post relevant snippets here to make things easier to understand.

If a user enters an operator, such as +, they may enter a digit or opening parenthesis but may not enter another operator or closing parenthesis.

Notice that my mainwindow then has a private stack of States:

QStack<State> history;

As the user provides input, the code creates a new State object and pushes it onto the stack. Here's the .cpp file (there are TODO statements for things I want to do in the future; please ignore those):

One idea that comes to mind is having each of the slot methods (e.g. on_digit_released() and on_binary_button_released()) check what character came before and proceeding accordingly. I think this would eliminate some of the memory workload, but it would also be a bit messier. Would the trade-off be worth it?

Updated code with new documentation. Some function bodies were changed. The code related to the question is fundamentally (functionally) the same.
Source Link
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "operator.h"
#include <QDebug> // debug output
#include <QChar>
#include <QShortcut> // keyboard input
#include <QKeySequence> // keyboard input
#include <QFont> // Qt fonts
// TODO add parentheses operations to calculator

/* Constructor for MainWindow objects. Connects all button signals
 * to their appropriate private slots to handle user input and sets
 * up some basic member variables.
 */
MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),
 ui(new Ui::MainWindow)
{
 // Basic variable setups
 ui->setupUi(this);
 input = ui->labelInput;
 reset();
 // Connect digits' released() signals to on_digit_released
 connect(ui->button0, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button1, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button2, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button3, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button4, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button5, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button6, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button7, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button8, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button9, SIGNAL(released()), this, SLOT(on_digit_released()));
 // Connect binary operators' released() signals to on_binary_button_released
 connect(ui->buttonPlus, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonMinus, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonTimes, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonDivide, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 // Shortcuts for math input via keyboard
 // TODO potential memory leaks
 QShortcut *shortcut0 = new QShortcut(QKeySequence("0"), this);
 QShortcut *shortcut1 = new QShortcut(QKeySequence("1"), this);
 QShortcut *shortcut2 = new QShortcut(QKeySequence("2"), this);
 QShortcut *shortcut3 = new QShortcut(QKeySequence("3"), this);
 QShortcut *shortcut4 = new QShortcut(QKeySequence("4"), this);
 QShortcut *shortcut5 = new QShortcut(QKeySequence("5"), this);
 QShortcut *shortcut6 = new QShortcut(QKeySequence("6"), this);
 QShortcut *shortcut7 = new QShortcut(QKeySequence("7"), this);
 QShortcut *shortcut8 = new QShortcut(QKeySequence("8"), this);
 QShortcut *shortcut9 = new QShortcut(QKeySequence("9"), this);
 QShortcut *shortcutDecimal = new QShortcut(QKeySequence("."), this);
 QShortcut *shortcutPlus = new QShortcut(QKeySequence("+"), this);
 QShortcut *shortcutMinus = new QShortcut(QKeySequence("-"), this);
 QShortcut *shortcutTimes = new QShortcut(QKeySequence("SHIFT+8"), this);
 QShortcut *shortcutDivide = new QShortcut(QKeySequence("/"), this);
 QShortcut *shortcutOpenParenth = new QShortcut(QKeySequence("SHIFT+9"), this);
 QShortcut *shortcutCloseParenth = new QShortcut(QKeySequence("SHIFT+0"), this);
 QShortcut *shortcutEnter = new QShortcut(QKeySequence(Qt::Key_Return), this);
 QShortcut *shortcutBackspace = new QShortcut(QKeySequence("Backspace"), this);
 connect(shortcut0, SIGNAL(activated()), ui->button0, SLOT(click()));
 connect(shortcut1, SIGNAL(activated()), ui->button1, SLOT(click()));
 connect(shortcut2, SIGNAL(activated()), ui->button2, SLOT(click()));
 connect(shortcut3, SIGNAL(activated()), ui->button3, SLOT(click()));
 connect(shortcut4, SIGNAL(activated()), ui->button4, SLOT(click()));
 connect(shortcut5, SIGNAL(activated()), ui->button5, SLOT(click()));
 connect(shortcut6, SIGNAL(activated()), ui->button6, SLOT(click()));
 connect(shortcut7, SIGNAL(activated()), ui->button7, SLOT(click()));
 connect(shortcut8, SIGNAL(activated()), ui->button8, SLOT(click()));
 connect(shortcut9, SIGNAL(activated()), ui->button9, SLOT(click()));
 connect(shortcutDecimal, SIGNAL(activated()), ui->buttonDecimalPoint, SLOT(click()));
 connect(shortcutPlus, SIGNAL(activated()), ui->buttonPlus, SLOT(click()));
 connect(shortcutMinus, SIGNAL(activated()), ui->buttonMinus, SLOT(click()));
 connect(shortcutTimes, SIGNAL(activated()), ui->buttonTimes, SLOT(click()));
 connect(shortcutDivide, SIGNAL(activated()), ui->buttonDivide, SLOT(click()));
 connect(shortcutOpenParenth, SIGNAL(activated()), ui->buttonOpenParenth, SLOT(click()));
 connect(shortcutCloseParenth, SIGNAL(activated()), ui->buttonCloseParenth, SLOT(click()));
 connect(shortcutEnter, SIGNAL(activated()), ui->buttonEquals, SLOT(click()));
 connect(shortcutBackspace, SIGNAL(activated()), ui->buttonBack, SLOT(click()));
}
MainWindow::~MainWindow()
{
 delete input;
 delete ui;
}
/* Used by other functions whento checkingcheck if an operator was
 * already applied directly before the current attempt at
 * either evaluating the expression or adding auser's newinput operatortoken.
 */
bool MainWindow::operatorUsedDirectlyBefore() const
{
 if(input->text().length() >= 2)
 {
 // Operators have a space after (e.g., " + "). Hence length()-2.
 // If input is like "1 + 2", return false b/c " " is not operator.
 return ((bool)operators.count(input->text().at(input->text().length()-21)));
 }
 return false;
}
/* WhenCalled when a user clicks any of the digit buttonbuttons is(0-9) pressed,or
 its* textenters getsa relayeddigit from their keyboard (0-9). Appends the corresponding
 * digit to inputthe labelinput.
 */
void MainWindow::on_digit_released()
{
 State currentState = history.top();
 if(currentState.digitAllowed)
 {
 QPushButton *button = (QPushButton*)sender();
 history.push(State(true, true, false, true,
 history.top().numOpenParenths,
 history.top().numClosingParenths));
 // Initial input is just a 0
 if(input->text().length() == 1 && input->text()[0] == '0')
 {
 input->setText(input->text().replace(0, 1, button->text()));
 }
 // Otherwise, append the digit
 else
 {
 input->setText(input->text().append(button->text()));
 }
 }
}
/* Called when a user clicks the '.' button or uses the keyboard
 * shortcut '.' Appends a decimal point to the last digit that was
 * entered.
 */
void MainWindow::on_buttonDecimalPoint_released()
{
 // TODO prevent entry of multiple decimal points
 if(!operatorUsedDirectlyBefore())
 {
 input->setText(input->text() + ".");
 }
}
/* HandlesCalled functionalitywhen ofa negatinguser clicks the '±' button. Negates an input expression.
 * Negating an expression appliesApplies the negative sign to the
 * last number entered (if one exists).
 * Negating a
 * negative number will remove the negative sign. Negating the
 * right operand of addition will change the '+' operator to subtraction.
 * Similarly, negating the right operand of subtraction will change the
 * '–' operator to addition.
 */
void MainWindow::on_buttonNegate_released()
{
 if(operatorUsedDirectlyBefore()){ return; }
 // Check if there are currently any operators in input
 bool inputHasOperators = false;
 for// If so, get the index of the last one (hence reverse traversal)
 int iindexOfLastOperator = 0;-1;
  for(int i <= input->text().length(); i++- 1; i >= 0; i--)
 {
 if(operators.count(input->text().at(i)) == 1)
 {
 inputHasOperators = true;
 indexOfLastOperator = i;
 break;
 }
 }
 // InputIf expressionwe entered an operator and then tried to negate something
 if(indexOfLastOperator == input->text().length()-1){ return; }
 // Case 1: Input contains operators
 if(inputHasOperators)
 {
 intQChar indexOfLastWhitespacelastOperator = input->text().lastIndexOfat(' 'indexOfLastOperator);
 // If the number is negative andin partquestion ofis analready expressionnegative, w/undo opsit
 if(input->text().at(indexOfLastWhitespace+1indexOfLastOperator+1) == '-')
 {
 input->setText(input->text().replace(indexOfLastWhitespace+1indexOfLastOperator+1, 1, ""));
 }
 // Otherwise,If justnot, alet's positivecheck numberwhat partkind of anoperator expressioncame w/before opsit
 else
 {
 // If it was a plus, change that to a minus
 if(lastOperator == '+')
 {
 input->setText(input->text().replace(indexOfLastOperator, 1, "–"));
 }
 // If it was a minus, change that to a plus
 else if(lastOperator == '–')
 {
 input->setText(input->text().replace(indexOfLastOperator, 1, "+"));
 }
 // Otherwise, just negate the last number
 else
 {
 input->setText(input->text().insert(indexOfLastWhitespace+1indexOfLastOperator+1, '-'));
 }
 }
 }
 // TODO or parenthetical expression?
 // Case 2: No operators and already negative number
 else if(input->text().at(0) == '-')
 {
 input->setText(input->text().replace(0, 1, ""));
 }
 // Case 3: No operators and positive number
 else
 {
 input->setText(input->text().prepend("-"));
 }
}
/* IfCalled thewhen a user pressesclicks any binary operation's button, like
 * plus, minus, multiply(+, or divide-, this slot will be called
 *, to/). insertInserts the appropriate operation into the current input.
 */
void MainWindow::on_binary_button_released()
{
 QPushButton *button = (QPushButton*)sender();
 State currentState = history.top();
 if(currentState.operatorAllowed)
 {
 input->setText(input->text().append(button->text()));
 history.push(State(true, false, true, false,
 currentState.numOpenParenths,
 currentState.numClosingParenths));
 }
}
/* IfCalled thewhen a user releasesclicks the ='=' button, thenor theyuses wantthe keyboard
 * theshortcut answer'Enter'. toChecks theirif the input tois beready calculatedfor evaluation.
 * EmitsIf so, emits input_is_ready signal, toeffectively givehanding Calculatorits
 * the input it needsstring to dothe itsCalculator jobfor processing.
 */
void MainWindow::on_buttonEquals_released()
{
 State currentState = history.top();
 if(!operatorUsedDirectlyBefore() &&
 currentState.numOpenParenths == currentState.numClosingParenths)
 {
 emit input_is_ready(input->text());
 }
}
/* Used to reset user input. Sets the input string to "0", clears its
 * history, and applies all initial input restrictions.
 */
void MainWindow::reset()
{
 input->setText("0");
 history.clear();
 history.push(State(true, true, false, false));
}
/* Pretty straightforward. Basically,Called ifwhen thea user *clicks pressesthe and'Clear' releasesbutton theor clearuses button,the thenkeyboard
 * setshortcut the'Delete'. inputResets stringthe toinput. justSee 0MainWindow::reset().
 */
void MainWindow::on_buttonClear_released()
{
 reset();
}
/* Called when a user clicks the 'Back' button or uses the
 * keyboard shortcut 'Backspace'. Reverts the calculator to * its previous state of input,. Reapplies all appropriate input
 * therebyrestrictions applyingthat allexisted appropriateprior restrictionsto deletion of the last token.
 */
void MainWindow::on_buttonBack_released()
{
 // If the input is just a single digit, set it to 0
 if(input->text().length() == 1)
 {
 input->setText("0");
 }
 // Otherwise, if we have 2 or more entries, remove last char
 else
 {
 QString text = input->text();
 input->setText(text.remove(text.length()-1, 1));
 }
 if(history.size() >= 2){ history.pop();}
}
void MainWindow::on_buttonRoot_released()
{
 // TODO nth root
}
/* Called when a user pushes the '(' button or uses the
 * keyboard shortcut SHIFT+9. Appends an opening parenthesis
 * to the input. Disables entry of closing parentheses
 * and operators directly afterwards.
 */
void MainWindow::on_buttonOpenParenth_released()
{
 if(history.top().openParenthAllowed)
 {
 input->setText(input->text().append("("));
 history.push(State(true, false, true, truefalse,
 history.top().numOpenParenths+1,
 history.top().numClosingParenths));
 }
}
/* Called when a user pushes the ')' button or uses the
 * keyboard shortcut SHIFT+0. Appends a closing parenthesis
 * to the input. Disables entry of opening parentheses and
 * digits directly afterwards.
 */
void MainWindow::on_buttonCloseParenth_released()
{
 State currentState = history.top();
 if(currentState.closingParenthAllowed &&
 (currentState.numClosingParenths < currentState.numOpenParenths))
 {
 input->setText(input->text().append(")"));
 history.push(State(false, true, false, true,
 history.top().numOpenParenths,
 history.top().numClosingParenths+1));
 }
}
void MainWindow::on_buttonXSquared_released()
{
 // TODO x squared
}
void MainWindow::on_buttonSqrt_released()
{
 // TODO square root
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "operator.h"
#include <QDebug> // debug output
#include <QChar>
#include <QShortcut> // keyboard input
#include <QKeySequence> // keyboard input
#include <QFont> // Qt fonts
// TODO add parentheses operations to calculator

MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),
 ui(new Ui::MainWindow)
{
 // Basic variable setups
 ui->setupUi(this);
 input = ui->labelInput;
 reset();
 // Connect digits' released() signals to on_digit_released
 connect(ui->button0, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button1, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button2, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button3, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button4, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button5, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button6, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button7, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button8, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button9, SIGNAL(released()), this, SLOT(on_digit_released()));
 // Connect binary operators' released() signals to on_binary_button_released
 connect(ui->buttonPlus, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonMinus, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonTimes, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonDivide, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 // Shortcuts for math input via keyboard
 // TODO potential memory leaks
 QShortcut *shortcut0 = new QShortcut(QKeySequence("0"), this);
 QShortcut *shortcut1 = new QShortcut(QKeySequence("1"), this);
 QShortcut *shortcut2 = new QShortcut(QKeySequence("2"), this);
 QShortcut *shortcut3 = new QShortcut(QKeySequence("3"), this);
 QShortcut *shortcut4 = new QShortcut(QKeySequence("4"), this);
 QShortcut *shortcut5 = new QShortcut(QKeySequence("5"), this);
 QShortcut *shortcut6 = new QShortcut(QKeySequence("6"), this);
 QShortcut *shortcut7 = new QShortcut(QKeySequence("7"), this);
 QShortcut *shortcut8 = new QShortcut(QKeySequence("8"), this);
 QShortcut *shortcut9 = new QShortcut(QKeySequence("9"), this);
 QShortcut *shortcutDecimal = new QShortcut(QKeySequence("."), this);
 QShortcut *shortcutPlus = new QShortcut(QKeySequence("+"), this);
 QShortcut *shortcutMinus = new QShortcut(QKeySequence("-"), this);
 QShortcut *shortcutTimes = new QShortcut(QKeySequence("SHIFT+8"), this);
 QShortcut *shortcutDivide = new QShortcut(QKeySequence("/"), this);
 QShortcut *shortcutOpenParenth = new QShortcut(QKeySequence("SHIFT+9"), this);
 QShortcut *shortcutCloseParenth = new QShortcut(QKeySequence("SHIFT+0"), this);
 QShortcut *shortcutEnter = new QShortcut(QKeySequence(Qt::Key_Return), this);
 QShortcut *shortcutBackspace = new QShortcut(QKeySequence("Backspace"), this);
 connect(shortcut0, SIGNAL(activated()), ui->button0, SLOT(click()));
 connect(shortcut1, SIGNAL(activated()), ui->button1, SLOT(click()));
 connect(shortcut2, SIGNAL(activated()), ui->button2, SLOT(click()));
 connect(shortcut3, SIGNAL(activated()), ui->button3, SLOT(click()));
 connect(shortcut4, SIGNAL(activated()), ui->button4, SLOT(click()));
 connect(shortcut5, SIGNAL(activated()), ui->button5, SLOT(click()));
 connect(shortcut6, SIGNAL(activated()), ui->button6, SLOT(click()));
 connect(shortcut7, SIGNAL(activated()), ui->button7, SLOT(click()));
 connect(shortcut8, SIGNAL(activated()), ui->button8, SLOT(click()));
 connect(shortcut9, SIGNAL(activated()), ui->button9, SLOT(click()));
 connect(shortcutDecimal, SIGNAL(activated()), ui->buttonDecimalPoint, SLOT(click()));
 connect(shortcutPlus, SIGNAL(activated()), ui->buttonPlus, SLOT(click()));
 connect(shortcutMinus, SIGNAL(activated()), ui->buttonMinus, SLOT(click()));
 connect(shortcutTimes, SIGNAL(activated()), ui->buttonTimes, SLOT(click()));
 connect(shortcutDivide, SIGNAL(activated()), ui->buttonDivide, SLOT(click()));
 connect(shortcutOpenParenth, SIGNAL(activated()), ui->buttonOpenParenth, SLOT(click()));
 connect(shortcutCloseParenth, SIGNAL(activated()), ui->buttonCloseParenth, SLOT(click()));
 connect(shortcutEnter, SIGNAL(activated()), ui->buttonEquals, SLOT(click()));
 connect(shortcutBackspace, SIGNAL(activated()), ui->buttonBack, SLOT(click()));
}
MainWindow::~MainWindow()
{
 delete ui;
}
/* Used by other functions when checking if an operator was
 * already applied directly before the current attempt at
 * either evaluating the expression or adding a new operator.
 */
bool MainWindow::operatorUsedDirectlyBefore() const
{
 if(input->text().length() >= 2)
 {
 // Operators have a space after (e.g., " + "). Hence length()-2.
 // If input is like "1 + 2", return false b/c " " is not operator.
 return ((bool)operators.count(input->text().at(input->text().length()-2)));
 }
 return false;
}
/* When a digit button is pressed, its text gets relayed to input label */
void MainWindow::on_digit_released()
{
 State currentState = history.top();
 if(currentState.digitAllowed)
 {
 QPushButton *button = (QPushButton*)sender();
 history.push(State(true, true, false, true,
 history.top().numOpenParenths,
 history.top().numClosingParenths));
 if(input->text().length() == 1 && input->text()[0] == '0')
 {
 input->setText(input->text().replace(0, 1, button->text()));
 }
 else
 {
 input->setText(input->text().append(button->text()));
 }
 }
}
void MainWindow::on_buttonDecimalPoint_released()
{
 // TODO prevent entry of multiple decimal points
 if(!operatorUsedDirectlyBefore())
 {
 input->setText(input->text() + ".");
 }
}
/* Handles functionality of negating an input expression.
 * Negating an expression applies the negative sign to the
 * last number entered (if one exists). Negating a
 * negative number will remove the negative sign.
 */
void MainWindow::on_buttonNegate_released()
{
 if(operatorUsedDirectlyBefore()){ return; }
 // Check if there are currently any operators in input
 bool inputHasOperators = false;
 for(int i = 0; i < input->text().length(); i++)
 {
 if(operators.count(input->text().at(i)) == 1)
 {
 inputHasOperators = true;
 break;
 }
 }
 // Input expression contains operators
 if(inputHasOperators)
 {
 int indexOfLastWhitespace = input->text().lastIndexOf(' ');
 // If the number is negative and part of an expression w/ ops
 if(input->text().at(indexOfLastWhitespace+1) == '-')
 {
 input->setText(input->text().replace(indexOfLastWhitespace+1, 1, ""));
 }
 // Otherwise, just a positive number part of an expression w/ ops
 else
 {
 input->setText(input->text().insert(indexOfLastWhitespace+1, '-'));
 }
 }
 // TODO or parenthetical expression?
 // No operators and already negative number
 else if(input->text().at(0) == '-')
 {
 input->setText(input->text().replace(0, 1, ""));
 }
 // No operators and positive number
 else
 {
 input->setText(input->text().prepend("-"));
 }
}
/* If the user presses any binary operation's button, like
 * plus, minus, multiply, or divide, this slot will be called
 * to insert the appropriate operation into the current input.
 */
void MainWindow::on_binary_button_released()
{
 QPushButton *button = (QPushButton*)sender();
 State currentState = history.top();
 if(currentState.operatorAllowed)
 {
 input->setText(input->text().append(button->text()));
 history.push(State(true, false, true, false,
 currentState.numOpenParenths,
 currentState.numClosingParenths));
 }
}
/* If the user releases the = button, then they want
 * the answer to their input to be calculated.
 * Emits input_is_ready signal to give Calculator
 * the input it needs to do its job.
 */
void MainWindow::on_buttonEquals_released()
{
 State currentState = history.top();
 if(!operatorUsedDirectlyBefore() &&
 currentState.numOpenParenths == currentState.numClosingParenths)
 {
 emit input_is_ready(input->text());
 }
}
/*
 *
 */
void MainWindow::reset()
{
 input->setText("0");
 history.clear();
 history.push(State(true, true, false, false));
}
/* Pretty straightforward. Basically, if the user * presses and releases the clear button, then
 * set the input string to just 0.
 */
void MainWindow::on_buttonClear_released()
{
 reset();
}
/* Reverts the calculator to its previous state of input,
 * thereby applying all appropriate restrictions.
 */
void MainWindow::on_buttonBack_released()
{
 // If the input is just a single digit, set it to 0
 if(input->text().length() == 1)
 {
 input->setText("0");
 }
 // Otherwise, if we have 2 or more entries, remove last char
 else
 {
 QString text = input->text();
 input->setText(text.remove(text.length()-1, 1));
 }
 if(history.size() >= 2){ history.pop();}
}
void MainWindow::on_buttonRoot_released()
{
 // TODO nth root
}
void MainWindow::on_buttonOpenParenth_released()
{
 if(history.top().openParenthAllowed)
 {
 input->setText(input->text().append("("));
 history.push(State(true, false, true, true,
 history.top().numOpenParenths+1,
 history.top().numClosingParenths));
 }
}
void MainWindow::on_buttonCloseParenth_released()
{
 State currentState = history.top();
 if(currentState.closingParenthAllowed &&
 (currentState.numClosingParenths < currentState.numOpenParenths))
 {
 input->setText(input->text().append(")"));
 history.push(State(false, true, false, true,
 history.top().numOpenParenths,
 history.top().numClosingParenths+1));
 }
}
void MainWindow::on_buttonXSquared_released()
{
 // TODO x squared
}
void MainWindow::on_buttonSqrt_released()
{
 // TODO square root
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "operator.h"
#include <QDebug> // debug output
#include <QChar>
#include <QShortcut> // keyboard input
#include <QKeySequence> // keyboard input
#include <QFont> // Qt fonts
/* Constructor for MainWindow objects. Connects all button signals
 * to their appropriate private slots to handle user input and sets
 * up some basic member variables.
 */
MainWindow::MainWindow(QWidget *parent) :
 QMainWindow(parent),
 ui(new Ui::MainWindow)
{
 // Basic variable setups
 ui->setupUi(this);
 input = ui->labelInput;
 reset();
 // Connect digits' released() signals to on_digit_released
 connect(ui->button0, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button1, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button2, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button3, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button4, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button5, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button6, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button7, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button8, SIGNAL(released()), this, SLOT(on_digit_released()));
 connect(ui->button9, SIGNAL(released()), this, SLOT(on_digit_released()));
 // Connect binary operators' released() signals to on_binary_button_released
 connect(ui->buttonPlus, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonMinus, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonTimes, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 connect(ui->buttonDivide, SIGNAL(released()),
 this, SLOT(on_binary_button_released()));
 // Shortcuts for math input via keyboard
 // TODO potential memory leaks
 QShortcut *shortcut0 = new QShortcut(QKeySequence("0"), this);
 QShortcut *shortcut1 = new QShortcut(QKeySequence("1"), this);
 QShortcut *shortcut2 = new QShortcut(QKeySequence("2"), this);
 QShortcut *shortcut3 = new QShortcut(QKeySequence("3"), this);
 QShortcut *shortcut4 = new QShortcut(QKeySequence("4"), this);
 QShortcut *shortcut5 = new QShortcut(QKeySequence("5"), this);
 QShortcut *shortcut6 = new QShortcut(QKeySequence("6"), this);
 QShortcut *shortcut7 = new QShortcut(QKeySequence("7"), this);
 QShortcut *shortcut8 = new QShortcut(QKeySequence("8"), this);
 QShortcut *shortcut9 = new QShortcut(QKeySequence("9"), this);
 QShortcut *shortcutDecimal = new QShortcut(QKeySequence("."), this);
 QShortcut *shortcutPlus = new QShortcut(QKeySequence("+"), this);
 QShortcut *shortcutMinus = new QShortcut(QKeySequence("-"), this);
 QShortcut *shortcutTimes = new QShortcut(QKeySequence("SHIFT+8"), this);
 QShortcut *shortcutDivide = new QShortcut(QKeySequence("/"), this);
 QShortcut *shortcutOpenParenth = new QShortcut(QKeySequence("SHIFT+9"), this);
 QShortcut *shortcutCloseParenth = new QShortcut(QKeySequence("SHIFT+0"), this);
 QShortcut *shortcutEnter = new QShortcut(QKeySequence(Qt::Key_Return), this);
 QShortcut *shortcutBackspace = new QShortcut(QKeySequence("Backspace"), this);
 connect(shortcut0, SIGNAL(activated()), ui->button0, SLOT(click()));
 connect(shortcut1, SIGNAL(activated()), ui->button1, SLOT(click()));
 connect(shortcut2, SIGNAL(activated()), ui->button2, SLOT(click()));
 connect(shortcut3, SIGNAL(activated()), ui->button3, SLOT(click()));
 connect(shortcut4, SIGNAL(activated()), ui->button4, SLOT(click()));
 connect(shortcut5, SIGNAL(activated()), ui->button5, SLOT(click()));
 connect(shortcut6, SIGNAL(activated()), ui->button6, SLOT(click()));
 connect(shortcut7, SIGNAL(activated()), ui->button7, SLOT(click()));
 connect(shortcut8, SIGNAL(activated()), ui->button8, SLOT(click()));
 connect(shortcut9, SIGNAL(activated()), ui->button9, SLOT(click()));
 connect(shortcutDecimal, SIGNAL(activated()), ui->buttonDecimalPoint, SLOT(click()));
 connect(shortcutPlus, SIGNAL(activated()), ui->buttonPlus, SLOT(click()));
 connect(shortcutMinus, SIGNAL(activated()), ui->buttonMinus, SLOT(click()));
 connect(shortcutTimes, SIGNAL(activated()), ui->buttonTimes, SLOT(click()));
 connect(shortcutDivide, SIGNAL(activated()), ui->buttonDivide, SLOT(click()));
 connect(shortcutOpenParenth, SIGNAL(activated()), ui->buttonOpenParenth, SLOT(click()));
 connect(shortcutCloseParenth, SIGNAL(activated()), ui->buttonCloseParenth, SLOT(click()));
 connect(shortcutEnter, SIGNAL(activated()), ui->buttonEquals, SLOT(click()));
 connect(shortcutBackspace, SIGNAL(activated()), ui->buttonBack, SLOT(click()));
}
MainWindow::~MainWindow()
{
 delete input;
 delete ui;
}
/* Used by other functions to check if an operator was
 * applied directly before the current user's input token.
 */
bool MainWindow::operatorUsedDirectlyBefore() const
{
 return ((bool)operators.count(input->text().at(input->text().length()-1)));
}
/* Called when a user clicks any of the digit buttons (0-9) or
 * enters a digit from their keyboard (0-9). Appends the corresponding
 * digit to the input.
 */
void MainWindow::on_digit_released()
{
 State currentState = history.top();
 if(currentState.digitAllowed)
 {
 QPushButton *button = (QPushButton*)sender();
 history.push(State(true, true, false, true,
 history.top().numOpenParenths,
 history.top().numClosingParenths));
 // Initial input is just a 0
 if(input->text().length() == 1 && input->text()[0] == '0')
 {
 input->setText(input->text().replace(0, 1, button->text()));
 }
 // Otherwise, append the digit
 else
 {
 input->setText(input->text().append(button->text()));
 }
 }
}
/* Called when a user clicks the '.' button or uses the keyboard
 * shortcut '.' Appends a decimal point to the last digit that was
 * entered.
 */
void MainWindow::on_buttonDecimalPoint_released()
{
 // TODO prevent entry of multiple decimal points
 if(!operatorUsedDirectlyBefore())
 {
 input->setText(input->text() + ".");
 }
}
/* Called when a user clicks the '±' button. Negates an input expression.
 * Applies the negative sign to the last number entered (if one exists).
 * Negating a negative number will remove the negative sign. Negating the
 * right operand of addition will change the '+' operator to subtraction.
 * Similarly, negating the right operand of subtraction will change the
 * '–' operator to addition.
 */
void MainWindow::on_buttonNegate_released()
{
 // Check if there are currently any operators in input
 bool inputHasOperators = false;
 // If so, get the index of the last one (hence reverse traversal)
 int indexOfLastOperator = -1;
  for(int i = input->text().length() - 1; i >= 0; i--)
 {
 if(operators.count(input->text().at(i)) == 1)
 {
 inputHasOperators = true;
 indexOfLastOperator = i;
 break;
 }
 }
 // If we entered an operator and then tried to negate something
 if(indexOfLastOperator == input->text().length()-1){ return; }
 // Case 1: Input contains operators
 if(inputHasOperators)
 {
 QChar lastOperator = input->text().at(indexOfLastOperator);
 // If the number in question is already negative, undo it
 if(input->text().at(indexOfLastOperator+1) == '-')
 {
 input->setText(input->text().replace(indexOfLastOperator+1, 1, ""));
 }
 // If not, let's check what kind of operator came before it
 else
 {
 // If it was a plus, change that to a minus
 if(lastOperator == '+')
 {
 input->setText(input->text().replace(indexOfLastOperator, 1, "–"));
 }
 // If it was a minus, change that to a plus
 else if(lastOperator == '–')
 {
 input->setText(input->text().replace(indexOfLastOperator, 1, "+"));
 }
 // Otherwise, just negate the last number
 else
 {
 input->setText(input->text().insert(indexOfLastOperator+1, '-'));
 }
 }
 }
 // TODO or parenthetical expression?
 // Case 2: No operators and already negative number
 else if(input->text().at(0) == '-')
 {
 input->setText(input->text().replace(0, 1, ""));
 }
 // Case 3: No operators and positive number
 else
 {
 input->setText(input->text().prepend("-"));
 }
}
/* Called when a user clicks any binary operation's button
 * (+, -, *, /). Inserts the appropriate operation into input.
 */
void MainWindow::on_binary_button_released()
{
 QPushButton *button = (QPushButton*)sender();
 State currentState = history.top();
 if(currentState.operatorAllowed)
 {
 input->setText(input->text().append(button->text()));
 history.push(State(true, false, true, false,
 currentState.numOpenParenths,
 currentState.numClosingParenths));
 }
}
/* Called when a user clicks the '=' button or uses the keyboard
 * shortcut 'Enter'. Checks if the input is ready for evaluation.
 * If so, emits input_is_ready signal, effectively handing its
 * input string to the Calculator for processing.
 */
void MainWindow::on_buttonEquals_released()
{
 State currentState = history.top();
 if(!operatorUsedDirectlyBefore() &&
 currentState.numOpenParenths == currentState.numClosingParenths)
 {
 emit input_is_ready(input->text());
 }
}
/* Used to reset user input. Sets the input string to "0", clears its
 * history, and applies all initial input restrictions.
 */
void MainWindow::reset()
{
 input->setText("0");
 history.clear();
 history.push(State(true, true, false, false));
}
/* Called when a user clicks the 'Clear' button or uses the keyboard
 * shortcut 'Delete'. Resets the input. See MainWindow::reset().
 */
void MainWindow::on_buttonClear_released()
{
 reset();
}
/* Called when a user clicks the 'Back' button or uses the
 * keyboard shortcut 'Backspace'. Reverts the calculator to * its previous state of input. Reapplies all appropriate input
 * restrictions that existed prior to deletion of the last token.
 */
void MainWindow::on_buttonBack_released()
{
 // If the input is just a single digit, set it to 0
 if(input->text().length() == 1)
 {
 input->setText("0");
 }
 // Otherwise, if we have 2 or more entries, remove last char
 else
 {
 QString text = input->text();
 input->setText(text.remove(text.length()-1, 1));
 }
 if(history.size() >= 2){ history.pop();}
}
void MainWindow::on_buttonRoot_released()
{
 // TODO nth root
}
/* Called when a user pushes the '(' button or uses the
 * keyboard shortcut SHIFT+9. Appends an opening parenthesis
 * to the input. Disables entry of closing parentheses
 * and operators directly afterwards.
 */
void MainWindow::on_buttonOpenParenth_released()
{
 if(history.top().openParenthAllowed)
 {
 input->setText(input->text().append("("));
 history.push(State(true, false, true, false,
 history.top().numOpenParenths+1,
 history.top().numClosingParenths));
 }
}
/* Called when a user pushes the ')' button or uses the
 * keyboard shortcut SHIFT+0. Appends a closing parenthesis
 * to the input. Disables entry of opening parentheses and
 * digits directly afterwards.
 */
void MainWindow::on_buttonCloseParenth_released()
{
 State currentState = history.top();
 if(currentState.closingParenthAllowed &&
 (currentState.numClosingParenths < currentState.numOpenParenths))
 {
 input->setText(input->text().append(")"));
 history.push(State(false, true, false, true,
 history.top().numOpenParenths,
 history.top().numClosingParenths+1));
 }
}
void MainWindow::on_buttonXSquared_released()
{
 // TODO x squared
}
void MainWindow::on_buttonSqrt_released()
{
 // TODO square root
}
offering alternative
Source Link
Loading
added 14452 characters in body
Source Link
Loading
deleted 113 characters in body
Source Link
Loading
Source Link
Loading
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /