<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
這幾天我們團隊用python做了一個遊戲,引擎用的pygame,UI介面用的eel(一個python庫,用於利用HTML開發桌面應用程式,主要是因為QT機制太過複雜,而博主Deadpool不願做費力不討好的事,import一個eel,便可通過HTML、CSS、JS開發桌面應用程式,這簡直不要太爽,另外,關於eel的使用也許我會在後續文章中寫到)
如果是python環境,那麼:pip install pyinstaller;如果是conda環境,那麼:conda install pyinstaller
假設專案根目錄為F:xxxxxx,該目錄的檔案管理如下:
- Web(包括HTML、CSS、JS) - 遊戲資原始檔夾 (包括圖片巴拉巴拉的) - Class1.py (Class1中匯入Class3) - Class2.py - Class3.py - Python_Main.py (Python_Main中匯入Class1,Class2和eel)
eel庫一般在site-pakages裡
開啟__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
python -m eel F:xxxxxxPython_Main.py F:xxxxxxWeb
這裡暫時不加-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()), "/<path:path>": (_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【page】.size; position = eel._start_geometry.pages【page】.position; } if(size != null){ window.resizeTo(size[0], size[1]); } if(position != null){ window.moveTo(position[0], position[1]); } }, _init: function() { eel._mock_py_functions(); document.addEventListener("DOMContentLoaded", function(event) { let page = window.location.pathname.substring(1); eel._position_window(page); let websocket_addr = (eel._host + '/eel').replace('http', 'ws'); websocket_addr += ('?page=' + page); eel._websocket = new WebSocket(websocket_addr); eel._websocket.onopen = function() { for(let i = 0; i < eel._py_functions.length; i++){ let py_function = eel._py_functions[i]; eel._import_py_function(py_function); } while(eel._mock_queue.length > 0) { let call = eel._mock_queue.shift(); eel._websocket.send(eel._toJSON(call)); } }; eel._websocket.onmessage = function (e) { let message = JSON.parse(e.data); if(message.hasOwnProperty('call') ) { // Python making a function call into us if(message.name in eel._exposed_functions) { let return_val = eel._exposed_functions[message.name](...message.args); eel._websocket.send(eel._toJSON({'return': message.call, 'value': return_val})); } } else if(message.hasOwnProperty('return')) { // Python returning a value to us if(message['return'] in eel._call_return_callbacks) { eel._call_return_callbacks[message['return']](message.value); } } else { throw 'Invalid message ' + message; } }; }); } } eel._init(); if(typeof require !== 'undefined'){ // Avoid name collisions when using Electron, so jQuery etc work normally window.nodeRequire = require; delete window.require; delete window.exports; delete window.module; } ''' try: _eel_js_file = pkg.resource_filename('eel', 'eel.js') _eel_js = open(_eel_js_file, encoding='utf-8').read() except: _eel_js = eelJS _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()), "/<path:path>": (_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')
ok,下面在執行一次cmd,執行之前的程式碼,不報錯了。但是,這次又遇到了新的問題。
'utf-8' codec can't decode byte 0xce
woc。這編碼怎麼又出了問題。百度一下,找到了解決方案。在cmd中先輸入
chcp 65001
然後再執行
python -m eel F:xxxxxxPython_Main.py F:xxxxxxWeb
這下總算是成功打包了。
打包後的檔案一般在cmd的啟動路徑下能夠找到。如,我的Python_Main.exe檔案就應該在C:UsersDeadpool裡
發現在Deadpool檔案裡多出了三個檔案,分別是Python_Main.spec, dist和build。開啟dist後,發現有一個Python_Main資料夾,開啟,便能找到Python_Main.exe,點選,彈出小黑框。接著,恭喜你,主介面顯示404 Not Found。What?Woc。又是什麼問題,難道我需要把Web資料夾移到這個Python_Main資料夾裡?經驗證答案是“是”。事實上,我們需要把所有檔案都移到這個Python_Main裡面,也就是前面的Web以及遊戲資原始檔夾。OK,問題解決。感動啊 (ಥ _ ಥ)~~~~當我滿懷激情的點選了開始遊戲,小黑框裡又一個錯誤出現了:
pygame.error: Couldn't open xxxxxx.png
百度一下,原來需要用pyinstaller打包時,pygame的load圖片必須用絕對路徑。ok,那就只有修改程式碼,重新打包。問題解決。接下來,就是如何處理掉小黑框了。現在,先讓我們看看Python_Main.spec檔案:
# -*- mode: python ; coding: utf-8 -*- block_cipher = None a = Analysis(['F:\xx\xx\Python_Main.py'], pathex=['C:\Users\Deadpool'], binaries=[], datas=[('F:\AnocondaApp\lib\site-packages\eel\eel.js', 'eel'), ('F:\xx\xx\Web', 'F:\xx\xx\Web')], hiddenimports=['bottle_websocket'], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, [], exclude_binaries=True, name='Python_Main', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=True, icon='') coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name='Python_Main')
簡單分析後,把console = True改為console = False,咦,這裡還有一個icon,應該不會是遊戲圖示吧,試著改改,於是我讓icon = ‘F:xxxxWebfavicon.ico’。改完後,儲存。
如何執行spec檔案呢?無所不能的網友給出了答案。
chcp 65001 pyinstaller Python_Main.spec
ok,完成。下面,就開始享受自己的exe遊戲吧!!以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45