Eel은 Python 기능과 라이브러리에 대한 전체 액세스 권한을 갖고 간단한 Electron과 유사한 오프라인 HTML/JS GUI 앱을 만들기 위한 작은 Python 라이브러리입니다.
Eel은 로컬 웹 서버를 호스팅하고 Python에서 함수에 주석을 달아 Javascript에서 호출할 수 있도록 하며 그 반대의 경우도 마찬가지입니다.
Eel은 짧고 간단한 GUI 애플리케이션을 작성하는 번거로움을 없애도록 설계되었습니다. Python과 웹 개발에 익숙하다면 아마도 주어진 폴더에서 임의의 파일 이름을 선택하는 이 예제로 넘어가십시오(브라우저에서는 불가능한 일입니다).
Python에서 GUI 앱을 만드는 데는 여러 가지 옵션이 있지만 HTML/JS를 사용하려는 경우(예: jQueryUI 또는 Bootstrap을 사용하기 위해) 일반적으로 클라이언트(Javascript)와 통신하기 위해 많은 상용구 코드를 작성해야 합니다. ) 측을 서버(Python) 측으로 변경합니다.
(내가 아는 한) Electron과 가장 유사한 Python은 cefpython입니다. 원하던 무게감에 비해 좀 무겁습니다.
Eel은 Electron이나 cefpython만큼 완전한 기능을 갖춘 것은 아닙니다. Atom과 같은 완전한 애플리케이션을 만드는 데는 적합하지 않을 것입니다. 하지만 팀에서 내부적으로 사용하는 작은 유틸리티 스크립트와 동등한 GUI를 만드는 데는 매우 적합합니다.
어떤 이유로 동급 최고의 숫자 분석 및 수학 라이브러리 중 다수는 Python(Tensorflow, Numpy, Scipy 등)에 있지만 최고의 시각화 라이브러리 중 다수는 Javascript(D3, THREE.js 등)에 있습니다. Eel을 사용하면 이러한 기능을 간단한 유틸리티 앱으로 쉽게 결합하여 개발을 지원할 수 있기를 바랍니다.
원한다면 Discord에서 Eel의 사용자 및 관리자와 함께하세요.
pip
사용하여 pypi에서 설치:
pip install eel
현재 Jinja2를 사용하여 HTML 템플릿에 대한 지원을 포함하려면 다음을 수행하세요.
pip install eel[jinja2]
Eel 애플리케이션은 다양한 웹 기술 파일(.html, .js, .css)로 구성된 프런트엔드와 다양한 Python 스크립트로 구성된 백엔드로 분할됩니다.
모든 프런트엔드 파일은 단일 디렉터리에 넣어야 합니다(필요한 경우 이 디렉터리 내의 폴더로 더 나눌 수 있습니다).
my_python_script.py <-- Python scripts
other_python_module.py
static_web_folder/ <-- Web folder
main_page.html
css/
style.css
img/
logo.png
시작 페이지 main.html
포함하여 web
이라는 디렉터리에 모든 프런트엔드 파일을 넣으면 앱이 다음과 같이 시작된다고 가정해 보겠습니다.
import eel
eel . init ( 'web' )
eel . start ( 'main.html' )
그러면 기본 설정(http://localhost:8000)으로 웹 서버가 시작되고 http://localhost:8000/main.html에 대한 브라우저가 열립니다.
Chrome 또는 Chromium이 설치된 경우 OS의 기본 브라우저 설정에 관계없이 기본적으로 앱 모드( --app
cmdline 플래그 사용)에서 열립니다(이 동작을 재정의할 수 있음).
추가 옵션을 eel.start()
에 키워드 인수로 전달할 수 있습니다.
일부 옵션에는 앱이 있는 모드(예: 'chrome'), 앱이 실행되는 포트, 앱의 호스트 이름 및 추가 명령줄 플래그 추가가 포함됩니다.
Eel v0.12.0부터 start()
에 다음 옵션을 사용할 수 있습니다.
'chrome'
, 'electron'
, 'edge'
, 'msie'
, 'custom'
). 창을 열지 않으려면 None
또는 False
일 수도 있습니다. 기본값: 'chrome'
'localhost'
)0
사용하십시오. 기본값: 8000
.start()
호출이 호출 스레드를 차단해야 하는지 여부를 나타내는 부울입니다. 기본값: True
my_templates
) 기본값: None
eel.start('main.html', mode='chrome-app', port=8080, cmdline_args=['--start-fullscreen', '--browser-startup-dialog'])
. 기본: []
None
None
{'size': (200, 100), 'position': (300, 50)}
형식의 사전이어야 합니다. 기본: {}None
app
Bottle 인스턴스가 아닌 경우 사용자 정의 앱 인스턴스에서 eel.register_eel_routes(app)
호출해야 합니다.shutdown_delay
초를 기다린 다음 웹소켓 연결이 있는지 확인합니다. 그렇지 않으면 Eel이 닫힙니다. 사용자가 브라우저를 닫고 프로그램을 종료하려는 경우. 기본적으로 shutdown_delay 값은 1.0
초입니다. 프론트엔드 폴더의 파일 외에도 Javascript 라이브러리가 /eel.js
에서 제공됩니다. 모든 페이지에 다음을 포함해야 합니다.
< script type =" text/javascript " src =" /eel.js " > </ script >
이 라이브러리를 포함하면 Python 측과 통신하는 데 사용할 수 있는 eel
개체가 생성됩니다.
@eel.expose
로 장식된 Python 코드의 모든 함수는 다음과 같습니다.
@ eel . expose
def my_python_function ( a , b ):
print ( a , b , a + b )
...다음과 같이 Javascript 측의 eel
객체에 메서드로 표시됩니다...
console . log ( "Calling Python..." ) ;
eel . my_python_function ( 1 , 2 ) ; // This calls the Python function that was decorated
마찬가지로, 다음과 같이 노출되는 모든 Javascript 함수는...
eel . expose ( my_javascript_function ) ;
function my_javascript_function ( a , b , c , d ) {
if ( a < b ) {
console . log ( c * d ) ;
}
}
Python 측에서 다음과 같이 호출할 수 있습니다...
print ( 'Calling Javascript...' )
eel . my_javascript_function ( 1 , 2 , 3 , 4 ) # This calls the Javascript function
노출된 이름은 두 번째 인수를 전달하여 재정의할 수도 있습니다. 앱이 빌드 중에 JavaScript를 축소하는 경우 Python 측에서 기능을 해결할 수 있는지 확인하는 데 필요할 수 있습니다.
eel . expose ( someFunction , "my_javascript_function" ) ;
복잡한 개체를 인수로 전달할 때 내부적으로 JSON으로 변환되어 웹소켓(정보가 손실될 수 있는 프로세스)으로 전송된다는 점을 명심하세요.
전체 예제 보기: example/01 - hello_world
이것을 Hello, World! 예를 들어, web/hello.html
짧은 HTML 페이지가 있습니다.
<!DOCTYPE html >
< html >
< head >
< title > Hello, World! </ title >
<!-- Include eel.js - note this file doesn't exist in the 'web' directory -->
< script type =" text/javascript " src =" /eel.js " > </ script >
< script type =" text/javascript " >
eel . expose ( say_hello_js ) ; // Expose this function to Python
function say_hello_js ( x ) {
console . log ( "Hello from " + x ) ;
}
say_hello_js ( "Javascript World!" ) ;
eel . say_hello_py ( "Javascript World!" ) ; // Call a Python function
</ script >
</ head >
< body >
Hello, World!
</ body >
</ html >
그리고 짧은 Python 스크립트 hello.py
:
import eel
# Set web files folder and optionally specify which file types to check for eel.expose()
# *Default allowed_extensions are: ['.js', '.html', '.txt', '.htm', '.xhtml']
eel . init ( 'web' , allowed_extensions = [ '.js' , '.html' ])
@ eel . expose # Expose this function to Javascript
def say_hello_py ( x ):
print ( 'Hello from %s' % x )
say_hello_py ( 'Python World!' )
eel . say_hello_js ( 'Python World!' ) # Call a Javascript function
eel . start ( 'hello.html' ) # Start (this blocks and enters loop)
Python 스크립트( python hello.py
)를 실행하면 hello.html
표시하는 브라우저 창이 열리고 다음을 볼 수 있습니다.
Hello from Python World!
Hello from Javascript World!
...터미널에서 그리고...
Hello from Javascript World!
Hello from Python World!
...브라우저 콘솔에서(열려면 F12를 누르세요)
Python 코드에서는 브라우저 창이 시작되기 전에 Javascript 함수가 호출된다는 점을 알 수 있습니다. 이와 같은 초기 호출은 대기열에 추가된 다음 웹소켓이 설정되면 전송됩니다.
우리는 코드를 단일 애플리케이션으로 구성한다고 생각하고 싶지만 Python 인터프리터와 브라우저 창은 별도의 프로세스에서 실행됩니다. 이로 인해 둘 사이의 통신이 약간 혼란스러울 수 있습니다. 특히 항상 한 쪽에서 다른 쪽으로 값을 명시적으로 보내야 하는 경우에는 더욱 그렇습니다.
Eel은 앱 반대편에서 반환 값을 검색하는 두 가지 방법을 지원하므로 코드를 간결하게 유지하는 데 도움이 됩니다.
Python 측에서 영원히 중단되는 것을 방지하기 위해 JavaScript 측에서 값을 검색하려고 시도할 때 시간 초과가 설정되었습니다. 기본값은 10000밀리초(10초)입니다. 이는 eel.init
에 대한 _js_result_timeout
매개변수를 사용하여 변경할 수 있습니다. JavaScript 측에는 해당 시간 초과가 없습니다.
노출된 함수를 호출하면 이후 즉시 콜백 함수를 전달할 수 있습니다. 이 콜백은 함수가 반대편에서 실행을 마치면 반환 값과 함께 비동기식으로 자동 호출됩니다.
예를 들어, Javascript에 정의되고 노출된 다음 함수가 있는 경우:
eel . expose ( js_random ) ;
function js_random ( ) {
return Math . random ( ) ;
}
그런 다음 Python에서는 다음과 같이 Javascript 측에서 임의의 값을 검색할 수 있습니다.
def print_num ( n ):
print ( 'Got this from Javascript:' , n )
# Call Javascript function, and pass explicit callback function
eel . js_random ()( print_num )
# Do the same with an inline lambda as callback
eel . js_random ()( lambda n : print ( 'Got this from Javascript:' , n ))
(그 반대의 경우에도 동일하게 작동합니다.)
대부분의 상황에서 상대방에 대한 호출은 위젯 상태나 입력 필드 내용과 같은 일부 데이터를 빠르게 검색하기 위한 것입니다. 이러한 경우 전체를 콜백으로 나누는 것보다 몇 밀리초 동안 동기적으로 기다린 다음 코드를 계속 진행하는 것이 더 편리합니다.
반환 값을 동기적으로 검색하려면 두 번째 대괄호 세트에 아무것도 전달하지 마십시오. 따라서 Python에서는 다음과 같이 작성합니다.
n = eel . js_random ()() # This immediately returns the value
print ( 'Got this from Javascript:' , n )
브라우저 창이 시작된 후에만 동기 반환을 수행할 수 있습니다( eel.start()
호출한 후). 그렇지 않으면 분명히 호출이 중단됩니다.
Javascript에서는 async
함수 내부에서 await
사용하는 경우를 제외하고는 콜백을 기다리는 동안 차단하는 것을 허용하지 않습니다. 따라서 Javascript 측의 해당 코드는 다음과 같습니다.
async function run ( ) {
// Inside a function marked 'async' we can use the 'await' keyword.
let n = await eel . py_random ( ) ( ) ; // Must prefix call with 'await', otherwise it's the same syntax
console . log ( "Got this from Python: " + n ) ;
}
run ( ) ;
Eel은 Javascript와 유사한 비동기 이벤트 루프를 제공하는 Bottle 및 Gevent를 기반으로 구축되었습니다. 많은 Python 표준 라이브러리는 암시적으로 단일 실행 스레드가 있다고 가정합니다. 이를 처리하기 위해 Gevent는 time
과 같은 많은 표준 모듈을 "원숭이 패치"할 수 있습니다. 이 원숭이 패치는 . Monkey 패치가 필요한 경우 import eel
호출하면 자동으로 수행됩니다.import eel
전에 import gevent.monkey
오고 gevent.monkey.patch_all()
호출해야 합니다. Monkey 패치는 디버거와 같은 작업을 방해할 수 있으므로 필요하지 않은 한 피해야 합니다.
대부분의 경우 time.sleep()
사용을 피하고 대신 gevent
에서 제공하는 버전을 사용하면 문제가 없습니다. 편의를 위해 가장 일반적으로 필요한 두 가지 gevent 메소드인 sleep()
및 spawn()
Eel에서 직접 제공됩니다(가져오는 time
및/또는 gevent
도 절약하기 위해).
이 예에서는...
import eel
eel . init ( 'web' )
def my_other_thread ():
while True :
print ( "I'm a thread" )
eel . sleep ( 1.0 ) # Use eel.sleep(), not time.sleep()
eel . spawn ( my_other_thread )
eel . start ( 'main.html' , block = False ) # Don't block on this call
while True :
print ( "I'm a main loop" )
eel . sleep ( 1.0 ) # Use eel.sleep(), not time.sleep()
...그러면 세 개의 "스레드"(greenlet)가 실행됩니다.
my_other_thread
메소드, "나는 스레드입니다"를 반복적으로 인쇄while
루프에 갇히게 되는 메인 Python 스레드는 "I'm a main loop"를 반복적으로 인쇄합니다. Python 인터프리터가 설치되지 않은 컴퓨터에서 실행될 수 있는 프로그램으로 앱을 패키징하려면 PyInstaller 를 사용해야 합니다.
pip install PyInstaller
python -m eel [your_main_script] [your_web_folder]
실행합니다(예를 들어 python -m eel hello.py web
실행할 수 있음).dist/
가 생성됩니다.--exclude module_name
플래그가 있는 모듈을 제외하는 등 유효한 PyInstaller 플래그를 전달할 수 있습니다. 예를 들어 python -m eel file_access.py web --exclude win32com --exclude numpy --exclude cryptography
실행할 수 있습니다.--onefile --noconsole
플래그를 추가하여 단일 실행 파일을 빌드하세요.더 많은 옵션을 보려면 PyInstaller 설명서를 참조하세요.
Windows 10 사용자의 경우 Microsoft Edge( eel.start(.., mode='edge')
)가 기본적으로 설치되며 기본 브라우저가 설치되지 않은 경우 유용한 대체 수단이 됩니다. 예제를 참조하세요: