关于Pyinstaller打包eel和pygame需要注意的坑第1/3页

这几天我们团队用python做了一个游戏,引擎用的pygame,UI界面用的eel(一个python库,用于利用HTML开发桌面应用程序,主要是因为QT机制太过复杂,而博主Deadpool不愿做费力不讨好的事,import一个eel,便可通过HTML、CSS、JS开发桌面应用程序,这简直不要太爽,另外,关于eel的使用也许我会在后续文章中写到)

1、打开cmd / Visual Studio等等可以装包的工具

下载pyinstaller

  • 如果是python环境,那么:pip install pyinstaller
  • 如果是conda环境,那么:conda install pyinstaller

2、找到你的python_main程序

假设项目根目录为F:\xx\xx\xx,该目录的文件管理如下:

	- Web(包括HTML、CSS、JS)
	- 游戏资源文件夹 (包括图片巴拉巴拉的)
	- Class1.py (Class1中导入Class3)
	- Class2.py
	- Class3.py
	- Python_Main.py (Python_Main中导入Class1,Class2和eel)

3、查看eel库

eel库一般在site-pakages里

关于Pyinstaller打包eel和pygame需要注意的坑第1/3页_第1张图片

打开__main__.py文件

#eel的__main__.py文件
from __future__ import print_function
import sys
import pkg_resources as pkg
import PyInstaller.__main__ as pyi
import os
args = sys.argv[1:]
main_script = args.pop(0)
web_folder = args.pop(0)
print("Building executable with main script '%s' and web folder '%s'...\n" %
      (main_script, web_folder))
eel_js_file = pkg.resource_filename('eel', 'eel.js')
js_file_arg = '%s%seel' % (eel_js_file, os.pathsep)
web_folder_arg = '%s%s%s' % (web_folder, os.pathsep, web_folder)
needed_args = ['--hidden-import', 'bottle_websocket',
               '--add-data', js_file_arg, '--add-data', web_folder_arg]
full_args = [main_script] + needed_args + args
print('Running:\npyinstaller', ' '.join(full_args), '\n')
pyi.run(full_args)

注意到print(“Building executable with main script ‘%s’ and web folder ‘%s’…\n” %

(main_script, web_folder))和print(‘Running:\npyinstaller’, ’ '.join(full_args), ‘\n’)可以知道eel的打包原理还是利用pyinstaller

4、打开cmd开始将含有eel的python_main打包起来

python -m eel F:\xx\xx\xx\Python_Main.py F:\xx\xx\xx\Web

这里暂时不加-w -f -i参数。(因为无论如何必然会出错) 

报错,提示找不到eel.js文件。呼呼,我就奇怪了,eel.js文件不就在那儿摆着吗。

打开eel的__init__.py文件查看

from __future__ import print_function   # Python 2 compatibility stuff
from builtins import range
from io import open
import gevent as gvt
import json as jsn
import bottle as btl
import bottle.ext.websocket as wbs
import re as rgx
import os
import eel.browsers as brw
import random as rnd
import sys
import pkg_resources as pkg
import socket
_eel_js_file = pkg.resource_filename('eel', 'eel.js')
_eel_js = open(_eel_js_file, encoding='utf-8').read()
	
_websockets = []
_call_return_values = {}
_call_return_callbacks = {}
_call_number = 0
_exposed_functions = {}
_js_functions = []
_mock_queue = []
_mock_queue_done = set()
# All start() options must provide a default value and explanation here
_start_args = {
    'mode':             'chrome',                   # What browser is used
    'host':             'localhost',                # Hostname use for Bottle server
    'port':             8000,                       # Port used for Bottle server (use 0 for auto)
    'block':            True,                       # Whether start() blocks calling thread
    'jinja_templates':  None,                       # Folder for jinja2 templates
    'cmdline_args':     ['--disable-http-cache'],   # Extra cmdline flags to pass to browser start
    'size':             None,                       # (width, height) of main window
    'position':         None,                       # (left, top) of main window
    'geometry':         {},                         # Dictionary of size/position for all windows
    'close_callback':   None,                       # Callback for when all windows have closed
    'app_mode':  True,                              # (Chrome specific option)
    'all_interfaces': False,                        # Allow bottle server to listen for connections on all interfaces
    'disable_cache': True,                          # Sets the no-store response header when serving assets
    'app': btl.default_app(),                       # Allows passing in a custom Bottle instance, e.g. with middleware
}
# == Temporary (suppressable) error message to inform users of breaking API change for v1.0.0 ===
_start_args['suppress_error'] = False
api_error_message = '''
----------------------------------------------------------------------------------
  'options' argument deprecated in v1.0.0, see https://github.com/ChrisKnott/Eel
  To suppress this error, add 'suppress_error=True' to start() call.
  This option will be removed in future versions
----------------------------------------------------------------------------------
'''
# ===============================================================================================
# Public functions
def expose(name_or_function=None):
    # Deal with '@eel.expose()' - treat as '@eel.expose'
    if name_or_function is None:
        return expose
    if type(name_or_function) == str:   # Called as '@eel.expose("my_name")'
        name = name_or_function
        def decorator(function):
            _expose(name, function)
            return function
        return decorator
    else:
        function = name_or_function
        _expose(function.__name__, function)
        return function
def init(path, allowed_extensions=['.js', '.html', '.txt', '.htm',
                                   '.xhtml', '.vue']):
    global root_path, _js_functions
    root_path = _get_real_path(path)
    js_functions = set()
    for root, _, files in os.walk(root_path):
        for name in files:
            if not any(name.endswith(ext) for ext in allowed_extensions):
                continue
            try:
                with open(os.path.join(root, name), encoding='utf-8') as file:
                    contents = file.read()
                    expose_calls = set()
                    finder = rgx.findall(r'eel\.expose\(([^\)]+)\)', contents)
                    for expose_call in finder:
                        # If name specified in 2nd argument, strip quotes and store as function name
                        if ',' in expose_call:
                            expose_call = rgx.sub(r'["\']', '', expose_call.split(',')[1])
                        expose_call = expose_call.strip()
                        # Verify that function name is valid
                        msg = "eel.expose() call contains '(' or '='"
                        assert rgx.findall(r'[\(=]', expose_call) == [], msg
                        expose_calls.add(expose_call)
                    js_functions.update(expose_calls)
            except UnicodeDecodeError:
                pass    # Malformed file probably
    _js_functions = list(js_functions)
    for js_function in _js_functions:
        _mock_js_function(js_function)
def start(*start_urls, **kwargs):
    _start_args.update(kwargs)
    if 'options' in kwargs:
        if _start_args['suppress_error']:
            _start_args.update(kwargs['options'])
        else:
            raise RuntimeError(api_error_message)
    if _start_args['port'] == 0:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 0))
        _start_args['port'] = sock.getsockname()[1]
        sock.close()
    if _start_args['jinja_templates'] != None:
        from jinja2 import Environment, FileSystemLoader, select_autoescape
        templates_path = os.path.join(root_path, _start_args['jinja_templates'])
        _start_args['jinja_env'] = Environment(loader=FileSystemLoader(templates_path),
                                 autoescape=select_autoescape(['html', 'xml']))
    # Launch the browser to the starting URLs
    show(*start_urls)
    def run_lambda():
        if _start_args['all_interfaces'] == True:
            HOST = '0.0.0.0'
        else:
            HOST = _start_args['host']
        app = _start_args['app']  # type: btl.Bottle
        for route_path, route_params in BOTTLE_ROUTES.items():
            route_func, route_kwargs = route_params
            app.route(path=route_path, callback=route_func, **route_kwargs)
        return btl.run(
            host=HOST,
            port=_start_args['port'],
            server=wbs.GeventWebSocketServer,
            quiet=True,
            app=app)
    # Start the webserver
    if _start_args['block']:
        run_lambda()
    else:
        spawn(run_lambda)
def show(*start_urls):
    brw.open(start_urls, _start_args)
def sleep(seconds):
    gvt.sleep(seconds)
def spawn(function, *args, **kwargs):
    gvt.spawn(function, *args, **kwargs)
# Bottle Routes
def _eel():
    start_geometry = {'default': {'size': _start_args['size'],
                                  'position': _start_args['position']},
                      'pages':   _start_args['geometry']}
    page = _eel_js.replace('/** _py_functions **/',
                           '_py_functions: %s,' % list(_exposed_functions.keys()))
    page = page.replace('/** _start_geometry **/',
                        '_start_geometry: %s,' % _safe_json(start_geometry))
    btl.response.content_type = 'application/javascript'
    _set_response_headers(btl.response)
    return page
def _static(path):
    response = None
    if 'jinja_env' in _start_args and 'jinja_templates' in _start_args:
        template_prefix = _start_args['jinja_templates'] + '/'
        if path.startswith(template_prefix):
            n = len(template_prefix)
            template = _start_args['jinja_env'].get_template(path[n:])
            response = btl.HTTPResponse(template.render())
    if response is None:
        response = btl.static_file(path, root=root_path)
    _set_response_headers(response)
    return response
def _websocket(ws):
    global _websockets
    for js_function in _js_functions:
        _import_js_function(js_function)
    page = btl.request.query.page
    if page not in _mock_queue_done:
        for call in _mock_queue:
            _repeated_send(ws, _safe_json(call))
        _mock_queue_done.add(page)
    _websockets += [(page, ws)]
    while True:
        msg = ws.receive()
        if msg is not None:
            message = jsn.loads(msg)
            spawn(_process_message, message, ws)
        else:
            _websockets.remove((page, ws))
            break
    _websocket_close(page)
BOTTLE_ROUTES = {
    "/eel.js": (_eel, dict()),
    "/": (_static, dict()),
    "/eel": (_websocket, dict(apply=[wbs.websocket]))
}
# Private functions
def _safe_json(obj):
    return jsn.dumps(obj, default=lambda o: None)
def _repeated_send(ws, msg):
    for attempt in range(100):
        try:
            ws.send(msg)
            break
        except Exception:
            sleep(0.001)
def _process_message(message, ws):
    if 'call' in message:
        return_val = _exposed_functions[message['name']](*message['args'])
        _repeated_send(ws, _safe_json({ 'return': message['call'],
                                        'value': return_val  }))
    elif 'return' in message:
        call_id = message['return']
        if call_id in _call_return_callbacks:
            callback = _call_return_callbacks.pop(call_id)
            callback(message['value'])
        else:
            _call_return_values[call_id] = message['value']
    else:
        print('Invalid message received: ', message)
def _get_real_path(path):
    if getattr(sys, 'frozen', False):
        return os.path.join(sys._MEIPASS, path)
    else:
        return os.path.abspath(path)
def _mock_js_function(f):
    exec('%s = lambda *args: _mock_call("%s", args)' % (f, f), globals())
def _import_js_function(f):
    exec('%s = lambda *args: _js_call("%s", args)' % (f, f), globals())
def _call_object(name, args):
    global _call_number
    _call_number += 1
    call_id = _call_number + rnd.random()
    return {'call': call_id, 'name': name, 'args': args}
def _mock_call(name, args):
    call_object = _call_object(name, args)
    global _mock_queue
    _mock_queue += [call_object]
    return _call_return(call_object)
def _js_call(name, args):
    call_object = _call_object(name, args)
    for _, ws in _websockets:
        _repeated_send(ws, _safe_json(call_object))
    return _call_return(call_object)
def _call_return(call):
    call_id = call['call']
    def return_func(callback=None):
        if callback is not None:
            _call_return_callbacks[call_id] = callback
        else:
            for w in range(10000):
                if call_id in _call_return_values:
                    return _call_return_values.pop(call_id)
                sleep(0.001)
    return return_func
def _expose(name, function):
    msg = 'Already exposed function with name "%s"' % name
    assert name not in _exposed_functions, msg
    _exposed_functions[name] = function
def _websocket_close(page):
    close_callback = _start_args.get('close_callback')
    if close_callback is not None:
        sockets = [p for _, p in _websockets]
        close_callback(page, sockets)
    else:
        # Default behaviour - wait 1s, then quit if all sockets are closed
        sleep(1.0)
        if len(_websockets) == 0:
            sys.exit()
def _set_response_headers(response):
    if _start_args['disable_cache']:
        # https://stackoverflow.com/a/24748094/280852
        response.set_header('Cache-Control', 'no-store')

注意到了吗?代码中有这样两句:

_eel_js_file = pkg.resource_filename('eel', 'eel.js')
_eel_js = open(_eel_js_file, encoding='utf-8').read()
 

一不做,二不休,把eel.js的内容复制下来,然后,奥里给。将代码做如下修改(即贴即用)

from __future__ import print_function   # Python 2 compatibility stuff
from builtins import range
from io import open
import gevent as gvt
import json as jsn
import bottle as btl
import bottle.ext.websocket as wbs
import re as rgx
import os
import eel.browsers as brw
import random as rnd
import sys
import pkg_resources as pkg
import socket
eelJS = '''
eel = {
    _host: window.location.origin,
    set_host: function (hostname) {
        eel._host = hostname
    },
    expose: function(f, name) {
        if(name === undefined){
            name = f.toString();
            let i = 'function '.length, j = name.indexOf('(');
            name = name.substring(i, j).trim();
        }
        eel._exposed_functions[name] = f;
    },
    guid: function() {
        return eel._guid;
    },
    // These get dynamically added by library when file is served
    /** _py_functions **/
    /** _start_geometry **/
    _guid: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
            (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
        ),
    _exposed_functions: {},
    _mock_queue: [],
    _mock_py_functions: function() {
        for(let i = 0; i < eel._py_functions.length; i++) {
            let name = eel._py_functions[i];
            eel[name] = function() {
                let call_object = eel._call_object(name, arguments);
                eel._mock_queue.push(call_object);
                return eel._call_return(call_object);
            }
        }
    },
    _import_py_function: function(name) {
        let func_name = name;
        eel[name] = function() {
            let call_object = eel._call_object(func_name, arguments);
            eel._websocket.send(eel._toJSON(call_object));
            return eel._call_return(call_object);
        }
    },
    _call_number: 0,
    _call_return_callbacks: {},
    _call_object: function(name, args) {
        let arg_array = [];
        for(let i = 0; i < args.length; i++){
            arg_array.push(args[i]);
        }
        let call_id = (eel._call_number += 1) + Math.random();
        return {'call': call_id, 'name': name, 'args': arg_array};
    },
    _sleep: function(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    },
    _toJSON: function(obj) {
        return JSON.stringify(obj, (k, v) => v === undefined ? null : v);
    },
    _call_return: function(call) {
        return function(callback = null) {
            if(callback != null) {
                eel._call_return_callbacks[call.call] = callback;
            } else {
                return new Promise(function(resolve) {
                    eel._call_return_callbacks[call.call] = resolve;
                });
            }
        }
    },
    _position_window: function(page) {
        let size = eel._start_geometry['default'].size;
        let position = eel._start_geometry['default'].position;
        if(page in eel._start_geometry.pages) {
            size = eel._start_geometry.pages
                            
   
   
   
   
123下一页阅读全文
  • Pyinstaller
  • 打包
  • eel
  • pygame

相关文章

  • 简单介绍Python中的RSS处理

    简单介绍Python中的RSS处理

    这篇文章主要介绍了简单介绍Python中的RSS处理,本文来自于IBM官方开发者技术文档,需要的朋友可以参考下
    2015-04-04
  • 在Python中使用matplotlib模块绘制数据图的示例

    在Python中使用matplotlib模块绘制数据图的示例

    这篇文章主要介绍了在Python中使用matplotlib模块绘制数据图的示例,matplotlib模块经常被用来实现数据的可视化,需要的朋友可以参考下
    2015-05-05
  • Python+OpenCV数字图像处理之ROI区域的提取

    Python+OpenCV数字图像处理之ROI区域的提取

    ROI区域又叫感兴趣区域。在机器视觉、图像处理中,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,ROI。本文主要为大家介绍如何通过Python+OpenCV提取ROI区域,需要的朋友可以了解一下
    2021-12-12
  • python实现五子棋游戏

    python实现五子棋游戏

    这篇文章主要为大家详细介绍了python实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06
  • Python中使用partial改变方法默认参数实例

    Python中使用partial改变方法默认参数实例

    这篇文章主要介绍了Python中使用partial改变方法默认参数实例,本文直接给出使用实例,代码中包含详细注释,需要的朋友可以参考下
    2015-04-04
  • 深入浅析python with语句简介

    深入浅析python with语句简介

    with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,这篇文章给大家介绍了python with语句简介,感兴趣的朋友一起看看吧
    2018-04-04
  • PyCharm第一次安装及使用教程

    PyCharm第一次安装及使用教程

    本文通过图文并茂的形式给大家介绍PyCharm第一次安装及使用教程,分步骤通过实际案例给大家操作演示,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • Python 动态导入对象,importlib.import_module()的使用方法

    Python 动态导入对象,importlib.import_module()的使用方法

    今天小编就为大家分享一篇Python 动态导入对象,importlib.import_module()的使用方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • python pandas实现excel转为html格式的方法

    python pandas实现excel转为html格式的方法

    今天小编就为大家分享一篇python pandas实现excel转为html格式的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • TensorFlow tf.nn.conv2d实现卷积的方式

    TensorFlow tf.nn.conv2d实现卷积的方式

    今天小编就为大家分享一篇TensorFlow tf.nn.conv2d实现卷积的方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01

最新评论

你可能感兴趣的:(关于Pyinstaller打包eel和pygame需要注意的坑第1/3页)