Когда мне в школе мне задали сделать итоговый проект для допуска к ОГЭ. Я долго думал над темой проекта, и решил совместить две вещи в которых я хорошо разбираюсь, Квадратные уравнения и мой любимый язык программирования Python.

На этапе идеи я сразу понял с помощью чего именно я сделаю свой проект. Изначально у меня получилось разработать консольное приложение, состоящее из бесконечного цикла цикла и простого алгоритма который спрашивает тип уравнения: Полное квадратное/неполное квадратное, и после выбора мы могли вводить a, b и c/a и b.

Эту версию проекта я "накидал" за один вечер, когда я показал результат учителю, он порекомендовал мне сделать графический интерфейс, я моментально вспомнил Python библиотеку Tkinter для создания графического интерфейса.

Тут уже пришлось покумекать как именно это сделать, ведь на практике я еще на тот момент не сталкивался с этим фреймворком. В ходе поиска документации по данному фреймворку я столкнулся с тем что все материала были содержали слишком много "воды". На тот момент нейросети были чем то новым и неизведанным, как раз так совпало что в том промежутке времени появилась первая публичная модель ChatGPT, с помощью которой я нашел нужный материал, сделал техническую документацию и даже подредактировал саму программу.

Приступаем к работе

Для работы с графическим интерфейсом нам понадобится сама библиотека tkinter, а также отдельный модуль из неё что бы выводить результат решения, messagebox.

import tkinter as tk # с помощью as tk мы присваиваем псевдоним tk, для сокращения
from tkinter import messagebox # из tkinter достаем метод, который нам позволит выводить результаты в окошке
import math # библиотека которая нам позволит делать математические вычисления

Далее нам нужно создать функцию которая будет принимать 3 числа, в нашем случает это a, b, c.

Назовём функцию solve_quadratic_equation(): и внутри неё мы осуществим ввод данных. В Tkinter все поля ввода(Entry) хранят информацию в виде строк. Что бы получить то, что ввёл пользователь, используя метод .get():

def solve_quadratic_equation():
    # ввод даннных(квадратные уравнения)
    a = float(entry_a.get())
    b = float(entry_b.get())
    c = float(entry_c.get())

    discriminant = b**2 - 4*a*c # формула дискриминанта
  • entry_a.get() - берёт текст из поля entry_a и возвращает его в виде строки.
    Например: пользователь ввёл 2.5, то entry_a.get() вернёт "2.5".

  • float() - преобразует полученную строку в число с плавающей запятой (тип float)

  • Результат сохраняется в переменную a, b или c, с которой мы уже может производить математические расчёты.

Далее нам нужно проверить полученный из формулы discriminant = b**2 - 4ac дискриминант на:

  • Больше нуля

  • Равен нулю

  • Меньше нуля

Если переменная discriminant больше нуля, то мы производим расчёты для двух действительных корней:

# если дискриминант больше нуля
if discriminant > 0:
    x1 = (-b + math.sqrt(discriminant)) / (2*a)
    x2 = (-b - math.sqrt(discriminant)) / (2*a)
    result = f"Корни уравнения: x1 = {x1}, x2 = {x2}"

Если вышеупомянутая переменная равна нулю, то мы производим расчёты для одного действительного корня:

# если дискриминант равен нулю    
elif discriminant == 0:
    x = -b / (2*a)
    result = f"Уравнение имеет единственный корень: x = {x}"

И так как других исходов кроме как дискриминант меньше нуля, быть не может, мы используем оператор сравнения else:

# если дискриминант меньше нуля    
else:
    result = "Уравнение не имеет действительных корней"

Теперь с помощью модуля messagebox, который мы импортировали в самом начале, выводим результат решения полного квадратного уравнения.

# вывод результата
messagebox.showinfo("Результат", result)

Далее нам нужно реализовать функцию для решения неполного квадратного уравнения. Создадим и назовем функциюsolve_incomplete_quadratic_equation():. И как в прошлой функции мы осуществим ввод уже двух чисел, a и b, тем же способом, сделаем расчеты корня уравнения и выведем их во всплывающем окне.

# создание функции для не полного квадратного уравнения
def solve_incomplete_quadratic_equation():
    # ввод данных
    a_2 = float(entry_incomplete_a.get())
    b_2 = float(entry_incomplete_b.get())

    x = -(b / a)
    result = f"Корень уравнения: x = {x}"
    # вывод результата
    messagebox.showinfo("Результат", result)

И так, мы с вами закончили прописывать логику программы, сейчас нам нужно создать само окно, и внутри него сделать поля ввода a, b, c / a, b

Для начала создадим окно:

window = tk.Tk()

Дадим ему название:

window.title("Калькулятор квадратных уравнений")

А также зададим ему геометрию, ширину и высоту окна, у нас оно будет 500 на 500:

window.geometry("500x500")

Далее нам нужно создать виджеты:

  • Надпись "Квадратные уравнения"

  • Поля ввода для a, b и c

  • Кнопка "Решить"

label_title = tk.Label(window, text="Квадратные уравнения")

label_a = tk.Label(window, text="Введите a:")
entry_a = tk.Entry(window)

label_b = tk.Label(window, text="Введите b:")
entry_b = tk.Entry(window)

label_c = tk.Label(window, text="Введите c:")
entry_c = tk.Entry(window)
label_title = tk.Label(window, text="Квадратные уравнения")

Эта строка создаёт надпись на экране.

  • tk.Label(...) - отвечает за создание текста в окне.

  • window - указывает, в каком окне будет находиться эта надпись.

  • text="Квадратные уравнения" - текст, который мы хотим показать пользователю.

  • label_title - имя переменной, в который мы сохраняем созданную метку, что бы потом её можно было разместить на экране с помощью .pack()

Сейчас нам надо вспомнить момент с вводом данных. Там к a, b и c мы присваивали entry_a, entry_b, entry_c. Сейчас нам нужно введённые данные сохранить в entry_a, entry_b, entry_c после нажатия на кнопку "Решить".

Как происходит получение из поля ввода или же присваивание?

  • entry_a.get() спрашивает у меня entry_a: "Что сейчас находится внутри тебя?"

  • Метод .get() возвращает текст, который ввёл пользователь. Например: "-5".

  • float(...) превращает эту строку в настоящее число (тип float). "-5" в 5.0

  • a = ... сохраняет это число в переменную a, что бы потом использовать в формуле дискриминанта.

Далее нам надо создать кнопку "Решить", по нажатию которой у нас вызовется функция solve_quadratic_equation() которая получит введённые пользователем числа и выведет окошко с результатом.

button_solve_quadratic = tk.Button(window, text="Решить", command=solve_quadratic_equation)
  • tk.Button(...) - отвечает за создание кнопки в окне.

  • window - указывает, в каком окне будет находиться эта надпись.

  • text="Решить" - текст, который мы хотим показать пользователю.

  • command=solve_quadratic_equation - что происходит при нажатии на кнопку

Сейчас нам остаётся отрисовать все виджеты которые мы создали. С помощью метода .pack() мы сможем разместить созданные виджеты внутри окна.

label_title.pack()
label_a.pack()
entry_a.pack()
label_b.pack()
entry_b.pack()
label_c.pack()
entry_c.pack()
button_solve_quadratic.pack()

Продублируем все действия для неполных квадратных уравнений

# Создание виджетов для квадратных неполных уравнений
label_incomplete_title = tk.Label(window, text="Квадратные неполные уравнения")
label_incomplete_a = tk.Label(window, text="Введите a:")
entry_incomplete_a = tk.Entry(window)
label_incomplete_b = tk.Label(window, text="Введите b:")
entry_incomplete_b = tk.Entry(window)
button_solve_incomplete = tk.Button(window, text="Решить", command=solve_incomplete_quadratic_equation)

label_incomplete_title.pack()
label_incomplete_a.pack()
entry_incomplete_a.pack()
label_incomplete_b.pack()
entry_incomplete_b.pack()
button_solve_incomplete.pack()

И что бы у нас всё появилось нам нужна эта строчка:

window.mainloop()

Она необходима для работы любого приложения на Tkinter.

Что происходит?

mainloop() запускает бесконечный цикл приложения.

Итоги

Мы сделали простое приложение которое считает корни полного/неполного квадратного уравнения и всё это в обёртке Tkinter.

Полный код приложения

import tkinter as tk
from tkinter import messagebox
import math

# создание функции для квадратного уравнения
def solve_quadratic_equation():
    # ввод даннных(квадратные уравнения)
    a = float(entry_a.get())
    b = float(entry_b.get())
    c = float(entry_c.get())

    discriminant = b**2 - 4*a*c # формула дискриминанта

    # если дискриминант больше нуля
    if discriminant > 0:
        x1 = (-b + math.sqrt(discriminant)) / (2*a)
        x2 = (-b - math.sqrt(discriminant)) / (2*a)
        result = f"Корни уравнения: x1 = {x1}, x2 = {x2}"

    # если дискриминант равен нулю    
    elif discriminant == 0:
        x = -b / (2*a)
        result = f"Уравнение имеет единственный корень: x = {x}"
    
    # если дискриминант меньше нуля    
    else:
        result = "Уравнение не имеет действительных корней"
    
    # вывод результата
    messagebox.showinfo("Результат", result)

# создание функции для не полного квадратного уравнения
def solve_incomplete_quadratic_equation():
    # ввод данных
    a_2 = float(entry_incomplete_a.get())
    b_2 = float(entry_incomplete_b.get())

    x = -(b_2 / a_2)
    result = f"Корень уравнения: x = {x}"
    # вывод результата
    messagebox.showinfo("Результат", result)

  # Создание главного окна
window = tk.Tk()
window.title("Калькулятор квадратных уравнений")
window.geometry("500x500")

# Создание виджетов
label_title = tk.Label(window, text="Квадратные уравнения")

label_a = tk.Label(window, text="Введите a:")
entry_a = tk.Entry(window)

label_b = tk.Label(window, text="Введите b:")
entry_b = tk.Entry(window)

label_c = tk.Label(window, text="Введите c:")
entry_c = tk.Entry(window)

button_solve_quadratic = tk.Button(window, text="Решить", command=solve_quadratic_equation)

label_title.pack()
label_a.pack()
entry_a.pack()
label_b.pack()
entry_b.pack()
label_c.pack()
entry_c.pack()
button_solve_quadratic.pack()

# Создание виджетов для квадратных неполных уравнений
label_incomplete_title = tk.Label(window, text="Квадратные неполные уравнения")
label_incomplete_a = tk.Label(window, text="Введите a:")
entry_incomplete_a = tk.Entry(window)
label_incomplete_b = tk.Label(window, text="Введите b:")
entry_incomplete_b = tk.Entry(window)
button_solve_incomplete = tk.Button(window, text="Решить", command=solve_incomplete_quadratic_equation)

label_incomplete_title.pack()
label_incomplete_a.pack()
entry_incomplete_a.pack()
label_incomplete_b.pack()
entry_incomplete_b.pack()
button_solve_incomplete.pack()

# Запуск главного цикла окна
window.mainloop()

Комментарии (20)


  1. bear11
    04.05.2026 17:27

    а теперь перепишите это на фреймворке Kivy - и программа у вас превратится в жар птицу (по красоте) и ее даже можно будет без особых переделок запускать на Android.


  1. HemulGM
    04.05.2026 17:27

    Хорошо, вот на Delphi:

    procedure TFormMain.ButtonCalcClick(Sender: TObject);
    begin
      var A := EditA.Text.ToDouble;
      var B := EditB.Text.ToDouble;
      var C := EditC.Text.ToDouble;
    
      var Discriminant := Power(B, 2) - 4 * A * C;
    
      if Discriminant > 0 then
      begin
        var X1 := (-B + Sqrt(Discriminant)) / (2 * A);
        var X2 := (-B - Sqrt(Discriminant)) / (2 * A);
        MemoResult.Text := Format('x1 = %f'#13#10'x2 = %f', [X1, X2]);
      end
      else
        MemoResult.Text := 'Уравнение не имеет действительных корней';
    end;
    
    procedure TFormMain.ButtonCalcIncompClick(Sender: TObject);
    begin
      var A := EditIncompA.Text.ToDouble;
      var B := EditIncompB.Text.ToDouble;
    
      var X := -(B / A);
      MemoResultIncomp.Text := Format('x = %f', [X]);
    end;

    Это весь код, что я написал. На Андроид, Мак, Линукс и Иос тоже запустится


    1. red_dragon
      04.05.2026 17:27

      А ты тоже школьник? Dephi с торрентов? На всех, тобой перечисленных OS, тестил? Где у тебя файл с описанием GUI? Сколько "весит" бинарник этого твоего "на Deplhi"?


      1. HemulGM
        04.05.2026 17:27

        Не школьник.

        Delphi имеет бесплатную Community версию. У меня вообще платная.

        На всех указанных OS это будет работать.

        Собрать могу хоть сейчас на все платформы на одной машине.

        Файл с описанием GUI трогать не надо, это всегда автогенерация. Его пишет сама среда разработки. Ты визуально конструируешь UI.

        Ехе весит около 15мб. Не имея никаких зависимостей. На других платформах - также (но Линукс требует gtk).


        1. red_dragon
          04.05.2026 17:27

          Упрощу вопрос. Зачем ты привёл своё решение на Delphi? Что хотел показать этим?


          1. bear11
            04.05.2026 17:27

            Интересно, как такой “графический hello world” сейчас, на текущее время

            • можно реализовать на разных языках, что получается,

            • какие возможности кроссплатформенности

            • как выглядит исходный текст

            • насколько быстро это можно реализовать

            • какие другие проблемы возникают То есть тема вполне для обсуждения на habr.


        1. K0Jlya9
          04.05.2026 17:27

          15мб хелоуворлд бгг. 1кб это должно весить, и работать на любой платформе начиная как минимум с винХП.

          <!DOCTYPE html>
          <html lang="ru">
          <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
          <title>Калькулятор квадратных уравнений</title>
          
          <style>
              * {
                  box-sizing: border-box;
                  font-family: "Segoe UI", sans-serif;
              }
          
              body {
                  margin: 0;
                  min-height: 100vh;
                  display: flex;
                  justify-content: center;
                  align-items: center;
                  background: #1e1e1e;
                  color: white;
              }
          
              .window {
                  width: 900px;
                  background: #252525;
                  border-radius: 12px;
                  padding: 18px;
                  box-shadow: 0 0 25px rgba(0,0,0,0.4);
              }
          
              .title {
                  display: flex;
                  align-items: center;
                  gap: 10px;
                  margin-bottom: 18px;
                  font-size: 20px;
                  font-weight: 600;
              }
          
              .logo {
                  width: 28px;
                  height: 28px;
                  border-radius: 50%;
                  background: #b32025;
                  display: flex;
                  align-items: center;
                  justify-content: center;
                  font-weight: bold;
              }
          
              .content {
                  display: flex;
                  gap: 20px;
              }
          
              .card {
                  flex: 1;
                  background: #2c2c2c;
                  border-radius: 12px;
                  padding: 18px;
              }
          
              .card h2 {
                  margin-top: 0;
                  margin-bottom: 18px;
                  font-size: 22px;
                  font-weight: 500;
              }
          
              .input-group {
                  position: relative;
                  margin-bottom: 14px;
              }
          
              .input-group input {
                  width: 100%;
                  padding: 14px 45px 14px 14px;
                  border: 1px solid #555;
                  border-radius: 6px;
                  background: #3a3a3a;
                  color: white;
                  font-size: 18px;
                  outline: none;
              }
          
              .input-group span {
                  position: absolute;
                  right: 14px;
                  top: 50%;
                  transform: translateY(-50%);
                  color: #bdbdbd;
                  font-size: 18px;
              }
          
              button {
                  width: 100%;
                  padding: 14px;
                  border: none;
                  border-radius: 6px;
                  background: #59c6f4;
                  color: black;
                  font-size: 18px;
                  cursor: pointer;
                  transition: 0.2s;
                  margin-bottom: 16px;
              }
          
              button:hover {
                  background: #71d3fb;
              }
          
              .result {
                  min-height: 90px;
                  background: #3a3a3a;
                  border: 1px solid #555;
                  border-radius: 6px;
                  padding: 14px;
                  font-size: 20px;
                  line-height: 1.6;
                  white-space: pre-line;
              }
          </style>
          </head>
          
          <body>
          
          <div class="window">
          
              <div class="title">
                  <div class="logo">D</div>
                  Калькулятор квадратных уравнений
              </div>
          
              <div class="content">
          
                  <!-- Полное квадратное -->
                  <div class="card">
          
                      <h2>Квадратные уравнения</h2>
          
                      <div class="input-group">
                          <input type="number" id="a" value="-4">
                          <span>A</span>
                      </div>
          
                      <div class="input-group">
                          <input type="number" id="b" value="5">
                          <span>B</span>
                      </div>
          
                      <div class="input-group">
                          <input type="number" id="c" value="6">
                          <span>C</span>
                      </div>
          
                      <button onclick="solveQuadratic()">Решить</button>
          
                      <div class="result" id="quadraticResult"></div>
          
                  </div>
          
                  <!-- Неполное -->
                  <div class="card">
          
                      <h2>Квадратные неполные уравнения</h2>
          
                      <div class="input-group">
                          <input type="number" id="na" value="-1">
                          <span>A</span>
                      </div>
          
                      <div class="input-group">
                          <input type="number" id="nb" value="5">
                          <span>B</span>
                      </div>
          
                      <button onclick="solveSimple()">Решить</button>
          
                      <div class="result" id="simpleResult"></div>
          
                  </div>
          
              </div>
          
          </div>
          
          <script>
          
          function solveQuadratic() {
          
              let a = parseFloat(document.getElementById('a').value);
              let b = parseFloat(document.getElementById('b').value);
              let c = parseFloat(document.getElementById('c').value);
          
              let d = b * b - 4 * a * c;
          
              let result = '';
          
              if (d < 0) {
                  result = 'Нет действительных корней';
              }
              else if (d === 0) {
                  let x = (-b / (2 * a)).toFixed(2);
                  result = `x = ${x}`;
              }
              else {
                  let x1 = ((-b - Math.sqrt(d)) / (2 * a)).toFixed(2);
                  let x2 = ((-b + Math.sqrt(d)) / (2 * a)).toFixed(2);
          
                  result = `x1 = ${x1}\nx2 = ${x2}`;
              }
          
              document.getElementById('quadraticResult').innerText = result;
          }
          
          function solveSimple() {
          
              let a = parseFloat(document.getElementById('na').value);
              let b = parseFloat(document.getElementById('nb').value);
          
              if (a === 0) {
                  document.getElementById('simpleResult').innerText = 'A не может быть 0';
                  return;
              }
          
              let x = Math.sqrt(-b / a);
          
              if (isNaN(x)) {
                  document.getElementById('simpleResult').innerText = 'Нет решения';
              } else {
                  document.getElementById('simpleResult').innerText =
                      `x = ${x.toFixed(2)}`;
              }
          }
          
          solveQuadratic();
          solveSimple();
          
          </script>
          
          </body>
          </html>


          1. HemulGM
            04.05.2026 17:27

            У тебя это просто текст и он не запустится нигде без специальной программы. Вообще нигде и никогда.


      1. HemulGM
        04.05.2026 17:27

        Другие ОС
        Android
        Android
        Linux (Ubuntu)
        Linux (Ubuntu)
        Среда. Дизайнер (код окна трогать не надо)


        1. red_dragon
          04.05.2026 17:27

          Я знаю, что такое Delphi. C четвёртой по седьмую версию пользовался довольно активно и даже деньги зарабатывал этим. Но раз уж ты осилил скриншоты, дай ссылку на .apk и бинарник для Linux. Сильно интересно стало, насколько это действительно кросплатформенно.


          1. HemulGM
            04.05.2026 17:27

            Насколько "кроссплатформенно" можешь посмотреть здесь:
            https://github.com/HemulGM/ChatGPT
            Это куда более крупный проект. С доступными бинарниками


  1. Metotron0
    04.05.2026 17:27

    Вообще не уровень хабра. Да и цель статьи не понятна. Если хотели научить аудиторию использовать tkinter, то зачем описывать, как в питоне импортируются пакеты, и что означает else? Если хотели обучить синтаксису питона, то этого явно мало, да и пример для обучения выбран не самый показательный.


    1. morginalium8
      04.05.2026 17:27

      автору 15-16 лет. и он уже имеет грамотную речь, умение описывать свои идеи, доводить их до реализации и описывать результат.
      я технические знания придут



  1. morginalium8
    04.05.2026 17:27

    Можешь глянуть, я тут что-то похожее делал - только там вообще все решить можно


  1. eulampius
    04.05.2026 17:27

    А что за дичь с "неполными" квадратными уравнениями? Это же просто частные случаи, когда один или несколько коэффициентов равны нулю. Вот только, если a == 0,то уравнение перестаёт быть квадратным, а если a == b == 0, то оно перестаёт быть уравнением и становится равенством, только в одном случае истинным, а в остальных ложным. И все это можно запрограммировать без лишних полей ввода. Просто не вводим коэффициенты, если их нет. Корни, кстати, тоже можно представить не в виде одной-двух переменных, а иначе, более универсально.


  1. D7ILeucoH
    04.05.2026 17:27

    Я понял. Теперь дети пишут свои потуги в виде статей на Хабре. Вот это уровень, вот это мы приехали.

    То моё беспокойство что я не написал ни одного поста - было нивелировано тем, что смысл постов просто обесценился.

    Наличие научных публикаций в дипломе, на которое так пытались дрочить научруки, и чего я не стал делать - очевидно что полная хуйня. Круто что не вляпался в это ЧСВшное дерьмо... Просто делаешь что хочешь, для себя, и как сам это видишь. Всё. Объяснять кому-то очевидные с одной стороны, и агностические с другой стороны темы - бред. Главное - результат. А он есть.

    Блин, смысл статьи то в чём? Я однажды смог ускорить обучение нейронки в 10к раз, что шло вразрез с подобием доки (на тот момент их не было), и просто рассказал об этом ребятам в команде, и всё. Похоже в этом было бы гораздо больше смысла, чем в этой статье.

    Да и в целом алгоритм туповатый. Как и UX. Суть программы это упростить что-то, а не усложнить, задавая очевидные и ненужные вопросы. Задача - либо ввод уравнения целиком (с парсингом), либо коэффициентов, и всё. Ввод коэффициента A,B,C - тоже звучит тупо, потому что не понятно к какой степени они относятся. Эти цифры в принципе не были нужны. Очевидно что нужно было вместо них писать неизвестную с показателем. А сейчас программа зачем? Половина это копипаста, перегруженный интерфейс, так ещё и непонятный.


    1. K0Jlya9
      04.05.2026 17:27

      В соседней "статье" вполне себе великовозрастный чувак просто спросил у роботов какая книга-игра-фильм им нравится в каком то аспекте и тупо заполнил всю статью копипастой ответов. Хорошо что теперь на хабре есть кнопка для игнора авторов, плохо что нельзя игнорить сразу целые сообщества паразитов.


  1. ArtyomOchkin
    04.05.2026 17:27

    Неплохо для первого проекта. Я так баловался с реализацией алгоритма дисперсии, это поинтереснее будет.

    Можете ознакомиться с ttk.button() или, лучше, с более совершенными формами tkinter вроде customtkinter. Тогда можно сделать близкое к WinUI стилю, более современно выглядящее приложение, а не просто WinForms, примерно такое, как товарищ выше в комментах прислал. Это возможно и c Python.

    Если интересно, потом покажу свой старый эксперимент "DisperiaPro", на котором я разбирался с компиляцией бинарников на Python и реализовал подсчёт среднеквадратичного отклонения, дисперсии и др.


  1. ysrgsyn
    04.05.2026 17:27

    Ну чего вы негативите так народ?

    Ну да, тема не хабровская совсем, просто школьник написал код и показал это миру.

    В целом, он не виноват что его пост одобрили и опубликовали)

    Раз есть что есть, лучше поддержим его как старшие коллегия))

    А негативить идём под посты типа "я X и за 30 секунд сделал свое приложение через ИИ, так что эти ваши прахрамисты никому не нужны"


    1. Metotron0
      04.05.2026 17:27

      https://habr.com/ru/articles/1031410/
      Дык, это, там +51

      А-а, там шутка юмора. Не выкупил при беглом осмотре.