mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-20 10:47:35 -05:00
Compare commits
1 Commits
danyeaw-ad
...
pygame-ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0fcfa7a07 |
@@ -223,6 +223,13 @@ for (const [TYPE, interpreter] of TYPES) {
|
|||||||
else element.after(show);
|
else element.after(show);
|
||||||
}
|
}
|
||||||
if (!show.id) show.id = getID();
|
if (!show.id) show.id = getID();
|
||||||
|
if (TYPE === "py") {
|
||||||
|
const canvas2D = element.getAttribute("canvas2d") || element.getAttribute("canvas");
|
||||||
|
if (canvas2D) {
|
||||||
|
const canvas = queryTarget(document, canvas2D);
|
||||||
|
wrap.interpreter.canvas.setCanvas2D(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// allows the code to retrieve the target element via
|
// allows the code to retrieve the target element via
|
||||||
// document.currentScript.target if needed
|
// document.currentScript.target if needed
|
||||||
|
|||||||
@@ -84,7 +84,19 @@ export const hooks = {
|
|||||||
},
|
},
|
||||||
worker: {
|
worker: {
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onReady: new SetFunction(),
|
onReady: new SetFunction([
|
||||||
|
(wrap, xworker) => {
|
||||||
|
if (wrap.type === "py") {
|
||||||
|
const { interpreter } = wrap;
|
||||||
|
const element = wrap.run('from polyscript import currentScript;currentScript');
|
||||||
|
const canvas2D = element.getAttribute("canvas2d") || element.getAttribute("canvas");
|
||||||
|
if (canvas2D) {
|
||||||
|
const canvas = element.ownerDocument.getElementById(canvas2D);
|
||||||
|
interpreter.canvas.setCanvas2D(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]),
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onBeforeRun: new SetFunction(),
|
onBeforeRun: new SetFunction(),
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default {
|
|||||||
"fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A",
|
"fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A",
|
||||||
"ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs",
|
"ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs",
|
||||||
"flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A<len(input):C.append(_transform(B,input,input[A]));A+=1\n\treturn _json.dumps(C,*D,**E)",
|
"flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A<len(input):C.append(_transform(B,input,input[A]));A+=1\n\treturn _json.dumps(C,*D,**E)",
|
||||||
"magic_js.py": "import json,sys,js as globalThis\nfrom polyscript import config as _config,js_modules\nfrom pyscript.util import NotSupported\nRUNNING_IN_WORKER=not hasattr(globalThis,'document')\nconfig=json.loads(globalThis.JSON.stringify(_config))\nif'MicroPython'in sys.version:config['type']='mpy'\nelse:config['type']='py'\nclass JSModule:\n\tdef __init__(A,name):A.name=name\n\tdef __getattr__(B,field):\n\t\tA=field\n\t\tif not A.startswith('_'):return getattr(getattr(js_modules,B.name),A)\nfor name in globalThis.Reflect.ownKeys(js_modules):sys.modules[f\"pyscript.js_modules.{name}\"]=JSModule(name)\nsys.modules['pyscript.js_modules']=js_modules\nif RUNNING_IN_WORKER:\n\timport polyscript;PyWorker=NotSupported('pyscript.PyWorker','pyscript.PyWorker works only when running in the main thread')\n\ttry:import js;window=polyscript.xworker.window;document=window.document;js.document=document;js_import=window.Function('return (...urls) => Promise.all(urls.map((url) => import(url)))')()\n\texcept:message='Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer';globalThis.console.warn(message);window=NotSupported('pyscript.window',message);document=NotSupported('pyscript.document',message);js_import=None\n\tsync=polyscript.xworker.sync\n\tdef current_target():return polyscript.target\nelse:\n\timport _pyscript;from _pyscript import PyWorker,js_import;window=globalThis;document=globalThis.document;sync=NotSupported('pyscript.sync','pyscript.sync works only when running in a worker')\n\tdef current_target():return _pyscript.target",
|
"magic_js.py": "import json,sys,js as globalThis\nfrom polyscript import config as _config,js_modules\nfrom pyscript.util import NotSupported\nRUNNING_IN_WORKER=not hasattr(globalThis,'document')\nconfig=json.loads(globalThis.JSON.stringify(_config))\nif'MicroPython'in sys.version:config['type']='mpy'\nelse:config['type']='py'\nclass JSModule:\n\tdef __init__(A,name):A.name=name\n\tdef __getattr__(B,field):\n\t\tA=field\n\t\tif not A.startswith('_'):return getattr(getattr(js_modules,B.name),A)\nfor name in globalThis.Reflect.ownKeys(js_modules):sys.modules[f\"pyscript.js_modules.{name}\"]=JSModule(name)\nsys.modules['pyscript.js_modules']=js_modules\nif RUNNING_IN_WORKER:\n\timport polyscript;PyWorker=NotSupported('pyscript.PyWorker','pyscript.PyWorker works only when running in the main thread')\n\ttry:import js;window=polyscript.xworker.window;document=window.document;js.screen=window.screen;js.document=document;js_import=window.Function('return (...urls) => Promise.all(urls.map((url) => import(url)))')()\n\texcept:message='Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer';globalThis.console.warn(message);window=NotSupported('pyscript.window',message);document=NotSupported('pyscript.document',message);js_import=None\n\tsync=polyscript.xworker.sync\n\tdef current_target():return polyscript.target\nelse:\n\timport _pyscript;from _pyscript import PyWorker,js_import;window=globalThis;document=globalThis.document;sync=NotSupported('pyscript.sync','pyscript.sync works only when running in a worker')\n\tdef current_target():return _pyscript.target",
|
||||||
"media.py": "from pyscript import window\nfrom pyscript.ffi import to_js\nclass Device:\n\tdef __init__(A,device):A._dom_element=device\n\t@property\n\tdef id(self):return self._dom_element.deviceId\n\t@property\n\tdef group(self):return self._dom_element.groupId\n\t@property\n\tdef kind(self):return self._dom_element.kind\n\t@property\n\tdef label(self):return self._dom_element.label\n\tdef __getitem__(A,key):return getattr(A,key)\n\t@classmethod\n\tasync def load(E,audio=False,video=True):\n\t\tB=video;A=window.Object.new();A.audio=audio\n\t\tif isinstance(B,bool):A.video=B\n\t\telse:\n\t\t\tA.video=window.Object.new()\n\t\t\tfor C in B:setattr(A.video,C,to_js(B[C]))\n\t\tD=await window.navigator.mediaDevices.getUserMedia(A);return D\n\tasync def get_stream(A):B=A.kind.replace('input','').replace('output','');C={B:{'deviceId':{'exact':A.id}}};return await A.load(**C)\nasync def list_devices():return[Device(A)for A in await window.navigator.mediaDevices.enumerateDevices()]",
|
"media.py": "from pyscript import window\nfrom pyscript.ffi import to_js\nclass Device:\n\tdef __init__(A,device):A._dom_element=device\n\t@property\n\tdef id(self):return self._dom_element.deviceId\n\t@property\n\tdef group(self):return self._dom_element.groupId\n\t@property\n\tdef kind(self):return self._dom_element.kind\n\t@property\n\tdef label(self):return self._dom_element.label\n\tdef __getitem__(A,key):return getattr(A,key)\n\t@classmethod\n\tasync def load(E,audio=False,video=True):\n\t\tB=video;A=window.Object.new();A.audio=audio\n\t\tif isinstance(B,bool):A.video=B\n\t\telse:\n\t\t\tA.video=window.Object.new()\n\t\t\tfor C in B:setattr(A.video,C,to_js(B[C]))\n\t\tD=await window.navigator.mediaDevices.getUserMedia(A);return D\n\tasync def get_stream(A):B=A.kind.replace('input','').replace('output','');C={B:{'deviceId':{'exact':A.id}}};return await A.load(**C)\nasync def list_devices():return[Device(A)for A in await window.navigator.mediaDevices.enumerateDevices()]",
|
||||||
"storage.py": "_C='memoryview'\n_B='bytearray'\n_A='generic'\nfrom polyscript import storage as _storage\nfrom pyscript.flatted import parse as _parse\nfrom pyscript.flatted import stringify as _stringify\ndef _to_idb(value):\n\tA=value\n\tif A is None:return _stringify(['null',0])\n\tif isinstance(A,(bool,float,int,str,list,dict,tuple)):return _stringify([_A,A])\n\tif isinstance(A,bytearray):return _stringify([_B,[A for A in A]])\n\tif isinstance(A,memoryview):return _stringify([_C,[A for A in A]])\n\traise TypeError(f\"Unexpected value: {A}\")\ndef _from_idb(value):\n\tC=value;A,B=_parse(C)\n\tif A=='null':return\n\tif A==_A:return B\n\tif A==_B:return bytearray(B)\n\tif A==_C:return memoryview(bytearray(B))\n\treturn C\nclass Storage(dict):\n\tdef __init__(B,store):A=store;super().__init__({A:_from_idb(B)for(A,B)in A.entries()});B.__store__=A\n\tdef __delitem__(A,attr):A.__store__.delete(attr);super().__delitem__(attr)\n\tdef __setitem__(B,attr,value):A=value;B.__store__.set(attr,_to_idb(A));super().__setitem__(attr,A)\n\tdef clear(A):A.__store__.clear();super().clear()\n\tasync def sync(A):await A.__store__.sync()\nasync def storage(name='',storage_class=Storage):\n\tif not name:raise ValueError('The storage name must be defined')\n\treturn storage_class(await _storage(f\"@pyscript/{name}\"))",
|
"storage.py": "_C='memoryview'\n_B='bytearray'\n_A='generic'\nfrom polyscript import storage as _storage\nfrom pyscript.flatted import parse as _parse\nfrom pyscript.flatted import stringify as _stringify\ndef _to_idb(value):\n\tA=value\n\tif A is None:return _stringify(['null',0])\n\tif isinstance(A,(bool,float,int,str,list,dict,tuple)):return _stringify([_A,A])\n\tif isinstance(A,bytearray):return _stringify([_B,[A for A in A]])\n\tif isinstance(A,memoryview):return _stringify([_C,[A for A in A]])\n\traise TypeError(f\"Unexpected value: {A}\")\ndef _from_idb(value):\n\tC=value;A,B=_parse(C)\n\tif A=='null':return\n\tif A==_A:return B\n\tif A==_B:return bytearray(B)\n\tif A==_C:return memoryview(bytearray(B))\n\treturn C\nclass Storage(dict):\n\tdef __init__(B,store):A=store;super().__init__({A:_from_idb(B)for(A,B)in A.entries()});B.__store__=A\n\tdef __delitem__(A,attr):A.__store__.delete(attr);super().__delitem__(attr)\n\tdef __setitem__(B,attr,value):A=value;B.__store__.set(attr,_to_idb(A));super().__setitem__(attr,A)\n\tdef clear(A):A.__store__.clear();super().clear()\n\tasync def sync(A):await A.__store__.sync()\nasync def storage(name='',storage_class=Storage):\n\tif not name:raise ValueError('The storage name must be defined')\n\treturn storage_class(await _storage(f\"@pyscript/{name}\"))",
|
||||||
"util.py": "import js,sys,inspect\ndef as_bytearray(buffer):\n\tA=js.Uint8Array.new(buffer);B=A.length;C=bytearray(B)\n\tfor D in range(0,B):C[D]=A[D]\n\treturn C\nclass NotSupported:\n\tdef __init__(A,name,error):object.__setattr__(A,'name',name);object.__setattr__(A,'error',error)\n\tdef __repr__(A):return f\"<NotSupported {A.name} [{A.error}]>\"\n\tdef __getattr__(A,attr):raise AttributeError(A.error)\n\tdef __setattr__(A,attr,value):raise AttributeError(A.error)\n\tdef __call__(A,*B):raise TypeError(A.error)\ndef is_awaitable(obj):\n\tA=obj;from pyscript import config as B\n\tif B['type']=='mpy':\n\t\tif'<closure <generator>'in repr(A):return True\n\t\treturn inspect.isgeneratorfunction(A)\n\treturn inspect.iscoroutinefunction(A)",
|
"util.py": "import js,sys,inspect\ndef as_bytearray(buffer):\n\tA=js.Uint8Array.new(buffer);B=A.length;C=bytearray(B)\n\tfor D in range(0,B):C[D]=A[D]\n\treturn C\nclass NotSupported:\n\tdef __init__(A,name,error):object.__setattr__(A,'name',name);object.__setattr__(A,'error',error)\n\tdef __repr__(A):return f\"<NotSupported {A.name} [{A.error}]>\"\n\tdef __getattr__(A,attr):raise AttributeError(A.error)\n\tdef __setattr__(A,attr,value):raise AttributeError(A.error)\n\tdef __call__(A,*B):raise TypeError(A.error)\ndef is_awaitable(obj):\n\tA=obj;from pyscript import config as B\n\tif B['type']=='mpy':\n\t\tif'<closure <generator>'in repr(A):return True\n\t\treturn inspect.isgeneratorfunction(A)\n\treturn inspect.iscoroutinefunction(A)",
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ if RUNNING_IN_WORKER:
|
|||||||
|
|
||||||
window = polyscript.xworker.window
|
window = polyscript.xworker.window
|
||||||
document = window.document
|
document = window.document
|
||||||
|
# weird + not worth it as it does not work anyway
|
||||||
|
js.screen = window.screen
|
||||||
js.document = document
|
js.document = document
|
||||||
# this is the same as js_import on main and it lands modules on main
|
# this is the same as js_import on main and it lands modules on main
|
||||||
js_import = window.Function(
|
js_import = window.Function(
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
30
core/tests/manual/game/aliens.css
Normal file
30
core/tests/manual/game/aliens.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/* (c) https://github.com/ryanking13/pyodide-pygame-demo/blob/main/examples/aliens.html */
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.demo {
|
||||||
|
background-color: #fff;
|
||||||
|
margin: 20px auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.demo-header {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
padding: 15px 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.demo-content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
394
core/tests/manual/game/aliens.py
Normal file
394
core/tests/manual/game/aliens.py
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
""" (c) https://github.com/ryanking13/pyodide-pygame-demo/blob/main/examples/aliens.html
|
||||||
|
pygame.examples.aliens
|
||||||
|
|
||||||
|
Shows a mini game where you have to defend against aliens.
|
||||||
|
|
||||||
|
What does it show you about pygame?
|
||||||
|
|
||||||
|
* pygame.sprite, the difference between Sprite and Group.
|
||||||
|
* dirty rectangle optimization for processing for speed.
|
||||||
|
* music with pygame.mixer.music, including fadeout
|
||||||
|
* sound effects with pygame.Sound
|
||||||
|
* event processing, keyboard handling, QUIT handling.
|
||||||
|
* a main loop frame limited with a game clock from the pygame.time module
|
||||||
|
* fullscreen switching.
|
||||||
|
|
||||||
|
|
||||||
|
Controls
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Left and right arrows to move.
|
||||||
|
* Space bar to shoot.
|
||||||
|
* f key to toggle between fullscreen.
|
||||||
|
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
# import basic pygame modules
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
# see if we can load more than standard BMP
|
||||||
|
if not pygame.image.get_extended():
|
||||||
|
raise SystemExit("Sorry, extended image module required")
|
||||||
|
|
||||||
|
|
||||||
|
# game constants
|
||||||
|
MAX_SHOTS = 2 # most player bullets onscreen
|
||||||
|
ALIEN_ODDS = 22 # chances a new alien appears
|
||||||
|
BOMB_ODDS = 60 # chances a new bomb will drop
|
||||||
|
ALIEN_RELOAD = 12 # frames between new aliens
|
||||||
|
SCREENRECT = pygame.Rect(0, 0, 640, 480)
|
||||||
|
SCORE = 0
|
||||||
|
|
||||||
|
|
||||||
|
main_dir = str(pathlib.Path(pygame.__file__).parent / "examples")
|
||||||
|
|
||||||
|
def load_image(file):
|
||||||
|
"""loads an image, prepares it for play"""
|
||||||
|
file = os.path.join(main_dir, "data", file)
|
||||||
|
try:
|
||||||
|
surface = pygame.image.load(file)
|
||||||
|
except pygame.error:
|
||||||
|
raise SystemExit(f'Could not load image "{file}" {pygame.get_error()}')
|
||||||
|
return surface.convert()
|
||||||
|
|
||||||
|
|
||||||
|
def load_sound(file):
|
||||||
|
"""because pygame can be be compiled without mixer."""
|
||||||
|
if not pygame.mixer:
|
||||||
|
return None
|
||||||
|
file = os.path.join(main_dir, "data", file)
|
||||||
|
try:
|
||||||
|
sound = pygame.mixer.Sound(file)
|
||||||
|
return sound
|
||||||
|
except pygame.error:
|
||||||
|
print(f"Warning, unable to load, {file}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Each type of game object gets an init and an update function.
|
||||||
|
# The update function is called once per frame, and it is when each object should
|
||||||
|
# change its current position and state.
|
||||||
|
#
|
||||||
|
# The Player object actually gets a "move" function instead of update,
|
||||||
|
# since it is passed extra information about the keyboard.
|
||||||
|
|
||||||
|
|
||||||
|
class Player(pygame.sprite.Sprite):
|
||||||
|
"""Representing the player as a moon buggy type car."""
|
||||||
|
|
||||||
|
speed = 10
|
||||||
|
bounce = 24
|
||||||
|
gun_offset = -11
|
||||||
|
images = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pygame.sprite.Sprite.__init__(self, self.containers)
|
||||||
|
self.image = self.images[0]
|
||||||
|
self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
|
||||||
|
self.reloading = False
|
||||||
|
self.origtop = self.rect.top
|
||||||
|
self.facing = -1
|
||||||
|
|
||||||
|
def move(self, direction):
|
||||||
|
if direction:
|
||||||
|
self.facing = direction
|
||||||
|
self.rect.move_ip(direction * self.speed, 0)
|
||||||
|
self.rect = self.rect.clamp(SCREENRECT)
|
||||||
|
if direction < 0:
|
||||||
|
self.image = self.images[0]
|
||||||
|
elif direction > 0:
|
||||||
|
self.image = self.images[1]
|
||||||
|
self.rect.top = self.origtop - (self.rect.left // self.bounce % 2)
|
||||||
|
|
||||||
|
def gunpos(self):
|
||||||
|
pos = self.facing * self.gun_offset + self.rect.centerx
|
||||||
|
return pos, self.rect.top
|
||||||
|
|
||||||
|
|
||||||
|
class Alien(pygame.sprite.Sprite):
|
||||||
|
"""An alien space ship. That slowly moves down the screen."""
|
||||||
|
|
||||||
|
speed = 13
|
||||||
|
animcycle = 12
|
||||||
|
images = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pygame.sprite.Sprite.__init__(self, self.containers)
|
||||||
|
self.image = self.images[0]
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
self.facing = random.choice((-1, 1)) * Alien.speed
|
||||||
|
self.frame = 0
|
||||||
|
if self.facing < 0:
|
||||||
|
self.rect.right = SCREENRECT.right
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.rect.move_ip(self.facing, 0)
|
||||||
|
if not SCREENRECT.contains(self.rect):
|
||||||
|
self.facing = -self.facing
|
||||||
|
self.rect.top = self.rect.bottom + 1
|
||||||
|
self.rect = self.rect.clamp(SCREENRECT)
|
||||||
|
self.frame = self.frame + 1
|
||||||
|
self.image = self.images[self.frame // self.animcycle % 3]
|
||||||
|
|
||||||
|
|
||||||
|
class Explosion(pygame.sprite.Sprite):
|
||||||
|
"""An explosion. Hopefully the Alien and not the player!"""
|
||||||
|
|
||||||
|
defaultlife = 12
|
||||||
|
animcycle = 3
|
||||||
|
images = []
|
||||||
|
|
||||||
|
def __init__(self, actor):
|
||||||
|
pygame.sprite.Sprite.__init__(self, self.containers)
|
||||||
|
self.image = self.images[0]
|
||||||
|
self.rect = self.image.get_rect(center=actor.rect.center)
|
||||||
|
self.life = self.defaultlife
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""called every time around the game loop.
|
||||||
|
|
||||||
|
Show the explosion surface for 'defaultlife'.
|
||||||
|
Every game tick(update), we decrease the 'life'.
|
||||||
|
|
||||||
|
Also we animate the explosion.
|
||||||
|
"""
|
||||||
|
self.life = self.life - 1
|
||||||
|
self.image = self.images[self.life // self.animcycle % 2]
|
||||||
|
if self.life <= 0:
|
||||||
|
self.kill()
|
||||||
|
|
||||||
|
|
||||||
|
class Shot(pygame.sprite.Sprite):
|
||||||
|
"""a bullet the Player sprite fires."""
|
||||||
|
|
||||||
|
speed = -11
|
||||||
|
images = []
|
||||||
|
|
||||||
|
def __init__(self, pos):
|
||||||
|
pygame.sprite.Sprite.__init__(self, self.containers)
|
||||||
|
self.image = self.images[0]
|
||||||
|
self.rect = self.image.get_rect(midbottom=pos)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""called every time around the game loop.
|
||||||
|
|
||||||
|
Every tick we move the shot upwards.
|
||||||
|
"""
|
||||||
|
self.rect.move_ip(0, self.speed)
|
||||||
|
if self.rect.top <= 0:
|
||||||
|
self.kill()
|
||||||
|
|
||||||
|
|
||||||
|
class Bomb(pygame.sprite.Sprite):
|
||||||
|
"""A bomb the aliens drop."""
|
||||||
|
|
||||||
|
speed = 9
|
||||||
|
images = []
|
||||||
|
|
||||||
|
def __init__(self, alien):
|
||||||
|
pygame.sprite.Sprite.__init__(self, self.containers)
|
||||||
|
self.image = self.images[0]
|
||||||
|
self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""called every time around the game loop.
|
||||||
|
|
||||||
|
Every frame we move the sprite 'rect' down.
|
||||||
|
When it reaches the bottom we:
|
||||||
|
|
||||||
|
- make an explosion.
|
||||||
|
- remove the Bomb.
|
||||||
|
"""
|
||||||
|
self.rect.move_ip(0, self.speed)
|
||||||
|
if self.rect.bottom >= 470:
|
||||||
|
Explosion(self)
|
||||||
|
self.kill()
|
||||||
|
|
||||||
|
|
||||||
|
class Score(pygame.sprite.Sprite):
|
||||||
|
"""to keep track of the score."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pygame.sprite.Sprite.__init__(self)
|
||||||
|
self.font = pygame.Font(None, 20)
|
||||||
|
self.font.set_italic(1)
|
||||||
|
self.color = "white"
|
||||||
|
self.lastscore = -1
|
||||||
|
self.update()
|
||||||
|
self.rect = self.image.get_rect().move(10, 450)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""We only update the score in update() when it has changed."""
|
||||||
|
if SCORE != self.lastscore:
|
||||||
|
self.lastscore = SCORE
|
||||||
|
msg = "Score: %d" % SCORE
|
||||||
|
self.image = self.font.render(msg, 0, self.color)
|
||||||
|
|
||||||
|
|
||||||
|
async def main(winstyle=0):
|
||||||
|
# Initialize pygame
|
||||||
|
pygame.mixer.pre_init(44100, 32, 2, 1024)
|
||||||
|
pygame.init()
|
||||||
|
if pygame.mixer and not pygame.mixer.get_init():
|
||||||
|
print("Warning, no sound")
|
||||||
|
pygame.mixer = None
|
||||||
|
|
||||||
|
fullscreen = False
|
||||||
|
# Set the display mode
|
||||||
|
winstyle = 0 # |FULLSCREEN
|
||||||
|
screen = pygame.display.set_mode(SCREENRECT.size, winstyle)
|
||||||
|
|
||||||
|
# Load images, assign to sprite classes
|
||||||
|
# (do this before the classes are used, after screen setup)
|
||||||
|
img = load_image("player1.gif")
|
||||||
|
Player.images = [img, pygame.transform.flip(img, 1, 0)]
|
||||||
|
img = load_image("explosion1.gif")
|
||||||
|
Explosion.images = [img, pygame.transform.flip(img, 1, 1)]
|
||||||
|
Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")]
|
||||||
|
Bomb.images = [load_image("bomb.gif")]
|
||||||
|
Shot.images = [load_image("shot.gif")]
|
||||||
|
|
||||||
|
# decorate the game window
|
||||||
|
icon = pygame.transform.scale(Alien.images[0], (32, 32))
|
||||||
|
pygame.display.set_icon(icon)
|
||||||
|
pygame.display.set_caption("Pygame Aliens")
|
||||||
|
pygame.mouse.set_visible(0)
|
||||||
|
|
||||||
|
# create the background, tile the bgd image
|
||||||
|
bgdtile = load_image("background.gif")
|
||||||
|
background = pygame.Surface(SCREENRECT.size)
|
||||||
|
for x in range(0, SCREENRECT.width, bgdtile.get_width()):
|
||||||
|
background.blit(bgdtile, (x, 0))
|
||||||
|
screen.blit(background, (0, 0))
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
# load the sound effects
|
||||||
|
boom_sound = load_sound("boom.wav")
|
||||||
|
shoot_sound = load_sound("car_door.wav")
|
||||||
|
if pygame.mixer:
|
||||||
|
music = os.path.join(main_dir, "data", "house_lo.wav")
|
||||||
|
pygame.mixer.music.load(music)
|
||||||
|
pygame.mixer.music.play(-1)
|
||||||
|
|
||||||
|
# Initialize Game Groups
|
||||||
|
aliens = pygame.sprite.Group()
|
||||||
|
shots = pygame.sprite.Group()
|
||||||
|
bombs = pygame.sprite.Group()
|
||||||
|
all = pygame.sprite.RenderUpdates()
|
||||||
|
lastalien = pygame.sprite.GroupSingle()
|
||||||
|
|
||||||
|
# assign default groups to each sprite class
|
||||||
|
Player.containers = all
|
||||||
|
Alien.containers = aliens, all, lastalien
|
||||||
|
Shot.containers = shots, all
|
||||||
|
Bomb.containers = bombs, all
|
||||||
|
Explosion.containers = all
|
||||||
|
Score.containers = all
|
||||||
|
|
||||||
|
# Create Some Starting Values
|
||||||
|
global score
|
||||||
|
alienreload = ALIEN_RELOAD
|
||||||
|
clock = pygame.Clock()
|
||||||
|
|
||||||
|
# initialize our starting sprites
|
||||||
|
global SCORE
|
||||||
|
player = Player()
|
||||||
|
Alien() # note, this 'lives' because it goes into a sprite group
|
||||||
|
if pygame.font:
|
||||||
|
all.add(Score())
|
||||||
|
|
||||||
|
# Run our main loop whilst the player is alive.
|
||||||
|
while player.alive():
|
||||||
|
# get input
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
return
|
||||||
|
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
||||||
|
return
|
||||||
|
elif event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == pygame.K_f:
|
||||||
|
if not fullscreen:
|
||||||
|
print("Changing to FULLSCREEN")
|
||||||
|
screen_backup = screen.copy()
|
||||||
|
screen = pygame.display.set_mode(
|
||||||
|
SCREENRECT.size, winstyle | pygame.FULLSCREEN, bestdepth
|
||||||
|
)
|
||||||
|
screen.blit(screen_backup, (0, 0))
|
||||||
|
else:
|
||||||
|
print("Changing to windowed mode")
|
||||||
|
screen_backup = screen.copy()
|
||||||
|
screen = pygame.display.set_mode(
|
||||||
|
SCREENRECT.size, winstyle, bestdepth
|
||||||
|
)
|
||||||
|
screen.blit(screen_backup, (0, 0))
|
||||||
|
pygame.display.flip()
|
||||||
|
fullscreen = not fullscreen
|
||||||
|
|
||||||
|
keystate = pygame.key.get_pressed()
|
||||||
|
|
||||||
|
# clear/erase the last drawn sprites
|
||||||
|
all.clear(screen, background)
|
||||||
|
|
||||||
|
# update all the sprites
|
||||||
|
all.update()
|
||||||
|
|
||||||
|
# handle player input
|
||||||
|
direction = keystate[pygame.K_RIGHT] - keystate[pygame.K_LEFT]
|
||||||
|
player.move(direction)
|
||||||
|
firing = keystate[pygame.K_SPACE]
|
||||||
|
if not player.reloading and firing and len(shots) < MAX_SHOTS:
|
||||||
|
Shot(player.gunpos())
|
||||||
|
if pygame.mixer:
|
||||||
|
shoot_sound.play()
|
||||||
|
player.reloading = firing
|
||||||
|
|
||||||
|
# Create new alien
|
||||||
|
if alienreload:
|
||||||
|
alienreload = alienreload - 1
|
||||||
|
elif not int(random.random() * ALIEN_ODDS):
|
||||||
|
Alien()
|
||||||
|
alienreload = ALIEN_RELOAD
|
||||||
|
|
||||||
|
# Drop bombs
|
||||||
|
if lastalien and not int(random.random() * BOMB_ODDS):
|
||||||
|
Bomb(lastalien.sprite)
|
||||||
|
|
||||||
|
# Detect collisions between aliens and players.
|
||||||
|
for alien in pygame.sprite.spritecollide(player, aliens, 1):
|
||||||
|
if pygame.mixer:
|
||||||
|
boom_sound.play()
|
||||||
|
Explosion(alien)
|
||||||
|
Explosion(player)
|
||||||
|
SCORE = SCORE + 1
|
||||||
|
player.kill()
|
||||||
|
|
||||||
|
# See if shots hit the aliens.
|
||||||
|
for alien in pygame.sprite.groupcollide(aliens, shots, 1, 1).keys():
|
||||||
|
if pygame.mixer:
|
||||||
|
boom_sound.play()
|
||||||
|
Explosion(alien)
|
||||||
|
SCORE = SCORE + 1
|
||||||
|
|
||||||
|
# See if alien bombs hit the player.
|
||||||
|
for bomb in pygame.sprite.spritecollide(player, bombs, 1):
|
||||||
|
if pygame.mixer:
|
||||||
|
boom_sound.play()
|
||||||
|
Explosion(player)
|
||||||
|
Explosion(bomb)
|
||||||
|
player.kill()
|
||||||
|
|
||||||
|
# draw the scene
|
||||||
|
dirty = all.draw(screen)
|
||||||
|
pygame.display.update(dirty)
|
||||||
|
|
||||||
|
# cap the framerate at 40fps. Also called 40HZ or 40 times per second.
|
||||||
|
await asyncio.sleep(0.025)
|
||||||
|
|
||||||
|
if pygame.mixer:
|
||||||
|
pygame.mixer.music.fadeout(1000)
|
||||||
|
|
||||||
|
main()
|
||||||
24
core/tests/manual/game/index.html
Normal file
24
core/tests/manual/game/index.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="aliens.css" />
|
||||||
|
<link rel="stylesheet" href="../../../dist/core.css" />
|
||||||
|
<script type="module" src="../../../dist/core.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script
|
||||||
|
type="py"
|
||||||
|
src="aliens.py"
|
||||||
|
canvas="canvas"
|
||||||
|
config='{"packages":["pygame-ce"]}'
|
||||||
|
></script>
|
||||||
|
<div class="demo">
|
||||||
|
<div class="demo-header">pygame.examples.aliens</div>
|
||||||
|
<div class="demo-content">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user