Вступление
Тема красивых интерфейсов была и будет актуальна всегда, кто-то использует для создания графических приложений C#, кто-то использует Java, кто-то уходит в дебри C++, ну а кто-то извращается с Python, используя tkinter или PyQT. Существуют различные технологии, позволяющие совмещать красоту графических дизайнов, создаваемых на HTML, CSS и JS с гибкостью в реализации логики приложения, которую может дать другой язык. Так вот, для тех, кто пишет на самом лучшем языке программирования Python, есть возможность писать красивые интерфейсы на HTML, CSS и JS и прикручивать к ним логику на Python.
В этой статья я хочу показать на небольшом примере, как же все таки создать простое и красивое десктопное приложение на HTML, CSS, JS и Python. В качестве библиотеки для связи всех компонентов будем использовать EEL.
Установка
Оговорюсь, что я работаю на windows 10 x64.
Для начала установим саму библиотеку выполнив простую команду:pip install eel
После установки одной лишь библиотеки можно начинать наш путь.
Наше приложение будет выглядеть таким образом:
Логика приложения очень простая: при нажатии кнопки “рассчитать” считываются значения в поле для ввода рублей, полученные данные отправляются в Python, где происходит расчет курса валют. Python возвращает нам данные и мы выводим из через JS
Раскидываем по папкам
Разобьем теперь наш проект на модули. В папку “front” будем класть всё, что связано с графической частью нашего приложения. В папку “back” будем помещать все наши исходники на python. Еще создадим папку middle, в которую будем помещать методы, вызываемые из JS (об этом чуть позже). В Главной директории создадим файл main.py, он будет запускать наше приложение.
Логика на Python
В качестве теста реализуем простое приложение конвертер валют (да-да, банально, никому не надо, на гитхабе миллион проектов). Для этого на python установим библиотеку CurrencyConverter: pip install currencyconverter
Теперь приступим к написанию логики на python. Реализуем метод, который на вход будет принимать сумму, валюту с которой нам надо перевести и валюту, на которую нам надо перевести. Создадим “convert.py” в директории “back” и запишем в него следующий код:
from currency_converter import CurrencyConverter
converter = CurrencyConverter()
def convert_value(value: float, from_cur: str, to_cur: str):
return converter.convert(value, from_cur, to_cur)
Теперь в директории “middle” создадим файл “mid_convert.py” и пропишем следующий код:
from back.convert import convert_value
import eel
@eel.expose
def convert_value_py(value:float, from_cur:str, to_cur:str)->float:
return convert_value(value, from_cur, to_cur)
Что здесь вообще происходит? Файл “mid_convert.py” будет служит связующим звеном между JS и Python. Декоратор @eel.expose дает нашей программе понять, что функция, описанная ниже, может быть вызвана из JS. Таким образом, любая функция, которую мы в будущем захотим вызывать из JS, должна быть обернута декоратором @eel.expose. Очевидно, что не имеет смысла делать лишних модулей, если у вас простой проект, можно было обернуть декоратором функцию “convert_value” в модуле “convert.py”. В больших проектах разбиение программы на такие слои позволит легче расширять и оптимизировать код.
Подключаем Python к JS
Теперь подключим нашу логику на Python к самому JS. Для этого примера я использую простую страничку, сверстанную чисто при помощи HTML, CSS (но вы можете подключить к своей страничке vue.js, react.js и другие популярные фреймворки, которые нужны вам для работы с графикой). Первым делом необходимо подключить библиотеку eel к самому JS:
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="main.css">
<script type="text/javascript" src="/eel.js"></script>
</head>
Далее реализуем функционал кнопки “рассчитать”. По нажатию кнопки первым делом необходимо считать значение рублей и пойти по каждой валюте, чтобы перевести значение.
async function create_cur_for_rub (){
// получаем значение введенных рублей
let value_rub = parseFloat(document.getElementById("rub-input").value);
// получаем список всех input'ов, в которые надо перевести рубли
let list_curs = document.getElementById("list-cur").children;
for (let div_cur of list_curs) {
// берем название денежной единицы
let name_cur = div_cur.getElementsByTagName("span")[0].textContent;
// теперь передает значение в Python для обработки
let value_cur = await eel.convert_value_py(value_rub, "RUB", name_cur)();
// выводим на экран полученное значение
div_cur.getElementsByTagName("input")[0].value = value_cur;
}
}
document.getElementById("btn-sum").onclick = create_cur_for_rub;
Обратите внимание на строку “let value_cur = await eel.convert_value_py(value_rub, "RUB", name_cur)();”. Важно, что вызов функции из питона происходит в асинхронном режиме, поэтому обязательно необходимо сделать асинхронной ту функцию, в которой есть вызов Python кода. При вызове функции из eel необходимо ставить пару (), где в первые круглые скобки будут передаваться наши аргументы.
Проверим, работает ли.
Как мы видим, все работает.
Запуск программы
Для запуска кода используем файл “main.py”. В нем импортируем библиотеку eel и всё, что мы писали в файле “min_convert.py” для того, чтобы при запуске проекта подтянуть все функции, которые мы оборачивали в eel.expose:
import eel
from middle.mid_convert import *
Теперь необходимо проинициализировать через eel ту директорию, где лежит front-end нашего приложения. При помощи команды eel.init(args). Далее для запуска самого приложения вызывает метод eel.start(args):
if __name__ == '__main__':
eel.init('front')
eel.start('index.html', mode="chrome", size=(760, 760))
Метод eel.start принимает на вход несколько параметров (подробнее о них см. официальную документацию по eel). Самым интересным параметром является “mode”. При помощи него вы можете запустить свой проект, как в браузере, так и в виде отдельного приложения. Например, вы можете указать параметр “default”, в таком случае приложению будет запускать в браузере по умолчанию. Если у вас стоит google chrome, то указав параметр “mode=chrome” ваша программа откроется в виде отдельного приложения.
Также можно использовать chromium. Для этого скачиваем его с официального сайта. Разархивируем его в какую-нибудь папку и прописываем следующий код в нашем “main.py”:
eel.browsers.set_path("chrome", "C:/Users/admin/Documents/chrome-win/chrome.exe")
eel.start('index.html', mode="chrome", size=(760, 760))
Вывод
Как мы видим, то создавать красивые десктопные приложения с использованием Python в качестве языка для реализации основной логики не так сложно. Вы можете устанавливать какие угодно библиотеки, реализовать нужный код с использованием Python, а писать красивые интерфейсы на HTML,CSS и JS.
P.S. это моя первая статья на Habr, пишите Ваши пожелания, замечания и отзывы, буду рад увидеть любой комментарий.
TiesP
Интересно, по сути это получается веб-приложение, но без веб-сервера? Я не специалист, и для меня ближайший аналог — это запуск локально node.js в качестве веб-сервера, который обрабатывает разные клиентские запросы… Здесь же вообще получается локальная страничка, которая без всяких запросов может выполнять различные функции с помощью python (ищу какие-то аналогии)
daniilgorbenko Автор
Это больше похоже на обычное десктопное приложение, где пользователь выполняет роль и клиента и сервера (но при этом полноценно используется html, css и js для графики, а не фреймворки). Но главный плюс такого подхода, что вы можете легко отделить графическую часть от логики приложения. То есть, вы можете написать приложением таким образом, как в статье, но при желании затем разъединить логику python кода от html, css, js, тогда у вас получится полноценное веб-приложение. И по итогу вам не надо будет мучаться, чтобы переписывать код клиента или сервера, так как можно будет заменить вызов функций, обернутых @eel.expose на вызов API.
DesertFlow
Eel запускает локальный веб-сервер (по умолчанию на localhost:8000) и открывает Chrome с флагом --app, позволяющий задавать размер окна и чтобы это выглядело как обычное приложение. А потом через свой же веб-сервер позволяет функциям из javascript обмениваться данными с функциями на python. И наоборот. Внутри это все сделано на вебсокетах. Из-за этого есть минус, что все такие функции асинхронные. Впрочем, в javascript это можно обойти через await, а на питоне еще проще.
Самое интересное, что в качестве браузера можно использовать не только Chrome, но и встроенный в Windows 10 Edge, а всю программу с помощью PyInstaller запаковать в один .exe. В итоге получается автономная программа, которая будет работать на любом Windows 10, не требуя установленного Python.
Подробное и довольно понятное описание, как это все работает, есть на страничке этой библиотеки: https://github.com/ChrisKnott/Eel
TiesP
Спасибо, теперь всё ясно) Насчет запуска автономно на windows — это похоже на старинную технологию HTA (она правда отображает с помощью встроенного IE, но также имеет доступ к системе, файлам и т.д.). Лет 10 назад сделал для себя некий ежедневник в HTA с сохранением данных в xml… а всегда хотелось запускать какие-то свои поделки типа редакторов локально в любом браузере без ограничений HTA.
DesertFlow
Да, похоже. Таких гибридов "javascript + что-то для доступа к системе" уже полно) HTA, Electron, NW.js (бывший Node-Webkit). Даже для питона, помимо EEL, есть CEF Python.
Для тех кто не понял, зачем это нужно, поясню. С помощью таких инструментов из браузерного javascript можно вызывать функции python для доступа к дискам и остальным функциям системы. В Electron и NW это сделано с помощью Node.js. Там это органично переплелось, потому что оба работают на javascript. Что и обеспечило такой связке большую популярность. Браузер для отображения интерфейса, а Node.js для всего остального, что недоступно из браузерного javascript. Доступ к файлам и т.д.
К преимуществам EEL можно отнести то, что не только из браузерного javascript можно вызывать сложные расчеты на питоне (numpy, scipy, нейронные сети на Tensorflow/Pytorch), но и из питона можно вызывать браузерные функции. Что позволяет из питона использовать браузер для визуализации данных крутыми javascript библиотеками, вроде D3.js, Three.js.
Ну а недостаток, что для каждой функции, которую хотите вызвать из другого языка (из python в javascript или наоборот), надо создавать шаблон, как описано в документации к EEL.
daniilgorbenko Автор
Самым большим плюсом, почему я вообще посмотрел в сторону именно EEL, я бы выделил то, что есть возможность легко разделить логику приложения от графической оболочки. То есть я пишу back на python, так как это мой основной язык, и мне не очень хочется лезть в JS, а другой человек делает всю верстку и логику графической части приложения. И задачи, которые решал python, можно было легко решить при помощи уже готовых библиотек, вместо того, чтобы переписывать эти библиотеки на другой язык.
А так да, конечно, можно использовать другие связки, которые вы описали выше, но они не всегда подходят для конкретных задач.