Let’s take a look at the effect:

Isn’t that cool?

Want it or not?

Want to.

Of course the author knows you do.

Otherwise they wouldn’t click in, right?

All right. Let’s get down to business.

1. An overview of the

This is a simplified version of the calculator that imitates Win10. I did it in terms of Qt, and I just typed in the whole expression and got the answer. It is mainly divided into three parts. The interface section, the event handling section, and the expression handling section.

  • The interface section is the calculator you see, including the title bar, the output box in the middle, and the buttons.
  • Event handling is the processing of the corresponding mouse and keyboard events.
  • The expression processing part is to process the entire input string and return the result of the evaluation, which of course also supports the error determination function.

2. New construction

Select Widgets Application.

The name.

It’s just going to be minus Gw.

You can change the name by default

2. The interface

(1) button

Button, basically press the change can be. Layout change, color change, font change, those are the three main. First, open the.ui file:

A. Add a Grid Layout and resize it.

B. Drag the Push Button into the SizePolicy property and select Expanding in both the horizontal and vertical properties.

C. Adjust the colors and set the styleSheet and font

Here is the author’s reference style:

Border: 1 px groove RGB (220220220); Background - color: RGB (243243243);

Font:

Here according to personal preferences can be adjusted.

D. Duplicate the button and set it up

E. content instead

Here we change not only the characters inside, but also the corresponding object names.

Then fine-tune each key, including size, font and color, to make the overall effect better.

Numbers should be noted to have a “bold” effect, symbols as far as possible “fine” a point.

F. Modify the overall size and add spacing

Adjust the spacing. Pay attention to detail.

Here is the calculator that comes with Win10:

See the interval?

This is what the author wants.

You can run it first.

The spacing on both sides can be adjusted with the Widget size later.

(2) the output frame

The output box is very simple, just a QlineEdit.

A. add QLineEdit

B. Adjust the size and set the background color

The author of the QSS:

Border: 0 px groove RGB (243243243); Background - color: RGB (245245245);

C. Set font, Read Only, Align

(3) the title bar

The title bar is also very simple, a QboxLayout

A. Create a Horizontal Layout

B. Add details

QLabel Enter the title, two QPushButtons for minimize and close, and two spacers to leave some space between the title and the left.

In fact, it is the effect of imitating the title bar of Win10

I’m not going to do maximization here. This can be optionally implemented because it involves rearranging buttons.

(4) overall treatment

A. the title bar

Do step on the title bar to move to the right place, at the same time, delete their own QMenuBar, QToolBar, QStatusBar.

B. Adjust the overall size while adding transparency

That’s what it looks like, and I’ve selected 0.9 for transparency.

It’s perfect!

3. Event handling

(1) Cursor event

A. the title bar

A. Drag effect

So let’s get rid of that title bar.

setWindowFlags(windowFlags() | Qt::FramelessWindowHint);

Add a mouse listener in protected:

void mousePressEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);

Add two QPoints to the private member. They represent the coordinates of the current window and the cursor.

QPoint mousePoint;
QPoint windowPoint;

The first function is triggered when the mouse is pressed. The event->button() determines if it is the left button, and if so, gets the coordinates of mouse and sets the window coordinates.

When the second function fires, it determines whether to hold down the left button and use MainWindow’s move method to move the window.

Event ->globalPos() subtracts the original cursor coordinates to get the window coordinate change, and then adds the original coordinate to the change.

void MainWindow::mousePressEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { mousePoint = event->globalPos(); windowPoint = frameGeometry().topLeft(); } } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if(event->buttons() & Qt::LeftButton) { move(windowPoint + event->globalPos() - mousePoint); }}

B. Minimize and close

Here we use minimization as an example, and close the same thing by changing the function call. Right-click on the Minimize button and select Go to Slot:

Select clicked ()

Add a minimization function:

Here is the function that was turned off:

B. the keys

There are two mouse events for keystrokes:

  • Cursor moves in and out events, adds shadows for keys, deepens colors, and more
  • Click the event to add or subtract the corresponding characters in the output box

A. Inward and outward events

This is done through event filters. Add an eventFilter() function

 bool eventFilter(QObject *,QEvent *);

First, determine the event type by event->type(). If the cursor is hovering, then judge the corresponding objects to increase the shadow effect.

addNumButtonEffet():

void MainWindow::addNumButtonEffect(QPushButton *button,QGraphicsDropShadowEffect *shadow) { shadow->setEnabled(true); The button - > setStyleSheet (" border: 1 px groove RGB (220220220);" "Background - color: RGB (193193193);" ); }

Here QGraphicsDropShadowEffect * shadow initialization ready in advance.

Then add the event filter:

Here’s how to compare the effect with or without shadows:

No Shadows:

Add a shadow:

Uh… This may be a screenshot tool, not to see how much effect, but directly on the machine is a relatively big difference, it is recommended to add a shadow.

B. Click events

A click event is when a key is clicked and the user can see the response in the output box.

Select the key in turn, right click Go to slot:

Select clicked ()

Then add a processing function, the author here to achieve a function to add text and clear the focus, add text is the corresponding key is added to the output box after the cursor click, as for why to clear the focus….

Because…

Because of the space.

Because of the author’s “good” habit of adding Spaces before and after operators

Clicking will leave the focus on the button. Tacking a space on the keyboard will “press” the button by default, so if you don’t clear the focus, pressing a space will print a 7 on the output box after the cursor clicks on a button, such as 7, and pressing a space will print an 8 on the output box after the cursor clicks 8.

Note also the default suggestive 0 when adding text here.

void MainWindow::appendText(const QString &s)
{
    if(ui->box->text() == "0")
        ui->box->setText(s);
    else
        ui->box->setText(ui->box->text()+s);
}

void MainWindow::appendTextAndClearFocus(QPushButton *button, const QString &s)
{
    appendText(s);
    button->clearFocus();
}

(2) Keyboard events

Keyboard event is the main processing of each key press when the shadow and output box add output. Keyboard events are handled by the following two functions:

void keyPressEvent(QKeyEvent *);
void keyReleaseEvent(QKeyEvent *);

The first is triggered when the button is pressed down, and the second is triggered when the button is released.

A. Add A shadow

Add a shadow and color burn effect when a key is pressed.

Each key is judged by event->key().

The keys can be seen here

Then add to KeyRealeseEvent () to remove the corresponding shadow:

void MainWindow::keyReleaseEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_0: case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: case Qt::Key_Plus: case Qt::Key_Minus: case Qt::Key_Asterisk: case Qt::Key_Slash: case Qt::Key_AsciiCircum: case Qt::Key_Percent: case Qt::Key_ParenLeft: case Qt::Key_ParenRight: case Qt::Key_BraceLeft: case Qt::Key_BraceRight: case Qt::Key_BracketLeft: case Qt::Key_BracketRight: case Qt::Key_Backspace: case Qt::Key_Space: case Qt::Key_Period: case Qt::Key_Escape: case Qt::Key_Equal: case Qt::Key_Return: removeNumButtonEffect(ui->num0,num0_shadow); removeNumButtonEffect(ui->num1,num1_shadow); removeNumButtonEffect(ui->num2,num2_shadow); removeNumButtonEffect(ui->num3,num3_shadow); removeNumButtonEffect(ui->num4,num4_shadow); removeNumButtonEffect(ui->num5,num5_shadow); removeNumButtonEffect(ui->num6,num6_shadow); removeNumButtonEffect(ui->num7,num7_shadow); removeNumButtonEffect(ui->num8,num8_shadow); removeNumButtonEffect(ui->num9,num9_shadow); removeSignButtonEffect(ui->plus,plus_shadow); removeSignButtonEffect(ui->minus,minus_shadow); removeSignButtonEffect(ui->mutiply,mutiply_shadow); removeSignButtonEffect(ui->divide,divide_shadow); removeSignButtonEffect(ui->pow,pow_shadow); removeSignButtonEffect(ui->percent,percent_shadow); removeSignButtonEffect(ui->parentheses,parentheses_shadow); removeSignButtonEffect(ui->parentheses,parentheses_shadow); removeSignButtonEffect(ui->brace,brace_shadow); removeSignButtonEffect(ui->brace,brace_shadow); removeSignButtonEffect(ui->bracket,bracket_shadow); removeSignButtonEffect(ui->bracket,bracket_shadow); removeSignButtonEffect(ui->backspace,backspace_shadow); removeSignButtonEffect(ui->blank,space_shadow); removeSignButtonEffect(ui->dot,dot_shadow); removeSignButtonEffect(ui->C,c_shadow); removeSignButtonEffect(ui->equal,equal_shadow); break; }}

It is possible to press multiple keys at the same time, and then release them at the same time and find that a certain key still has a shadow. Therefore, when one of the keys is released, the shadow of all the keys is removed.

B. Add output

To add output to the output box, call a function:

4. Reprocess the overall details

(1) Fade in effect

Look at the effect:

Here we actually use the animation of Qt, the animation for transparency changes.

void MainWindow::fadeIn(void) { QPropertyAnimation * changeOpacity = new QPropertyAnimation(this,"windowOpacity"); changeOpacity->setStartValue(0); ChangeOpacity - > setEndValue (0.91); changeOpacity->setDuration(2500); changeOpacity->start(); }

The first line indicates that the change is transparency, the second and third lines set the start and end values, then set the animation time in ms, and then start the animation.

(2) Set a fixed size

You can not set the maximum size here, but you must set the minimum size.

Setting this actually disallows drag and drop to change the size.

(3) Fade out effect

The fade out effect is similar to the fade in effect.

The fadeOut() function cannot be called directly before exit(0). Because the animation will start in another thread, the main thread needs to specify a number of seconds to sleep. After the fade-out effect is completed, the main thread will call exit(0).

void MainWindow::fadeOut(void) { QPropertyAnimation * changeOpacity = new QPropertyAnimation(this,"windowOpacity"); ChangeOpacity - > setStartValue (0.9); changeOpacity->setEndValue(0); changeOpacity->setDuration(2500); changeOpacity->start(); QTime start = QTime::currentTime().addMSecs(2500); while(QTime::currentTime() < start) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); }

Where addMSecs() represents the number of seconds to delay,while the body of the loop represents the processing of this thread event, where 100 represents the processing of events at most 100ms will return this statement.

I’m not going to fade out the image here.

5. Expression handling

Since this is the entire string input as an expression, it needs to be judged before it is evaluated. Therefore, it is divided into two parts: judgment and calculation.

There is a new console project being used, which will be integrated later.

(1) judgment

A check class is used for judgment. Since there are only 10 digit keys, addition, subtraction, multiplication and division, decimal point, remainder, power, large, middle, and small parentheses, and Spaces, they can be divided into these categories for judgment.

A. Remove all Spaces

void removeAllBlank(void) { size_t i = 0; while((i = s.find(' ',i)) ! = string::npos) s.erase(i,1); }

Remove all whitespace first to avoid judgment later.

[B]. Classification judgment

Categorize all characters in an expression into five categories:

  • digital
  • The decimal point
  • Operator symbol + – * / ^ %
  • The left parenthesis class ([{
  • Close bracket class)]}

It then determines whether the next character of each type is a allowed type, and returns false if it is not.

For example, a (or [or {

Then the next one can’t be the operation symbol or the decimal point, but of course it allows minus and plus, because we have minus 7 times plus 234.

This is the case.

And then I’m going to save that symbol to see if it’s followed by the corresponding close bracket.

if(isLeftBrace(i)) { if(isSignOrDot(i+1)) { if(s[i+1] ! = '-' && s[i+1] ! = '+') return false; } braces.push(s[i]); }

The whole judgment function is as follows:

bool valid(void) { if(isSignOrDot(0) || isRightBrace(0)) return false; len = s.size(); stack<char> braces; for(size_t i=0; i<len; ++i) { if(isLeftBrace(i)) { if(isSignOrDot(i+1)) { if(s[i+1] ! = '-' && s[i+1] ! = '+') return false; } if(isRightBrace(i+1)) return false; braces.push(s[i]); } else if(isRightBrace(i)) { if(isDot(i+1) || isDigit(i+1) || isLeftBrace(i+1)) return false; if(isRightBrace(i+1)) { stack<char> braces_copy(braces); if(braces_copy.empty()) return false; braces_copy.pop(); if(braces_copy.empty()) return false; } if(braces.empty()) return false; char brace = braces.top(); if((brace == '(' && s[i] ! = ')') || (brace == '[' && s[i] != ']') || (brace == '{' && s[i] ! = '}')) return false; braces.pop(); } else if(isSign(i)) { if(isSign(i+1) || isDot(i+1) || isRightBrace(i+1)) return false; } else if(isDot(i)) { if(isSignOrDot(i+1) || isBrace(i+1)) return false; } else if(isDigit(i)) { if(isRightBrace(i+1)) { if(braces.empty()) return false; char brace = braces.top(); if((brace == '(' && s[i+1] ! = ')') || (brace == '[' && s[i+1] != ']') || (brace == '{' && s[i+1] ! = '}')) return false; } } } return braces.empty(); }

In particular, if you run into a closing parenthesis, you need to check whether the close parenthesis exists alone, and if it matches the previous open parenthesis.

C. Add 0

This is the case for the unary operator -, such as (-7), and then converts it to (0-7):

string getResult(void) { size_t len = s.size(); for(size_t i = 0; i<len; ++i) { if(s[i] == '(' && (s[i+1] == '-' || s[i+1] == '+')) s.insert(i+1,"0"); } return s; }

After the open braces check if it is a – or a +, and if it is, insert a 0 in the corresponding position.

(2) is calculated

A.c alc auxiliary class

Two stacks are used in the Calc helper classes, the operator stack and the operand stack.

private:
    stack<char> operators;
    stack<double> operands;

There are two important methods:

bool canCalculate(char sign);
void calculate(void);

The first method takes the next symbol to enter as an argument, determines whether the first two numbers in the operand stack can be evaluated, and if so, uses the second function.

Calculate () takes two operands out of the stack with an operator, and then pushes the result back into the operand stack.

void calculate(void) { double post = popAndGetNum(); char sign = popAndGetSign(); double pre = popAndGetNum(); Double the result = 0.0; switch (sign) { case '+': result = pre+post; break; case '-': result = pre-post; break; case '*': result = pre*post; break; case '/': if(fabs(post) < 1e-6) { cout<<"Error.Divisor is 0."; exit(EXIT_FAILURE); } else result = pre / post; break; case '^': result = pow(pre,post); break; case '%': result = static_cast<int>(pre) % static_cast<int>(post); break; } push(result); } bool canCalculate(char sign) { if(sign == '(' || sign == '[' || sign == '{' || operators.empty()) return false; char t  = getSign(); if(t == '^') return true; switch (t) { case '+': case '-': return sign == '+' || sign == '-'; case '*': case '/': case '%': return sign == '+' || sign == '-' || sign == '*' || sign == '/' || sign == '%'; } return false; }

Here are the CALC classes:

class calc { private: stack<char> operators; stack<double> operands; char getSign(void) { return operators.top(); } double getNum(void) { return operands.top(); } void popSign(void) { operators.pop(); } void popNum(void) { operands.pop(); } double popAndGetNum(void) { double num = getNum(); popNum(); return num; } char popAndGetSign(void) { char sign = getSign(); popSign(); return sign; } public: void push(double num) { operands.push(num); } void push(char sign) { operators.push(sign); } char get(void) { return getSign(); } void pop(void) { popSign(); } double result(void) { return getNum(); } void calculate(void) { double post = popAndGetNum(); char sign = popAndGetSign(); double pre = popAndGetNum(); Double the result = 0.0; switch (sign) { case '+': result = pre+post; break; case '-': result = pre-post; break; case '*': result = pre*post; break; case '/': if(fabs(post) < 1e-6) { cout<<"Error.Divisor is 0."; exit(EXIT_FAILURE); } else result = pre / post; break; case '^': result = pow(pre,post); break; case '%': result = static_cast<int>(pre) % static_cast<int>(post); break; } push(result); } bool canCalculate(char sign) { if(sign == '(' || sign == '[' || sign == '{' || operators.empty()) return false; char t  = getSign(); if(t == '^') return true; switch (t) { case '+': case '-': return sign == '+' || sign == '-'; case '*': case '/': case '%': return sign == '+' || sign == '-' || sign == '*' || sign == '/' || sign == '%'; } return false; } bool empty(void) { return operators.empty(); } };

Private encapsulates some simple utility methods that operate on two stacks, the public pop() and get() operations on the operator stack. Because there is no external operation on the operand stack, the operation is done by calculate(). The public push is overloaded and can be pushed to either the operand stack or the operator stack.

B. Calculation part

The calculation part is placed directly inside main here:

int main(void) { check chk; while(! chk.inputAndCheck()) cout<<"Error! Please input again.\n"; string s = chk.getResult(); size_t len = s.size(); calc c; for(size_t i=0; i<len; ++i) { if(isdigit(s[i])) { double num; size_t i1 = i+1; while(i1 < len && (isdigit(s[i1]) || s[i1] == '.')) ++i1; istringstream input(s.substr(i,i1)); input>>num; i = i1-1; c.push(num); } else if(s[i] == '}' || s[i] == ']' || s[i] == ')') { char sign; char start = (s[i] == '}' ? '{' : ( s[i] == ']' ? '[' : '(')); while((sign = c.get()) != start) c.calculate(); c.pop(); } else //s[i] is [ ( { + - * / ^ % { while(c.canCalculate(s[i])) c.calculate(); c.push(s[i]); } } while(!c.empty()) c.calculate(); cout<<"result is "<<c.result()<<endl; return 0; }

Each character of the expression is processed one by one, and in the case of numbers, extracted and pressed. In the case of a closed parenthesis class, continue to extract from the operator stack until the expression inside the parenthesis is evaluated.

Otherwise, if the opening parenthesis or operator is evaluated, keep evaluating as long as it is available, extracting the two operands and pushing them, then pushing the new operator.

Finally, use result() to get the result.

C. test

I’ll show you a couple of long examples here

Of course the author tests many examples

6.6 / {2.3 + 34.3 * 2.22 5% 4 * 2 + 22% [2 + 3.4/5 - (4.3 + 3.2 * 33.3)]} + 34.3 + 7.8 * {2.4-6/6 + 0-0 * [23.4-3.4/6 + 4 * (2.2 + 3)]} + 0-0 + 0.0 = 3.4 (10.8569 + 3.34) + (2) / 3.34 + 34.3 * {[(3.4) ^ 2/3.4 + 3.4/3] 3.32 + [3 * (3)]} = 28.2656 9 ^ ^ 5-34.4 2.3 + 5% 34 + 66% in 78-6-78% + 4 (3) * 3.4 / {3 * (7) + (3 + 3 * * (8) (3.4 + 4.34) / 9.3 3.2 + 0.0-0] 0.0 0} + + ^ 4/6.888 = 3.4 55683.2

If you don’t believe me, you can do it by hand.

Integration of 6.

This section integrates the interface section with the expression processing section.

(1) Set the calling process of the interface and get the output results

The program that evaluates the expression is called mycalc.exe. Note that you put it under the corresponding project folder and use the QProcess call.

Executed with execute, the expression strips out all Spaces and is passed to the evaluator as a command-line argument. The evaluator then writes the result to a file called result.txt, which is read by Qt. If # is read, the expression is typed incorrectly.

Because fixed format is set in the calculation program, so for the result

1 + 2

Also returns

3.000000

I’m going to get rid of the extra zeros here, and I’m going to pay attention to the decimal point.

(2) Modify some details

A. Mouse and keyboard modification events

Modify the setText content to pass the result.

B. Format the number in Exe

Fixed format. Otherwise, it displays scientific counting. If decimal places are required, you can set setprecision.

C. Set an error message

When an error occurs here, it prints “#”, and the main program reads it and says “expression error, please retype.”

There is also an error with a divisor of 0. Note this:

D. Consider incorporating error handling

If you enter a dot, you can’t enter a dot, or if you enter a times or a division sign, you can’t enter another symbol:

7. Pack and publish

(1) First, download the Enigma Virtual Box

(2) Add environment variables

Add the bin in the Qt folder as shown in the figure to the PATH environment variable.

(3) Pack library files

To package with WinDeployQt, first put the program in Release mode, run it once, generate the Release EXE, then copy the EXE to a separate folder, then use the command line to enter the folder and run it

windelpoyqt xxx.exe

This command copies the required DLL to the current folder.

(4) Generate a single EXE

Open the Enigma Virtual Box and select

The first one is to select the exe of Release, the second one is to select the folder after packaging, and then select Add Files, select Recursive Add, and add all the files (folders) generated in the previous step.

Here select the compressed file. Then select Compress and wait for completion.

(5) test

Hit Run.

Done!!

8. The source code

  • 1. GitHub (which contains a fully executable single EXE) Note: Because you use LFS to upload large files, you should use it when Clone

    git lfs clone
  • 2. Code cloud

9. Reference links

1. Qt fade in

2. Qt buttons

3. Qt title bar

4. Event filters

5.Qt mouse events

6.Qt delay processing

7.Qt file read and write

8.Qt is packaged into a single file

10. The last

This simple calculator has a lot of room for improvement, such as adding various “numbers “: sine, cosine, tangent, arcsine, exponential, logarithm, higher derivatives, abstract functions, and compound functions. The in the mind not number

In addition, you can also improve the rectangle button, you can change the rounded rectangle or oval. In addition, for the shadow processing can add fade in and fade out effect.

Finally is the grinding. Because Win10 is a grinding effect, the author will not…. Last but not least, let’s see what it looks like (it’s just a simple expression due to GIF size limitations…). :

I hope you have a calculator of your own!