diff --git a/.gitignore b/.gitignore
index ba0430d..3040d64 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-__pycache__/
\ No newline at end of file
+__pycache__/
+.vscode/
\ No newline at end of file
diff --git a/PlotterDialog/ButtonGroup.py b/PlotterDialog/PlotterDialog/ButtonGroup.py
similarity index 89%
rename from PlotterDialog/ButtonGroup.py
rename to PlotterDialog/PlotterDialog/ButtonGroup.py
index 8c003de..9ea74d4 100644
--- a/PlotterDialog/ButtonGroup.py
+++ b/PlotterDialog/PlotterDialog/ButtonGroup.py
@@ -1,6 +1,8 @@
 from collections.abc import Callable
 
-from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton
+
+from .FlowLayout import FlowLayout
 
 
 class ButtonGroup(QWidget):
@@ -13,7 +15,7 @@ class ButtonGroup(QWidget):
     ):
         super().__init__()
         self.layout = QVBoxLayout()  # Создание основного лаяутв
-        Doplayout = QHBoxLayout()
+        Doplayout = FlowLayout()
         label = QLabel(category)
 
         self.layout.addWidget(label)
@@ -34,7 +36,6 @@ class ButtonGroup(QWidget):
             Doplayout.addWidget(button)  # отрисовывание кнопок
 
         Doplayout.setContentsMargins(0, 0, 0, 0)
-        Doplayout.addStretch(1)
 
         self.layout.addLayout(Doplayout)
 
diff --git a/PlotterDialog/PlotterDialog/FlowLayout.py b/PlotterDialog/PlotterDialog/FlowLayout.py
new file mode 100644
index 0000000..e756d9d
--- /dev/null
+++ b/PlotterDialog/PlotterDialog/FlowLayout.py
@@ -0,0 +1,147 @@
+#############################################################################
+##
+## Copyright (C) 2013 Riverbank Computing Limited.
+## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+## All rights reserved.
+##
+## This file is part of the examples of PyQt.
+##
+## $QT_BEGIN_LICENSE:BSD$
+## You may use this file under the terms of the BSD license as follows:
+##
+## "Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions are
+## met:
+##   * Redistributions of source code must retain the above copyright
+##     notice, this list of conditions and the following disclaimer.
+##   * Redistributions in binary form must reproduce the above copyright
+##     notice, this list of conditions and the following disclaimer in
+##     the documentation and/or other materials provided with the
+##     distribution.
+##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+##     the names of its contributors may be used to endorse or promote
+##     products derived from this software without specific prior written
+##     permission.
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+from PyQt5 import QtCore, QtWidgets
+
+
+class FlowLayout(QtWidgets.QLayout):
+    def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
+        super(FlowLayout, self).__init__(parent)
+        self._hspacing = hspacing
+        self._vspacing = vspacing
+        self._items = []
+        self.setContentsMargins(margin, margin, margin, margin)
+
+    def __del__(self):
+        del self._items[:]
+
+    def addItem(self, item):
+        self._items.append(item)
+
+    def horizontalSpacing(self):
+        if self._hspacing >= 0:
+            return self._hspacing
+        else:
+            return self.smartSpacing(QtWidgets.QStyle.PM_LayoutHorizontalSpacing)
+
+    def verticalSpacing(self):
+        if self._vspacing >= 0:
+            return self._vspacing
+        else:
+            return self.smartSpacing(QtWidgets.QStyle.PM_LayoutVerticalSpacing)
+
+    def count(self):
+        return len(self._items)
+
+    def itemAt(self, index):
+        if 0 <= index < len(self._items):
+            return self._items[index]
+
+    def takeAt(self, index):
+        if 0 <= index < len(self._items):
+            return self._items.pop(index)
+
+    def expandingDirections(self):
+        return QtCore.Qt.Orientations(0)
+
+    def hasHeightForWidth(self):
+        return True
+
+    def heightForWidth(self, width):
+        return self.doLayout(QtCore.QRect(0, 0, width, 0), True)
+
+    def setGeometry(self, rect):
+        super(FlowLayout, self).setGeometry(rect)
+        self.doLayout(rect, False)
+
+    def sizeHint(self):
+        return self.minimumSize()
+
+    def minimumSize(self):
+        size = QtCore.QSize()
+        for item in self._items:
+            size = size.expandedTo(item.minimumSize())
+        left, top, right, bottom = self.getContentsMargins()
+        size += QtCore.QSize(left + right, top + bottom)
+        return size
+
+    def doLayout(self, rect, testonly):
+        left, top, right, bottom = self.getContentsMargins()
+        effective = rect.adjusted(+left, +top, -right, -bottom)
+        x = effective.x()
+        y = effective.y()
+        lineheight = 0
+        for item in self._items:
+            widget = item.widget()
+            hspace = self.horizontalSpacing()
+            if hspace == -1:
+                hspace = widget.style().layoutSpacing(
+                    QtWidgets.QSizePolicy.PushButton,
+                    QtWidgets.QSizePolicy.PushButton,
+                    QtCore.Qt.Horizontal,
+                )
+            vspace = self.verticalSpacing()
+            if vspace == -1:
+                vspace = widget.style().layoutSpacing(
+                    QtWidgets.QSizePolicy.PushButton,
+                    QtWidgets.QSizePolicy.PushButton,
+                    QtCore.Qt.Vertical,
+                )
+            nextX = x + item.sizeHint().width() + hspace
+            if nextX - hspace > effective.right() and lineheight > 0:
+                x = effective.x()
+                y = y + lineheight + vspace
+                nextX = x + item.sizeHint().width() + hspace
+                lineheight = 0
+            if not testonly:
+                item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
+            x = nextX
+            lineheight = max(lineheight, item.sizeHint().height())
+        return y + lineheight - rect.y() + bottom
+
+    def smartSpacing(self, pm):
+        parent = self.parent()
+        if parent is None:
+            return -1
+        elif parent.isWidgetType():
+            return parent.style().pixelMetric(pm, None, parent)
+        else:
+            return parent.spacing()
diff --git a/PlotterDialog/GraphRequester.py b/PlotterDialog/PlotterDialog/GraphRequester.py
similarity index 92%
rename from PlotterDialog/GraphRequester.py
rename to PlotterDialog/PlotterDialog/GraphRequester.py
index 31165d0..fb3f0fa 100644
--- a/PlotterDialog/GraphRequester.py
+++ b/PlotterDialog/PlotterDialog/GraphRequester.py
@@ -19,6 +19,10 @@ class FocusNotifyingLineEdit(QLineEdit):
 
 
 class GraphRequester(QWidget):
+    LineEditGraf: FocusNotifyingLineEdit
+    LineEditX: FocusNotifyingLineEdit
+    LineEditY: FocusNotifyingLineEdit
+
     def __init__(self, nomer_grafika=1):
         super().__init__()
         layout = QVBoxLayout(self)
@@ -45,8 +49,8 @@ class GraphRequester(QWidget):
         layout_close_and_name.addWidget(Name_Close)
 
         layout.addLayout(layout_close_and_name)  # Вложения названия и закрыть
-        layout.addLayout(layout_x)  # Вложение
         layout.addLayout(layout_y)  # Вложение
+        layout.addLayout(layout_x)  # Вложение
 
         layout.setContentsMargins(0, 0, 0, 0)
         layout.addStretch(1)
diff --git a/PlotterDialog/PlotterDialog.py b/PlotterDialog/PlotterDialog/PlotterDialog.py
similarity index 55%
rename from PlotterDialog/PlotterDialog.py
rename to PlotterDialog/PlotterDialog/PlotterDialog.py
index f5cc9d9..15420ab 100644
--- a/PlotterDialog/PlotterDialog.py
+++ b/PlotterDialog/PlotterDialog/PlotterDialog.py
@@ -1,4 +1,4 @@
-from PyQt5.QtCore import Qt
+from PyQt5.QtCore import Qt, QTimer
 from PyQt5.QtWidgets import (
     QDialog,
     QVBoxLayout,
@@ -7,11 +7,19 @@ from PyQt5.QtWidgets import (
     QPushButton,
     QLineEdit,
     QMessageBox,
+    QHBoxLayout,
+    QCheckBox,
 )
 
+import numpy as np
+
 from .ButtonGroup import ButtonGroup
 from .GraphRequester import GraphRequester
 
+from parser.parser import Parser
+
+from GraphWidget.GraphWidget import GraphWidget
+
 
 class PlotterDialog(QDialog):
     focused_line_edit = None
@@ -20,10 +28,12 @@ class PlotterDialog(QDialog):
         self,
         variable_full_names: dict[str, str],
         function_full_names: dict[str, str],
-        # variable_values: dict[str, np.ndarray],
+        variable_values: dict[str, np.ndarray],
     ):
         super().__init__()
 
+        self.variable_values = variable_values
+
         self.setWindowTitle("Графопостроитель")
 
         layout_boss = QVBoxLayout()  # главный лояут
@@ -43,6 +53,8 @@ class PlotterDialog(QDialog):
         self.num_of_input = 0  # инициализация первого графика
         self.add_input()
 
+        QTimer.singleShot(0, self.focus_first_input)
+
         scrollWidget.setLayout(self.inputs_layout)
 
         self.scroll.setWidgetResizable(True)
@@ -70,6 +82,30 @@ class PlotterDialog(QDialog):
             )
         )
 
+        layout_boss.addSpacing(10)
+
+        buttons_layout = QHBoxLayout()
+
+        buttons_layout.setDirection(QHBoxLayout.RightToLeft)
+
+        submit_button = QPushButton("Построить")
+        reset_button = QPushButton("Сброс")
+
+        submit_button.clicked.connect(self.plot)
+        reset_button.clicked.connect(self.reset)
+
+        submit_button.setDefault(True)
+
+        buttons_layout.addWidget(submit_button)
+        buttons_layout.addWidget(reset_button)
+
+        self.subplots_checkbox = QCheckBox("Рисовать графики на отдельных осях")
+        buttons_layout.addWidget(self.subplots_checkbox)
+
+        buttons_layout.addStretch(1)
+
+        layout_boss.addLayout(buttons_layout)
+
         self.setLayout(layout_boss)
 
     def add_input(self):
@@ -102,11 +138,12 @@ class PlotterDialog(QDialog):
         line_edit = self.focused_line_edit
 
         if line_edit is None:
-            QMessageBox(
+            dlg = QMessageBox(
                 QMessageBox.Warning,
                 "Ошибка",
                 "Выберите поле ввода выражения",
-            ).exec()
+            )
+            dlg.exec()
 
             return
 
@@ -128,3 +165,65 @@ class PlotterDialog(QDialog):
     def insert_function(self, name: str):
         string = f" {name}()"
         self.insert_string(string, len(string) - 1)  # len - 1 for cursor between braces
+
+    def plot(self):
+        xs, ys, labels = [], [], []
+
+        for i in range(self.inputs_layout.count()):
+            graph_requester = self.inputs_layout.itemAt(i).widget()
+
+            if graph_requester is not None:
+                x_expr = graph_requester.LineEditX.text()
+                y_expr = graph_requester.LineEditY.text()
+                label = graph_requester.LineEditGraf.text()
+
+                if len(x_expr) * len(y_expr) == 0:
+                    dlg = QMessageBox(
+                        QMessageBox.Warning,
+                        "Ошибка",
+                        f'График "{label}" не задан',
+                    )
+                    dlg.exec()
+                    return
+
+                x = Parser(x_expr).evaluate(self.variable_values)
+                y = Parser(y_expr).evaluate(self.variable_values)
+
+                xs.append(x)
+                ys.append(y)
+                labels.append(label)
+
+        mult_subplots = self.subplots_checkbox.isChecked()
+
+        self.graph = GraphWidget(xs, ys, labels, mult_plots=mult_subplots)
+
+        self.graph.show()
+
+    def reset(self):
+        dlg = QMessageBox(
+            QMessageBox.Question,
+            "Очистка",
+            "Вы уверены, что хотите очистить все введённые выражения?",
+            buttons=QMessageBox.Yes | QMessageBox.No,
+        )
+
+        res = dlg.exec()
+
+        if res != QMessageBox.Yes:
+            return
+
+        while self.inputs_layout.count() > 0:
+            widget = self.inputs_layout.takeAt(0).widget()
+            if widget is not None:
+                widget.setParent(None)
+
+        self.focused_line_edit = None
+
+        self.num_of_input = 0
+
+        self.add_input()
+
+    def focus_first_input(self):
+        first_graph_request = self.inputs_layout.itemAt(0).widget()
+        if first_graph_request is not None:
+            first_graph_request.LineEditGraf.setFocus()
diff --git a/PlotterDialog/__init__.py b/PlotterDialog/PlotterDialog/__init__.py
similarity index 100%
rename from PlotterDialog/__init__.py
rename to PlotterDialog/PlotterDialog/__init__.py
diff --git a/PlotterDialog/PlotterDialog/__main__.py b/PlotterDialog/PlotterDialog/__main__.py
new file mode 100644
index 0000000..0cbeda8
--- /dev/null
+++ b/PlotterDialog/PlotterDialog/__main__.py
@@ -0,0 +1,48 @@
+import sys
+
+from PyQt5.QtWidgets import QApplication
+
+import numpy as np
+
+from .PlotterDialog import PlotterDialog
+
+app = QApplication(sys.argv)
+
+variables = [chr(ord("a") + i) for i in range(10)]
+
+functions = [
+    "abs",
+    "acos",
+    "acosh",
+    "acot",
+    "asin",
+    "asinh",
+    "atan",
+    "avg",
+    "cos",
+    "cosh",
+    "cot",
+    "exp",
+    "lg",
+    "ln",
+    "log2",
+    "max",
+    "min",
+    "prod",
+    "sgn",
+    "sin",
+    "sinh",
+    "sqrt",
+    "sum",
+    "tanh",
+    "tan",
+]
+
+dlg = PlotterDialog(
+    variable_full_names={key: key.upper() for key in variables},
+    function_full_names={key: key.upper() for key in functions},
+    variable_values={key: np.sort(np.random.random(10)) * 10 for key in variables},
+)
+dlg.show()
+
+sys.exit(app.exec())
diff --git a/PlotterDialog/__main__.py b/PlotterDialog/__main__.py
deleted file mode 100644
index d64c03a..0000000
--- a/PlotterDialog/__main__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import sys
-
-from PyQt5.QtWidgets import QApplication
-
-from .PlotterDialog import PlotterDialog
-
-app = QApplication(sys.argv)
-
-PlotterDialog(
-    variable_full_names={"a": "A", "b": "B", "c": "C"},
-    function_full_names={"exp": "экспонента", "ln": "Логарифм", "mod": "модуль"},
-).show()
-
-sys.exit(app.exec())
diff --git a/PlotterDialog/poetry.lock b/PlotterDialog/poetry.lock
index 4d0505b..2b9fc62 100644
--- a/PlotterDialog/poetry.lock
+++ b/PlotterDialog/poetry.lock
@@ -123,6 +123,25 @@ files = [
     {file = "docstring_to_markdown-0.13-py3-none-any.whl", hash = "sha256:aa487059d0883e70e54da25c7b230e918d9e4d40f23d6dfaa2b73e4225b2d7dd"},
 ]
 
+[[package]]
+name = "graphwidget"
+version = "0.1.0"
+description = ""
+category = "main"
+optional = false
+python-versions = ">=3.11,<3.13"
+files = []
+develop = true
+
+[package.dependencies]
+numpy = "^1.26.0"
+pyqt5 = "5.15.2"
+pyqt5-qt5 = "5.15.2"
+
+[package.source]
+type = "directory"
+url = "../GraphWidget"
+
 [[package]]
 name = "isort"
 version = "5.12.0"
@@ -308,6 +327,23 @@ files = [
     {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
 ]
 
+[[package]]
+name = "parser"
+version = "0.1.0"
+description = ""
+category = "main"
+optional = false
+python-versions = ">=3.11,<3.13"
+files = []
+develop = true
+
+[package.dependencies]
+numpy = "^1.26.0"
+
+[package.source]
+type = "directory"
+url = "../parser"
+
 [[package]]
 name = "parso"
 version = "0.8.3"
@@ -492,4 +528,4 @@ files = [
 [metadata]
 lock-version = "2.0"
 python-versions = ">=3.11,<3.13"
-content-hash = "cb0cbf2520f4660ecec4008bce226d433786b483e26cbc156fee2085fefbd6e0"
+content-hash = "d6fccd0b448db425a584bd320ed7fa1eb0862d8eebca2d11b8150cc761147d4b"
diff --git a/PlotterDialog/pyproject.toml b/PlotterDialog/pyproject.toml
index 488b523..fdf6d73 100644
--- a/PlotterDialog/pyproject.toml
+++ b/PlotterDialog/pyproject.toml
@@ -10,6 +10,8 @@ python = ">=3.11,<3.13"
 numpy = "^1.26.0"
 pyqt5 = "5.15.2"
 pyqt5-qt5 = "5.15.2"
+parser = { path = "../parser", develop = true }
+GraphWidget = { path = "../GraphWidget", develop = true }
 
 
 [tool.poetry.group.dev.dependencies]