当前位置:网站首页>Course design of compilation principle --- formula calculator (a simple calculator with interface developed based on QT)

Course design of compilation principle --- formula calculator (a simple calculator with interface developed based on QT)

2022-07-05 11:58:00 **inevitable**

It should meet the requirements of compiling principle course design , Made a simple calculator . Project based on Qt5.13.2 Development . Grammar analysis adopts LL(1) Recursive descent grammar . The calculator can realize +、-、*、/ Besides the function , You can also operate on some special functions and characters , such as PI,e,sin,cos,log wait . In the calculator interface, you can press the button to calculate , You can also input and calculate through the keyboard .

Reference material

Video link

Development tools used

Running environment :Qt 5.13.2
programing language :C++

Interface design

New project

Step1: Click on New Project

 Insert picture description here
Step2: choice Application Inside Qt Widgets Application

 Insert picture description here
Step3: Yes project Name it and save it in a file path ( Name any , But you can't have Chinese , Chinese symbols cannot have , The project path saves any , No requirement )
 Insert picture description here
Step4: Default to the next step
 Insert picture description here
Step5:Base class Choose as QWidget,Class name You can name it yourself , Here I'll name it Calcilator_UI, Also, you must check Generate from( Used to generate the interface )
 Insert picture description here
Step6: choice Desktop Qt 5.13.2 MinGW 64-bit
 Insert picture description here
Step7: Default next step
 Insert picture description here

Interface layout

 Insert picture description here

 Insert picture description here
 Insert picture description here
The layout here is just a demonstration , Friends can design a better layout according to their own aesthetic ideas

Adding styles

Step1: add to myhelper.h and myhelper.cpp file
 Insert picture description here
 Insert picture description here
Class name Defined as myhelper,Base class Defined as QWidget
 Insert picture description here
Default next step , Then click Finish myhelper Create success

Step2: Add the corresponding code
myhelper.h The documents are as follows :

#ifndef MYHELPER_H
#define MYHELPER_H

#include <QWidget>
#include <QtCore>
#include <QtGui>
#include <QDesktopWidget>
#include <QApplication>

class myhelper : public QWidget
{
    Q_OBJECT
public:
    explicit myhelper(QWidget *parent = nullptr);
    // Set to boot 
    static void AutoRunWithSystem(bool IsAutoRun, QString AppName, QString AppPath)
    {
        QSettings *reg = new QSettings(
            "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
            QSettings::NativeFormat);

        if (IsAutoRun) {
            reg->setValue(AppName, AppPath);
        } else {
            reg->setValue(AppName, "");
        }
    }

    // Set the code to UTF8
    static void SetUTF8Code()
    {
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);
#endif
    }

    // Set skin style 
    static void SetStyle(const QString &styleName)
    {
        QFile file(QString("://image/%1.css").arg(styleName));
        file.open(QFile::ReadOnly);
        QString qss = QLatin1String(file.readAll());
        qApp->setStyleSheet(qss);
        qApp->setPalette(QPalette(QColor("#F0F0F0")));
    }

    // Load Chinese characters 
    static void SetChinese()
    {
        QTranslator *translator = new QTranslator(qApp);
        translator->load("://image/qt_zh_CN.qm");
        qApp->installTranslator(translator);
    }

    // Judge whether it is IP Address 
    static bool IsIP(QString IP)
    {
        QRegExp RegExp("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)");
        return RegExp.exactMatch(IP);
    }



    // Time delay 
    static void Sleep(int sec)
    {
        QTime dieTime = QTime::currentTime().addMSecs(sec);
        while ( QTime::currentTime() < dieTime ) {
            QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
        }
    }

    // Center the form 
    static void FormInCenter(QWidget *frm)
    {
        int frmX = frm->width();
        int frmY = frm->height();
        QDesktopWidget w;
        int deskWidth = w.width();
        int deskHeight = w.height();
        QPoint movePoint(deskWidth / 2 - frmX / 2, deskHeight / 2 - frmY / 2);
        frm->move(movePoint);
    }

signals:

public slots:
};

#endif // MYHELPER_H

myhelper.cpp The documents are as follows :

#include "myhelper.h"

myhelper::myhelper(QWidget *parent) : QWidget(parent)
{

}

Step3: Add resources

 Insert picture description here
choice Qt Medium Qt Resource File

 Insert picture description here
 Insert picture description here
Remember that the name here must be rc, Because this is corresponding to the program , If not named rc, You need to make some modifications to the next program

The next step is to complete by default
 Insert picture description here
Then add the prefix
 Insert picture description here
The prefix name must be ’/’
 Insert picture description here
 Insert picture description here
Then add the current path image All styles in the file
 Insert picture description here

 Insert picture description here

And then compile it , The compiled results are as follows
 Insert picture description here
image File download

Step4: add to icon_style.h and icon_style.cpp file
Before you add it, start with Calculator.pro Add... To the file QT += sql, And compile it
 Insert picture description here
Then add icon_style.h and icon_style.cpp file
 Insert picture description here
Class name Defined as icon_style,Base class Defined as QWidget
 Insert picture description here
The next step is to complete by default
 Insert picture description here
Step5: Add the corresponding code
icon_style.h The documents are as follows :

#ifndef ICON_STYLE_H
#define ICON_STYLE_H

#include <QWidget>
#include <QFont>
#include <QFontDatabase>
#include <QMutex>
#include <QLabel>
#include <QPushButton>
#include <QApplication>

class icon_style : public QWidget
{
    Q_OBJECT
public:
    explicit icon_style(QWidget *parent = nullptr);
    static icon_style* Instance()
    {
        static QMutex mutex;
        if (!_instance) {
            QMutexLocker locker(&mutex);
            if (!_instance) {
                _instance = new icon_style;
            }
        }
        return _instance;
    }

    void SetIcon(QLabel* lab, QChar c, int size = 10);
    void SetIcon(QPushButton* btn, QChar c, int size = 10);

signals:

private:
    QFont iconFont;
    static icon_style* _instance;

public slots:
};

#endif // ICON_STYLE_H

icon_style.cpp The documents are as follows :

#include "icon_style.h"

icon_style* icon_style::_instance = 0;
icon_style::icon_style(QWidget *parent) : QWidget(parent)
{
    int fontId = QFontDatabase::addApplicationFont(":/image/fontawesome-webfont.ttf");
    QString fontName = QFontDatabase::applicationFontFamilies(fontId).at(0);
    iconFont = QFont(fontName);
}

void icon_style::SetIcon(QLabel* lab, QChar c, int size)
{
    iconFont.setPointSize(size);
    lab->setFont(iconFont);
    lab->setText(c);
}

void icon_style::SetIcon(QPushButton* btn, QChar c, int size)
{
    iconFont.setPointSize(size);
    btn->setFont(iconFont);
    btn->setText(c);
}

meanwhile calculator_ui.h The file is changed as follows :

#ifndef CALCULATOR_UI_H
#define CALCULATOR_UI_H

#include <QWidget>
#include <QApplication>

QT_BEGIN_NAMESPACE
namespace Ui { class Calculator_UI; }
QT_END_NAMESPACE

class Calculator_UI : public QWidget
{
    Q_OBJECT

public:
    Calculator_UI(QWidget *parent = nullptr);
    ~Calculator_UI();
    void InitStyle_Calculator();

private:
    Ui::Calculator_UI *ui;
    QPoint mousePoint;
    bool mousePressed;
    bool max;
    QRect location;
};
#endif // CALCULATOR_UI_H

calculator_ui.cpp The file is changed as follows :

#include "calculator_ui.h"
#include "ui_calculator_ui.h"

#include "myhelper.h"
#include "icon_style.h"


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

    myhelper::FormInCenter(this);
    this->InitStyle_Calculator();

}

Calculator_UI::~Calculator_UI()
{
    delete ui;
}

void Calculator_UI::InitStyle_Calculator()
{
    // Set the form title bar to hide 
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
    location = this->geometry();
    max = false;
    mousePressed = false;

    // Install event listener , Let the title bar recognize double clicking 
    ui->label_title->installEventFilter(this);

    icon_style::Instance()->SetIcon(ui->button_close, QChar(0xf00d), 10);
    icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf096), 10);
    icon_style::Instance()->SetIcon(ui->button_min, QChar(0xf068), 10);
    icon_style::Instance()->SetIcon(ui->btnMenu, QChar(0xf0c9), 10);
    icon_style::Instance()->SetIcon(ui->lab_Ico, QChar(0xf015), 12);
}

main.cpp The file is modified as follows :

#include "calculator_ui.h"

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    myhelper::SetUTF8Code();
   myhelper::SetStyle("blue");// Blue style 

   myhelper::SetChinese();

    Calculator_UI w;
    w.show();
    return a.exec();
}

The effect after compilation and operation is shown in the following figure :

 Insert picture description here
Press the button on the interface here and there will be no response , Then add zoom , Zoom in and close button functions
First, in the ui Add slot function in the design interface , That is, for shrinking , Right click the zoom in and close buttons , Then click go to slot , Then press ok that will do . Here we use button_close For example :
 Insert picture description here
 Insert picture description here
button_max and button_min similar

calculator_ui.h The file is added as follows :

#ifndef CALCULATOR_UI_H
#define CALCULATOR_UI_H

#include <QWidget>
#include <QApplication>

QT_BEGIN_NAMESPACE
namespace Ui { class Calculator_UI; }
QT_END_NAMESPACE

class Calculator_UI : public QWidget
{
    Q_OBJECT

public:
    Calculator_UI(QWidget *parent = nullptr);
    ~Calculator_UI();
    void InitStyle_Calculator();

protected:
    bool eventFilter(QWidget *obj, QEvent *event);
    void mouseMoveEvent(QMouseEvent *e);
    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *);

private slots:
    void on_button_close_clicked();

    void on_button_max_clicked();

    void on_button_min_clicked();

private:
    Ui::Calculator_UI *ui;
    QPoint mousePoint;
    bool mousePressed;
    bool max;
    QRect location;
};
#endif // CALCULATOR_UI_H

calculator_ui.cpp The file is added as follows :

#include "calculator_ui.h"
#include "ui_calculator_ui.h"

#include "myhelper.h"
#include "icon_style.h"


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

    myhelper::FormInCenter(this);
    this->InitStyle_Calculator();

}

Calculator_UI::~Calculator_UI()
{
    delete ui;
}

void Calculator_UI::InitStyle_Calculator()
{
    // Set the form title bar to hide 
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
    location = this->geometry();
    max = false;
    mousePressed = false;

    // Install event listener , Let the title bar recognize double clicking 
    ui->label_title->installEventFilter(this);

    icon_style::Instance()->SetIcon(ui->button_close, QChar(0xf00d), 10);
    icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf096), 10);
    icon_style::Instance()->SetIcon(ui->button_min, QChar(0xf068), 10);
    icon_style::Instance()->SetIcon(ui->btnMenu, QChar(0xf0c9), 10);
    icon_style::Instance()->SetIcon(ui->lab_Ico, QChar(0xf015), 12);
}

bool Calculator_UI::eventFilter(QWidget*obj, QEvent *event)
{
    if (event->type() == QEvent::MouseButtonDblClick) {
        this->on_button_max_clicked();
        return true;
    }
    return QObject::eventFilter(obj, event);
}
void Calculator_UI::mouseMoveEvent(QMouseEvent *e)
{
    if (mousePressed && (e->buttons() && Qt::LeftButton) && !max)
    {
        this->move(e->globalPos() - mousePoint);
        e->accept();
    }
}

void Calculator_UI::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton) {
        mousePressed = true;
        mousePoint = e->globalPos() - this->pos();
        e->accept();
    }
}

void Calculator_UI::mouseReleaseEvent(QMouseEvent *)
{
    mousePressed = false;
}


void Calculator_UI::on_button_max_clicked()
{
    if (max) {
        this->setGeometry(location);
        icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf096), 10);
        ui->button_max->setToolTip(" Maximize ");
    } else {
        location = this->geometry();
        this->setGeometry(qApp->desktop()->availableGeometry());
        icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf079), 10);
        ui->button_max->setToolTip(" Restore ");
    }
    max = !max;
}

void Calculator_UI::on_button_min_clicked()
{
    this->showMinimized();
}

void Calculator_UI::on_button_close_clicked()
{
    qApp->exit();
}

After adding the corresponding code , The interface can be reduced , close , Move and zoom , Note that the interface will not zoom in after pressing the zoom in button , Because in interface design , The maximum length of the overall interface , The width and the minimum length and width are fixed , If you want to see the zoom function, you only need to make corresponding adjustments and layout in the interface design

Lexical analysis

preparation

add to token.h and token.cpp file , The steps are similar to the previous style addition .
Right click Calculator_Design -> Click on Add New -> choice C++ Inside C++ Class -> Class name As the token -> Base class As the QWidget -> Click next -> Click finish

After the project is completed , When formally writing code , First the Calculator_Design.pro Medium c++ 11 Change it to c++ 17 standard , Because it needs to be used in later programming c++17 New characteristics .
 Insert picture description here

Code addition

token.h
#ifndef TOKEN_H
#define TOKEN_H

#include <QWidget>
#include <string>
#include <tuple>  // Used to store the return value 
#include <variant>
#include <functional> // hold lambda Stored as a variable 
#include <optional>


enum class TokenType
{
    // Numbers 
    Number,
    // Symbol , English letter a-z,A-Z
    Symbo,
    // Used to indicate the end of 
    End,	// Stop sign 
    // Used to indicate an error 
    Error,	// Error symbol 
    Plus = '+',
    Minus = '-',
    Mul = '*',
    Div = '/',
    Lp = '(',
    Rp = ')',

};

struct Token  // Define the symbolic structure 
{
    TokenType type;  // Symbol type 
    std::variant<double,std::string> value = 0.0;
};


// Find the specific definition of the symbol in the symbol table 
std::optional<std::variant<double, std::function<double(double)>>> getSymboValue(std::string symbo);


//input = "1 + 2 * 3"
//tokenize("1 + 2 * 3") ->  Symbol 1 +  The remaining string "+ 2 * 3"
//tokenize("+ 2 * 3") ->  Operator + +  The remaining string "2 * 3"
//tokenize("2 * 3") ->  Numbers 2 +  The remaining string "* 3"
//....
// The return values here are multiple , It can be used tuple
std::tuple<Token, std::string> tokenize(std::string input);
// Input is  std::string input
// The first return value is Token, The second return value is the remaining string std::string

class token : public QWidget
{
    Q_OBJECT
public:
    explicit token(QWidget *parent = nullptr);

signals:

public slots:
};

#endif // TOKEN_H

token.cpp
#include "token.h"
#include <math.h>
#include <stdexcept>  // exception handling 
#include <unordered_map> // The index value can be a string 
#include <functional> // hold lambda Stored as a variable 


// The symbol table 
// The index value is string
// The return type is variant, There are two possibilities. One is double, The other is to input double, Return to double A function of 
static std::unordered_map<std::string, std::variant<double, std::function<double(double)>>> SymboTable
{
    {"PI",atan(1.0) * 4},
    {"e",exp(1.0)},
    {"sin",[](double val) {return sin(val); }},
    {"cos",[](double val) {return cos(val); }},
    {"asin",[](double val) {return asin(val); }},
    {"acos",[](double val) {return acos(val); }},
    {"tan",[](double val) {return tan(val); }},
    {"atan",[](double val) {return atan(val); }},
    {"sqrt",[](double val) {return sqrt(val); }},
    {"log", [](double val) {return log(val); }},
};


// Analytic symbol 
static std::tuple<std::string, std::string> parseSymbo(std::string input)
{
    std::string symbo;
    while (1)
    {
        // The input string ends , Get out of the loop 
        if (input.empty())
        {
            break;
        }
        // Get the first character 
        char ch = input.front();
        if (isalpha(ch))
        {
            symbo.push_back(ch);
            input.erase(input.begin());
        }
        else
        {
            break;
        }
    }
    return { symbo,input };
}

// use static Indicates that this function is only used in this .cpp Meaningful in the document 
static std::tuple<double, std::string> parseNumber(std::string input)
{
    std::string numstr;
    // It means that the decimal point is encountered for the first time 
    // avoid 1.5.5 This situation 
    bool firstDot = true;

    while (1)
    {
        if (input.empty())
        {
            break;
        }
        char ch = input.front();

        if ((ch >= '0' && ch <= '9') || (ch == '.' && firstDot))
        {
            numstr.push_back(ch);

            input.erase(input.begin());
            if (ch == '.')
            {
                firstDot = false;
            }

        }
        else
        {
            break;
        }

    }
    //stod Used to convert characters into numbers 
    return { std::stod(numstr) , input };

}

// Find the specific definition of the symbol in the symbol table 
std::optional<std::variant<double, std::function<double(double)>>> getSymboValue(std::string symbo)
{
    if (auto iter = SymboTable.find(symbo); iter != SymboTable.end())
    {
        // Return its specific value 
        return { iter->second };
    }
    return {};
}


std::tuple<Token, std::string> tokenize(std::string input)
{
    Token tk;
    char ch;
    // The first thing is to remove the opening space 
    do
    {
        if (input.empty()) // Input empty 
        {
            tk.type = TokenType::End;
            return { tk,"" };
        }
        else
        {
            // Get input The first character 
            ch = input.front();// Take the first character 
            // And change this character from input Remove the beginning of 
            input.erase(input.begin());
        }

    } while (ch == ' ');
    // The second thing is to produce the corresponding character based on the first non blank character token For return 
    switch (ch)
    {
    case '+':
    case '-':
    case '*':
    case '/':
    case '(':
    case ')':
        tk.type = TokenType(ch);
        break;
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        tk.type = TokenType::Number;
        // hold ch Re add to input The beginning of 
        // For example, it begins with 1.0
        input.insert(input.begin(), ch);
        // Turn the number in this string into a real number 
        std::tie(tk.value, input) = parseNumber(input);
        break;
    default:
        // Judge whether it is a-z,A-Z The characters of 
        if (isalpha(ch))
        {
            tk.type = TokenType::Symbo;
            // hold ch Re add to input The beginning of 
            input.insert(input.begin(), ch);
            // Parse this out symbo
            std::tie(tk.value, input) = parseSymbo(input);
        }
        else
        {
            // Use exceptions to express errors 
            throw std::runtime_error(" error :  There are illegal symbols !\n");
        }
        break;
    }
    return { tk,input };
}

token::token(QWidget *parent) : QWidget(parent)
{

}

Syntax analysis

preparation

add to parser.h and parser.cpp file , The steps are similar to the previous style addition .
Right click Calculator_Design -> Click on Add New -> choice C++ Inside C++ Class -> Class name As the parser -> Base class As the QWidget -> Click next -> Click finish
After the project is completed , Start writing code

Code addition

parser.h
#ifndef PARSER_H
#define PARSER_H

#include <QWidget>
#include <string>
#include <tuple>

// Parsing is not one-time , Instead, analyze them one by one   So return with tuple To return multiple values 
std::tuple<double, std::string> parseExpress(std::string input);

class parser : public QWidget
{
    Q_OBJECT
public:
    explicit parser(QWidget *parent = nullptr);
    bool is_contain_lp = false;

signals:


public slots:
};

#endif // PARSER_H

parser.cpp
#include "parser.h"
#include "token.h"

// Parsing is not one-time , Instead, analyze them one by one   So return with tuple To return multiple values 
static std::tuple<double, std::string> parseFactor(std::string input)
{
    double result;
    Token tk;
    // Parse the first token
    std::tie(tk, input) = tokenize(input);

    switch (tk.type)
    {
    case TokenType::Number:
        result = std::get<double>(tk.value);
        break;

    case TokenType::Symbo:
    {	// First search whether the symbol is defined in the symbol table 
        auto search = getSymboValue(std::get<std::string>(tk.value));

        if (search)
        {
            // There are two situations 
            // situation 1 symbo Is a constant , here result be equal to symbo The specific value of the corresponding constant 
            if (std::holds_alternative<double>(search.value()))
            {
                result = std::get<double>(search.value());
            }
            // situation 2 symbo Is the function , Continue parsing at this time (E) And put the parsed E Act on symbo Corresponding function 
            // The value calculated by the function is assigned to result
            else
            {
                // Get the function ontology 
                auto fun = std::get<std::function<double(double)>>(search.value());
                // Analyze a (
                std::tie(tk, input) = tokenize(input);
                if (tk.type != TokenType::Lp)
                {
                    throw std::runtime_error(" Grammar mistakes : The expression is missing '('\n");
                }
                // Analytic expression 
                double v;
                std::tie(v, input) = parseExpress(input);
                // analysis )
                std::tie(tk, input) = tokenize(input);
                if (tk.type != TokenType::Rp)
                {
                    throw std::runtime_error(" Grammar mistakes : The expression is missing ')'!\n");
                }
                result = fun(v);

            }
        }
        // An exception is thrown when the symbol is not found 
        else
        {
            throw std::runtime_error(" Grammar mistakes :  There are illegal symbols  "+ std::get<std::string>(tk.value)+"\n");
        }

        break;
    }
    case TokenType::Lp:
        // analysis (E) Inside E
        std::tie(result, input) = parseExpress(input);
        // Analyze another )
        std::tie(tk, input) = tokenize(input);
        // If the parsed one is not ) It means that the expression entered is wrong 
        if (tk.type != TokenType::Rp)
        {
            throw std::runtime_error(" Grammar mistakes : The expression is missing ')'!\n");
        }
        break;
    default:
        // Grammatical mistakes , Exception should be thrown 
        throw std::runtime_error(" Grammar mistakes :  The expression is missing numbers or '('!\n");
        break;
    }
    return { result,input };
}
// Parsing is not one-time , Instead, analyze them one by one   So return with tuple To return multiple values 
static std::tuple<double, std::string> parseTerm(std::string input)
{
    double result;
    // translate T -> FR Medium F
    std::tie(result, input) = parseFactor(input);

    // translate T -> FR Medium R
    bool loop = true; // Use this variable to jump out of the loop 
    while (loop)
    {
        Token op;
        std::string res;
        double term;
        // translate R -> *TR | /TR | null  One of the first token, That's it  +  perhaps  -  Or empty 
        std::tie(op, res) = tokenize(input);

        switch (op.type)
        {
        case TokenType::Mul:
            // analysis *TR Medium T
            std::tie(term, input) = parseFactor(res);
            // Update according to the semantics of multiplication result The numerical 
            result *= term;
            break;
        case TokenType::Div:
            // analysis /TR Medium T
            std::tie(term, input) = parseFactor(res);
            // You can't divide by 0
            if (term == 0.0)
            {
                throw std::runtime_error(" error :  The divisor cannot be zero 0!\n");
            }
            // Update according to the semantics of division result The numerical 
            result /= term;
            break;
        default:
            // Exit loop 
            loop = false;
            break;
        }
    }

    return { result,input };
}

// Parsing is not one-time , Instead, analyze them one by one   So return with tuple To return multiple values 
//E -> TR
//R -> +TR | -TR | null
 std::tuple<double, std::string> parseExpress(std::string input)
{
    double result;
    // translate E -> TR
    std::tie(result, input) = parseTerm(input);

    // translate E -> TR Medium R
    bool loop = true; // Use this variable to jump out of the loop 
    while (loop)
    {
        Token op;
        std::string res;
        double term;
        // translate R -> +TR | -TR | null  One of the first token, That's it  +  perhaps  -  Or empty 
        std::tie(op, res) = tokenize(input);

        switch (op.type)
        {
        case TokenType::Plus:
            // analysis +TR Medium T
            std::tie(term,input) = parseTerm(res);
            // Update according to the semantics of addition result The numerical 
            result += term;
            break;
        case TokenType::Minus:
            // analysis -TR Medium T
            std::tie(term, input) = parseTerm(res);
            // Update according to the semantics of subtraction result The numerical 
            result -= term;
            break;
        default:
            // Exit loop 
            loop = false;
            break;
        }
    }

    return { result,input };
}
parser::parser(QWidget *parent) : QWidget(parent)
{

}

Test without interface

main.cpp

#include "calculator_ui.h"

#include "myhelper.h"
#include <QApplication>
#include "parser.h"
#include <stdio.h>
#include <QDebug>


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //std::string input = "1.23 + 2 * (1 + 2 / 5 - 4)";
    std::string input = "1.23 + 2 * (1 + 2 / 5 - 4)";
    //std::string input = "1+tan(PI/4)";
    //std::string input = "1+sin(PI/6)*sqrt(32*sin(PI/6))";
    //auto[result,res] = parseExpress(input);
    try
    {
        auto[result, res] = parseExpress(input);
        qDebug() << result;
    }
    catch (std::exception& e)
    {
        qDebug() << e.what();
    }
    myhelper::SetUTF8Code();
   myhelper::SetStyle("blue");// Blue style 

   myhelper::SetChinese();

    Calculator_UI w;
    w.show();
    return a.exec();
}

The test results are as follows : Insert picture description here

Realize the interface calculation function

preparation

double-click calculator_ui.ui -> Right click each button -> Click to go to slot -> Click on ok

Code addition

calculator_ui.h
#ifndef CALCULATOR_UI_H
#define CALCULATOR_UI_H

#include <QWidget>
#include <QApplication>

QT_BEGIN_NAMESPACE
namespace Ui { class Calculator_UI; }
QT_END_NAMESPACE

class Calculator_UI : public QWidget
{
    Q_OBJECT

public:
    Calculator_UI(QWidget *parent = nullptr);
    ~Calculator_UI();
    void InitStyle_Calculator();

protected:
    bool eventFilter(QWidget *obj, QEvent *event);
    void mouseMoveEvent(QMouseEvent *e);
    void mousePressEvent(QMouseEvent *e);
    void mouseReleaseEvent(QMouseEvent *);

private slots:
    void on_button_close_clicked();

    void on_button_max_clicked();

    void on_button_min_clicked();

    void on_button_0_clicked();

    void on_button_1_clicked();

    void on_button_2_clicked();

    void on_button_3_clicked();

    void on_button_4_clicked();

    void on_button_5_clicked();

    void on_button_6_clicked();

    void on_button_7_clicked();

    void on_button_8_clicked();

    void on_button_9_clicked();

    void on_button_dot_clicked();

    void on_button_clear_clicked();

    void on_button_back_clicked();

    void on_button_Plus_clicked();

    void on_button_Minus_clicked();

    void on_button_Mul_clicked();

    void on_button_Div_clicked();

    void on_button_Lp_clicked();

    void on_button_Rp_clicked();

    void on_button_euqal_clicked();

    void on_button_sin_clicked();

    void on_button_asin_clicked();

    void on_button_cos_clicked();

    void on_button_acos_clicked();

    void on_button_tan_clicked();

    void on_button_atan_clicked();

    void on_button_sqrt_clicked();

    void on_button_log_clicked();

    void on_button_PI_clicked();

    void on_button_e_clicked();

private:
    Ui::Calculator_UI *ui;
    QPoint mousePoint;
    bool mousePressed;
    bool max;
    QRect location;

    // The initialization output box displays 0
    QString my_Display = "0";

    // Determine whether to input for the first time , If yes, yes true
    bool is_FirstInput = true;

    // To judge whether something is wrong , In case of error true, Otherwise false
    bool is_Error = false;

    // Determine whether there is an operator , If yes, it is true, Otherwise false
    bool is_Sym = false;

    // Judge "(" Is it a number or +-*/(, If it is a number, it is true, Otherwise false
    bool lp_left_is_num = false;
};
#endif // CALCULATOR_UI_H
calculator_ui.cpp
#include "calculator_ui.h"
#include "ui_calculator_ui.h"

#include "myhelper.h"
#include "icon_style.h"
#include <string>
#include <stdexcept>
#include "parser.h"

#include <QVariantList>
// Set the display mode of the text box 
#include <QCompleter>
#include <QStringList>

#include <QPushButton>
#include <QLineEdit>

//QT Regular expression engine 
#include <QRegExp>


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

    myhelper::FormInCenter(this);
    this->InitStyle_Calculator();
    ui->lineEdit->setText(my_Display);

}

Calculator_UI::~Calculator_UI()
{
    delete ui;
}

void Calculator_UI::InitStyle_Calculator()
{
    // Set the form title bar to hide 
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
    location = this->geometry();
    max = false;
    mousePressed = false;

    // Install event listener , Let the title bar recognize double clicking 
    ui->label_title->installEventFilter(this);

    icon_style::Instance()->SetIcon(ui->button_close, QChar(0xf00d), 10);
    icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf096), 10);
    icon_style::Instance()->SetIcon(ui->button_min, QChar(0xf068), 10);
    icon_style::Instance()->SetIcon(ui->btnMenu, QChar(0xf0c9), 10);
    icon_style::Instance()->SetIcon(ui->lab_Ico, QChar(0xf015), 12);
}

bool Calculator_UI::eventFilter(QWidget*obj, QEvent *event)
{
    if (event->type() == QEvent::MouseButtonDblClick) {
        this->on_button_max_clicked();
        return true;
    }
    return QObject::eventFilter(obj, event);
}
void Calculator_UI::mouseMoveEvent(QMouseEvent *e)
{
    if (mousePressed && (e->buttons() && Qt::LeftButton) && !max)
    {
        this->move(e->globalPos() - mousePoint);
        e->accept();
    }
}

void Calculator_UI::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton) {
        mousePressed = true;
        mousePoint = e->globalPos() - this->pos();
        e->accept();
    }
}

void Calculator_UI::mouseReleaseEvent(QMouseEvent *)
{
    mousePressed = false;
}


void Calculator_UI::on_button_max_clicked()
{
    if (max) {
        this->setGeometry(location);
        icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf096), 10);
        ui->button_max->setToolTip(" Maximize ");
    } else {
        location = this->geometry();
        this->setGeometry(qApp->desktop()->availableGeometry());
        icon_style::Instance()->SetIcon(ui->button_max, QChar(0xf079), 10);
        ui->button_max->setToolTip(" Restore ");
    }
    max = !max;
}

void Calculator_UI::on_button_min_clicked()
{
    this->showMinimized();
}

void Calculator_UI::on_button_close_clicked()
{
    qApp->exit();
}



void Calculator_UI::on_button_0_clicked()
{
    // Assign the contents of the text box to my_Display
    my_Display = ui->lineEdit->text();
    // Get the information of pressing the button 
    QString text = ui->button_0->text();
    if(my_Display!="0")   // Not the first one 0( It's not just one 0)
    {
        // Find the last one +-*/() The location of 
        auto index = my_Display.lastIndexOf(QRegExp("[+-*/()]"));
        if(index!=-1)
        {
            // Find the last one +-*/() String after 
            auto res = my_Display.right(my_Display.length()-index-1);
            if(res != "0")
            {
                my_Display += "0";
                ui->lineEdit->setText(my_Display);

            }
        }
        else
        {
            my_Display += "0";
            ui->lineEdit->setText(my_Display);
        }
    }


    bool is_Sym = my_Display.contains(QRegExp("[+-*/]"));
    // Used to display the initial 0.0000.....number
    bool is_Number = my_Display.contains(QRegExp("[123456789]"));
    if(!is_Sym && is_Number)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_1_clicked()
{
    // Here, the content of the text box is assigned to my_Display In order to give consideration to keyboard input 
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_1->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_2_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_2->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}


void Calculator_UI::on_button_3_clicked()
{
    my_Display = ui->lineEdit->text();
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_3->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_4_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_4->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_5_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_5->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_6_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_6->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_7_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_7->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_8_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_8->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_9_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_9->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_dot_clicked()
{
    my_Display = ui->lineEdit->text();
    // Find the last of the text first '.'
    auto index = my_Display.lastIndexOf('.');
    if(index == -1)  // There is no decimal point in front 
    {
        my_Display += '.';
        ui->lineEdit->setText(my_Display);
        is_Sym = true;
        is_FirstInput = false;
        is_Error = false;
    }
    else
    {
        // Take out the string after the last decimal point 
        auto res = my_Display.right(my_Display.length()- index - 1);
        // Judge this res Whether there is an operator in () + - * / ( Regular expressions )
        //rbegin Of r It means reverse
        if(res.contains(QRegExp("[()+-*/]")) && res.rbegin()->isNumber())
        {
            my_Display += '.';
            ui->lineEdit->setText(my_Display);
        }
    }

}

// Empty 
void Calculator_UI::on_button_clear_clicked()
{
    my_Display = ui->lineEdit->text();
    my_Display = "0";
    is_FirstInput = true;
    ui->lineEdit->setText(my_Display);
}

// Backspace 
void Calculator_UI::on_button_back_clicked()
{
    my_Display = ui->lineEdit->text();
    if(my_Display != "")
    {
        my_Display.remove(my_Display.length()-1,1);
        ui->lineEdit->setText(my_Display);

    }
    if(my_Display == "")
    {
        my_Display = "0";
        ui->lineEdit->setText(my_Display);
        is_FirstInput = true;
    }
}

void Calculator_UI::on_button_Plus_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_Plus->text();

    // Add a new button to the existing text 
    my_Display += text;
    // In order to make the calculation continue 
    is_Sym = my_Display.contains(QRegExp("[+-*/]"));

    // Set back 
    ui->lineEdit->setText(my_Display);

}

void Calculator_UI::on_button_Minus_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_Minus->text();

    // Add a new button to the existing text 
    my_Display += text;

    is_Sym = my_Display.contains(QRegExp("[+-*/]"));
    // Set back 
    ui->lineEdit->setText(my_Display);
}

void Calculator_UI::on_button_Mul_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_Mul->text();

    // Add a new button to the existing text 
    my_Display += text;

    is_Sym = my_Display.contains(QRegExp("[+-*/]"));
    // Set back 
    ui->lineEdit->setText(my_Display);
}

void Calculator_UI::on_button_Div_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_Div->text();

    // Add a new button to the existing text 
    my_Display += text;

    is_Sym = my_Display.contains(QRegExp("[+-*/]"));
    // Set back 
    ui->lineEdit->setText(my_Display);
}

void Calculator_UI::on_button_Lp_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_Lp->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add the value of the new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
    // Find the last one "(" The location of 
    auto index = my_Display.lastIndexOf("(");

    // Find the last one "(" The first character and itself 
    auto res = my_Display.right(my_Display.length()-index+1);
    res = res.left(res.length()-1);
    bool lp_left_is_sym = res.contains(QRegExp("[+-*/(gnst]")); 
   //g,n,s,t respectively log,tan,atan... The last character 
    if(!lp_left_is_sym && index!=0)
    {
        lp_left_is_num = true;
    }
}

void Calculator_UI::on_button_Rp_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_Rp->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add the value of the new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }

}

// Calculation 
void Calculator_UI::on_button_euqal_clicked()
{
    my_Display = ui->lineEdit->text();
    // Statistics '(' The number of 
    int count_lp=0;
    // Statistics ')' The number of 
    int count_rp=0;
    for(int i=0;i<my_Display.length();i++)
    {
        if(my_Display[i] == '(')
        {
            count_lp++;
        }
        if(my_Display[i] == ')')
        {
            count_rp++;
        }

    }
    try
    {
        auto[result, res] = parseExpress(my_Display.toLatin1().data());
        my_Display = QString::number(result);
        is_Sym = false;
        // Set back 
        ui->lineEdit->setText(my_Display);
        if(lp_left_is_num == true)
        {
            is_Error = true;
            lp_left_is_num = false;
            throw std::runtime_error(" error :  Symbol '(' Missing operator on the left !\n");
        }
        if(count_lp != count_rp)
        {
            throw std::runtime_error(" error :  Symbol  '('  And  ')'  Number mismatch !\n");
        }
    }
    catch (std::exception& e)
    {
        my_Display = e.what();
        is_Error = true;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}



void Calculator_UI::on_button_sin_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_sin->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_asin_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_asin->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_cos_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_cos->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_acos_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_acos->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_tan_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_tan->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_atan_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_atan->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_sqrt_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_sqrt->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_log_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_log->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_PI_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_PI->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

void Calculator_UI::on_button_e_clicked()
{
    my_Display = ui->lineEdit->text();
    // Get text information 
    QString text = ui->button_e->text();

    if(is_FirstInput || is_Error || !is_Sym)
    {
        my_Display = text;
        ui->lineEdit->setText(my_Display);
        is_FirstInput = false;
        is_Error = false;
        is_Sym = true;
    }
    else
    {
        // Add a new button to the existing text 
        my_Display += text;
        // Set back 
        ui->lineEdit->setText(my_Display);
    }
}

Test with interface

Calculate the test correctly

test 1

 Insert picture description here
 Insert picture description here

test 2

 Insert picture description here
 Insert picture description here

test 3

 Insert picture description here
 Insert picture description here

Miscalculation test

test 1

 Insert picture description here
 Insert picture description here

test 2

 Insert picture description here
 Insert picture description here

test 3

 Insert picture description here
 Insert picture description here

test 4

 Insert picture description here

 Insert picture description here

Complete project Download

Clean Calculator Beta

原网站

版权声明
本文为[**inevitable**]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140533367203.html