Evgenii Legotckoi
Evgenii LegotckoiApril 21, 2018, 4:18 p.m.

Qt/C++ - Tutorial 076. Visualizing Mathematical Formulas on Qt

Recently, a rather interesting question appeared on the forum, about how you can visualize formulas in Qt. Unfortunately, I did not have the opportunity to deal with the site and the forum for a long time, but decided that presenting my possible vision of the problem even after a while would be useful.

The question is to visualize the formula, which is written in the form of a line.

For example, you type sqrt(5) in some input field, and in some widget you will display a graphical display of the square root of 5, that is, not the result, but the formula itself. Similar functionality is implemented in Latex and LibreOffice .

It will look like this.

By itself, the task of writing such a functional in my opinion can be quite difficult and time consuming, especially if it comes to implementing a functional with embedded formulas, etc. Therefore, I will focus on the concept that occurred to me to implement the mapping of the simplest variants of formulas, i.e. without nested subformulas.


Basic Provisions

To implement this functionality requires:

  • Using regular expressions to highlight the formula
  • Display the formula on the widget

I believe that for this task the use of regular expressions will be quite appropriate, since you need to correctly parse the string and allocate the instances of the formulas. To create a graphical display of the formula, we'll write a special FormulaWidget class that will contain a regular expression for searching in the formula string, as well as a method that will draw the formula at the specified point on the widget using the QPainter object that will be provided by the widget itself. This method will have to return a new position to draw the next formula so that they do not overlap if several formulas are passed in the row.

To draw formulas, create a class inherited from QWidget and redefine its paintEvent () method, which is responsible for rendering the contents of this widget.

Main application window

As the main application window, the class inherited from QWidget will be used. You select it when creating a project. Also, it will have a graphical form file, in which it will be necessary to arrange widgets of interest to us:

  • QLineEdit - where we write down the formula
  • FormulaWidget - where the formula will be displayed

In the graphical editor, you will need to add the object of a normal widget and use the context menu to convert it to the previously created FormulaWidget class. In the context menu there is for this the item "Promote to ...".

I will not touch the contents of the main.cpp file, because everything is created by default.

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp

To transfer the typed text to the widgets of the formula, we will use the connection of the textChanged signal from QLineEdit to the setFormula slot, which we will create in the FormulaWidget class.

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->lineEdit, &QLineEdit::textChanged, ui->formulaWidget, &FormulaWidget::setFormula);
}

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

Classes for drawing formulas

Now consider the class itself for visualizing the formulas and the class of the formula itself. Both of these classes will be located in one header file ( FormulaWidget.h ) and an implementation file ( FormulaWidget.cpp ).

FormulaItem

FormulaWidget.h

// The visualization class of the formula
class FormulaItem
{
public:
    explicit FormulaItem(QString value) : m_value(value){}

    static const QString REGULAR_EXPRESSION; // The string of the regular expression for finding the formula
    // Метод отрисовки формулы
    QPoint draw(const QPoint& pos, QPainter& p) const;

private:
    QString m_value; // The value of the formula
};

FormulaWidget.cpp

const QString FormulaItem::REGULAR_EXPRESSION = "sqrt\\((?<value>\\d+)\\)";

QPoint FormulaItem::draw(const QPoint& pos, QPainter& p) const
{
    int valueWidth = p.fontMetrics().width(m_value);
    int valueHeight = p.fontMetrics().height();

    p.drawLine(pos.x(), 4 + valueHeight / 2, pos.x() + 5, 4 + valueHeight);
    p.drawLine(pos.x() + 5, 4 + valueHeight, pos.x() + 10, pos.y() + 1);
    p.drawLine(pos.x() + 10, pos.y() + 1, pos.x() + 14 + valueWidth, pos.y() + 1);

    p.drawText(QRect(pos.x() + 12, pos.y() + 4, pos.x() + 12 + valueWidth, pos.y() + 4 + valueHeight), m_value);
    return QPoint(pos.x() + valueWidth + 20, pos.y());
}

The static constant variable REGULAR_EXPRESSION is a string that contains a regular expression for finding the occurrence of a formula.

In Qt, there are two classes for working with regular expressions:

  1. QRegExp -I used it in an article on writing highlighting the syntax of html markup.
  2. QRegularExpression - about it I have not written articles yet, and this will be the first article with its use

The first class was introduced earlier, the second was introduced only in Qt 5.0. The first, as I understand it, is Qt's own regular expression implementation. And the second class is already the implementation of regular expressions with syntax support exclusively for Perl. At the moment, the documentation recommends using QRegularExpression , and to study the syntax of regular expressions, to study the documentation in Perl.

As for the terrible draw method, its implementation is responsible for the beautiful drawing of the formula with the setting of the position of the value inside the square root. There is enough pseudomathematics for per-pixel rendering of all lines and numbers with the presence of magic numbers. In real projects, try to avoid this and use named constants like PADDING_BOTTOM , OFFSET , etc.

One of the important points in the draw method is that the QPainter object must be passed by reference as an argument, but the reference can not be constant, because this object will be modified. So here everything is in order. From my own experience I will say that this is normal for working with rendering custom widgets, when their QPainter is passed through a non-constant link to other methods.

In the given regular expression there is a named captured value, by which we can extract the value, which is in the root, in order to further draw it.

"sqrt\((? \d+)\)" - in this regular expression, this name is value .

FormulaWidget

FormulaWidget.h

// Class for drawing all formulas
class FormulaWidget : public QWidget
{
    Q_OBJECT
    using BaseClass = QWidget;
public:
    explicit FormulaWidget(QWidget* parent = nullptr);

public slots:
    // Slot for setting the formula
    void setFormula(const QString& formula);

protected:
    virtual void paintEvent(QPaintEvent* event) override;

private:
    QList<FormulaItem> m_items;
};

There are two important methods in the class. The first is to set the line from which we will extract the formulas, and the second overridden method is paintEvent() . The overridden method is responsible for drawing the widget; in it, we will also draw all the formulas. This method is called in the Qt's event handling method stack. Directly this method is never called, it is called when certain events occur, but it is enough to call it the update() method.

FormulaWidget.cpp

FormulaWidget::FormulaWidget(QWidget* parent) :
    BaseClass(parent)
{
    // Set the background color of the widget, by default it is the same as in the system design of the OS
    QPalette pal = palette();
    pal.setColor(QPalette::Background, Qt::white);
    setAutoFillBackground(true);
    setPalette(pal);
}

void FormulaWidget::setFormula(const QString& formula)
{
    // Clear all formulas
    m_items.clear();

    // Create a regular expression object to find the formula
    QRegularExpression sqrt_value(FormulaItem::REGULAR_EXPRESSION);
    // We seek all occurrences of formula
    QRegularExpressionMatchIterator i = sqrt_value.globalMatch(formula);

    // create all objects of formulas
    while (i.hasNext())
    {
        QRegularExpressionMatch match = i.next();
        if (match.hasMatch())
        {
            m_items.append(FormulaItem(match.captured("value")));
        }
    }

    // Run the redrawing
    update();
}

void FormulaWidget::paintEvent(QPaintEvent* event)
{
    // For repainting, the QPainter object is used, 
    // which must necessarily receive the object for rendering which it replies
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    p.setPen(Qt::black);

    QPoint formulaPos(2, 2);

    // We draw all the formulas that we could find
    for (const FormulaItem& item : m_items)
    {
        formulaPos = item.draw(formulaPos, p);
    }
}

As a result of this code, the application shown in the screenshot at the beginning of the article will be received.

I also enclose the project code. Download

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • April 1, 2024, 12:29 a.m.

C++ - Тест 003. Условия и циклы

  • Result:78points,
  • Rating points2
B

C++ - Test 002. Constants

  • Result:16points,
  • Rating points-10
B

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
Last comments
k
kmssrFeb. 9, 2024, 5:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 9:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 7:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 19, 2023, 8:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
a
a_vlasovApril 14, 2024, 4:41 p.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 12:35 p.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 2:47 p.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
AC
Alexandru CodreanuJan. 19, 2024, 10:57 p.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Follow us in social networks