├── .gitignore ├── CHANGELOG.txt ├── LICENSE.txt ├── README.txt ├── doc ├── guide.txt ├── libtest.txt ├── port.txt └── pyjs_changes.zip ├── libtest.py ├── pyjsdl ├── __init__.py ├── app.py ├── color.py ├── constants.py ├── cursors.py ├── display.py ├── draw.py ├── env.py ├── event.py ├── font.py ├── image.py ├── key.py ├── mask.py ├── mixer.py ├── mouse.py ├── pyjsarray.py ├── pyjsobj.py ├── rect.py ├── sprite.py ├── surface.py ├── surfarray.py ├── time.py ├── transform.py ├── util.py ├── vector.py └── version.py └── test ├── __init__.py ├── color_test.py ├── cursor_test.py ├── draw_test.py ├── event_test.py ├── libtest.py ├── mask_test.py ├── rect_test.py ├── sprite_test.py ├── surface_test.py ├── surfarray_test.py ├── time_test.py ├── transform_test.py └── vector_test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | *.jpg 4 | *~ 5 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | 0.27_dev 2 | -refactor mouse positioning. 3 | -add mouse get_focused method. 4 | -add display resize method. 5 | 6 | 0.27 2025-01-18 7 | -revise surface alpha. 8 | -update transform to preserve surface alpha. 9 | -add vector copy method. 10 | -revise mixer set_num_channels method. 11 | -refactor mixer music rewind method. 12 | -revise mixer channel play promise handling. 13 | -add activeevent. 14 | -add closeevent. 15 | -add display set_icon. 16 | -update color object. 17 | -revise sprite to optimize. 18 | -revise vector elementwise object. 19 | -revise vector operator methods. 20 | -revise vector object instantiation. 21 | 22 | 0.26 2022-04-18 23 | -revise event set_blocked processing. 24 | -revise event object for performance. 25 | -refactor event handler for performance. 26 | -add key set_repeat method. 27 | -revise key event handler. 28 | -update key event handler. 29 | -update surface blit with optional rect return. 30 | -add surface alpha methods. 31 | -add surface blits method. 32 | 33 | 0.25 2021-11-07 34 | -revise sprite orderedupdates. 35 | -add sprite group alias. 36 | -refactor sprite collide methods. 37 | -add sprite layeredupdates. 38 | -add cursors get_cursor_types. 39 | -add canvas contextmenu handler. 40 | -add vector object. 41 | -revise mixer processing. 42 | -add mixer music object. 43 | -add mixer channel endevent. 44 | -add mixer channel queue. 45 | 46 | 0.24 2021-04-28 47 | -revise rect move/inflate args processing. 48 | -revise rect attributes. 49 | -revise ndarray shape for inheritance. 50 | -add env check. 51 | -revise quit method to stop mixer. 52 | -revise quit method to stop timers. 53 | -update time set_timer for event argument. 54 | -revise app to use webkit2. 55 | -update for python 2/3 compatibility. 56 | 57 | 0.23 2021-04-06 58 | -update event handling. 59 | -update surface get_at to return color object. 60 | -refactor surface colorkey methods. 61 | -revise sprite blit process. 62 | -update mask for optimization. 63 | -update ndarray and bitset for optimization. 64 | -update time wait to properly delay callback. 65 | -revise requestanimationframe shim. 66 | -update clock with performance time. 67 | -update clock tick with performance time. 68 | -update asynchronous loop process to optimize. 69 | -update for python 2/3 compatibility. 70 | 71 | 0.22 2019-04-06 72 | -add display set_callback method. 73 | -update Canvas handling of callback change. 74 | -add time set_timer method. 75 | -update sprite to revise member groups. 76 | -update sprite argument handling. 77 | -update sprite collide_mask to use mask.overlap. 78 | -refactor rect for optimization. 79 | -update display setup to accept callback function or object with run method. 80 | -update rect to define __slots__. 81 | -add mouse set_cursor and get_cursor methods. 82 | -add surface toDataURL method. 83 | -update mouse set_cursor to use custom image. 84 | -update surface set_at to optimize. 85 | -update mouse set_cursor to use cursor data. 86 | -update transform rotozoom to optimize. 87 | -update display update method (_update) to optimize. 88 | -update surface blit method (_blit_clear) to optimize. 89 | -update draw arc of nonequal dimension to process properly. 90 | -update draw ellipse to use float scale argument. 91 | -update draw arc to optimize. 92 | -update asynchronous loop process to optimize. 93 | -refactor time clock object. 94 | -revise with absolute import statements. 95 | -add display methods getAbsoluteLeft/getAbsoluteTop/getScrollLeft/getScrollTop. 96 | -add touch event support. 97 | -update font with minor rendering adjustment. 98 | -update font to include initiation from a file. 99 | -update event peek option of all types. 100 | -update event methods to optimize. 101 | -update color constructor to accept hex '#rrggbb' argument. 102 | -update draw methods to optimize context state change. 103 | -update draw methods to optimize with optional rect return. 104 | -add app.py to launch app in webkit on desktop. 105 | 106 | 0.21 2015-04-12 107 | -update font to improve fonts access. 108 | -add rect.contains method. 109 | -update image load methods for imagedata objects. 110 | -add display.setup_images method. 111 | -add key.name method. 112 | -add mask.toString method. 113 | -update display.update to properly handle clipping. 114 | -update surface.blit with clipping. 115 | -update rect union methods to process properly. 116 | -add rect iter method. 117 | -add rect collide methods - collidelistall, collidedict, and collidedictall. 118 | -add sprite collide methods - collide_rect_ratio, collide_circle, and collide_circle_ratio. 119 | -refactor to isolate pyjs. 120 | -refactor for optimization. 121 | -correct event.pump to maintain queue. 122 | -update IE9+ compatibility files in pyjs_changes.zip. 123 | -add mousewheel event listener. 124 | -release under MIT license. 125 | 126 | 0.20 2014-11-09 127 | -add rect union methods. 128 | -update methods to use rect intersection. 129 | -correct surface subsurface rect access. 130 | -add rect rectPool to utilize a rect pool. 131 | -update sprite draw for performance. 132 | -update sprite collide methods processing. 133 | -add rect clamp methods. 134 | -update display setup callback function. 135 | -update display repaint process. 136 | -add mixer. 137 | 138 | 0.19 2014-08-20 139 | -update bitset array to avoid js reserved work. 140 | -update quit function to terminate program iteration. 141 | -update transform.rotozoom for correct scaling. 142 | -add env.pyjs_mode with strict/optimized bool attributes to check pyjs-S/-O mode. 143 | -correct modifier keys keyevent detect with pyjs -S compilation (worked in -O) with sets membership testing of onKeyDown keycode that appears due to js/pyjs numeric difference. 144 | -update display event to properly clear modifier keys held with onMouseLeave event. 145 | -update display set_caption/get_caption to access Canvas element id. 146 | -add display.get_active method. 147 | -restructure event handler to isolate from UserEvent and JEvent objects. 148 | -update display.update for performance. 149 | -update event eventtype list as a set object. 150 | -update rect inflate/inflate_ip positioning. 151 | 152 | 0.18 2014-02-27 153 | -update Pyjsarray typedarray constructor for Chrome compatibility. 154 | -update Surface constructor to int convert argument. 155 | -update Transform.rotate positioning. 156 | -update Transform rotate/flip to restore context. 157 | -add Textbox/Textarea resize method. 158 | -update Surface.resize for width/height attributes. 159 | -update Canvas.resize to align elements. 160 | -update Canvas to subclass Surface. 161 | -update Canvas with buffered surface optional. 162 | -add display.is_canvas and update Canvas blit. 163 | -update Surface constructor to take optional arguments. 164 | -update Event.poll noevent return. 165 | -update Surface.get_at to return color tuple. 166 | -update Rect attribute positioning. 167 | -add Rect equality and nonzero methods. 168 | -add util.Pyjs_Mode to check Pyjs compilation mode. 169 | -add Color object. 170 | -update to use Color object. 171 | -update Rect constructor. 172 | -update draw methods with minor adjustments. 173 | -update Mask.fill method. 174 | -update to use Rect object. 175 | -update Sprite group update to account for member changes. 176 | -update util.Timer for output to textarea rather than console logger. 177 | -update Surfarray array2d PyImageInteger processing. 178 | -update Surfarray make_surface method. 179 | -add draw.ellipse method. 180 | -update Pyjsarray Typedarray constructor when type not implemented. 181 | -change license from GPL to LGPL. 182 | 183 | 0.17 2013-11-01 184 | -update Pyjsarray ImageData object for IE compatibility. 185 | -add util.call to call unbound methods. 186 | -update Surface.blit to int convert argument. 187 | -update Rect.inflate and Rect.inflate_ip for proper positioning. 188 | -add Textbox/Textarea and display.textbox_init for text input. 189 | -update mouse positioning with page scroll position. 190 | -update Surfarray.blit_array. 191 | -add Pyjsarray Ndarray methods. 192 | 193 | 0.16 2013-09-01 194 | -add Surfarray using Pyjsarray. 195 | -update Pyjsarray with Ndarray and ImageData objects. 196 | -update image.load 197 | -update display.update rect processing 198 | -add display.update_rect method 199 | -update display.update/display.update_rect/surface._blit_clear rect clipping 200 | 201 | 0.15 2013-07-30 202 | -add Mask using Pyjsarray and Pyjsbitset. 203 | -add Pyjsarray and Pyjsbitset based on JavaScript TypedArray object. 204 | -add Surface get_at/set_at using Canvas getImageData and putImageData. 205 | -update Surface.subarray. 206 | -update Canvas and Surface to use pyjamas.Canvas.HTML5Canvas. 207 | -update Rect.createIntersection. 208 | -update pyjs_changes.zip - Pyjs git update for HTML5canvas usage. 209 | 210 | 0.14 2013-05-15 211 | -add Font rendering to canvas. 212 | -update Event so key event works in IE browser. 213 | -update Display.update rect argument for pyjs -O compilation. 214 | -update Event key handling for IE browser. 215 | -update Sprite Group __contains__ to work with pyjs. 216 | -update Surface.blit to take rect argument and return rect. 217 | -update Surface.fill color argument. 218 | -update pyjs_changes.zip. 219 | 220 | 0.13 2013-05-04 221 | -add key events to Canvas. 222 | -update Event for process JS key events. 223 | -update Mouse methods variable. 224 | 225 | 0.12 2013-04-29 226 | -add mouse events to Canvas. 227 | -update Event to process JS mouse events. 228 | -update Mouse.get_pressed and Mouse.get_pos for JS mouse events. 229 | -update Time.wait to use JS Timeout. 230 | 231 | 0.11 2013-04-26 232 | -update Sprite.clear to allow callable argument. 233 | -update Surface._blit_clear with clipping. 234 | -update Surface.blits to directly call drawImage. 235 | -update Surface.fill rect argument. 236 | -update Draw.arc, Transform.rotate, and Rect with int() division. 237 | 238 | 0.10 2013-04-24 239 | -initial release 240 | 241 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 James Garnon 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Pyjsdl - Python-to-JavaScript Multimedia Framework 2 | 3 | Pyjsdl module is modelled on Pygame/SDL commands that wraps JavaScript functionality including HTML5 canvas. The module permits scripts coded in Python/Pygame to compile to JavaScript using the Pyjs compiler (http://pyjs.org), allowing deployment of JavaScript applications without extensive editing of the script. The module supports a substantial portion of Pygame functionality. Information concerning use of the Pyjsdl module is included in doc/guide.txt. 4 | 5 | The Pyjsdl-ts package (https://gatc.ca/projects/pyjsdl-ts/) is a port of Pyjsdl that compiles to JavaScript using the Transcrypt compiler (https://www.transcrypt.org/), information is included in doc/port.txt. 6 | 7 | Pyjsdl is released under the MIT License, see LICENSE.txt for further information. 8 | 9 | Pyjsdl page: https://gatc.ca/projects/pyjsdl/ 10 | Pyjsdl docs: https://gatc.ca/projects/pyjsdl/doc/ 11 | 12 | -------------------------------------------------------------------------------- /doc/guide.txt: -------------------------------------------------------------------------------- 1 | Pyjsdl Module Guide 2 | 3 | Pyjsdl module is modelled on Pygame/SDL commands that wraps JavaScript functionality including HTML5 canvas. To use Pyjsdl module, place pyjsdl folder in the script folder or on the module path. Import pyjsdl into the Python script, or use the statement 'import pyjsdl as pygame' to maintain the Pygame commands. During pyjsdl initiation, use the statement pyjsdl.display.setup(run, images) to provide the canvas the main function to execute at a timed interval and program images to preload, where the 'run' function contains statements derived from the main loop to be repeated each frame and 'images' is a list of image paths in the form ['./images/img.png']. 4 | 5 | Python code using Pyjsdl is compiled to a JavaScript application with the Pyjs compiler (http://pyjs.org). Install Pyjs from https://github.com/pyjs/pyjs repository, for instance to install into a virtualenv: 6 | 7 | virtualenv -p python2 env 8 | source env/bin/activate 9 | pip install git+https://github.com/pyjs/pyjs.git#egg=pyjs 10 | 11 | After installation, check pyjs_changes.zip in the package for required changes. Compile the Python code using pyjsbuild. The code can be compiled in strict (-S) mode for maximum Python compatibility: 12 | 13 | pyjsbuild -S --dynamic-link script.py -o output 14 | 15 | The code can be compiled in optimized (-O) mode for enhanced performance with reduced Python compatibility, optionally with --enable-descriptor-proto/--enable-operator-funcs: 16 | 17 | pyjsbuild -O --dynamic-link script.py -o output 18 | 19 | Some additional changes to the Python script may be necessary for Pyjs compilation. A web application can be deployed following compilation to JavaScript, which can run in a web browser on a pc or mobile. To run on a local HTTP server, use command 'python3 -m http.server' and browse to localhost:8000. During development, use the web browser devtools console that displays print and error output, and to maintain updated development changes set devtools to open with cache disabled. The app can also run on the desktop using the app.py script, check instructions within the script. 20 | 21 | Further information is available on the Pyjsdl project page (https://gatc.ca/projects/pyjsdl/) and in the API documentation (https://gatc.ca/projects/pyjsdl/doc/). 22 | 23 | -------------------------------------------------------------------------------- /doc/libtest.txt: -------------------------------------------------------------------------------- 1 | Libtest 2 | 3 | The libtest script tests the functionality of the Pyjsdl library, capable of running with Pyjs/Pyjsdl or Python/Pygame. To test Pyjsdl, the system should have Pyjs installed and the pyjsdl folder on script path. The libtest.py script in package root executes the tests in the test folder. Pyjs compiled code is run in web browser from libtest.html in output folder with local server running. Tests are compiled with Pyjs in strict mode for maximum Python compatibility, optimized for reduced Python compatibility with performance, or optimized mode with --enable-descriptor-proto/--enable-operator-funcs options to restore some Python compatibility. 4 | 5 | Build in strict mode: 6 | 7 | 'pyjsbuild -S --dynamic-link libtest.py -o outputs' 8 | 9 | Build in optimized mode: 10 | 11 | 'pyjsbuild -O --dynamic-link libtest.py -o outputo' 12 | 13 | Build in optimized mode with feature options: 14 | 15 | 'pyjsbuild -O --dynamic-link --enable-descriptor-proto --enable-operator-funcs libtest.py -o outputopf' 16 | 17 | -------------------------------------------------------------------------------- /doc/port.txt: -------------------------------------------------------------------------------- 1 | Pyjsdl - Python-to-JavaScript Multimedia Framework 2 | 3 | The pyjsdl-ts package is a port of pyjsdl (https://gatc.ca/projects/pyjsdl/) module with the same functionality. Pyjsdl module is modelled on Pygame/SDL commands that wraps JavaScript functionality including HTML5 canvas. The module permits scripts coded in Python/Pygame to compile to JavaScript using the Transcrypt compiler (https://www.transcrypt.org/), allowing deployment of JavaScript applications without extensive editing of the script. 4 | 5 | Pyjsdl page: https://gatc.ca/projects/pyjsdl-ts/ 6 | Pyjsdl docs: https://gatc.ca/projects/pyjsdl-ts/doc/ 7 | 8 | -------------------------------------------------------------------------------- /doc/pyjs_changes.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggatc/pyjsdl/1ba9e5ca68807bd73cc03e933e931bc08f28e934/doc/pyjs_changes.zip -------------------------------------------------------------------------------- /libtest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Libtest 5 | 6 | Check doc/libtest.txt for information. 7 | 8 | 9 | tests = ['surface_test', 10 | 'rect_test', 11 | 'draw_test', 12 | 'transform_test', 13 | 'surfarray_test', 14 | 'mask_test', 15 | 'color_test', 16 | 'cursor_test', 17 | 'sprite_test', 18 | 'event_test', 19 | 'time_test', 20 | 'vector_test'] 21 | """ 22 | 23 | 24 | tests = [] #test specific module if added to tests list, default all tests 25 | catch_exception = True #exception in test caught, set to False to raise exception 26 | 27 | 28 | import sys 29 | sys.dont_write_bytecode = True 30 | 31 | from test import libtest 32 | 33 | libtest.main(tests, catch_exception) 34 | 35 | -------------------------------------------------------------------------------- /pyjsdl/__init__.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Python-to-JavaScript Multimedia Framework 2 | #Copyright (c) 2013 James Garnon 3 | # 4 | #Permission is hereby granted, free of charge, to any person obtaining a copy 5 | #of this software and associated documentation files (the "Software"), to deal 6 | #in the Software without restriction, including without limitation the rights 7 | #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | #copies of the Software, and to permit persons to whom the Software is 9 | #furnished to do so, subject to the following conditions: 10 | # 11 | #The above copyright notice and this permission notice shall be included in 12 | #all copies or substantial portions of the Software. 13 | # 14 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | #THE SOFTWARE. 21 | 22 | """ 23 | **Pyjsdl - Pyjs Canvas Library** 24 | """ 25 | 26 | __version__ = '0.27' 27 | 28 | from pyjsdl import env 29 | from pyjsdl import util 30 | from pyjsdl.display import Display 31 | from pyjsdl.surface import Surface 32 | from pyjsdl.rect import Rect 33 | from pyjsdl.image import Image 34 | from pyjsdl.event import Event 35 | from pyjsdl.key import Key 36 | from pyjsdl.mouse import Mouse 37 | from pyjsdl.color import Color 38 | from pyjsdl.mixer import Mixer 39 | from pyjsdl.time import Time 40 | from pyjsdl.vector import Vector2 41 | from pyjsdl import draw 42 | from pyjsdl import transform 43 | from pyjsdl import surface 44 | from pyjsdl import surfarray 45 | from pyjsdl import mask 46 | from pyjsdl import font 47 | from pyjsdl import sprite 48 | from pyjsdl import cursors 49 | from pyjsdl import version 50 | from pyjsdl.constants import * 51 | 52 | 53 | __docformat__ = 'restructuredtext' 54 | 55 | 56 | _initialized = False 57 | 58 | def init(): 59 | """ 60 | Initialize module. 61 | """ 62 | global time, display, image, event, key, mouse, mixer, _initialized 63 | if _initialized: 64 | return 65 | else: 66 | _initialized = True 67 | event = Event() 68 | env.set_env('event', event) 69 | time = Time() 70 | display = Display() 71 | image = Image() 72 | mixer = Mixer() 73 | mouse = Mouse() 74 | key = Key() 75 | 76 | init() 77 | 78 | 79 | def setup(callback, images=None): 80 | """ 81 | Initialize module for script execution. 82 | 83 | Argument include callback function to run and optional images list to preload. 84 | Callback function can also be an object with a run method to call. 85 | The images can be image URL, or file-like object or base64 data in format (name.ext,data). 86 | """ 87 | display.setup(callback, images) 88 | 89 | 90 | def set_callback(callback): 91 | """ 92 | Set callback function. 93 | 94 | Argument callback function to run. 95 | Callback function can also be an object with a run method to call. 96 | """ 97 | display.set_callback(callback) 98 | 99 | 100 | def setup_images(images): 101 | """ 102 | Add images to image preload list. 103 | 104 | The argument is an image or list of images representing an image URL, or file-like object or base64 data in format (name.ext,data). 105 | Image preloading occurs at setup call. 106 | """ 107 | display.set_images(images) 108 | 109 | 110 | def quit(): 111 | """ 112 | Terminates canvas repaint and callback function. 113 | """ 114 | canvas = display.get_canvas() 115 | canvas.stop() 116 | mixer.quit() 117 | time._stop_timers() 118 | 119 | 120 | class error(RuntimeError): 121 | """ 122 | Exception object. 123 | """ 124 | pass 125 | 126 | 127 | def bounding_rect_return(setting): 128 | """ 129 | Bounding rect return. 130 | 131 | Set whether blit/draw return bounding Rect. 132 | Setting (bool) defaults to True on module initialization. 133 | """ 134 | surface.bounding_rect_return(setting) 135 | draw.bounding_rect_return(setting) 136 | 137 | -------------------------------------------------------------------------------- /pyjsdl/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | #Pyjsdl - Copyright (C) 2013 4 | #Released under the MIT License 5 | 6 | """ 7 | Pyjsdl App 8 | 9 | Script launches HTML app on desktop using Gtk/Webkit. 10 | Copy app script to the application root and optionally rename. 11 | Run the script once to create an ini file and edit to configure. 12 | 13 | Tested under Linux Gnome desktop with the installed packages: 14 | gir1.2-webkit2-4.0, python-gi (py2), python3-gi (py3). 15 | On other OS, additional installation steps may be required. 16 | """ 17 | 18 | import gi 19 | gi.require_version('Gtk', '3.0') 20 | gi.require_version('WebKit2', '4.0') 21 | from gi.repository import Gtk, WebKit2 22 | import multiprocessing 23 | import os.path 24 | import sys 25 | 26 | if sys.version_info.major >= 3: 27 | from socketserver import TCPServer 28 | from http.server import SimpleHTTPRequestHandler 29 | else: 30 | from SocketServer import TCPServer 31 | from SimpleHTTPServer import SimpleHTTPRequestHandler 32 | 33 | 34 | class Server(TCPServer): 35 | 36 | allow_reuse_address = True 37 | 38 | def __init__(self, port): 39 | TCPServer.__init__(self, ("", port), SimpleHTTPRequestHandler) 40 | self.process = multiprocessing.Process(target=self.serve_forever) 41 | 42 | def initiate(self): 43 | self.process.daemon = True 44 | self.process.start() 45 | 46 | def terminate(self): 47 | self.process.terminate() 48 | 49 | 50 | class QuietServer(Server): 51 | def __init__(self, port): 52 | TCPServer.__init__(self, ("", port), QuietHandler) 53 | self.process = multiprocessing.Process(target=self.serve_forever) 54 | 55 | 56 | class QuietHandler(SimpleHTTPRequestHandler): 57 | 58 | def log_message(self, format, *args): 59 | pass 60 | 61 | 62 | class App(object): 63 | 64 | def __init__(self, config): 65 | self.config = config 66 | self.window = Gtk.Window() 67 | self.window.resize(self.config.width+16,self.config.height+16) 68 | if self.config.app_name is not None: 69 | self.window.set_title(self.config.app_name) 70 | else: 71 | title = self.config.app_uri.split('/')[-1].split('.')[0] 72 | self.window.set_title(title.capitalize()) 73 | self.window.connect('destroy', Gtk.main_quit) 74 | self.web = None 75 | self.server = None 76 | 77 | def webview_setup(self): 78 | self.web = WebKit2.WebView() 79 | uri = 'http://%s:%d/%s' % (self.config.server_ip, 80 | self.config.server_port, 81 | self.config.app_uri) 82 | self.web.load_uri(uri) 83 | self.window.add(self.web) 84 | 85 | def webview(self): 86 | self.webview_setup() 87 | self.window.show_all() 88 | Gtk.main() 89 | 90 | def server_enable(self): 91 | if not self.server: 92 | if self.config.server_log: 93 | self.server = Server(self.config.server_port) 94 | else: 95 | self.server = QuietServer(self.config.server_port) 96 | self.server.initiate() 97 | 98 | def server_disable(self): 99 | if self.server: 100 | self.server.terminate() 101 | 102 | 103 | class Config(object): 104 | 105 | def __init__(self): 106 | self.server_ip = 'localhost' 107 | self.server_port = 8000 108 | self.server_log = False 109 | self.app_uri = None 110 | self.app_name = None 111 | self.width = 500 112 | self.height = 500 113 | self.config_name = sys.argv[0].split('.')[0]+'.ini' 114 | if os.path.exists(self.config_name): 115 | cfg_setting = self.read_ini() 116 | else: 117 | self.create_ini() 118 | print('Enter configuration info in %s.' % self.config_name) 119 | sys.exit() 120 | for setting in cfg_setting: 121 | if setting == 'app_uri': 122 | self.app_uri = cfg_setting['app_uri'].strip() 123 | if setting == 'app_name': 124 | self.app_name = cfg_setting['app_name'].strip() 125 | if setting == 'window_width': 126 | self.width = int(cfg_setting['window_width'].strip()) 127 | if setting == 'window_height': 128 | self.height = int(cfg_setting['window_height'].strip()) 129 | if setting == 'server_ip': 130 | self.server_ip = cfg_setting['server_ip'].strip() 131 | if setting == 'server_port': 132 | self.server_port = int(cfg_setting['server_port'].strip()) 133 | if setting == 'server_log': 134 | server_log = cfg_setting['server_log'].strip().lower() 135 | self.server_log = {'true':True, 'false':False}[server_log] 136 | 137 | def create_ini(self): 138 | f = open(self.config_name, 'w') 139 | f.write('#App Configuration\n\n') 140 | f.write('app_uri output/app.html\n\n') 141 | f.write('app_name App\n\n') 142 | f.write('window_width 500\n\n') 143 | f.write('window_height 500\n\n') 144 | f.write('server_ip localhost\n\n') 145 | f.write('server_port 8000\n\n') 146 | f.write('server_log false\n\n') 147 | f.close() 148 | 149 | def read_ini(self): 150 | cfg_file = open(self.config_name) 151 | cfg = [ln.strip().split(' ',1) for ln in cfg_file if ln[:1].isalpha()] 152 | cfg = dict(cfg) 153 | cfg_file.close() 154 | return cfg 155 | 156 | 157 | def main(): 158 | config = Config() 159 | app = App(config) 160 | app.server_enable() 161 | app.webview() 162 | app.server_disable() 163 | 164 | 165 | if __name__ == '__main__': 166 | main() 167 | 168 | -------------------------------------------------------------------------------- /pyjsdl/color.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Color module** 6 | 7 | The module provides a color object. 8 | """ 9 | 10 | from pyjsdl.pyjsobj import Color as _Color 11 | 12 | 13 | class Color(_Color): 14 | """ 15 | Color object. 16 | """ 17 | 18 | def __init__(self, *color): 19 | """ 20 | Initialize Color object. 21 | 22 | Alternative arguments: 23 | 24 | * r,g,b,a 25 | * r,g,b 26 | * (r,g,b,a) 27 | * (r,g,b) 28 | * integer argb 29 | * html string '#rrggbb[aa]' 30 | * hex string '0xrrggbb[aa]' 31 | * Color 32 | 33 | Color has the attributes:: 34 | 35 | r, g, b, a 36 | 37 | Operator functionality in --optimized mode (-O) requires --enable-operator-funcs. 38 | Module initialization places Color in module's namespace. 39 | """ 40 | ln = len(color) 41 | if ln == 1: 42 | _color = color[0] 43 | if hasattr(_color, '__len__'): 44 | ln = len(_color) 45 | else: 46 | _color = color 47 | if ln == 4: 48 | self.r = _color[0] 49 | self.g = _color[1] 50 | self.b = _color[2] 51 | self.a = _color[3] 52 | elif ln == 3: 53 | self.r = _color[0] 54 | self.g = _color[1] 55 | self.b = _color[2] 56 | self.a = 255 57 | else: 58 | if not hasattr(_color, 'startswith'): 59 | self.r = (_color>>16) & 0xff 60 | self.g = (_color>>8) & 0xff 61 | self.b = _color & 0xff 62 | self.a = (_color>>24) & 0xff 63 | else: 64 | _color = _color.lower() 65 | if _color.startswith('#'): 66 | _color = _color.lstrip('#') 67 | elif _color.startswith('0x'): 68 | _color = _color.lstrip('0x') 69 | if len(_color) == 6: 70 | _color += 'ff' 71 | self.r = int(_color[0:2], 16) 72 | self.g = int(_color[2:4], 16) 73 | self.b = int(_color[4:6], 16) 74 | self.a = int(_color[6:8], 16) 75 | 76 | def __str__(self): 77 | return "rgba(%d, %d, %d, %f)" % (self.r, self.g, self.b, self.a/255.0) 78 | 79 | def __repr__(self): 80 | return "(%d, %d, %d, %d)" % (self.r, self.g, self.b, self.a) 81 | 82 | def __getitem__(self, index): 83 | return {0:self.r, 1:self.g, 2:self.b, 3:self.a}[index] 84 | 85 | def __setitem__(self, index, val): 86 | self.__setattr__({0:'r', 1:'g', 2:'b', 3:'a'}[index], val) 87 | 88 | def __iter__(self): 89 | return iter([self.r, self.g, self.b, self.a]) 90 | 91 | def __len__(self): 92 | return 4 93 | 94 | def __eq__(self, other): 95 | if hasattr(other, 'a'): 96 | return ( self.r == other.r and 97 | self.g == other.g and 98 | self.b == other.b and 99 | self.a == other.a ) 100 | else: 101 | if len(other) == 4: 102 | return ( self.a == other[3] and 103 | self.r == other[0] and 104 | self.g == other[1] and 105 | self.b == other[2] ) 106 | else: 107 | return ( self.r == other[0] and 108 | self.g == other[1] and 109 | self.b == other[2] ) 110 | 111 | def __ne__(self, other): 112 | if hasattr(other, 'a'): 113 | return ( self.r != other.r or 114 | self.g != other.g or 115 | self.b != other.b or 116 | self.a != other.a ) 117 | else: 118 | if len(other) == 4: 119 | return ( self.a != other[3] or 120 | self.r != other[0] or 121 | self.g != other[1] or 122 | self.b != other[2] ) 123 | else: 124 | return ( self.r != other[0] or 125 | self.g != other[1] or 126 | self.b != other[2] ) 127 | 128 | def __add__(self, other): 129 | r = self.r + other.r 130 | if r > 255: r = 255 131 | g = self.g + other.g 132 | if g > 255: g = 255 133 | b = self.b + other.b 134 | if b > 255: b = 255 135 | a = self.a + other.a 136 | if a > 255: a = 255 137 | return self.__class__(r,g,b,a) 138 | 139 | def __sub__(self, other): 140 | r = self.r - other.r 141 | if r < 0: r = 0 142 | g = self.g - other.g 143 | if g < 0: g = 0 144 | b = self.b - other.b 145 | if b < 0: b = 0 146 | a = self.a - other.a 147 | if a < 0: a = 0 148 | return self.__class__(r,g,b,a) 149 | 150 | def __mul__(self, other): 151 | r = self.r * other.r 152 | if r > 255: r = 255 153 | g = self.g * other.g 154 | if g > 255: g = 255 155 | b = self.b * other.b 156 | if b > 255: b = 255 157 | a = self.a * other.a 158 | if a > 255: a = 255 159 | return self.__class__(r,g,b,a) 160 | 161 | def __floordiv__(self, other): 162 | if other.r != 0: r = int(self.r // other.r) 163 | else: r = 0 164 | if other.g != 0: g = int(self.g // other.g) 165 | else: g = 0 166 | if other.b != 0: b = int(self.b // other.b) 167 | else: b = 0 168 | if other.a != 0: a = int(self.a // other.a) 169 | else: a = 0 170 | return self.__class__(r,g,b,a) 171 | 172 | def __mod__(self, other): 173 | if other.r != 0: r = self.r % other.r 174 | else: r = 0 175 | if other.g != 0: g = self.g % other.g 176 | else: g = 0 177 | if other.b != 0: b = self.b % other.b 178 | else: b = 0 179 | if other.a != 0: a = self.a % other.a 180 | else: a = 0 181 | return self.__class__(r,g,b,a) 182 | 183 | def __iadd__(self, other): 184 | self.r += other.r 185 | if self.r > 255: self.r = 255 186 | self.g += other.g 187 | if self.g > 255: self.g = 255 188 | self.b += other.b 189 | if self.b > 255: self.b = 255 190 | self.a += other.a 191 | if self.a > 255: self.a = 255 192 | return self 193 | 194 | def __isub__(self, other): 195 | self.r -= other.r 196 | if self.r < 0: self.r = 0 197 | self.g -= other.g 198 | if self.g < 0: self.g = 0 199 | self.b -= other.b 200 | if self.b < 0: self.b = 0 201 | self.a -= other.a 202 | if self.a < 0: self.a = 0 203 | return self 204 | 205 | def __imul__(self, other): 206 | self.r *= other.r 207 | if self.r > 255: self.r = 255 208 | self.g *= other.g 209 | if self.g > 255: self.g = 255 210 | self.b *= other.b 211 | if self.b > 255: self.b = 255 212 | self.a *= other.a 213 | if self.a > 255: self.a = 255 214 | return self 215 | 216 | def __ifloordiv__(self, other): 217 | if other.r != 0: self.r //= other.r 218 | else: self.r = 0 219 | if other.g != 0: self.g //= other.g 220 | else: self.g = 0 221 | if other.b != 0: self.b //= other.b 222 | else: self.b = 0 223 | if other.a != 0: self.a //= other.a 224 | else: self.a = 0 225 | return self 226 | 227 | def __imod__(self, other): 228 | if other.r != 0: self.r %= other.r 229 | else: self.r = 0 230 | if other.g != 0: self.g %= other.g 231 | else: self.g = 0 232 | if other.b != 0: self.b %= other.b 233 | else: self.b = 0 234 | if other.a != 0: self.a %= other.a 235 | else: self.a = 0 236 | return self 237 | 238 | def __invert__(self): 239 | return self.__class__(~self.r + 256, 240 | ~self.g + 256, 241 | ~self.b + 256, 242 | ~self.a + 256) 243 | 244 | def normalize(self): 245 | """ 246 | Return normalized color values. 247 | """ 248 | return (self.r / 255.0, 249 | self.g / 255.0, 250 | self.b / 255.0, 251 | self.a / 255.0) 252 | 253 | def correct_gamma(self, gamma): 254 | """ 255 | Return gamma-corrected Color. 256 | """ 257 | return self.__class__(int(round((((self.r) / 255.0)**gamma) * 255.0)), 258 | int(round((((self.g) / 255.0)**gamma) * 255.0)), 259 | int(round((((self.b) / 255.0)**gamma) * 255.0)), 260 | int(round((((self.a) / 255.0)**gamma) * 255.0))) 261 | 262 | def premul_alpha(self): 263 | """ 264 | Return alpha-multipled Color. 265 | """ 266 | return self.__class__(int(round(self.r * (self.a/255.0))), 267 | int(round(self.g * (self.a/255.0))), 268 | int(round(self.b * (self.a/255.0))), 269 | self.a) 270 | 271 | def lerp(self, color, t): 272 | """ 273 | Return a Color linear interpolated by t to the given color. 274 | """ 275 | if t < 0.0 or t > 1.0: 276 | raise ValueError('Argument t must be in range 0 to 1') 277 | if hasattr(color, 'a'): 278 | return self.__class__(int(round(self.r * (1-t) + color.r * t)), 279 | int(round(self.g * (1-t) + color.g * t)), 280 | int(round(self.b * (1-t) + color.b * t)), 281 | int(round(self.a * (1-t) + color.a * t))) 282 | else: 283 | if len(color) == 3: 284 | return self.__class__(int(round(self.r * (1-t) + color[0] * t)), 285 | int(round(self.g * (1-t) + color[1] * t)), 286 | int(round(self.b * (1-t) + color[2] * t)), 287 | int(round(self.a * (1-t) + 255 * t))) 288 | elif len(color) == 4: 289 | return self.__class__(int(round(self.r * (1-t) + color[0] * t)), 290 | int(round(self.g * (1-t) + color[1] * t)), 291 | int(round(self.b * (1-t) + color[2] * t)), 292 | int(round(self.a * (1-t) + color[3] * t))) 293 | else: 294 | raise ValueError('invalid color argument') 295 | 296 | def update(self, *color): 297 | """ 298 | Update color values. 299 | """ 300 | ln = len(color) 301 | if ln == 1: 302 | _color = color[0] 303 | if hasattr(_color, '__len__'): 304 | ln = len(_color) 305 | else: 306 | _color = color 307 | if ln == 4: 308 | self.r = _color[0] 309 | self.g = _color[1] 310 | self.b = _color[2] 311 | self.a = _color[3] 312 | elif ln == 3: 313 | self.r = _color[0] 314 | self.g = _color[1] 315 | self.b = _color[2] 316 | self.a = 255 317 | else: 318 | if not hasattr(_color, 'startswith'): 319 | self.r = (_color>>16) & 0xff 320 | self.g = (_color>>8) & 0xff 321 | self.b = _color & 0xff 322 | self.a = (_color>>24) & 0xff 323 | else: 324 | _color = _color.lower() 325 | if _color.startswith('#'): 326 | _color = _color.lstrip('#') 327 | elif _color.startswith('0x'): 328 | _color = _color.lstrip('0x') 329 | if len(_color) == 6: 330 | _color += 'ff' 331 | self.r = int(_color[0:2], 16) 332 | self.g = int(_color[2:4], 16) 333 | self.b = int(_color[4:6], 16) 334 | self.a = int(_color[6:8], 16) 335 | 336 | def _get_cmy(self): 337 | return _rgb_to_cmy(self.r, self.g, self.b) 338 | 339 | def _set_cmy(self, val): 340 | self.r, self.g, self.b = _cmy_to_rgb(*val) 341 | 342 | def _get_hsva(self): 343 | return _rgba_to_hsva(self.r, self.g, self.b, self.a) 344 | 345 | def _set_hsva(self, val): 346 | self.r, self.g, self.b, self.a = _hsva_to_rgba(*val) 347 | 348 | def _get_hsla(self): 349 | return _rgba_to_hsla(self.r, self.g, self.b, self.a) 350 | 351 | def _set_hsla(self, val): 352 | self.r, self.g, self.b, self.a = _hsla_to_rgba(*val) 353 | 354 | cmy = property(_get_cmy, _set_cmy) 355 | "CMY color." 356 | 357 | hsva = property(_get_hsva, _set_hsva) 358 | "HSVA color." 359 | 360 | hsla = property(_get_hsla, _set_hsla) 361 | "HSLA color." 362 | 363 | 364 | def _rgb_to_cmy(r, g, b): 365 | r, g, b = r/255.0, g/255.0, b/255.0 366 | c, m, y = 1-r, 1-g, 1-b 367 | return (c, m, y) 368 | 369 | 370 | def _cmy_to_rgb(c, m, y): 371 | r, g, b = 1-c, 1-m, 1-y 372 | return (int(r*255), int(g*255), int(b*255)) 373 | 374 | 375 | def _rgba_to_hsva(r, g, b, a): 376 | r, g, b, a = r/255.0, g/255.0, b/255.0, a/255.0 377 | c_max = max(r, g, b) 378 | c_min = min(r, g, b) 379 | delta = c_max - c_min 380 | if delta == 0: 381 | h = 0.0 382 | elif c_max == r: 383 | h = (60 * ((g - b) / delta) + 360) % 360.0 384 | elif c_max == g: 385 | h = (60 * ((b - r) / delta) + 120) % 360.0 386 | elif c_max == b: 387 | h = (60 * ((r - g) / delta) + 240) % 360.0 388 | if c_max == 0: 389 | s = 0.0 390 | else: 391 | s = (delta / c_max) 392 | v = c_max 393 | return h, s*100.0, v*100.0, a*100.0 394 | 395 | 396 | def _hsva_to_rgba(h, s, v, a): 397 | h, s, v, a = h/360.0, s/100.0, v/100.0, a/100.0 398 | i = int(h*6.0) 399 | f = (h*6.0) - i 400 | p = (v * (1.0 - s)) 401 | q = (v * (1.0 - s * f)) 402 | t = (v * (1.0 - (s * (1.0-f)))) 403 | i %= 6 404 | if i == 0: 405 | r, g, b = v, t, p 406 | elif i == 1: 407 | r, g, b = q, v, p 408 | elif i == 2: 409 | r, g, b = p, v, t 410 | elif i == 3: 411 | r, g, b = p, q, v 412 | elif i == 4: 413 | r, g, b = t, p, v 414 | elif i == 5: 415 | r, g, b = v, p, q 416 | return int(r*255), int(g*255), int(b*255), int(a*255) 417 | 418 | 419 | def _rgba_to_hsla(r, g, b, a): 420 | r, g, b, a = r/255.0, g/255.0, b/255.0, a/255.0 421 | cmax = max(r, g, b) 422 | cmin = min(r, g, b) 423 | delta = cmax - cmin 424 | l = (cmax + cmin) / 2.0 425 | if delta == 0: 426 | s = 0.0 427 | else: 428 | s = delta / (1-abs(2*l-1)) 429 | if delta == 0: 430 | h = 0.0 431 | elif cmax == r: 432 | h = (60 * ((g - b) / delta) + 360) % 360.0 433 | elif cmax == g: 434 | h = (60 * ((b - r) / delta) + 120) % 360.0 435 | elif cmax == b: 436 | h = (60 * ((r - g) / delta) + 240) % 360.0 437 | return h, s*100.0, l*100.0, a*100.0 438 | 439 | 440 | def _hsla_to_rgba(h, s, l, a): 441 | h, s, l, a = h/360.0, s/100.0, l/100.0, a/100.0 442 | if l < 0.5: 443 | q = l * (1+s) 444 | else: 445 | q = l + s - l * s 446 | p = 2 * l - q 447 | t = h + 1.0/3.0 448 | if t < 0.0: t += 1.0 449 | elif t > 1.0: t -= 1.0 450 | if t < 1.0/6.0: 451 | r = p + (q - p) * 6.0 * t 452 | elif t < 1.0/2.0: 453 | r = q 454 | elif t < 2.0/3.0: 455 | r = p + (q - p) * (2.0/3.0-t) * 6.0 456 | else: 457 | r = p 458 | t = h 459 | if t < 0.0: t += 1.0 460 | elif t > 1.0: t -= 1.0 461 | if t < 1.0/6.0: 462 | g = p + (q - p) * 6.0 * t 463 | elif t < 1.0/2.0: 464 | g = q 465 | elif t < 2.0/3.0: 466 | g = p + (q - p) * (2.0/3.0-t) * 6.0 467 | else: 468 | g = p 469 | t = h - 1.0/3.0 470 | if t < 0.0: t += 1.0 471 | elif t > 1.0: t -= 1.0 472 | if t < 1.0/6.0: 473 | b = p + (q - p) * 6.0 * t 474 | elif t < 1.0/2.0: 475 | b = q 476 | elif t < 2.0/3.0: 477 | b = p + (q - p) * (2.0/3.0-t) * 6.0 478 | else: 479 | b = p 480 | return int(r*255), int(g*255), int(b*255), int(a*255) 481 | 482 | -------------------------------------------------------------------------------- /pyjsdl/constants.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Constants** 6 | 7 | Constants are defined as in Pygame/SDL and JavaScript events. Examine constants.py for information on the constants. The constant variables are imported into the Pyjsdl module namespace. 8 | """ 9 | 10 | 11 | KMOD_ALT = 4 12 | KMOD_CAPS = 0 13 | KMOD_CTRL = 2 14 | KMOD_LALT = 4 15 | KMOD_LCTRL = 2 16 | KMOD_LMETA = 0 17 | KMOD_LSHIFT = 1 18 | KMOD_META = 0 19 | KMOD_MODE = 0 20 | KMOD_NONE = 0 21 | KMOD_NUM = 0 22 | KMOD_RALT = 4 23 | KMOD_RCTRL = 2 24 | KMOD_RMETA = 0 25 | KMOD_RSHIFT = 1 26 | KMOD_SHIFT = 1 27 | K_0 = 48 28 | K_1 = 49 29 | K_2 = 50 30 | K_3 = 51 31 | K_4 = 52 32 | K_5 = 53 33 | K_6 = 54 34 | K_7 = 55 35 | K_8 = 56 36 | K_9 = 57 37 | K_ALT = 18 38 | K_AMPERSAND = 38 39 | K_ASTERISK = 42 40 | K_AT = 64 41 | K_BACKQUOTE = 96 42 | K_BACKSLASH = 92 43 | K_BACKSPACE = 8 44 | K_BREAK = 0x40000048 45 | K_CAPSLOCK = 0x40000039 46 | K_CARET = 94 47 | K_CLEAR = 0x4000009c 48 | K_COLON = 58 49 | K_COMMA = 44 50 | K_CTRL = 17 51 | K_DELETE = 127 52 | K_DOLLAR = 36 53 | K_DOWN = 0x40000051 54 | K_END = 0x4000004d 55 | K_EQUALS = 61 56 | K_ESCAPE = 27 57 | K_EURO = 0x400000b4 58 | K_EXCLAIM = 33 59 | K_F1 = 0x4000003a 60 | K_F2 = 0x4000003b 61 | K_F3 = 0x4000003c 62 | K_F4 = 0x4000003d 63 | K_F5 = 0x4000003e 64 | K_F6 = 0x4000003f 65 | K_F7 = 0x40000040 66 | K_F8 = 0x40000041 67 | K_F9 = 0x40000042 68 | K_F10 = 0x40000043 69 | K_F11 = 0x40000044 70 | K_F12 = 0x40000045 71 | K_F13 = 0x40000068 72 | K_F14 = 0x40000069 73 | K_F15 = 0x4000006a 74 | K_GREATER = 62 75 | K_HASH = 35 76 | K_HELP = 0x40000075 77 | K_HOME = 0x4000004a 78 | K_INSERT = 0x40000049 79 | K_KP0 = 0x40000062 80 | K_KP1 = 0x40000059 81 | K_KP2 = 0x4000005a 82 | K_KP3 = 0x4000005b 83 | K_KP4 = 0x4000005c 84 | K_KP5 = 0x4000005d 85 | K_KP6 = 0x4000005e 86 | K_KP7 = 0x4000005f 87 | K_KP8 = 0x40000060 88 | K_KP9 = 0x40000061 89 | K_KP_DIVIDE = 0x40000054 90 | K_KP_ENTER = 0x40000058 91 | K_KP_EQUALS = 0x40000067 92 | K_KP_MINUS = 0x40000056 93 | K_KP_MULTIPLY = 0x40000055 94 | K_KP_PERIOD = 0x40000063 95 | K_KP_PLUS = 0x40000057 96 | K_LALT = 18 97 | K_LCTRL = 17 98 | K_LEFT = 0x40000050 99 | K_LEFTBRACKET = 91 100 | K_LEFTPAREN = 40 101 | K_LESS = 60 102 | K_LMETA = 0x400000e3 103 | K_LSHIFT = 16 104 | K_LSUPER = 0x400000e3 105 | K_MENU = 0x40000076 106 | K_MINUS = 45 107 | K_MODE = 0x40000101 108 | K_NUMLOCK = 0x40000053 109 | K_PAGEDOWN = 0x4000004e 110 | K_PAGEUP = 0x4000004b 111 | K_PAUSE = 0x40000048 112 | K_PERIOD = 46 113 | K_PLUS = 43 114 | K_POWER = 0x40000066 115 | K_PRINT = 0x40000046 116 | K_QUESTION = 63 117 | K_QUOTE = 39 118 | K_QUOTEDBL = 34 119 | K_RALT = 18 120 | K_RCTRL = 17 121 | K_RETURN = 13 122 | K_RIGHT = 0x4000004F 123 | K_RIGHTBRACKET = 93 124 | K_RIGHTPAREN = 41 125 | K_RMETA = 0x400000e3 126 | K_RSHIFT = 16 127 | K_RSUPER = 0x400000e3 128 | K_SCROLLOCK = 0x40000047 129 | K_SCROLLLOCK = 0x40000047 130 | K_SEMICOLON = 59 131 | K_SHIFT = 16 132 | K_SLASH = 92 133 | K_SPACE = 32 134 | K_SYSREQ = 0x4000009a 135 | K_TAB = 9 136 | K_UNDERSCORE = 95 137 | K_UNKNOWN = 0 138 | K_UP = 0x40000052 139 | K_a = 97 140 | K_b = 98 141 | K_c = 99 142 | K_d = 100 143 | K_e = 101 144 | K_f = 102 145 | K_g = 103 146 | K_h = 104 147 | K_i = 105 148 | K_j = 106 149 | K_k = 107 150 | K_l = 108 151 | K_m = 109 152 | K_n = 110 153 | K_o = 111 154 | K_p = 112 155 | K_q = 113 156 | K_r = 114 157 | K_s = 115 158 | K_t = 116 159 | K_u = 117 160 | K_v = 118 161 | K_w = 119 162 | K_x = 120 163 | K_y = 121 164 | K_z = 122 165 | MOUSEBUTTONDOWN = 5 166 | MOUSEBUTTONUP = 6 167 | MOUSEMOTION = 4 168 | KEYDOWN = 2 169 | KEYUP = 3 170 | ACTIVEEVENT = 32768 171 | APPFOCUSMOUSE = 0 172 | APPMOUSEFOCUS = 0 173 | APPINPUTFOCUS = 1 174 | APPACTIVE = 2 175 | DOUBLEBUF = 0x40000000 176 | FULLSCREEN = -0x80000000 177 | HWACCEL = 256 178 | HWPALETTE = 0x20000000 179 | HWSURFACE = 1 180 | NOEVENT = 0 181 | NOFRAME = 32 182 | NUMEVENTS = 32 183 | OPENGL = 2 184 | QUIT = 12 185 | RESIZABLE = 16 186 | RLEACCEL = 16384 187 | RLEACCELOK = 8192 188 | SRCALPHA = 65536 189 | SRCCOLORKEY = 4096 190 | SWSURFACE = 0 191 | SYSWMEVENT = 13 192 | USEREVENT = 24 193 | 194 | -------------------------------------------------------------------------------- /pyjsdl/cursors.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Cursors module** 6 | 7 | The module provides access to mouse cursors. 8 | """ 9 | 10 | from pyjsdl.surface import Surface 11 | from pyjsdl.color import Color 12 | from pyjsdl import constants as Const 13 | 14 | 15 | arrow = diamond = broken_x = tri_left = tri_right = () 16 | "Unimplemented variables." 17 | 18 | 19 | def compile(strings, black='X', white='.', xor='o'): 20 | """ 21 | Compile binary data from cursor string. 22 | 23 | Arguments cursor string, and optional symbols representing colors. 24 | Data represents black and white pixels, xor color defaulting to black. 25 | Data should be a string list of width divisible by 8. 26 | Return cursor data and mask, can be used with mouse.set_cursor. 27 | """ 28 | data = [] 29 | mask = [] 30 | dbit = {black:1, white:0, xor:1} 31 | mbit = {black:1, white:1, xor:0} 32 | string = ''.join(strings) 33 | for i in range(0, len(string), 8): 34 | s = string[i:i+8] 35 | db = mb = 0 36 | if s != ' ': 37 | for j in range(8): 38 | c = s[j] 39 | if c == ' ': 40 | continue 41 | if dbit[c]: 42 | db |= 0x01<<7-j 43 | if mbit[c]: 44 | mb |= 0x01<<7-j 45 | data.append(int(db)) 46 | mask.append(int(mb)) 47 | return tuple(data), tuple(mask) 48 | 49 | 50 | def create_cursor(size, data, mask): 51 | """ 52 | Create cursor image from binary data. 53 | 54 | Arguments cursor size and its binary data and mask. 55 | Return surface, can be used with mouse.set_cursor. 56 | """ 57 | surface = Surface(size, Const.SRCALPHA) 58 | black = Color(0,0,0,255) 59 | white = Color(255,255,255,255) 60 | x = y = 0 61 | for i in range(len(data)): 62 | if data[i] or mask[i]: 63 | for j in range(8): 64 | if data[i] & 0x01<<7-j: 65 | surface.setFillStyle(black) 66 | surface.fillRect(x+j, y, 1, 1) 67 | elif mask[i] & 0x01<<7-j: 68 | surface.setFillStyle(white) 69 | surface.fillRect(x+j, y, 1, 1) 70 | x += 8 71 | if x >= size[0]: 72 | x = 0 73 | y += 1 74 | return surface 75 | 76 | 77 | def get_cursor_types(): 78 | #https://developer.mozilla.org/en-US/docs/Web/CSS/cursor 79 | """ 80 | Return list of cursor types from CSS Cursor API. 81 | """ 82 | types = ['default', 'auto', 'none', 'context-menu', 'help', 83 | 'pointer', 'progress', 'wait', 'cell', 'crosshair', 84 | 'text', 'vertical-text', 'alias', 'copy', 'move', 85 | 'no-drop', 'not-allowed', 'e-resize', 'n-resize', 86 | 'ne-resize', 'nw-resize', 's-resize', 'se-resize', 87 | 'sw-resize', 'w-resize', 'ew-resize', 'ns-resize', 88 | 'nesw-resize', 'nwse-resize', 'col-resize', 89 | 'row-resize', 'all-scroll', 'zoom-in', 'zoom-out', 90 | 'grab', 'grabbing'] 91 | return types 92 | 93 | -------------------------------------------------------------------------------- /pyjsdl/draw.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Draw module** 6 | 7 | The module provides functions to draw shapes on a surface. 8 | """ 9 | 10 | from math import pi as _pi 11 | from pyjsdl.rect import Rect 12 | from pyjsdl.color import Color 13 | 14 | 15 | _return_rect = True 16 | 17 | 18 | def rect(surface, color, rect, width=0): 19 | """ 20 | Draw rectangle shape. 21 | 22 | Arguments include surface to draw, color, Rect. 23 | Optional width argument of outline, which defaults to 0 for filled shape. 24 | Return bounding Rect. 25 | """ 26 | if hasattr(rect, 'width'): 27 | _rect = rect 28 | else: 29 | _rect = Rect(rect) 30 | if width: 31 | surface.setLineWidth(width) 32 | if surface._stroke_style != color: 33 | surface._stroke_style = color 34 | if hasattr(color, 'a'): 35 | surface.setStrokeStyle(color) 36 | else: 37 | surface.setStrokeStyle(Color(color)) 38 | surface.strokeRect(_rect.x, _rect.y, _rect.width, _rect.height) 39 | else: 40 | if surface._fill_style != color: 41 | surface._fill_style = color 42 | if hasattr(color, 'a'): 43 | surface.setFillStyle(color) 44 | else: 45 | surface.setFillStyle(Color(color)) 46 | surface.fillRect(_rect.x, _rect.y, _rect.width, _rect.height) 47 | if not _return_rect: 48 | return None 49 | if surface._display: 50 | return surface._display._surface_rect.clip(_rect) 51 | else: 52 | return surface.get_rect().clip(_rect) 53 | 54 | 55 | def circle(surface, color, position, radius, width=0): 56 | """ 57 | Draw circular shape. 58 | 59 | Arguments include surface to draw, color, position and radius. 60 | Optional width argument of outline, which defaults to 0 for filled shape. 61 | Return bounding Rect. 62 | """ 63 | surface.beginPath() 64 | surface.arc(position[0], position[1], radius, 0, 2*_pi, False) 65 | if width: 66 | surface.setLineWidth(width) 67 | if surface._stroke_style != color: 68 | surface._stroke_style = color 69 | if hasattr(color, 'a'): 70 | surface.setStrokeStyle(color) 71 | else: 72 | surface.setStrokeStyle(Color(color)) 73 | surface.stroke() 74 | else: 75 | if surface._fill_style != color: 76 | surface._fill_style = color 77 | if hasattr(color, 'a'): 78 | surface.setFillStyle(color) 79 | else: 80 | surface.setFillStyle(Color(color)) 81 | surface.fill() 82 | if not _return_rect: 83 | return None 84 | if surface._display: 85 | return surface._display._surface_rect.clip( 86 | Rect(position[0]-radius, position[1]-radius, 2*radius, 2*radius)) 87 | else: 88 | return surface.get_rect().clip( 89 | Rect(position[0]-radius, position[1]-radius, 2*radius, 2*radius)) 90 | 91 | 92 | def ellipse(surface, color, rect, width=0): 93 | """ 94 | Draw ellipse shape. 95 | 96 | Arguments include surface to draw, color, and rect. 97 | Optional width argument of outline, which defaults to 0 for filled shape. 98 | Return bounding Rect. 99 | """ 100 | if hasattr(rect, 'width'): 101 | _rect = rect 102 | else: 103 | _rect = Rect(rect) 104 | surface.saveContext() 105 | surface.translate(_rect.x + int(_rect.width/2), 106 | _rect.y + int(_rect.height/2)) 107 | if _rect.width >= _rect.height: 108 | surface.scale(_rect.width / (_rect.height*1.0), 1) 109 | radius = int(_rect.height/2) 110 | else: 111 | surface.scale(1, _rect.height / (_rect.width*1.0)) 112 | radius = int(_rect.width/2) 113 | surface.beginPath() 114 | surface.arc(0, 0, radius, 0, 2*_pi, False) 115 | if width: 116 | surface.setLineWidth(width) 117 | if surface._stroke_style != color: 118 | surface._stroke_style = color 119 | if hasattr(color, 'a'): 120 | surface.setStrokeStyle(color) 121 | else: 122 | surface.setStrokeStyle(Color(color)) 123 | surface.stroke() 124 | else: 125 | if surface._fill_style != color: 126 | surface._fill_style = color 127 | if hasattr(color, 'a'): 128 | surface.setFillStyle(color) 129 | else: 130 | surface.setFillStyle(Color(color)) 131 | surface.fill() 132 | surface.restoreContext() 133 | if not _return_rect: 134 | return None 135 | if surface._display: 136 | return surface._display._surface_rect.clip(_rect) 137 | else: 138 | return surface.get_rect().clip(_rect) 139 | 140 | 141 | def arc(surface, color, rect, start_angle, stop_angle, width=1): 142 | """ 143 | Draw arc shape. 144 | 145 | Arguments include surface to draw, color, rect, start_angle, stop_angle. 146 | Optional width argument of outline. 147 | Return bounding Rect. 148 | """ 149 | if hasattr(rect, 'width'): 150 | _rect = rect 151 | else: 152 | _rect = Rect(rect) 153 | if _rect.width == _rect.height: 154 | surface.beginPath() 155 | surface.arc(_rect.x + int(_rect.width/2), _rect.y + int(_rect.height/2), 156 | int(_rect.width/2), -start_angle, -stop_angle, True) 157 | if width: 158 | surface.setLineWidth(width) 159 | if surface._stroke_style != color: 160 | surface._stroke_style = color 161 | if hasattr(color, 'a'): 162 | surface.setStrokeStyle(color) 163 | else: 164 | surface.setStrokeStyle(Color(color)) 165 | surface.stroke() 166 | else: 167 | surface.closePath() 168 | if surface._fill_style != color: 169 | surface._fill_style = color 170 | if hasattr(color, 'a'): 171 | surface.setFillStyle(color) 172 | else: 173 | surface.setFillStyle(Color(color)) 174 | surface.fill() 175 | else: 176 | surface.saveContext() 177 | surface.translate(_rect.x + int(_rect.width/2), 178 | _rect.y + int(_rect.height/2)) 179 | if _rect.width >= _rect.height: 180 | surface.scale(_rect.width / (_rect.height*1.0), 1) 181 | radius = int(_rect.height/2) 182 | else: 183 | surface.scale(1, _rect.height / (_rect.width*1.0)) 184 | radius = int(_rect.width/2) 185 | surface.beginPath() 186 | surface.arc(0, 0, radius, -start_angle, -stop_angle, True) 187 | if width: 188 | surface.setLineWidth(width) 189 | if surface._stroke_style != color: 190 | surface._stroke_style = color 191 | if hasattr(color, 'a'): 192 | surface.setStrokeStyle(color) 193 | else: 194 | surface.setStrokeStyle(Color(color)) 195 | surface.stroke() 196 | else: 197 | surface.closePath() 198 | if surface._fill_style != color: 199 | surface._fill_style = color 200 | if hasattr(color, 'a'): 201 | surface.setFillStyle(color) 202 | else: 203 | surface.setFillStyle(Color(color)) 204 | surface.fill() 205 | surface.restoreContext() 206 | if not _return_rect: 207 | return None 208 | if surface._display: 209 | return surface._display._surface_rect.clip(_rect) 210 | else: 211 | return surface.get_rect().clip(_rect) 212 | 213 | 214 | def polygon(surface, color, pointlist, width=0): 215 | """ 216 | Draw polygon shape. 217 | 218 | Arguments include surface to draw, color, and pointlist. 219 | Optional width argument of outline, which defaults to 0 for filled shape. 220 | Return bounding Rect. 221 | """ 222 | surface.beginPath() 223 | surface.moveTo(*pointlist[0]) 224 | for point in pointlist[1:]: 225 | surface.lineTo(*point) 226 | surface.closePath() 227 | if width: 228 | surface.setLineWidth(width) 229 | if surface._stroke_style != color: 230 | surface._stroke_style = color 231 | if hasattr(color, 'a'): 232 | surface.setStrokeStyle(color) 233 | else: 234 | surface.setStrokeStyle(Color(color)) 235 | surface.stroke() 236 | else: 237 | if surface._fill_style != color: 238 | surface._fill_style = color 239 | if hasattr(color, 'a'): 240 | surface.setFillStyle(color) 241 | else: 242 | surface.setFillStyle(Color(color)) 243 | surface.fill() 244 | if not _return_rect: 245 | return None 246 | xpts = [pt[0] for pt in pointlist] 247 | ypts = [pt[1] for pt in pointlist] 248 | xmin, xmax = min(xpts), max(xpts) 249 | ymin, ymax = min(ypts), max(ypts) 250 | if surface._display: 251 | return surface._display._surface_rect.clip( 252 | Rect(xmin, ymin, xmax-xmin+1, ymax-ymin+1)) 253 | else: 254 | return surface.get_rect().clip( 255 | Rect(xmin, ymin, xmax-xmin+1, ymax-ymin+1)) 256 | 257 | 258 | def line(surface, color, point1, point2, width=1): 259 | """ 260 | Draw line. 261 | 262 | Arguments include surface to draw, color, point1, point2. 263 | Optional width argument of line. 264 | Return bounding Rect. 265 | """ 266 | surface.beginPath() 267 | surface.moveTo(*point1) 268 | surface.lineTo(*point2) 269 | surface.setLineWidth(width) 270 | if surface._stroke_style != color: 271 | surface._stroke_style = color 272 | if hasattr(color, 'a'): 273 | surface.setStrokeStyle(color) 274 | else: 275 | surface.setStrokeStyle(Color(color)) 276 | surface.stroke() 277 | if not _return_rect: 278 | return None 279 | xpts = [pt[0] for pt in (point1,point2)] 280 | ypts = [pt[1] for pt in (point1,point2)] 281 | xmin, xmax = min(xpts), max(xpts) 282 | ymin, ymax = min(ypts), max(ypts) 283 | if surface._display: 284 | return surface._display._surface_rect.clip( 285 | Rect(xmin, ymin, xmax-xmin+1, ymax-ymin+1)) 286 | else: 287 | return surface.get_rect().clip( 288 | Rect(xmin, ymin, xmax-xmin+1, ymax-ymin+1)) 289 | 290 | 291 | def lines(surface, color, closed, pointlist, width=1): 292 | """ 293 | Draw interconnected lines. 294 | 295 | Arguments include surface to draw, color, closed, and pointlist. 296 | Optional width argument of line. 297 | Return bounding Rect. 298 | """ 299 | surface.beginPath() 300 | surface.moveTo(*pointlist[0]) 301 | for point in pointlist[1:]: 302 | surface.lineTo(*point) 303 | if closed: 304 | surface.closePath() 305 | surface.setLineWidth(width) 306 | if surface._stroke_style != color: 307 | surface._stroke_style = color 308 | if hasattr(color, 'a'): 309 | surface.setStrokeStyle(color) 310 | else: 311 | surface.setStrokeStyle(Color(color)) 312 | surface.stroke() 313 | if not _return_rect: 314 | return None 315 | xpts = [pt[0] for pt in pointlist] 316 | ypts = [pt[1] for pt in pointlist] 317 | xmin, xmax = min(xpts), max(xpts) 318 | ymin, ymax = min(ypts), max(ypts) 319 | if surface._display: 320 | return surface._display._surface_rect.clip( 321 | Rect(xmin, ymin, xmax-xmin+1, ymax-ymin+1)) 322 | else: 323 | return surface.get_rect().clip( 324 | Rect(xmin, ymin, xmax-xmin+1, ymax-ymin+1)) 325 | 326 | 327 | def aaline(surface, color, point1, point2, blend=1): 328 | """ 329 | Draw line. 330 | 331 | Arguments include surface to draw, color, point1, point2. 332 | Return bounding Rect. 333 | """ 334 | rect = line(surface, color, point1, point2) 335 | return rect 336 | 337 | 338 | def aalines(surface, color, closed, pointlist, blend=1): 339 | """ 340 | Draw interconnected lines. 341 | 342 | Arguments include surface to draw, color, closed, and pointlist. 343 | Return bounding Rect. 344 | """ 345 | rect = lines(surface, color, closed, pointlist) 346 | return rect 347 | 348 | 349 | def bounding_rect_return(setting): 350 | """ 351 | Bounding rect return. 352 | 353 | Set whether draw functions return bounding Rect. 354 | Setting (bool) defaults to True on module initialization. 355 | """ 356 | global _return_rect 357 | _return_rect = setting 358 | 359 | 360 | #depreciated 361 | set_return = bounding_rect_return 362 | 363 | -------------------------------------------------------------------------------- /pyjsdl/env.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | import os, sys 5 | 6 | 7 | if os.name != 'pyjs': 8 | print('Use Pyjs to compile script to a JS app') 9 | sys.exit() 10 | 11 | 12 | canvas = None 13 | 14 | frame = None 15 | 16 | pyjs_mode = None 17 | 18 | event = None 19 | 20 | 21 | def get_canvas(): 22 | """ 23 | Return Canvas object. 24 | """ 25 | return canvas 26 | 27 | 28 | def get_frame(): 29 | """ 30 | Return Webpage frame. 31 | """ 32 | return frame 33 | 34 | 35 | def get_pyjsmode(): 36 | """ 37 | Return Pyjs mode object. 38 | """ 39 | return pyjs_mode 40 | 41 | 42 | def set_env(key, val): 43 | setattr(sys.modules[__name__], key, val) 44 | 45 | -------------------------------------------------------------------------------- /pyjsdl/font.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Font module** 6 | 7 | The module provides font access and rendering on a surface. 8 | """ 9 | 10 | from math import ceil as _ceil 11 | from pyjsdl.surface import Surface 12 | from pyjsdl.color import Color 13 | from pyjsdl.pyjsobj import HTML5Canvas 14 | 15 | 16 | _initialized = False 17 | _surf = None 18 | 19 | 20 | def init(): 21 | """ 22 | Initialize font module. 23 | """ 24 | global _surf, _initialized, match_font 25 | if _initialized: 26 | return 27 | try: 28 | _surf = HTML5Canvas(1,1) 29 | _surf.measureText('x') 30 | except: 31 | _surf = None 32 | _initialized = True 33 | init() 34 | 35 | 36 | def quit(): 37 | """ 38 | Unintialize font module. 39 | """ 40 | global _surf, _initialized 41 | _surf = None 42 | _initialized = False 43 | 44 | 45 | def get_init(): 46 | """ 47 | Check if font module is initialized. 48 | """ 49 | return _initialized 50 | 51 | 52 | def get_default_font(): 53 | """ 54 | Return default font. 55 | """ 56 | return Font._font[0] 57 | 58 | 59 | def get_fonts(): 60 | """ 61 | Return font names, which have fallback fonts if unavailable. 62 | """ 63 | return Font._font 64 | 65 | 66 | def match_font(name): 67 | """ 68 | Find system font. 69 | 70 | Argument name is a font name, or comma-delimited string of font names. 71 | Return font string in compliant format. 72 | """ 73 | fallback = False 74 | font = [fn.strip().lower() for fn in name.split(',')] 75 | for i, fn in enumerate(font): 76 | if fn in Font._font: 77 | fallback = True 78 | continue 79 | else: 80 | f = ''.join(c for c in fn if c.isalnum()) 81 | if f in Font._font_alt: 82 | font[i] = Font._font[Font._font_alt[f]] 83 | fallback = True 84 | if not fallback: 85 | font.append(Font._font[0]) 86 | font = ','.join(font) 87 | return font 88 | 89 | 90 | class Font(object): 91 | """ 92 | Font object. 93 | """ 94 | 95 | _font = [ 96 | 'arial', 'bitstream vera sans', 'bitstream vera serif', 97 | 'book antiqua', 'comic sans ms', 'courier new', 'courier', 98 | 'dejavu sans', 'dejavu sans mono', 'dejavu serif', 99 | 'freesans', 'garamond', 'georgia', 'helvetica', 100 | 'impact', 'liberation sans', 'liberation serif', 101 | 'lucida console', 'lucida serif', 'nimbus mono l', 102 | 'nimbus roman no9 l', 'nimbus sans l', 'palatino', 103 | 'times new roman', 'times', 'tahoma', 'verdana', 104 | 'cursive', 'monospace', 'sans-serif', 'serif'] 105 | 106 | _font_alt = { 107 | 'arial': 0, 'bitstreamverasans': 1, 'bitstreamveraserif': 2, 108 | 'bookantiqua': 3, 'comicsansms': 4, 'couriernew': 5, 'courier': 6, 109 | 'dejavusans': 7, 'dejavusansmono': 8, 'dejavuserif': 9, 110 | 'freesans': 10, 'garamond': 11, 'georgia': 12, 'helvetica': 13, 111 | 'impact': 14, 'liberationsans': 15, 'liberationserif': 16, 112 | 'lucidaconsole': 17, 'lucidaserif': 18, 'nimbusmonol': 19, 113 | 'nimbusromanno9l': 20, 'nimbussansl': 21, 'palatino': 22, 114 | 'timesnewroman': 23, 'times': 24, 'tahoma': 25, 'verdana': 26, 115 | 'cursive': 27, 'monospace': 28, 'sansserif': 29, 'serif': 30} 116 | 117 | _font_family = [ 118 | ['arial', 'helvetica', 'liberation sans', 'nimbus sans l', 119 | 'freesans', 'tahoma', 'sans-serif'], 120 | ['verdana', 'bitstream vera sans', 'dejavu sans', 'sans-serif'], 121 | ['impact', 'sans-serif'], 122 | ['comic sans ms', 'cursive', 'sans-serif'], 123 | ['courier new', 'courier', 'lucida console', 124 | 'dejavu sans mono', 'monospace'], 125 | ['times new roman', 'times', 'liberation serif', 126 | 'nimbus roman no9 l', 'serif'], 127 | ['garamond', 'book antiqua', 'palatino', 128 | 'liberation serif', 'nimbus roman no9 l', 'serif'], 129 | ['georgia', 'bitstream vera serif', 'lucida serif', 130 | 'liberation serif', 'dejavu serif', 'serif']] 131 | 132 | def __init__(self, name, size): 133 | """ 134 | Return Font object. 135 | 136 | Arguments include name of a font and size of font. The name argument can be a string of comma-delimited names to specify fallbacks and use a default font if none found. A font can be loaded from a filename 'fontname.ext' that was set in a css file; note: load process may be delayed a frame. 137 | 138 | Example of font file declaration: 139 | 140 | # Add css file link to app.html section: 141 | 142 | 143 | # Add font-face to app.css: 144 | # (note:font-family is lowercase filename w/o ext) 145 | @font-face { 146 | font-family: 'freesansbold'; 147 | src: url('freesansbold.ttf'); 148 | } 149 | """ 150 | if not name: 151 | font = [Font._font[0]] 152 | else: 153 | font = [fn.strip().lower() for fn in name.split(',')] 154 | load_custom_font = False 155 | fallback = None 156 | for i, fn in enumerate(font): 157 | if '.' in fn: 158 | fn = fn.split('.')[0] 159 | font[i] = fn 160 | load_custom_font = True 161 | if fn in Font._font: 162 | if not fallback: 163 | fallback = fn 164 | else: 165 | f = ''.join(c for c in fn if c.isalnum()) 166 | if f in Font._font_alt: 167 | font[i] = Font._font[Font._font_alt[f]] 168 | if not fallback: 169 | fallback = font[i] 170 | if fallback: 171 | for ff in Font._font_family: 172 | if fallback in ff: 173 | font.extend(f for f in ff if f not in font) 174 | break 175 | else: 176 | font.extend(Font._font_family[0]) 177 | self.fontname = ','.join(font) 178 | self.fontsize = size 179 | self.bold = '' 180 | self.italic = '' 181 | self.fontstyle = self.bold + ' ' + self.italic 182 | self.underline = False 183 | self.char_size = None 184 | if load_custom_font: 185 | self.render('x') 186 | self._nonimplemented_methods() 187 | 188 | def __str__(self): 189 | return "%s(%r)" % (self.__class__, self.__dict__) 190 | 191 | def __repr__(self): 192 | return "%s(%r)" % (self.__class__, self.__dict__) 193 | 194 | def render(self, text, antialias=True, color=(0,0,0), 195 | background=None, surface=None): #optional surface for text rendering 196 | """ 197 | Render text onto surface. 198 | 199 | Arguments are text to render, and optional antialias, RGB color of text, RGB color of background, and surface for text rendering. 200 | """ 201 | if not surface: 202 | w,h = self.size(text) 203 | surf = Surface((w,h)) 204 | else: 205 | surf = surface 206 | w,h = surface.width, surface.height 207 | if background: 208 | surf.setFillStyle(Color(background)) 209 | surf.fillRect(0,0,w,h) 210 | surf.setFont('%s %dpx %s' % (self.fontstyle, 211 | self.fontsize, 212 | self.fontname)) 213 | # if antialias: pass 214 | surf.setFillStyle(Color(color)) 215 | surf.setTextAlign('center') 216 | surf.setTextBaseline('middle') 217 | surf.fillText(text,w/2,h/2) 218 | if self.underline: 219 | surf.setLineWidth(self.fontsize/20) 220 | surf.setStrokeStyle(Color(color)) 221 | surf.beginPath() 222 | surf.moveTo(0, h*0.85) 223 | surf.lineTo(w, h*0.85) 224 | surf.stroke() 225 | return surf 226 | 227 | def size(self, text): 228 | """ 229 | Return size (width, height) of rendered text. 230 | """ 231 | if _surf: #>IE9 - use exception if HTML5Canvas not implemented 232 | _surf.setFont('%s %dpx %s' % (self.fontstyle, 233 | self.fontsize, 234 | self.fontname)) 235 | x = _surf.measureText(text) 236 | else: #estimate 237 | x = self._size_estimate(text) 238 | if x < 1: 239 | x = 1 240 | y = int(self.fontsize * 1.2) 241 | return (x, y) 242 | 243 | def _size_estimate(self, text=None): #for browsers HTML5Canvas not implemented 244 | if not self.char_size: 245 | self.char_size = self._get_char_size() 246 | self.fontname = ','.join(Font._font_family[0]) 247 | self.fontstyle = '' 248 | size = [] 249 | for char in text: 250 | try: 251 | size.append(self.char_size[char] * self.fontsize) 252 | except KeyError: 253 | size.append(self.char_size['x'] * self.fontsize) 254 | x = _ceil( sum(size) ) 255 | return x 256 | 257 | def set_underline(self, setting=True): 258 | """ 259 | Set font underline style. 260 | 261 | Optional setting argument, default to True. 262 | """ 263 | self.underline = setting 264 | 265 | def get_underline(self): 266 | """ 267 | Check if font is underlined. 268 | """ 269 | return self.underline 270 | 271 | def set_bold(self, setting=True): 272 | """ 273 | Set font bold style. 274 | 275 | Optional setting argument, default to True. 276 | """ 277 | self.bold = {True:'bold', False:''}[setting] 278 | self.fontstyle = self.bold + ' ' + self.italic 279 | 280 | def get_bold(self): 281 | """ 282 | Check if font is bold. 283 | """ 284 | if self.bold: 285 | return True 286 | else: 287 | return False 288 | 289 | def set_italic(self, setting=True): 290 | """ 291 | Set font italic style. 292 | 293 | Optional setting argument, default to True. 294 | """ 295 | self.italic = {True:'italic', False:''}[setting] 296 | self.fontstyle = self.bold + ' ' + self.italic 297 | 298 | def get_italic(self): 299 | """ 300 | Check if font is italized. 301 | """ 302 | if self.italic: 303 | return True 304 | else: 305 | return False 306 | 307 | def get_linesize(self): 308 | """ 309 | Return linesize of font. 310 | """ 311 | return int(self.fontsize*1.2) 312 | 313 | def _nonimplemented_methods(self): 314 | self.metrics = lambda *arg: [] 315 | self.get_height = lambda *arg: 0 316 | self.get_ascent = lambda *arg: 0 317 | self.get_descent = lambda *arg: 0 318 | 319 | def _get_char_size(self, font=None): #for browsers HTML5Canvas not implemented 320 | if not font: 321 | return {'a': 0.6, 'b': 0.6, 'c': 0.5, 'd': 0.6, 'e': 0.6, 'f': 0.3, 'g': 0.6, 'h': 0.6, 'i': 0.2, 'j': 0.2, 'k': 0.5, 'l': 0.2, 'm': 0.8, 'n': 0.6, 'o': 0.6, 'p': 0.6, 'q': 0.6, 'r': 0.3, 's': 0.5, 't': 0.3, 'u': 0.6, 'v': 0.5, 'w': 0.7, 'x': 0.5, 'y': 0.5, 'z': 0.5, 'A': 0.7, 'B': 0.7, 'C': 0.7, 'D': 0.7, 'E': 0.7, 'F': 0.6, 'G': 0.8, 'H': 0.7, 'I': 0.3, 'J': 0.5, 'K': 0.7, 'L': 0.6, 'M': 0.8, 'N': 0.7, 'O': 0.8, 'P': 0.7, 'Q': 0.8, 'R': 0.7, 'S': 0.7, 'T': 0.6, 'U': 0.7, 'V': 0.7, 'W': 0.9, 'X': 0.7, 'Y': 0.7, 'Z': 0.6, '0': 0.6, '1': 0.6, '2': 0.6, '3': 0.6, '4': 0.6, '5': 0.6, '6': 0.6, '7': 0.6, '8': 0.6, '9': 0.6, '.': 0.3, ',': 0.3, ':': 0.3, ';': 0.3, '?': 0.6, '~': 0.6, '!': 0.3, '@': 1, '#': 0.6, '$': 0.6, '%': 0.9, '^': 0.5, '&': 0.7, '=': 0.6, '+': 0.6, '-': 0.3, '*': 0.4, '/': 0.3, '\\': 0.3, '_': 0.6, '<': 0.6, '>': 0.6, '(': 0.3, ')': 0.3, '{': 0.3, '}': 0.3, '[': 0.3, ']': 0.3, "'": 0.2, '"': 0.4, ' ': 0.3} 322 | else: 323 | fontsize = 10 324 | _surf.setFont('%dpx %s' % (fontsize, font)) #generated font='arial' 325 | char_size = {} 326 | for chrs in ('abcdefghijklmnopqrstuvwxyz', 327 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 328 | '0123456789', 329 | '.,:;?~!@#$%^&=+-*/\_<>(){}[]\'\" '): 330 | for char in chrs: 331 | char_size[char] = float(_surf.measureText(char)/fontsize) 332 | return char_size 333 | 334 | 335 | class SysFont(Font): 336 | """ 337 | SysFont object. 338 | """ 339 | 340 | def __init__(self, name, size, bold=False, italic=False): 341 | """ 342 | Return SysFont subclassed of Font. 343 | 344 | Arguments include name of a system font and size of font, with optional bold and italic style. The name argument can be a string of comma-delimited names to specify fallbacks and use a default font if none found. 345 | """ 346 | Font.__init__(self,name,size) 347 | self.bold = {True:'bold', False:''}[bold] 348 | self.italic = {True:'italic', False:''}[bold] 349 | self.fontstyle = self.bold + ' ' + self.italic 350 | 351 | -------------------------------------------------------------------------------- /pyjsdl/image.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Image module** 6 | 7 | The module provides function to load images and convert them to surface objects. 8 | """ 9 | 10 | import os 11 | from pyjsdl.surface import Surface, Surf 12 | from pyjsdl import env 13 | import pyjsdl 14 | 15 | 16 | class Image(object): 17 | """ 18 | Image object. 19 | """ 20 | 21 | def __init__(self): 22 | """ 23 | Initialize Image module. 24 | 25 | Module initialization creates pyjsdl.image instance. 26 | """ 27 | self.images = None 28 | 29 | def load(self, img_file, namehint=None): 30 | """ 31 | Retrieve image from preloaded images. 32 | 33 | The img_file argument is an image URL, or an image data object whereby namehint argument is used to retrieve the image. 34 | Return the image as a Surface. 35 | """ 36 | if not namehint: 37 | image = self.get_image(img_file) 38 | else: 39 | image = self.get_image(namehint) 40 | surface = self.convert_image(image) 41 | return surface 42 | 43 | def get_image(self, img_file): 44 | """ 45 | Return the original image. 46 | """ 47 | if self.images is None: 48 | self.images = {} 49 | for img in env.canvas.images: 50 | self.images[os.path.normpath(img)] = env.canvas.images[img] 51 | img_file = os.path.normpath(img_file) 52 | try: 53 | image = self.images[img_file] 54 | except KeyError: 55 | raise pyjsdl.error("Failed to retrieve image file %s" % img_file) 56 | return image 57 | 58 | def convert_image(self, image): 59 | """ 60 | Return the image as a Surface. 61 | """ 62 | if env.canvas._isCanvas: 63 | surface = Surface((image.width,image.height)) 64 | surface.drawImage(image, 0, 0) 65 | else: 66 | surface = Surf(image) 67 | return surface 68 | 69 | -------------------------------------------------------------------------------- /pyjsdl/key.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Key module** 6 | 7 | The module provides keyboard functionality. 8 | """ 9 | 10 | from pyjsdl import env 11 | from pyjsdl import constants as Const 12 | 13 | 14 | class Key(object): 15 | """ 16 | Key object. 17 | """ 18 | 19 | def __init__(self): 20 | """ 21 | Provides methods to access the key function. 22 | 23 | Module initialization creates pyjsdl.key instance. 24 | """ 25 | self.keyPress = env.event.keyPress 26 | self.keyMod = env.event.keyMod 27 | self.alt = Const.K_ALT 28 | self.ctrl = Const.K_CTRL 29 | self.shift = Const.K_SHIFT 30 | self._keys = {} 31 | self._nonimplemented_methods() 32 | 33 | def name(self, keycode): 34 | """ 35 | Return name of key of a keycode. 36 | """ 37 | if not self._keys: 38 | for keyname in dir(Const): 39 | if keyname.startswith('K_'): 40 | name = keyname.split('_')[1].lower() 41 | if len(name) != 1: 42 | self._keys[getattr(Const, keyname)] = name 43 | if keycode in self._keys: 44 | return self._keys[keycode] 45 | else: 46 | return chr(keycode) 47 | 48 | def get_mods(self): 49 | """ 50 | Return int modifier keys alt|ctrl|shift. 51 | """ 52 | return (self.keyMod[self.alt][self.keyPress[self.alt]] | 53 | self.keyMod[self.ctrl][self.keyPress[self.ctrl]] | 54 | self.keyMod[self.shift][self.keyPress[self.shift]]) 55 | 56 | def set_repeat(self, delay=0, interval=0): 57 | """ 58 | Set key repeat. 59 | 60 | Set key repeat delay (ms) and interval (ms) settings. 61 | Key repeat initially disabled. 62 | """ 63 | if delay < 0 or interval < 0: 64 | raise ValueError('repeat settings must be positive integers') 65 | if not delay: 66 | env.event.keyRepeat[0] = 0 67 | env.event.keyRepeat[1] = 0 68 | else: 69 | env.event.keyRepeat[0] = delay 70 | if interval: 71 | env.event.keyRepeat[1] = interval 72 | else: 73 | env.event.keyRepeat[1] = delay 74 | return None 75 | 76 | def get_repeat(self): 77 | """ 78 | Get key repeat settings. 79 | """ 80 | return env.event.keyRepeat 81 | 82 | def _nonimplemented_methods(self): 83 | self.get_focused = lambda *arg: None 84 | self.get_pressed = lambda *arg: None 85 | self.set_mods = lambda *arg: None 86 | 87 | 88 | _code = {'Backquote':Const.K_BACKQUOTE, 'Backslash':Const.K_BACKSLASH, 89 | 'Backspace':Const.K_BACKSPACE, 'BracketLeft':Const.K_LEFTBRACKET, 90 | 'BracketRight':Const.K_RIGHTBRACKET, 'Comma':Const.K_COMMA, 91 | 'Digit0':Const.K_0, 'Digit1':Const.K_1, 'Digit2':Const.K_2, 92 | 'Digit3':Const.K_3, 'Digit4':Const.K_4, 'Digit5':Const.K_5, 93 | 'Digit6':Const.K_6, 'Digit7':Const.K_7, 'Digit8':Const.K_8, 94 | 'Digit9':Const.K_8, 'Equal':Const.K_EQUALS, 95 | 'KeyA':Const.K_a, 'KeyB':Const.K_b, 'KeyC':Const.K_c, 96 | 'KeyD':Const.K_d, 'KeyE':Const.K_e, 'KeyF':Const.K_f, 97 | 'KeyG':Const.K_g, 'KeyH':Const.K_h, 'KeyI':Const.K_i, 98 | 'KeyJ':Const.K_j, 'KeyK':Const.K_k, 'KeyL':Const.K_l, 99 | 'KeyM':Const.K_m, 'KeyN':Const.K_n, 'KeyO':Const.K_o, 100 | 'KeyP':Const.K_p, 'KeyQ':Const.K_q, 'KeyR':Const.K_r, 101 | 'KeyS':Const.K_s, 'KeyT':Const.K_t, 'KeyU':Const.K_u, 102 | 'KeyV':Const.K_v, 'KeyW':Const.K_w, 'KeyX':Const.K_x, 103 | 'KeyY':Const.K_y, 'KeyZ':Const.K_z, 'Minus':Const.K_MINUS, 104 | 'Period':Const.K_PERIOD, 'Quote':Const.K_QUOTE, 105 | 'Semicolon':Const.K_SEMICOLON, 'Slash':Const.K_SLASH, 106 | 'AltLeft':Const.K_LALT, 'AltRight':Const.K_RALT, 107 | 'CapsLock':Const.K_CAPSLOCK, 'ContextMenu':Const.K_MENU, 108 | 'ControlLeft':Const.K_LCTRL, 'ControlRight':Const.K_RCTRL, 109 | 'Enter':Const.K_RETURN, 'MetaLeft':Const.K_LMETA, 110 | 'MetaRight':Const.K_RMETA, 'ShiftLeft':Const.K_LSHIFT, 111 | 'ShiftRight':Const.K_RSHIFT, 'Space':Const.K_SPACE, 112 | 'Tab':Const.K_TAB, 'Delete':Const.K_DELETE, 'End':Const.K_END, 113 | 'Help':Const.K_HELP, 'Home':Const.K_HOME, 'Insert':Const.K_INSERT, 114 | 'PageDown':Const.K_PAGEDOWN, 'PageUp':Const.K_PAGEUP, 115 | 'ArrowDown':Const.K_DOWN, 'ArrowLeft':Const.K_LEFT, 116 | 'ArrowRight':Const.K_RIGHT, 'ArrowUp':Const.K_UP, 117 | 'NumLock':Const.K_NUMLOCK, 'Numpad0':Const.K_KP0, 118 | 'Numpad1':Const.K_KP1, 'Numpad2':Const.K_KP2, 119 | 'Numpad3':Const.K_KP3, 'Numpad4':Const.K_KP4, 120 | 'Numpad5':Const.K_KP5, 'Numpad6':Const.K_KP6, 121 | 'Numpad7':Const.K_KP7, 'Numpad8':Const.K_KP8, 122 | 'Numpad9':Const.K_KP9, 'NumpadAdd':Const.K_KP_PLUS, 123 | 'NumpadDecimal':Const.K_KP_PERIOD, 'NumpadDivide':Const.K_KP_DIVIDE, 124 | 'NumpadEnter':Const.K_KP_ENTER, 'NumpadEqual':Const.K_KP_EQUALS, 125 | 'NumpadMultiply':Const.K_KP_MULTIPLY, 'NumpadSubtract':Const.K_KP_MINUS, 126 | 'F1':Const.K_F1, 'F2':Const.K_F2, 'F3':Const.K_F3, 127 | 'F4':Const.K_F4, 'F5':Const.K_F5, 'F6':Const.K_F6, 128 | 'F7':Const.K_F7, 'F8':Const.K_F8, 'F9':Const.K_F9, 129 | 'F10':Const.K_F10, 'F11':Const.K_F11, 'F12':Const.K_F12, 130 | 'PrintScreen':Const.K_PRINT, 'ScrollLock':Const.K_SCROLLOCK, 131 | 'Pause':Const.K_PAUSE, 'Escape':Const.K_ESCAPE, 132 | 'Unidentified':Const.K_UNKNOWN} 133 | 134 | _modKey = {'Alt':Const.K_ALT , 'Control':Const.K_CTRL, 'Shift':Const.K_SHIFT} 135 | 136 | _modKeyCode = {Const.K_ALT, Const.K_CTRL, Const.K_SHIFT} 137 | 138 | _specialKey = {'ArrowUp':Const.K_UP, 'ArrowDown':Const.K_DOWN, 139 | 'ArrowLeft':Const.K_LEFT, 'ArrowRight':Const.K_RIGHT, 140 | 'Up':Const.K_UP, 'Down':Const.K_DOWN, 141 | 'Left':Const.K_LEFT, 'Right':Const.K_RIGHT, 142 | 'Home':Const.K_HOME, 'End':Const.K_END, 143 | 'PageUp':Const.K_PAGEUP, 'PageDown':Const.K_PAGEDOWN, 144 | 'Backspace':Const.K_BACKSPACE, 'Delete':Const.K_DELETE, 145 | 'Insert':Const.K_INSERT, 'Clear':Const.K_CLEAR, 146 | 'Escape':Const.K_ESCAPE, 'Esc':Const.K_ESCAPE, 147 | 'CapsLock':Const.K_CAPSLOCK, 'Meta':Const.K_LMETA, 148 | 'ContextMenu':Const.K_MENU, 'PrintScreen':Const.K_PRINT, 149 | 'ScrollLock':Const.K_SCROLLLOCK, 'Pause':Const.K_PAUSE, 150 | 'NumLock':Const.K_NUMLOCK, 151 | 'F1':Const.K_F1, 'F2':Const.K_F2, 'F3':Const.K_F3, 152 | 'F4':Const.K_F4, 'F5':Const.K_F5, 'F6':Const.K_F6, 153 | 'F7':Const.K_F7, 'F8':Const.K_F8, 'F9':Const.K_F9, 154 | 'F10':Const.K_F10, 'F11':Const.K_F11, 'F12':Const.K_F12, 155 | 'Alt':Const.K_ALT , 'Control':Const.K_CTRL, 'Shift':Const.K_SHIFT} 156 | 157 | _specialKeyCode = {38:Const.K_UP, 40:Const.K_DOWN, 158 | 37:Const.K_LEFT, 39:Const.K_RIGHT, 159 | 36:Const.K_HOME, 35:Const.K_END, 160 | 33:Const.K_PAGEUP, 34:Const.K_PAGEDOWN, 161 | 8:Const.K_BACKSPACE, 46:Const.K_DELETE, 162 | 45:Const.K_INSERT, 12:Const.K_CLEAR, 163 | 13:Const.K_RETURN, 9:Const.K_TAB, 164 | 27:Const.K_ESCAPE, 20:Const.K_CAPSLOCK, 165 | 92:Const.K_LMETA, 93:Const.K_MENU, 166 | 44:Const.K_PRINT, 145:Const.K_SCROLLLOCK, 167 | 19:Const.K_PAUSE, 144:Const.K_NUMLOCK, 168 | 112:Const.K_F1, 113:Const.K_F2, 114:Const.K_F3, 169 | 115:Const.K_F4, 116:Const.K_F5, 117:Const.K_F6, 170 | 118:Const.K_F7, 119:Const.K_F8, 120:Const.K_F9, 171 | 121:Const.K_F10, 122:Const.K_F11, 123:Const.K_F12, 172 | 18:Const.K_ALT, 17:Const.K_CTRL, 16:Const.K_SHIFT} 173 | 174 | -------------------------------------------------------------------------------- /pyjsdl/mask.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Mask module** 6 | 7 | The module provides surface mask functionality. 8 | """ 9 | 10 | from pyjsdl.pyjsarray import BitSet 11 | from pyjsdl.color import Color 12 | import sys 13 | 14 | if sys.version_info < (3,): 15 | from pyjsdl.util import _range as range 16 | 17 | 18 | def from_surface(surface, threshold=127): 19 | """ 20 | Mask from surface. 21 | 22 | Return Mask derived from surface using alpha transparency. 23 | Optional argument to set alpha threshold. 24 | """ 25 | mask = Mask((surface.width, surface.height)) 26 | if not mask.bit: 27 | return None 28 | pixels = surface.impl.getImageData(0, 0, surface.width, surface.height) 29 | width, height = surface.width*4, surface.height 30 | for y in range(0, height): 31 | xpix = 0 32 | i = (y*width)+3 33 | for x in range(0, width, 4): 34 | if surface._getPixel(pixels, i+x) > threshold: 35 | mask.set_at((xpix,y)) 36 | xpix += 1 37 | return mask 38 | 39 | 40 | def from_threshold(surface, color, threshold=(0,0,0,255)): 41 | """ 42 | Mask from surface. 43 | 44 | Return Mask from surface using a given color. 45 | Optional threshold argument to set color range and alpha threshold. 46 | """ 47 | mask = Mask((surface.width, surface.height)) 48 | if not mask.bit: 49 | return None 50 | pixels = surface.impl.getImageData(0, 0, surface.width, surface.height) 51 | if threshold == (0,0,0,255): 52 | color = Color(color) 53 | color = (color.r,color.g,color.b) 54 | width, height = surface.width*4, surface.height 55 | for y in range(0, height): 56 | xpix = 0 57 | i = y*width 58 | for x in range(0, width, 4): 59 | ix = i+x 60 | if (surface._getPixel(pixels, ix) == color[0] and 61 | surface._getPixel(pixels, ix+1) == color[1] and 62 | surface._getPixel(pixels, ix+2) == color[2] and 63 | surface._getPixel(pixels, ix+3) >= threshold[3]): 64 | mask.set_at((xpix,y)) 65 | xpix += 1 66 | else: 67 | color = Color(color) 68 | col = {} 69 | for i, c in enumerate(('r','g','b')): 70 | if threshold[i]: 71 | col[c+'1'] = color[i] - threshold[i] - 1 72 | col[c+'2'] = color[i] + threshold[i] + 1 73 | else: 74 | col[c+'1'] = color[i] - 1 75 | col[c+'2'] = color[i] + 1 76 | col['a'] = threshold[3] - 1 77 | width, height = surface.width*4, surface.height 78 | for y in range(0, height): 79 | xpix = 0 80 | i = y*width 81 | for x in range(0, width, 4): 82 | ix = i+x 83 | if ((col['r1'] < surface._getPixel(pixels, ix) < col['r2']) and 84 | (col['g1'] < surface._getPixel(pixels, ix+1) < col['g2']) and 85 | (col['b1'] < surface._getPixel(pixels, ix+2) < col['b2']) and 86 | (surface._getPixel(pixels, ix+3) > col['a'])): 87 | mask.set_at((xpix,y)) 88 | xpix += 1 89 | return mask 90 | 91 | 92 | class Mask(object): 93 | """ 94 | Mask object. 95 | """ 96 | 97 | def __init__(self, size): 98 | """ 99 | Initialize Mask object. 100 | 101 | The size argument is (width, height) of the mask. 102 | The mask is represented by a list of Bitset. 103 | """ 104 | self.width = int(size[0]) 105 | self.height = int(size[1]) 106 | self.bit = [] 107 | for bitset in range(self.height): 108 | self.bit.append(BitSet(self.width)) 109 | 110 | def __str__(self): 111 | return self.toString() 112 | 113 | def __repr__(self): 114 | return "%s(%r)" % (self.__class__, self.__dict__) 115 | 116 | def get_size(self): 117 | """ 118 | Return width, height of mask. 119 | """ 120 | return (self.width, self.height) 121 | 122 | def get_at(self, pos): 123 | """ 124 | Return bit setting for given pos. 125 | """ 126 | return self.bit[pos[1]].get(pos[0]) 127 | 128 | def set_at(self, pos, value=1): 129 | """ 130 | Set bit for given pos. 131 | 132 | Optional value to set bit, either 1 or 0, defaults to 1. 133 | """ 134 | self.bit[pos[1]].set(pos[0], value) 135 | return None 136 | 137 | def fill(self): 138 | """ 139 | Fill mask. 140 | """ 141 | for bitset in self.bit: 142 | bitset.fill() 143 | return None 144 | 145 | def clear(self): 146 | """ 147 | Clear mask. 148 | """ 149 | for bitset in self.bit: 150 | bitset.clear() 151 | return None 152 | 153 | def invert(self): 154 | """ 155 | Invert bit value in mask. 156 | """ 157 | for bitset in self.bit: 158 | bitset.flip(0,self.width) 159 | return None 160 | 161 | def count(self): 162 | """ 163 | Return count of true bits in mask. 164 | """ 165 | true_bits = 0 166 | for bitset in self.bit: 167 | true_bits += bitset.cardinality() 168 | return true_bits 169 | 170 | def overlap(self, mask, offset): 171 | """ 172 | Check mask overlap. 173 | 174 | Return True if mask at offset position overlap with this mask. 175 | """ 176 | if offset[0] > 0: 177 | x1 = offset[0] 178 | x2 = 0 179 | else: 180 | x1 = 0 181 | x2 = -offset[0] 182 | if offset[1] > 0: 183 | y1 = offset[1] 184 | y2 = 0 185 | else: 186 | y1 = 0 187 | y2 = -offset[1] 188 | w = min(self.width-x1, mask.width-x2) 189 | h = min(self.height-y1, mask.height-y2) 190 | if w > 0 and h > 0: 191 | for y in range(h): 192 | if self.bit[y1+y].get(x1, x1+w).intersects( 193 | mask.bit[y2+y].get(x2, x2+w)): 194 | return True 195 | return None 196 | 197 | def toString(self, bit=('1','0')): 198 | """ 199 | Return string representation of mask. 200 | 201 | Optional bit argument specify bit character. 202 | """ 203 | cbit = {True:bit[0], False:bit[1]} 204 | cbitset = [] 205 | for bitset in self.bit: 206 | cbitset.append('\n') 207 | cbitset.extend([cbit[bitset.get(i)] 208 | for i in range(self.width)]) 209 | bitstr = ''.join(cbitset) 210 | return bitstr 211 | 212 | -------------------------------------------------------------------------------- /pyjsdl/mouse.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Mouse module** 6 | 7 | The module provides mouse functionality. 8 | """ 9 | 10 | from pyjsdl import env 11 | from pyjsdl import cursors 12 | from pyjsdl.pyjsobj import DOM 13 | 14 | 15 | class Mouse(object): 16 | """ 17 | Mouse object. 18 | """ 19 | 20 | def __init__(self): 21 | """ 22 | Provides methods to access the mouse function. 23 | 24 | Module initialization creates pyjsdl.mouse instance. 25 | """ 26 | self.mousePos = env.event.mousePos 27 | self.mousePosPre = env.event.mousePosPre 28 | self.mousePosRel = env.event.mousePosRel 29 | self.mousePress = env.event.mousePress 30 | self._cursorVisible = True 31 | self._cursor = 'default' 32 | self._nonimplemented_methods() 33 | 34 | def get_pressed(self): 35 | """ 36 | Return state of mouse buttons as a tuple of bool for button1,2,3. 37 | """ 38 | return (self.mousePress[0], 39 | self.mousePress[1], 40 | self.mousePress[2]) 41 | 42 | def get_pos(self): 43 | """ 44 | Return x,y of mouse pointer. 45 | 46 | If the pointer is not in canvas, returns -1,-1 47 | """ 48 | if self.mousePos['x'] != -1: 49 | return (self.mousePos['x'] + env.frame.scrollLeft, 50 | self.mousePos['y'] + env.frame.scrollTop) 51 | else: 52 | return (self.mousePos['x'], self.mousePos['y']) 53 | 54 | def get_rel(self): 55 | """ 56 | Return relative x,y change of mouse position since last call. 57 | """ 58 | if self.mousePos['x'] != -1: 59 | rel = (self.mousePos['x'] - self.mousePosRel['x'], 60 | self.mousePos['y'] - self.mousePosRel['y']) 61 | self.mousePosRel['x'] = self.mousePos['x'] 62 | self.mousePosRel['y'] = self.mousePos['y'] 63 | return rel 64 | else: 65 | return (0, 0) 66 | 67 | def set_visible(self, visible): 68 | """ 69 | Set mouse cursor visibility according to visible bool argument. 70 | 71 | Return previous cursor visibility state. 72 | """ 73 | visible_pre = self._cursorVisible 74 | if visible: 75 | DOM.setStyleAttribute(env.canvas.getElement(), 76 | 'cursor', self._cursor) 77 | self._cursorVisible = True 78 | else: 79 | DOM.setStyleAttribute(env.canvas.getElement(), 80 | 'cursor', 'none') 81 | self._cursorVisible = False 82 | return visible_pre 83 | 84 | def get_focused(self): 85 | """ 86 | Check if mouse has focus. 87 | """ 88 | return self.mousePos['x'] != -1 89 | 90 | def set_cursor(self, *cursor): 91 | """ 92 | Set mouse cursor. 93 | 94 | Alternative arguments: 95 | * system cursor or cursor object 96 | * image url or surface, hotspot (x,y), and optional fallback 97 | * size, hotspot, data, mask, and optional fallback 98 | Refer to cursors module for details. 99 | """ 100 | args = len(cursor) 101 | if args == 1: 102 | self._cursor = cursor[0] 103 | elif args in (2,3): 104 | if isinstance(cursor[0], str): 105 | url = cursor[0] 106 | else: 107 | url = cursor[0].toDataURL() 108 | hotspot = cursor[1] 109 | if args == 2: 110 | fallback = 'default' 111 | else: 112 | fallback = cursor[2] 113 | self._cursor = 'url("%s") %d %d, %s' % (url, 114 | hotspot[0], 115 | hotspot[1], 116 | fallback) 117 | elif args in (4,5): 118 | size = cursor[0] 119 | hotspot = cursor[1] 120 | data = cursor[2] 121 | mask = cursor[3] 122 | if args == 4: 123 | fallback = 'default' 124 | else: 125 | fallback = cursor[4] 126 | surface = cursors.create_cursor(size, data, mask) 127 | url = surface.toDataURL() 128 | self._cursor = 'url("%s") %d %d, %s' % (url, 129 | hotspot[0], 130 | hotspot[1], 131 | fallback) 132 | else: 133 | self._cursor = 'default' 134 | if self._cursorVisible: 135 | DOM.setStyleAttribute(env.canvas.getElement(), 136 | 'cursor', self._cursor) 137 | 138 | def get_cursor(self): 139 | """ 140 | Return cursor object. 141 | """ 142 | return self._cursor 143 | 144 | def _nonimplemented_methods(self): 145 | self.set_pos = lambda *arg: None 146 | 147 | -------------------------------------------------------------------------------- /pyjsdl/pyjsobj.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Pyjsobj module** 6 | 7 | The module provides JavaScript functionality. 8 | """ 9 | 10 | from pyjamas import DOM 11 | from pyjamas import Window 12 | from pyjamas.ui.RootPanel import RootPanel 13 | from pyjamas.ui.FocusPanel import SimplePanel 14 | from pyjamas.ui.VerticalPanel import VerticalPanel 15 | from pyjamas.Canvas.Color import Color 16 | from pyjamas.Canvas.ImageLoader import loadImages 17 | from pyjamas.ui.TextBox import TextBox 18 | from pyjamas.ui.TextArea import TextArea 19 | from pyjamas.ui import Event 20 | from pyjamas.Canvas.HTML5Canvas import HTML5Canvas as _HTML5Canvas 21 | from pyjamas.media.Audio import Audio 22 | from __pyjamas__ import JS, doc, wnd 23 | 24 | 25 | class MouseWheelHandler(object): 26 | 27 | _listener = {} 28 | 29 | def __init__(self): 30 | element = self.getElement() 31 | if hasattr(element, 'onwheel'): 32 | self._mousewheel = 'wheel' 33 | elif hasattr(element, 'onmousewheel'): 34 | self._mousewheel = 'mousewheel' 35 | else: 36 | self._mousewheel = 'DOMMouseScroll' 37 | 38 | def addMouseWheelListener(self): 39 | element = self.getElement() 40 | listener = lambda event: self.onMouseWheel(event) 41 | self._listener[self] = listener 42 | element.addEventListener(self._mousewheel, listener) 43 | 44 | def removeMouseWheelListener(self): 45 | element = self.getElement() 46 | listener = self._listener[self] 47 | del self._listener[self] 48 | element.removeEventListener(self._mousewheel, listener) 49 | 50 | def onMouseWheel(self, event): 51 | pass 52 | 53 | 54 | class VisibilityChangeHandler(object): 55 | 56 | _listener = {} 57 | 58 | def __init__(self): 59 | self._visibilitychange = 'visibilitychange' 60 | self._pagehide = 'pagehide' 61 | wnd().onunload = None 62 | wnd().onbeforeunload = None 63 | 64 | def addVisibilityChangeListener(self): 65 | listener = lambda event: self.onVisibilityChange(event) 66 | self._listener[self._visibilitychange] = listener 67 | doc().addEventListener(self._visibilitychange, listener) 68 | 69 | def removeVisibilityChangeListener(self): 70 | listener = self._listener[self._visibilitychange] 71 | del self._listener[self._visibilitychange] 72 | doc().removeEventListener(self._visibilitychange, listener) 73 | 74 | def addPageHideListener(self): 75 | listener = lambda event: self.onPageHide(event) 76 | self._listener[self._pagehide] = listener 77 | wnd().addEventListener(self._pagehide, listener) 78 | 79 | def removePageHideListener(self): 80 | listener = self._listener[self._pagehide] 81 | del self._listener[self._pagehide] 82 | wnd().removeEventListener(self._pagehide, listener) 83 | 84 | def onVisibilityChange(self, event): 85 | pass 86 | 87 | def onPageHide(self, event): 88 | pass 89 | 90 | 91 | class HTML5Canvas(_HTML5Canvas, MouseWheelHandler, VisibilityChangeHandler): 92 | 93 | def __init__(self, coordX, coordY, *args, **kwargs): 94 | _HTML5Canvas.__init__(self, coordX, coordY, *args, **kwargs) 95 | MouseWheelHandler.__init__(self) 96 | VisibilityChangeHandler.__init__(self) 97 | 98 | def addMouseListener(self, listener): 99 | _HTML5Canvas.addMouseListener(self, listener) 100 | self.addMouseWheelListener() 101 | 102 | def addKeyEventListener(self, obj): 103 | element = obj.getElement() 104 | element.setAttribute('tabindex','0') 105 | listener = lambda event: self.onKeyEvent(event) 106 | _listener[self] = listener 107 | element.addEventListener('keydown', listener) 108 | 109 | def removeKeyEventListener(self, obj): 110 | element = obj.getElement() 111 | listener = _listener[self] 112 | del _listener[self] 113 | element.removeEventListener('keydown', listener) 114 | 115 | 116 | _listener = {} 117 | 118 | 119 | def set_icon(icon): 120 | if hasattr(icon, 'canvas'): 121 | icon = icon.canvas.toDataURL() 122 | obj = JS('parent.document') 123 | link = obj.querySelector("link[rel~='icon']") 124 | if not link: 125 | link = obj.createElement('link') 126 | link.rel = 'icon' 127 | obj.head.appendChild(link) 128 | link.href = icon 129 | 130 | 131 | class Document(object): 132 | 133 | def getVisibility(self): 134 | return doc().hidden 135 | 136 | document = Document() 137 | 138 | 139 | def requestAnimationFrameInit(): 140 | requestAnimationFramePolyfill() 141 | return wnd() 142 | 143 | 144 | def performanceNowInit(): 145 | performanceNowPolyfill() 146 | return wnd() 147 | 148 | 149 | def requestAnimationFramePolyfill(): 150 | JS( 151 | """ 152 | // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 153 | // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 154 | 155 | // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel 156 | 157 | // MIT license 158 | 159 | (function() { 160 | var lastTime = 0; 161 | var vendors = ['ms', 'moz', 'webkit', 'o']; 162 | for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 163 | window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 164 | window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 165 | || window[vendors[x]+'CancelRequestAnimationFrame']; 166 | } 167 | 168 | if (!window.requestAnimationFrame) 169 | window.requestAnimationFrame = function(callback, element) { 170 | var currTime = new Date().getTime(); 171 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 172 | var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 173 | timeToCall); 174 | lastTime = currTime + timeToCall; 175 | return id; 176 | }; 177 | 178 | if (!window.cancelAnimationFrame) 179 | window.cancelAnimationFrame = function(id) { 180 | clearTimeout(id); 181 | }; 182 | }()); 183 | """ 184 | ) 185 | 186 | 187 | def performanceNowPolyfill(): 188 | JS( 189 | """ 190 | // @license http://opensource.org/licenses/MIT 191 | // copyright Paul Irish 2015 192 | 193 | 194 | // Date.now() is supported everywhere except IE8. For IE8 we use the Date.now polyfill 195 | // github.com/Financial-Times/polyfill-service/blob/master/polyfills/Date.now/polyfill.js 196 | // as Safari 6 doesn't have support for NavigationTiming, we use a Date.now() timestamp for relative values 197 | 198 | // if you want values similar to what you'd get with real perf.now, place this towards the head of the page 199 | // but in reality, you're just getting the delta between now() calls, so it's not terribly important where it's placed 200 | 201 | 202 | (function(){ 203 | 204 | if ("performance" in window == false) { 205 | window.performance = {}; 206 | } 207 | 208 | Date.now = (Date.now || function () { // thanks IE8 209 | return new Date().getTime(); 210 | }); 211 | 212 | if ("now" in window.performance == false){ 213 | 214 | var nowOffset = Date.now(); 215 | 216 | if (performance.timing && performance.timing.navigationStart){ 217 | nowOffset = performance.timing.navigationStart 218 | } 219 | 220 | window.performance.now = function now(){ 221 | return Date.now() - nowOffset; 222 | } 223 | } 224 | 225 | })(); 226 | """ 227 | ) 228 | 229 | -------------------------------------------------------------------------------- /pyjsdl/surfarray.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Surfarray module** 6 | 7 | The module provides array access to surface pixel data. 8 | """ 9 | 10 | from pyjsdl.surface import Surface 11 | from pyjsdl.pyjsarray import Ndarray 12 | from pyjsdl.pyjsarray import Uint8ClampedArray 13 | from pyjsdl.pyjsarray import Uint8Array 14 | from pyjsdl.pyjsarray import Uint32Array 15 | from pyjsdl.pyjsarray import ImageData 16 | from pyjsdl.pyjsarray import ImageMatrix 17 | import sys 18 | 19 | if sys.version_info < (3,): 20 | from pyjsdl.util import _range as range 21 | 22 | 23 | def array(surface): 24 | """ 25 | Return data array of the Surface argument. 26 | 27 | Array consists of pixel data arranged by [y,x] in RGBA format. 28 | Data array most consistent to ImageData format. 29 | """ 30 | imagedata = surface.impl.getImageData(0, 0, surface.width, surface.height) 31 | return ImageMatrix(imagedata) 32 | 33 | 34 | def array2d(surface, copydata=False): 35 | """ 36 | Return data array of the Surface argument. 37 | 38 | Array consists of pixel data arranged by [x,y] in integer color format. 39 | Provides an interface to ImageData format. 40 | Creates a new formatted array if optional copydata argument is True. 41 | """ 42 | imagedata = surface.impl.getImageData(0, 0, surface.width, surface.height) 43 | if not copydata: 44 | return ImageMatrixInteger(imagedata) 45 | else: 46 | return ImageInteger(imagedata) 47 | 48 | 49 | def array3d(surface, copydata=False): 50 | """ 51 | Return data array of the Surface argument. 52 | 53 | Array consists of pixel data arranged by [x,y] in RGB format. 54 | Provides an interface to ImageData format. 55 | Creates a new formatted array if optional copydata argument is True. 56 | """ 57 | imagedata = surface.impl.getImageData(0, 0, surface.width, surface.height) 58 | if not copydata: 59 | return ImageMatrixRGB(imagedata) 60 | else: 61 | return ImageRGB(imagedata) 62 | 63 | 64 | def array_alpha(surface, copydata=False): 65 | """ 66 | Return data array of the Surface argument. 67 | 68 | Array consists of pixel data arranged by [x,y] of pixel alpha value. 69 | Provides an interface to ImageData format. 70 | Creates a new formatted array if optional copydata argument is True. 71 | """ 72 | imagedata = surface.impl.getImageData(0, 0, surface.width, surface.height) 73 | if not copydata: 74 | return ImageMatrixAlpha(imagedata) 75 | else: 76 | return ImageAlpha(imagedata) 77 | 78 | 79 | def make_surface(array): 80 | """ 81 | Generates image pixels from array data. 82 | 83 | Argument array containing image data. 84 | Return Surface generated from array. 85 | """ 86 | surface = Surface((array._imagedata.width,array._imagedata.height)) 87 | blit_array(surface, array) 88 | return surface 89 | 90 | 91 | def blit_array(surface, array): 92 | """ 93 | Generates image pixels from array data. 94 | 95 | Arguments include destination Surface and array containing image data. 96 | """ 97 | try: 98 | imagedata = array.getImageData() 99 | except (TypeError, AttributeError): 100 | imagedata = surface.impl.getImageData(0, 0, surface.width, surface.height) 101 | if len(array._shape) == 2: 102 | array2d = ImageMatrix(imagedata) 103 | for y in range(array2d.getHeight()): 104 | for x in range(array2d.getWidth()): 105 | value = array[x,y] 106 | array2d[y,x] = (value>>16 & 0xff, 107 | value>>8 & 0xff, 108 | value & 0xff, 109 | 255) 110 | imagedata = array2d.getImageData() 111 | else: 112 | imagedata.data.set(array.getArray()) 113 | surface.impl.putImageData(imagedata, 0, 0, 0, 0, surface.width, surface.height) 114 | return None 115 | 116 | 117 | class ImageMatrixRGB(ImageMatrix): 118 | """ 119 | Array of pixel data arranged by width/height in RGB format. 120 | 121 | Interface to ImageData. 122 | """ 123 | 124 | shape = ImageMatrix.shape 125 | 126 | def __getitem__(self, index): 127 | index = list(index) 128 | index[0], index[1] = index[1], index[0] 129 | index = tuple(index) 130 | return ImageMatrix.__getitem__(self, index) 131 | 132 | def __setitem__(self, index, value): 133 | index = list(index) 134 | index[0], index[1] = index[1], index[0] 135 | index = tuple(index) 136 | ImageMatrix.__setitem__(self, index, value) 137 | return None 138 | 139 | 140 | class ImageRGB(Ndarray): 141 | """ 142 | Array of pixel data arranged by width/height in RGB format. 143 | 144 | Array data derived from ImageData. 145 | """ 146 | 147 | def __init__(self, imagedata): 148 | self._imagedata = ImageData(imagedata) 149 | array = Ndarray(self._imagedata.data) 150 | array.setshape(self._imagedata.height, self._imagedata.width, 4) 151 | try: 152 | data = Uint8ClampedArray(self._imagedata.height 153 | * self._imagedata.width * 3) 154 | except NotImplementedError: 155 | data = Uint8Array(self._imagedata.height 156 | * self._imagedata.width * 3) 157 | index = 0 158 | for x in range(self._imagedata.width): 159 | for y in range(self._imagedata.height): 160 | for i in range(3): 161 | data[index] = array[y, x, i] 162 | index += 1 163 | try: 164 | Ndarray.__init__(self, data, 'uint8c') 165 | except NotImplementedError: 166 | Ndarray.__init__(self, data, 'uint8') 167 | self.setshape(self._imagedata.width, self._imagedata.height, 3) 168 | 169 | shape = Ndarray.shape 170 | 171 | def getImageData(self): 172 | """ 173 | Get ImageData. 174 | """ 175 | index = 0 176 | for x in range(self._imagedata.height): 177 | for y in range(self._imagedata.width): 178 | for i in range(3): 179 | self._imagedata.data[index + i] = self[y, x, i] 180 | index += 4 181 | return self._imagedata.getImageData() 182 | 183 | 184 | class ImageMatrixAlpha(ImageMatrix): 185 | """ 186 | Array of pixel data arranged by width/height of pixel alpha value. 187 | 188 | Interface to ImageData. 189 | """ 190 | 191 | shape = ImageMatrix.shape 192 | 193 | def __getitem__(self, index): 194 | return ImageMatrix.__getitem__(self, (index[1], index[0], 3)) 195 | 196 | def __setitem__(self, index, value): 197 | ImageMatrix.__setitem__(self, (index[1], index[0], 3), value) 198 | return None 199 | 200 | 201 | class ImageAlpha(Ndarray): 202 | """ 203 | Array of pixel data arranged by width/height of pixel alpha value. 204 | 205 | Array data derived from ImageData. 206 | """ 207 | 208 | def __init__(self, imagedata): 209 | self._imagedata = ImageData(imagedata) 210 | array = Ndarray(self._imagedata.data) 211 | array.setshape(self._imagedata.height, self._imagedata.width, 4) 212 | try: 213 | data = Uint8ClampedArray(self._imagedata.height 214 | * self._imagedata.width) 215 | except NotImplementedError: 216 | data = Uint8Array(self._imagedata.height 217 | * self._imagedata.width) 218 | index = 0 219 | for x in range(self._imagedata.width): 220 | for y in range(self._imagedata.height): 221 | data[index] = array[y, x, 3] 222 | index += 1 223 | try: 224 | Ndarray.__init__(self, data, 'uint8c') 225 | except NotImplementedError: 226 | Ndarray.__init__(self, data, 'uint8') 227 | self.setshape(self._imagedata.width, self._imagedata.height) 228 | 229 | shape = Ndarray.shape 230 | 231 | def getImageData(self): 232 | """ 233 | Get ImageData. 234 | """ 235 | index = 0 236 | for x in range(self._imagedata.height): 237 | for y in range(self._imagedata.width): 238 | self._imagedata.data[index + 3] = self[y, x] 239 | index += 4 240 | return self._imagedata.getImageData() 241 | 242 | 243 | class ImageMatrixInteger(ImageMatrix): 244 | """ 245 | Array of pixel data arranged by width/height in integer color format. 246 | 247 | Interface to ImageData. 248 | """ 249 | 250 | shape = ImageMatrix.shape 251 | 252 | def __getitem__(self, index): 253 | value = ImageMatrix.__getitem__(self, (index[1], index[0])) 254 | return value[0]<<16 | value[1]<<8 | value[2] | value[3]<<24 255 | 256 | def __setitem__(self, index, value): 257 | ImageMatrix.__setitem__(self, (index[1], index[0]), 258 | (value>>16 & 0xff, 259 | value>>8 & 0xff, 260 | value & 0xff, 261 | value>>24 & 0xff)) 262 | return None 263 | 264 | 265 | class ImageInteger(Ndarray): 266 | """ 267 | Array of pixel data arranged by width/height in integer color format. 268 | 269 | Array data derived from ImageData. 270 | """ 271 | 272 | def __init__(self, imagedata): 273 | self._imagedata = ImageData(imagedata) 274 | array = Ndarray(self._imagedata.data) 275 | array.setshape(self._imagedata.height, self._imagedata.width, 4) 276 | data = Uint32Array(self._imagedata.height 277 | * self._imagedata.width) 278 | index = 0 279 | for x in range(self._imagedata.width): 280 | for y in range(self._imagedata.height): 281 | data[index] = (array[y, x, 0] << 16 | 282 | array[y, x, 1] << 8 | 283 | array[y, x, 2] | 284 | array[y, x, 3] << 24) 285 | index += 1 286 | Ndarray.__init__(self, data, 'uint32') 287 | self.setshape(self._imagedata.width, self._imagedata.height) 288 | 289 | shape = Ndarray.shape 290 | 291 | def getImageData(self): 292 | """ 293 | Get ImageData. 294 | """ 295 | index = 0 296 | for x in range(self._imagedata.height): 297 | for y in range(self._imagedata.width): 298 | dat = self[y, x] 299 | self._imagedata.data[index] = dat>>16 & 0xff 300 | self._imagedata.data[index + 1] = dat>>8 & 0xff 301 | self._imagedata.data[index + 2] = dat & 0xff 302 | self._imagedata.data[index + 3] = dat>>24 & 0xff 303 | index += 4 304 | return self._imagedata.getImageData() 305 | 306 | -------------------------------------------------------------------------------- /pyjsdl/time.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Time module** 6 | 7 | The module provides time monitoring functionality. 8 | """ 9 | 10 | from pyjsdl import env 11 | from pyjsdl.pyjsobj import performanceNowInit 12 | from __pyjamas__ import JS 13 | 14 | 15 | class Clock(object): 16 | """ 17 | Clock object. 18 | """ 19 | 20 | _wnd = None 21 | 22 | def __init__(self): 23 | """ 24 | Initialize clock object. 25 | """ 26 | self._time = self.time() 27 | self._time_init = self._time 28 | self._time_diff = 0 29 | self._framerate = 0 30 | 31 | def get_time(self): 32 | """ 33 | Return time (in ms) between last two calls to tick(). 34 | """ 35 | return self._time_diff 36 | 37 | def tick(self, framerate=0): 38 | """ 39 | Call once per program cycle. 40 | 41 | An optional framerate will add pause to limit rate. 42 | Returns ms since last call. 43 | """ 44 | if self._framerate != framerate and not env.canvas._pause: 45 | self._framerate = framerate 46 | if framerate: 47 | env.canvas._framerate = 1000.0 / framerate 48 | else: 49 | env.canvas._framerate = 0.0 50 | self._time = self.time() 51 | self._time_diff = self._time - self._time_init 52 | self._time_init = self._time 53 | return self._time_diff 54 | 55 | def tick_busy_loop(self, framerate=0): 56 | """ 57 | Call once per program cycle. 58 | 59 | An optional framerate will add pause to limit rate. 60 | Returns ms since last call. 61 | """ 62 | return self.tick(framerate) 63 | 64 | def get_fps(self): 65 | """ 66 | Return fps. 67 | """ 68 | if not env.canvas._pause: 69 | return 1000.0 / env.canvas._frametime 70 | else: 71 | return 0.0 72 | 73 | def time(self): 74 | """ 75 | Return system time (in ms). 76 | """ 77 | return self._wnd.performance.now() 78 | 79 | 80 | class Time(object): 81 | """ 82 | Time object. 83 | """ 84 | 85 | _wnd = None 86 | 87 | def __init__(self): 88 | """ 89 | Initialize time object. 90 | """ 91 | self.Clock = Clock 92 | Time._wnd = performanceNowInit() 93 | Clock._wnd = Time._wnd 94 | self._time_init = self.time() 95 | self._framerate = 0 96 | self._timers = {} 97 | self.run = lambda: self.wait() 98 | 99 | def get_ticks(self): 100 | """ 101 | Get time ticks. 102 | 103 | Return ms since program start. 104 | """ 105 | return self.time() - self._time_init 106 | 107 | def delay(self, time): 108 | """ 109 | Time delay. 110 | 111 | Pause for given time (in ms). Return ms paused. 112 | Suspends the program, preferably use time.wait. 113 | """ 114 | start = self.time() 115 | while True: 116 | if self.time() - start > time: 117 | return self.time() - start 118 | 119 | def wait(self, time=0): 120 | """ 121 | Wait function. 122 | 123 | Timeout program callback for given time (in ms). 124 | """ 125 | if time: 126 | if not env.canvas._pause: 127 | self._framerate = env.canvas._framerate 128 | env.canvas._framerate = time * 10 129 | env.canvas._pause = True 130 | self.set_timeout(self, time) 131 | else: 132 | if env.canvas._pause: 133 | env.canvas._framerate = self._framerate 134 | env.canvas._rendertime = self.time() 135 | env.canvas._pause = False 136 | return time 137 | 138 | def set_timer(self, event, time, once=False): 139 | """ 140 | Set timer. 141 | 142 | Post event on queue at time (ms) intervals. 143 | Optional argument once set no timer repeat, defaults to False. 144 | Disable timer with time of 0. 145 | """ 146 | if hasattr(event, 'type'): 147 | eventType = event.type 148 | if eventType not in self._timers: 149 | self._timers[eventType] = _EventTimer(event) 150 | else: 151 | eventType = event 152 | if eventType not in self._timers: 153 | evt = env.event.Event(eventType) 154 | self._timers[eventType] = _EventTimer(evt) 155 | repeat = not once 156 | self._timers[eventType].set_timer(time, repeat) 157 | 158 | def _stop_timers(self): 159 | for eventType in self._timers: 160 | self._timers[eventType].set_timer(0, False) 161 | 162 | def time(self): 163 | """ 164 | Return system time (in ms). 165 | """ 166 | return self._wnd.performance.now() 167 | 168 | def set_timeout(self, obj, time): 169 | """ 170 | Set timeout. 171 | 172 | Timeout time (in ms) before triggering obj.run method. 173 | Return timer id. 174 | """ 175 | run = lambda: obj.run() 176 | id = JS("$wnd['setTimeout'](@{{run}}, @{{time}});") 177 | return id 178 | 179 | def clear_timeout(self, id): 180 | """ 181 | Clear timeout. 182 | 183 | Argument timer id of set_timeout. 184 | """ 185 | JS("$wnd['clearTimeout'](@{{id}});") 186 | return None 187 | 188 | def set_interval(self, obj, time): 189 | """ 190 | Set interval timeout. 191 | 192 | Recurring timeout time (in ms) before triggering obj.run method. 193 | Return timer id. 194 | """ 195 | run = lambda: obj.run() 196 | id = JS("$wnd['setInterval'](@{{run}}, @{{time}});") 197 | return id 198 | 199 | def clear_interval(self, id): 200 | """ 201 | Clear interval timeout. 202 | 203 | Argument timer id of set_interval. 204 | """ 205 | JS("$wnd['clearInterval'](@{{id}});") 206 | return None 207 | 208 | 209 | class _EventTimer: 210 | 211 | def __init__(self, event): 212 | self.event = event 213 | self.timer = None 214 | self.time = 0 215 | self.repeat = True 216 | 217 | def set_timer(self, time, repeat): 218 | if self.timer: 219 | self.repeat = False 220 | self.clear_timeout() 221 | if time: 222 | self.time = time 223 | self.repeat = repeat 224 | self.set_timeout() 225 | 226 | def set_timeout(self): 227 | run = lambda: self.run() 228 | timer = JS("$wnd['setTimeout'](@{{run}}, @{{self}}['time']);") 229 | self.timer = timer 230 | 231 | def clear_timeout(self): 232 | JS("$wnd['clearTimeout'](@{{self}}['timer']);") 233 | self.timer = None 234 | 235 | def run(self): 236 | env.event.post(self.event) 237 | if self.repeat: 238 | self.set_timeout() 239 | 240 | -------------------------------------------------------------------------------- /pyjsdl/transform.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Transform module** 6 | 7 | The module provides surface transformation functionality. 8 | """ 9 | 10 | from math import pi as _pi, fabs as _fabs, sin as _sin, cos as _cos, ceil as _ceil 11 | from pyjsdl.surface import Surface 12 | 13 | 14 | _deg_rad = _pi/180.0 15 | 16 | 17 | def rotate(surface, angle): 18 | """ 19 | Return Surface rotated by the given angle. 20 | """ 21 | if not angle: 22 | return surface.copy() 23 | theta = angle * _deg_rad 24 | width_i = surface.get_width() 25 | height_i = surface.get_height() 26 | cos_theta = _fabs( _cos(theta) ) 27 | sin_theta = _fabs( _sin(theta) ) 28 | width_f = int( (width_i * cos_theta) + (height_i * sin_theta) ) 29 | height_f = int( (width_i * sin_theta) + (height_i * cos_theta) ) 30 | surf = Surface((width_f, height_f)) 31 | surf.saveContext() 32 | surf.translate(width_f/2.0, height_f/2.0) 33 | surf.rotate(-theta) 34 | surf.drawImage(surface.canvas, -width_i/2, -height_i/2) 35 | surf.restoreContext() 36 | surf._colorkey = surface._colorkey 37 | surf._alpha = surface._alpha 38 | return surf 39 | 40 | 41 | def rotozoom(surface, angle, size): 42 | """ 43 | Return Surface rotated and resized by the given angle and size. 44 | """ 45 | if not angle: 46 | width = int(surface.get_width() * size) 47 | height = int(surface.get_height() * size) 48 | return scale(surface, (width, height)) 49 | theta = angle * _deg_rad 50 | width_i = int(surface.get_width() * size) 51 | height_i = int(surface.get_height() * size) 52 | cos_theta = _fabs( _cos(theta) ) 53 | sin_theta = _fabs( _sin(theta) ) 54 | width_f = int( _ceil((width_i * cos_theta) + (height_i * sin_theta)) ) 55 | if width_f % 2: 56 | width_f += 1 57 | height_f = int( _ceil((width_i * sin_theta) + (height_i * cos_theta)) ) 58 | if height_f % 2: 59 | height_f += 1 60 | surf = Surface((width_f, height_f)) 61 | surf.saveContext() 62 | surf.translate(width_f/2.0, height_f/2.0) 63 | surf.rotate(-theta) 64 | surf.drawImage(surface.canvas, 65 | 0, 0, surface.get_width(), surface.get_height(), 66 | -width_i/2, -height_i/2, width_i, height_i) 67 | surf.restoreContext() 68 | surf._colorkey = surface._colorkey 69 | surf._alpha = surface._alpha 70 | return surf 71 | 72 | 73 | def scale(surface, size, dest=None): 74 | """ 75 | Return Surface resized by the given size. 76 | 77 | An optional destination surface can be provided. 78 | """ 79 | if not dest: 80 | surf = Surface(size) 81 | else: 82 | surf = dest 83 | surf.drawImage(surface.canvas, 84 | 0, 0, surface.get_width(), surface.get_height(), 85 | 0, 0, size[0], size[1]) 86 | surf._colorkey = surface._colorkey 87 | surf._alpha = surface._alpha 88 | return surf 89 | 90 | 91 | def smoothscale(surface, size): 92 | """ 93 | Return Surface resized by the given size. 94 | 95 | An optional destination surface can be provided. 96 | Calls scale(). 97 | """ 98 | return scale(surface, size) 99 | 100 | 101 | def scale2x(surface, dest=None): 102 | """ 103 | Return Surface resized to twice its size. 104 | 105 | An optional destination surface can be provided. 106 | """ 107 | return scale(surface, 108 | (surface.get_width()*2, surface.get_height()*2), dest) 109 | 110 | 111 | def flip(surface, xbool=True, ybool=False): 112 | """ 113 | Return Surface that is flipped horizontally, vertically, or both. 114 | """ 115 | surf = Surface((surface.get_width(), surface.get_height())) 116 | surf.saveContext() 117 | if xbool and ybool: 118 | surf.translate(surface.get_width(), surface.get_height()) 119 | surf.scale(-1, -1) 120 | elif xbool: 121 | surf.translate(surface.get_width(), 0) 122 | surf.scale(-1, 1) 123 | elif ybool: 124 | surf.translate(0, surface.get_height()) 125 | surf.scale(1, -1) 126 | surf.drawImage(surface.canvas, 0, 0) 127 | surf.restoreContext() 128 | surf._colorkey = surface._colorkey 129 | surf._alpha = surface._alpha 130 | return surf 131 | 132 | -------------------------------------------------------------------------------- /pyjsdl/util.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | """ 5 | **Util module** 6 | 7 | The module provides profiling functionality. 8 | """ 9 | 10 | from pyjsdl.time import Time 11 | from pyjsdl.rect import Rect 12 | from pyjsdl import env 13 | from __pyjamas__ import JS, doc 14 | 15 | 16 | class Timer(object): 17 | """ 18 | Simple profiling timer. 19 | 20 | Output log can be directed to 'console' or to 'textarea'. 21 | If output is to textarea, may specify log length. 22 | """ 23 | 24 | def __init__(self, log='console', log_length=5): 25 | """ 26 | Initialize timer object. 27 | """ 28 | self.time = Time() 29 | self.time_i = self.get_time() 30 | self.dtime = [] 31 | self.number = 0 32 | self.log = None 33 | self.log_list = None 34 | self.log_num = 0 35 | self.log_scroll = True 36 | self.set_log(log, log_length) 37 | 38 | def get_time(self): 39 | """ 40 | Get current time. 41 | """ 42 | return self.time.time() 43 | 44 | def set_time(self): 45 | """ 46 | Set current time. 47 | """ 48 | self.time_i = self.get_time() 49 | 50 | def lap_time(self, time_i=None, time_f=None, number=100, print_result=True): 51 | """ 52 | Time lapsed since previous set_time. 53 | 54 | Optional arguments time_i and time_f, number of calls to average, and print_results to output. 55 | Return lapsed time on completion. 56 | """ 57 | if time_i is None: 58 | time_i = self.time_i 59 | if time_f is None: 60 | time_f = self.get_time() 61 | self.dtime.append(time_f-time_i) 62 | self.number += 1 63 | if self.number >= number: 64 | t_ave = ( sum(self.dtime)/number ) 65 | self.dtime = [] 66 | self.number = 0 67 | if print_result: 68 | if self.log_type == 'console': 69 | self.log_num += 1 70 | entry = "Time %d: %s" % (self.log_num, t_ave) 71 | print(entry) 72 | else: 73 | self.log_num += 1 74 | entry = "Time %d: %s" % (self.log_num, t_ave) 75 | self.print_log(entry) 76 | return t_ave 77 | 78 | def set_log(self, log, log_length=5): 79 | """ 80 | Set log output. 81 | 82 | Argument log can be 'console' or 'textarea'. 83 | """ 84 | if log in ('console','textarea'): 85 | self.log_type = log 86 | if log == 'textarea': 87 | if not self.log: 88 | size = env.canvas.surface.width-5, 102 89 | self.log = env.canvas.surface._display.Textarea(size) 90 | self.log.setReadonly(True) 91 | self.log.addMouseListener(self) 92 | self.onMouseUp = lambda sender,x,y: None 93 | self.onMouseMove = lambda sender,x,y: None 94 | self.onMouseEnter = lambda sender: None 95 | self.log_list = ['' for i in range(log_length)] 96 | self.log.toggle(True) 97 | else: 98 | if self.log: 99 | self.log.toggle(False) 100 | self.log_list = [] 101 | 102 | def onMouseDown(self, sender, x, y): 103 | """ 104 | Control log scroll. 105 | """ 106 | self.log_scroll = False 107 | 108 | def onMouseLeave(self, sender): 109 | """ 110 | Control log scroll. 111 | """ 112 | self.log_scroll = True 113 | 114 | def print_log(self, text): 115 | """ 116 | Print text to output. 117 | """ 118 | if self.log_type == 'console': 119 | print(text) 120 | else: 121 | self.log_list.pop(0) 122 | self.log_list.append(text+'\n') 123 | text = ''.join(self.log_list) 124 | self.log.setText(text) 125 | if self.log_scroll: 126 | self.log.setCursorPos(len(text)) 127 | 128 | 129 | class PyjsMode: 130 | """ 131 | Check Pyjs mode used to compile application. 132 | 133 | Attributes: 134 | strict/optimized to specify mode 135 | getattr_call/eq_call to specify functionality 136 | """ 137 | def __init__(self): 138 | self.getattr_call = self.test_getattr() 139 | self.eq_call = self.test_eq() 140 | self.strict, self.optimized = self.test_mode() 141 | 142 | def test_mode(self): 143 | """ 144 | Test if build mode is strict or optimized. 145 | """ 146 | if self.getattr_call and self.eq_call: 147 | return True, False 148 | else: 149 | return False, True 150 | 151 | def test_getattr(self): 152 | """ 153 | Test if object __getattr__ method is called. 154 | """ 155 | return (Rect(0,0,20,20).center == (10,10)) 156 | 157 | def test_eq(self): 158 | """ 159 | Test if object __eq__ method is called. 160 | """ 161 | return (Rect(0,0,20,20) == Rect(0,0,20,20)) 162 | 163 | env.set_env('pyjs_mode', PyjsMode()) 164 | 165 | 166 | def call(obj, func, args=()): 167 | """ 168 | Call unbound method. 169 | 170 | Argument obj is the object, func is the unbound method, and optional args is a tuple of arguments for the method. 171 | Returns the method's return value. 172 | """ 173 | return JS("@{{func}}.apply(@{{obj}}, @{{args}}['getArray']());") 174 | 175 | 176 | def createEvent(eventObject, eventType, eventOptions=None): 177 | """ 178 | Create JavaScript event. 179 | 180 | For instance: 181 | MouseEvent type 'mousedown', handled as a MouseButtonDown event. 182 | PageTransitionEvent type 'pagehide', handled as a Quit event. 183 | Default options are {'bubbles':True, 'cancelable':True}. 184 | """ 185 | opt = {'bubbles':True, 'cancelable':True} 186 | if eventOptions is not None: 187 | for key in eventOptions: 188 | opt[key] = eventOptions[key] 189 | options = JS("{}") 190 | for key in opt: 191 | val = opt[key] 192 | JS("@{{options}}[@{{key}}] = @{{val}}") 193 | event = JS("new @{{eventObject}}(@{{eventType}}, @{{options}})") 194 | return event 195 | 196 | 197 | def dispatchEvent(event, element=None): 198 | """ 199 | Dispatch JavaScript event. 200 | 201 | The event is dispatched from the element. 202 | Default element is the canvas. 203 | """ 204 | if element is None: 205 | element = doc().getElementsByTagName('canvas').item(0) 206 | if element is None: 207 | return False 208 | element.dispatchEvent(event) 209 | return True 210 | 211 | 212 | #code modified from pyjs 213 | class _dict(dict): 214 | 215 | def values(self): 216 | return dict.values(self).__iter__() 217 | 218 | def keys(self): 219 | return dict.__iter__(self) 220 | 221 | def items(self): 222 | return dict.items(self).__iter__() 223 | 224 | 225 | def _next(obj): 226 | return obj.next() 227 | 228 | 229 | try: 230 | _range = xrange 231 | except NameError: 232 | _range = range 233 | 234 | -------------------------------------------------------------------------------- /pyjsdl/version.py: -------------------------------------------------------------------------------- 1 | #Pyjsdl - Copyright (C) 2013 James Garnon 2 | #Released under the MIT License 3 | 4 | from pyjsdl import __version__ 5 | 6 | 7 | ver = __version__ 8 | "Module version" 9 | 10 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggatc/pyjsdl/1ba9e5ca68807bd73cc03e933e931bc08f28e934/test/__init__.py -------------------------------------------------------------------------------- /test/color_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | env = None 4 | pg = None 5 | 6 | 7 | def init(environ): 8 | global env, pg 9 | env = environ 10 | pg = env['pg'] 11 | tests = [test_color_constructor, 12 | test_color_update, 13 | test_color_get, 14 | test_color_comparison, 15 | test_color_operator, 16 | test_color_transform, 17 | test_color_conversion] 18 | return tests 19 | 20 | 21 | def _color_convert(color): 22 | if isinstance(color, tuple): 23 | if len(color) == 4: 24 | r, g, b, a = color[0], color[1], color[2], color[3] 25 | else: 26 | r, g, b, a = color[0], color[1], color[2], 255 27 | else: 28 | r, g, b, a = int((color>>16) & 0xff), int((color>>8) & 0xff), int(color & 0xff), int((color>>24) & 0xff) 29 | return r, g, b, a 30 | 31 | 32 | def _attr(color): 33 | return color.r, color.g, color.b, color.a 34 | 35 | 36 | def _rd(v1, v2, v3, v4=None): 37 | if v4 is None: 38 | return round(v1, 3), round(v2, 3), round(v3, 3) 39 | else: 40 | return round(v1, 3), round(v2, 3), round(v3, 3), round(v4, 3) 41 | 42 | 43 | def test_color_constructor(): 44 | c = pg.Color(255, 0, 0) 45 | r, g, b, a = _color_convert((255, 0, 0)) 46 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 47 | c = pg.Color(0, 255, 0, 255) 48 | r, g, b, a = _color_convert((0, 255, 0, 255)) 49 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 50 | if env['platform'] in ('jvm', 'js'): #pg: integer is rgba, not argb 51 | c = pg.Color((0xff<<24)+255) 52 | r, g, b, a = _color_convert((0xff<<24)+255) 53 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 54 | if env['platform'] in ['jvm', 'pc']: 55 | c = pg.Color((255, 0, 0)) 56 | r, g, b, a = _color_convert((255, 0, 0)) 57 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 58 | c = pg.Color((0, 255, 0, 255)) 59 | r, g, b, a = _color_convert((0, 255, 0, 255)) 60 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 61 | c = pg.Color('0xff0000') 62 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 63 | c = pg.Color('0xff0000ff') 64 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 65 | c = pg.Color('#ff0000') 66 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 67 | c = pg.Color('#ff0000ff') 68 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 69 | 70 | 71 | def test_color_update(): 72 | c = pg.Color(0, 0, 0) 73 | c.update(255, 0, 0) 74 | r, g, b, a = _color_convert((255, 0, 0)) 75 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 76 | c.update(0, 255, 0, 255) 77 | r, g, b, a = _color_convert((0, 255, 0, 255)) 78 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 79 | if env['platform'] in ('jvm', 'js'): #pg: integer is rgba, not argb 80 | c.update((0xff<<24)+255) 81 | r, g, b, a = _color_convert((0xff<<24)+255) 82 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 83 | if env['platform'] in ['jvm', 'pc']: 84 | c.update((255, 0, 0)) 85 | r, g, b, a = _color_convert((255, 0, 0)) 86 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 87 | c.update((0, 255, 0, 255)) 88 | r, g, b, a = _color_convert((0, 255, 0, 255)) 89 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 90 | c = pg.Color('0xff0000') 91 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 92 | c = pg.Color('0xff0000ff') 93 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 94 | c = pg.Color('#ff0000') 95 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 96 | c = pg.Color('#ff0000ff') 97 | assert (c.r, c.g, c.b, c.a) == (255, 0, 0, 255) 98 | 99 | 100 | def test_color_get(): 101 | c = pg.Color(0, 255, 0, 255) 102 | r, g, b, a = 0, 255, 0, 255 103 | assert (c.r, c.g, c.b, c.a) == (r, g, b, a) 104 | assert (c[0], c[1], c[2], c[3]) == (r, g, b, a) 105 | if env['platform'] == 'jvm': #java.awt.Color 106 | assert (c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()) == (r, g, b, a) 107 | if not (env['platform'] == 'jvm' and sys.version_info[1] < 5): 108 | #jy2.2.1 issue with property assignment 109 | c[0] = 10 110 | c.g = 20 111 | assert (c.r, c.g, c.b, c.a) == (10, 20, b, a) 112 | assert (c[0], c[1], c[2], c[3]) == (10, 20, b, a) 113 | if env['platform'] == 'jvm': #java.awt.Color 114 | assert (c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()) == (10, 20, b, a) 115 | c = pg.Color(50, 100, 150, 200) 116 | for i, _c in enumerate(c): 117 | assert _c == {0:50, 1:100, 2:150, 3:200}[i] 118 | assert [_c for _c in c] == [50, 100, 150, 200] 119 | 120 | 121 | def test_color_comparison(): 122 | c = pg.Color(255, 0, 0, 255) 123 | c1 = pg.Color(255, 0, 0, 255) 124 | c2 = pg.Color(0, 0, 0, 255) 125 | c3 = pg.Color(255, 0, 0) 126 | c4 = pg.Color(0, 0, 0) 127 | if env['executor'] != 'pyjs': 128 | #pyjs compares rect==tuple not __eq__ 129 | assert c == (255, 0, 0, 255) 130 | assert c != (0, 0, 0, 255) 131 | assert c == (255, 0, 0) 132 | assert c != (0, 0, 0) 133 | if not env['pyjs_opt']: 134 | #pyjs -O __eq__ ignored 135 | assert c == pg.Color(255, 0, 0, 255) 136 | assert c != pg.Color(0, 0, 0, 255) 137 | assert c == pg.Color(255, 0, 0) 138 | assert c != pg.Color(0, 0, 0) 139 | assert c == c1 140 | assert c != c2 141 | assert c == c3 142 | assert c != c4 143 | assert c.r==c1.r and c.g==c1.g and c.b==c1.b and c.a==c1.a 144 | assert c.r!=c2.r and c.g==c2.g and c.b==c2.b and c.a==c2.a 145 | assert c.r==c3.r and c.g==c3.g and c.b==c3.b and c.a==c3.a 146 | assert c.r!=c4.r and c.g==c4.g and c.b==c4.b and c.a==c4.a 147 | 148 | 149 | def test_color_operator(): 150 | if (env['pyjs_opt'] and not env['pyjs_attr']): #special methods ignored 151 | raise NotImplementedError 152 | if not env['pyjs_opt']: 153 | c1 = pg.Color(60, 220, 120, 100) 154 | c2 = pg.Color(40, 40, 40, 255) 155 | c3 = pg.Color(255, 0, 0, 100) 156 | assert c1 + c2 == pg.Color(100, 255, 160, 255) 157 | assert c1 + c3 == pg.Color(255, 220, 120, 200) 158 | assert c1 - c2 == pg.Color(20, 180, 80, 0) 159 | assert c1 - c3 == pg.Color(0, 220, 120, 0) 160 | assert c1 * c2 == pg.Color(255, 255, 255, 255) 161 | assert c1 * c3 == pg.Color(255, 0, 0, 255) 162 | assert c1 // c2 == pg.Color(1, 5, 3, 0) 163 | assert c1 // c3 == pg.Color(0, 0, 0, 1) 164 | assert c1 % c2 == pg.Color(20, 20, 0, 100) 165 | assert c1 % c3 == pg.Color(60, 0, 0, 0) 166 | c = pg.Color(0, 10, 20, 255) 167 | c += c1 168 | assert c == pg.Color(60, 230, 140, 255) 169 | c = pg.Color(0, 10, 20, 255) 170 | c -= c1 171 | assert c == pg.Color(0, 0, 0, 155) 172 | c = pg.Color(0, 10, 20, 255) 173 | c *= c1 174 | assert c == pg.Color(0, 255, 255, 255) 175 | c = pg.Color(0, 10, 20, 255) 176 | c //= c1 177 | assert c == pg.Color(0, 0, 0, 2) 178 | c = pg.Color(0, 10, 20, 255) 179 | c %= c1 180 | assert c == pg.Color(0, 10, 20, 55) 181 | c = pg.Color(0, 10, 20, 255) 182 | assert ~c == pg.Color(255, 245, 235, 0) 183 | if (env['pyjs_opt'] and env['pyjs_attr']): 184 | c1 = pg.Color(60, 220, 120, 100) 185 | c2 = pg.Color(40, 40, 40, 255) 186 | c3 = pg.Color(255, 0, 0, 100) 187 | assert _attr(c1 + c2) == (100, 255, 160, 255) 188 | assert _attr(c1 + c3) == (255, 220, 120, 200) 189 | assert _attr(c1 - c2) == (20, 180, 80, 0) 190 | assert _attr(c1 - c3) == (0, 220, 120, 0) 191 | assert _attr(c1 * c2) == (255, 255, 255, 255) 192 | assert _attr(c1 * c3) == (255, 0, 0, 255) 193 | assert _attr(c1 // c2) == (1, 5, 3, 0) 194 | assert _attr(c1 // c3) == (0, 0, 0, 1) 195 | assert _attr(c1 % c2) == (20, 20, 0, 100) 196 | assert _attr(c1 % c3) == (60, 0, 0, 0) 197 | c = pg.Color(0, 10, 20, 255) 198 | c += c1 199 | assert _attr(c) == (60, 230, 140, 255) 200 | c = pg.Color(0, 10, 20, 255) 201 | c -= c1 202 | assert _attr(c) == (0, 0, 0, 155) 203 | c = pg.Color(0, 10, 20, 255) 204 | c *= c1 205 | assert _attr(c) == (0, 255, 255, 255) 206 | c = pg.Color(0, 10, 20, 255) 207 | c //= c1 208 | assert _attr(c) == (0, 0, 0, 2) 209 | c = pg.Color(0, 10, 20, 255) 210 | c %= c1 211 | assert _attr(c) == (0, 10, 20, 55) 212 | c = pg.Color(0, 10, 20, 255) 213 | assert _attr(c.__invert__()) == (255, 245, 235, 0) #pyjs -O error with ~ 214 | 215 | 216 | def test_color_transform(): 217 | if not env['pyjs_opt']: 218 | c = pg.Color(0, 10, 100, 100) 219 | nr, ng, nb, na = c.normalize() 220 | assert (_rd( nr, ng, nb, na )) == (0.0, 0.039, 0.392, 0.392) 221 | c = pg.Color(255, 0, 100, 150) 222 | nc = c.normalize() 223 | nr, ng, nb, na = nc 224 | assert (_rd( nr, ng, nb, na )) == (1.0, 0.0, 0.392, 0.588) 225 | c = pg.Color(0, 10, 100, 100) 226 | assert c.premul_alpha() == pg.Color(0, 4, 39, 100) 227 | c = pg.Color(255, 0, 100, 150) 228 | assert c.premul_alpha() == pg.Color(150, 0, 59, 150) 229 | c = pg.Color(0, 10, 100, 100) 230 | assert c.correct_gamma(0.1) == pg.Color(0, 184, 232, 232) 231 | assert c.correct_gamma(0.9) == pg.Color(0, 14, 110, 110) 232 | assert c.correct_gamma(2.0) == pg.Color(0, 0, 39, 39) 233 | c = pg.Color(255, 0, 100, 150) 234 | assert c.correct_gamma(0.1) == pg.Color(255, 0, 232, 242) 235 | assert c.correct_gamma(0.9) == pg.Color(255, 0, 110, 158) 236 | assert c.correct_gamma(2.0) == pg.Color(255, 0, 39, 88) 237 | c1 = pg.Color(0, 10, 100, 100) 238 | c2 = pg.Color(0, 20, 200, 100) 239 | assert c1.lerp(c2, 0.0) == pg.Color(0, 10, 100, 100) 240 | assert c1.lerp(c2, 0.3) == pg.Color(0, 13, 130, 100) 241 | assert c1.lerp(c2, 0.5) == pg.Color(0, 15, 150, 100) 242 | assert c1.lerp(c2, 0.7) == pg.Color(0, 17, 170, 100) 243 | assert c1.lerp(c2, 1.0) == pg.Color(0, 20, 200, 100) 244 | c1 = pg.Color(50, 100, 150, 100) 245 | c2 = pg.Color(100, 200, 250, 200) 246 | assert c1.lerp(c2, 0.0) == pg.Color(50, 100, 150, 100) 247 | assert c1.lerp(c2, 0.3) == pg.Color(65, 130, 180, 130) 248 | assert c1.lerp(c2, 0.5) == pg.Color(75, 150, 200, 150) 249 | assert c1.lerp(c2, 0.7) == pg.Color(85, 170, 220, 170) 250 | assert c1.lerp(c2, 1.0) == pg.Color(100, 200, 250, 200) 251 | c1 = pg.Color(255, 0, 100, 150) 252 | c2 = (50, 100, 150, 200) 253 | assert c1.lerp(c2, 0.5) == pg.Color(153, 50, 125, 175) 254 | c1 = pg.Color(255, 0, 100, 150) 255 | c2 = (50, 100, 150) 256 | assert c1.lerp(c2, 0.5) == pg.Color(153, 50, 125, 203) 257 | if (env['pyjs_opt'] and env['pyjs_attr']): 258 | c = pg.Color(0, 10, 100, 100) 259 | nr, ng, nb, na = c.normalize() 260 | assert (_rd( nr, ng, nb, na )) == (0.0, 0.039, 0.392, 0.392) 261 | c = pg.Color(255, 0, 100, 150) 262 | nc = c.normalize() 263 | nr, ng, nb, na = nc 264 | assert (_rd( nr, ng, nb, na )) == (1.0, 0.0, 0.392, 0.588) 265 | c = pg.Color(0, 10, 100, 100) 266 | assert _attr(c.premul_alpha()) == (0, 4, 39, 100) 267 | c = pg.Color(255, 0, 100, 150) 268 | assert _attr(c.premul_alpha()) == (150, 0, 59, 150) 269 | c = pg.Color(0, 10, 100, 100) 270 | assert _attr(c.correct_gamma(0.1)) == (0, 184, 232, 232) 271 | assert _attr(c.correct_gamma(0.9)) == (0, 14, 110, 110) 272 | assert _attr(c.correct_gamma(2.0)) == (0, 0, 39, 39) 273 | c = pg.Color(255, 0, 100, 150) 274 | assert _attr(c.correct_gamma(0.1)) == (255, 0, 232, 242) 275 | assert _attr(c.correct_gamma(0.9)) == (255, 0, 110, 158) 276 | assert _attr(c.correct_gamma(2.0)) == (255, 0, 39, 88) 277 | c1 = pg.Color(0, 10, 100, 100) 278 | c2 = pg.Color(0, 20, 200, 100) 279 | assert _attr(c1.lerp(c2, 0.0)) == (0, 10, 100, 100) 280 | assert _attr(c1.lerp(c2, 0.3)) == (0, 13, 130, 100) 281 | assert _attr(c1.lerp(c2, 0.5)) == (0, 15, 150, 100) 282 | assert _attr(c1.lerp(c2, 0.7)) == (0, 17, 170, 100) 283 | assert _attr(c1.lerp(c2, 1.0)) == (0, 20, 200, 100) 284 | c1 = pg.Color(50, 100, 150, 100) 285 | c2 = pg.Color(100, 200, 250, 200) 286 | assert _attr(c1.lerp(c2, 0.0)) == (50, 100, 150, 100) 287 | assert _attr(c1.lerp(c2, 0.3)) == (65, 130, 180, 130) 288 | assert _attr(c1.lerp(c2, 0.5)) == (75, 150, 200, 150) 289 | assert _attr(c1.lerp(c2, 0.7)) == (85, 170, 220, 170) 290 | assert _attr(c1.lerp(c2, 1.0)) == (100, 200, 250, 200) 291 | c1 = pg.Color(255, 0, 100, 150) 292 | c2 = (50, 100, 150, 200) 293 | assert _attr(c1.lerp(c2, 0.5)) == (153, 50, 125, 175) 294 | c1 = pg.Color(255, 0, 100, 150) 295 | c2 = (50, 100, 150) 296 | assert _attr(c1.lerp(c2, 0.5)) == (153, 50, 125, 203) 297 | 298 | 299 | def test_color_conversion(): 300 | if (env['pyjs_opt'] and not env['pyjs_attr']): #property ignored 301 | c = pg.Color(255, 0, 0) 302 | assert _attr(c) == (255, 0, 0, 255) 303 | assert c._get_cmy() == (0.0, 1.0, 1.0) 304 | assert c._get_hsva() == (0.0, 100.0, 100.0, 100.0) 305 | assert c._get_hsla() == (0.0, 100.0, 50.0, 100.0) 306 | c._set_cmy((0.1, 0.2, 0.3)) 307 | _c, _m, _y = c._get_cmy() 308 | assert (_rd( _c, _m, _y )) == (0.102, 0.2, 0.302) 309 | assert _attr(c) == (229, 204, 178, 255) 310 | c._set_hsva((360.0, 100.0, 100.0, 100.0)) 311 | assert c._get_hsva() == (0.0, 100.0, 100.0, 100.0) 312 | assert _attr(c) == (255, 0, 0, 255) 313 | c._set_hsla((50.0, 50.0, 50.0, 50.0)) 314 | h, s, l, a = c._get_hsla() 315 | assert (_rd( h, s, l, a )) == (50.156, 50.394, 49.804, 49.804) 316 | assert _attr(c) == (191, 170, 63, 127) 317 | return 318 | c = pg.Color(255, 0, 0) 319 | if not env['pyjs_opt']: 320 | assert c == pg.Color(255, 0, 0, 255) 321 | else: 322 | assert _attr(c) == (255, 0, 0, 255) 323 | assert c.cmy == (0.0, 1.0, 1.0) 324 | assert c.hsva == (0.0, 100.0, 100.0, 100.0) 325 | assert c.hsla == (0.0, 100.0, 50.0, 100.0) 326 | c = pg.Color(10, 100, 200, 200) 327 | _c, _m, _y = c.cmy 328 | assert (_rd( _c, _m, _y )) == (0.961, 0.608, 0.216) 329 | h, s, v, a = c.hsva 330 | assert (_rd( h, s, v, a )) == (211.579, 95.0, 78.431, 78.431) 331 | h, s, l, a = c.hsla 332 | assert (_rd( h, s, l, a )) == (211.579, 90.476, 41.176, 78.431) 333 | if env['platform'] == 'jvm' and sys.version_info < (2,5): 334 | #jy2.2.1 issue with property assignment 335 | return 336 | c = pg.Color(255, 0, 0) 337 | assert c.cmy == (0.0, 1.0, 1.0) 338 | c.cmy = (0.1, 0.2, 0.3) 339 | _c, _m, _y = c.cmy 340 | assert (_rd( _c, _m, _y )) == (0.102, 0.2, 0.302) 341 | if not env['pyjs_opt']: 342 | assert c == pg.Color(229, 204, 178, 255) 343 | else: 344 | assert _attr(c) == (229, 204, 178, 255) 345 | c.cmy = (0.5, 0.5, 0.5) 346 | _c, _m, _y = c.cmy 347 | assert (_rd( _c, _m, _y )) == (0.502, 0.502, 0.502) 348 | if not env['pyjs_opt']: 349 | assert c == pg.Color(127, 127, 127, 255) 350 | else: 351 | assert _attr(c) == (127, 127, 127, 255) 352 | c.cmy = (0.0, 0.5, 1.0) 353 | _c, _m, _y = c.cmy 354 | assert (_rd( _c, _m, _y )) == (0.0, 0.502, 1.0) 355 | if not env['pyjs_opt']: 356 | assert c == pg.Color(255, 127, 0, 255) 357 | else: 358 | assert _attr(c) == (255, 127, 0, 255) 359 | c = pg.Color(255, 0, 0) 360 | assert c.hsva == (0.0, 100.0, 100.0, 100.0) 361 | c.hsva = (360.0, 100.0, 100.0, 100.0) 362 | assert c.hsva == (0.0, 100.0, 100.0, 100.0) 363 | if not env['pyjs_opt']: 364 | assert c == pg.Color(255, 0, 0, 255) 365 | else: 366 | assert _attr(c) == (255, 0, 0, 255) 367 | c.hsva = (60.0, 100.0, 100.0, 100.0) 368 | assert c.hsva == (60.0, 100.0, 100.0, 100.0) 369 | if not env['pyjs_opt']: 370 | assert c == pg.Color(255, 255, 0, 255) 371 | else: 372 | assert _attr(c) == (255, 255, 0, 255) 373 | c.hsva = (120.0, 100.0, 100.0, 100.0) 374 | assert c.hsva == (120.0, 100.0, 100.0, 100.0) 375 | if not env['pyjs_opt']: 376 | assert c == pg.Color(0, 255, 0, 255) 377 | else: 378 | assert _attr(c) == (0, 255, 0, 255) 379 | c.hsva = (0.0, 0.0, 0.0, 100.0) 380 | assert c.hsva == (0.0, 0.0, 0.0, 100.0) 381 | if not env['pyjs_opt']: 382 | assert c == pg.Color(0, 0, 0, 255) 383 | else: 384 | assert _attr(c) == (0, 0, 0, 255) 385 | c.hsva = (10.0, 100.0, 100.0, 100.0) 386 | h, s, v, a = c.hsva 387 | assert (_rd( h, s, v, a )) == (9.882, 100.0, 100.0, 100.0) 388 | if not env['pyjs_opt']: 389 | assert c == pg.Color(255, 42, 0, 255) 390 | else: 391 | assert _attr(c) == (255, 42, 0, 255) 392 | c = pg.Color(255, 0, 0) 393 | assert c.hsla == (0.0, 100.0, 50.0, 100.0) 394 | c.hsla = (360.0, 100.0, 100.0, 100.0) 395 | assert c.hsla == (0.0, 0.0, 100.0, 100.0) 396 | if not env['pyjs_opt']: 397 | assert c == pg.Color(255, 255, 255, 255) 398 | else: 399 | assert _attr(c) == (255, 255, 255, 255) 400 | c.hsla = (50.0, 50.0, 50.0, 50.0) 401 | h, s, l, a = c.hsla 402 | assert (_rd( h, s, l, a )) == (50.156, 50.394, 49.804, 49.804) 403 | if not env['pyjs_opt']: 404 | assert c == pg.Color(191, 170, 63, 127) 405 | else: 406 | assert _attr(c) == (191, 170, 63, 127) 407 | c.hsla = (0.0, 10.0, 20.0, 30.0) 408 | h, s, l, a = c.hsla 409 | assert (_rd( h, s, l, a )) == (0.0, 10.891, 19.804, 29.804) 410 | if not env['pyjs_opt']: 411 | assert c == pg.Color(56, 45, 45, 76) 412 | else: 413 | assert _attr(c) == (56, 45, 45, 76) 414 | c.hsla = (10.0, 20.0, 30.0, 40.0) 415 | h, s, l, a = c.hsla 416 | assert (_rd( h, s, l, a )) == (10.0, 19.737, 29.804, 40.0) 417 | if not env['pyjs_opt']: 418 | assert c == pg.Color(91, 66, 61, 102) 419 | else: 420 | assert _attr(c) == (91, 66, 61, 102) 421 | 422 | -------------------------------------------------------------------------------- /test/cursor_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_cursor] 10 | return tests 11 | 12 | 13 | def test_cursor(): 14 | cursor_str = ( '. .', 15 | ' . . ', 16 | ' .. ', 17 | ' .XX. ', 18 | ' .XX. ', 19 | ' .. ', 20 | ' . . ', 21 | '. .' ) 22 | size = (8,8) 23 | hotspot = (0,0) 24 | cursor_dat = pg.cursors.compile(cursor_str) 25 | pg.mouse.set_cursor(size, hotspot, *cursor_dat) 26 | if env['platform'] in ('jvm', 'js'): 27 | data, mask = cursor_dat[0], cursor_dat[1] 28 | cursor_surf = pg.cursors.create_cursor(size, data, mask) 29 | pg.mouse.set_cursor(cursor_surf, hotspot) 30 | cc1 = cursor_surf.get_at((0,0)) 31 | cc2 = cursor_surf.get_at((0,1)) 32 | c1 = pg.Color(255,255,255,255) 33 | c2 = pg.Color(0,0,0,0) 34 | if not env['pyjs_opt']: 35 | assert cc1 == c1 36 | assert cc2 == c2 37 | else: #pyjs -O __eq__ ignored 38 | assert cc1.r==c1.r and cc1.g==c1.g and cc1.b==c1.b and cc1.a==c1.a 39 | assert cc2.r==c2.r and cc2.g==c2.g and cc2.b==c2.b and cc2.a==c2.a 40 | 41 | -------------------------------------------------------------------------------- /test/draw_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | surface = None 4 | 5 | 6 | def init(environ): 7 | global env, pg, surface 8 | env = environ 9 | pg = env['pg'] 10 | surface = env['surface'] 11 | tests = [test_draw_rect, 12 | test_draw_circle, 13 | test_draw_ellipse, 14 | test_draw_arc, 15 | test_draw_polygon, 16 | test_draw_line, 17 | test_draw_lines] 18 | return tests 19 | 20 | 21 | def test_draw_rect(): 22 | data = [((10,6),0), ((10,8),1), ((10,10),1)], (5,8,10,5) 23 | surface.fill((0,0,0)) 24 | rect = pg.draw.rect(surface, (255,0,0), (5,8,10,5)) 25 | for pos in data[0]: 26 | c = surface.get_at(pos[0]) 27 | c = {True:1,False:0}[c.r>0] 28 | assert c == pos[1] 29 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 30 | data = [((10,6),0), ((10,8),1), ((10,10),0)], (5,8,10,5) 31 | surface.fill((0,0,0)) 32 | rect = pg.draw.rect(surface, (255,0,0,255), pg.Rect((5,8,10,5)), 1) 33 | for pos in data[0]: 34 | c = surface.get_at(pos[0]) 35 | c = {True:1,False:0}[c.r>0] 36 | assert c == pos[1] 37 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 38 | data = [((3,0),1), ((3,2),1), ((3,4),0)], (0,0,5,3) 39 | surface.fill((0,0,0)) 40 | rect = pg.draw.rect(surface, (255,0,0), (-5,-2,10,5)) 41 | for pos in data[0]: 42 | c = surface.get_at(pos[0]) 43 | c = {True:1,False:0}[c.r>0] 44 | assert c == pos[1] 45 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 46 | 47 | 48 | def test_draw_circle(): 49 | data = [((10,0),0), ((10,5),1), ((10,10),1)], (5,5,10,10) 50 | surface.fill((0,0,0)) 51 | rect = pg.draw.circle(surface, (255,0,0), (10,10), 5) 52 | data = [((10,0),0), ((10,5),1), ((10,10),0)], (5,5,10,10) 53 | surface.fill((0,0,0)) 54 | rect = pg.draw.circle(surface, (255,0,0,255), (10,10), 5, 1) 55 | for pos in data[0]: 56 | c = surface.get_at(pos[0]) 57 | c = {True:1,False:0}[c.r>0] 58 | assert c == pos[1] 59 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 60 | 61 | 62 | def test_draw_ellipse(): 63 | data = [((10,6),1), ((10,8),1), ((10,10),0)], (5,5,10,5) 64 | surface.fill((0,0,0)) 65 | rect = pg.draw.ellipse(surface, (255,0,0), (5,5,10,5)) 66 | for pos in data[0]: 67 | c = surface.get_at(pos[0]) 68 | c = {True:1,False:0}[c.r>0] 69 | assert c == pos[1] 70 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 71 | 72 | 73 | def test_draw_arc(): 74 | data = [((10,0),0), ((10,5),1), ((10,10),0)], (5,5,11,6) 75 | surface.fill((0,0,0)) 76 | rect = pg.draw.arc(surface, (255,0,0), (5,5,10,10), 0, 3.14) 77 | for pos in data[0]: 78 | c = surface.get_at(pos[0]) 79 | c = {True:1,False:0}[c.r>0] 80 | assert c == pos[1] 81 | if env['platform'] not in ('jvm','js'): 82 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 83 | else: #update to new boundary process 84 | assert (rect.x,rect.y,rect.width,rect.height) == (5,5,10,10) 85 | 86 | 87 | def test_draw_polygon(): 88 | data = [((10,4),0), ((10,6),1), ((10,8),1)], (5,5,11,11) 89 | surface.fill((0,0,0)) 90 | rect = pg.draw.polygon(surface, (255,0,0), ((10,5),(15,15),(5,15))) 91 | for pos in data[0]: 92 | c = surface.get_at(pos[0]) 93 | c = {True:1,False:0}[c.r>0] 94 | assert c == pos[1] 95 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 96 | 97 | 98 | def test_draw_line(): 99 | data = [((10,6),0), ((10,8),1), ((10,10),0)], (5,8,11,1) 100 | surface.fill((0,0,0)) 101 | rect = pg.draw.line(surface, (255,0,0), (5,8), (15,8), 1) 102 | for pos in data[0]: 103 | c = surface.get_at(pos[0]) 104 | c = {True:1,False:0}[c.r>0] 105 | assert c == pos[1] 106 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 107 | 108 | 109 | def test_draw_lines(): 110 | data = [((10,6),0), ((10,8),1), ((10,10),0)], (5,8,11,1) 111 | surface.fill((0,0,0)) 112 | rect = pg.draw.lines(surface, (255,0,0), True, ((7,8),(5,8),(15,8))) 113 | for pos in data[0]: 114 | c = surface.get_at(pos[0]) 115 | c = {True:1,False:0}[c.r>0] 116 | assert c == pos[1] 117 | assert (rect.x,rect.y,rect.width,rect.height) == data[1] 118 | 119 | -------------------------------------------------------------------------------- /test/event_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_event_get, 10 | test_event_poll, 11 | test_event_wait, 12 | test_event_peek, 13 | test_event_clear, 14 | test_event_block, 15 | test_event_post] 16 | return tests 17 | 18 | 19 | def test_event_get(): 20 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.USEREVENT] 21 | event_obj = {} 22 | for evt in events: 23 | event_obj[evt] = pg.event.Event(evt) 24 | pg.event.clear() 25 | assert pg.event.get() == [] 26 | for evt in events: 27 | pg.event.post(event_obj[evt]) 28 | evts = pg.event.get() 29 | assert [e.type for e in evts] == events 30 | for evt in events: 31 | pg.event.post(event_obj[evt]) 32 | evts = pg.event.get(events[0]) 33 | assert [e.type for e in evts] == events[:1] 34 | evts = pg.event.get() 35 | assert [e.type for e in evts] == events[1:] 36 | for evt in events: 37 | pg.event.post(event_obj[evt]) 38 | evts = pg.event.get([events[1],events[2]]) 39 | assert [e.type for e in evts] == events[1:] 40 | evts = pg.event.get() 41 | assert [e.type for e in evts] == events[:1] 42 | 43 | 44 | def test_event_poll(): 45 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.USEREVENT] 46 | event_obj = {} 47 | for evt in events: 48 | event_obj[evt] = pg.event.Event(evt) 49 | pg.event.clear() 50 | assert pg.event.poll().type == pg.NOEVENT 51 | for evt in events: 52 | pg.event.post(event_obj[evt]) 53 | evts = [pg.event.poll() for i in range(len(events))] 54 | assert [e.type for e in evts] == events 55 | 56 | 57 | def test_event_wait(): 58 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.USEREVENT] 59 | event_obj = {} 60 | for evt in events: 61 | event_obj[evt] = pg.event.Event(evt) 62 | pg.event.clear() 63 | for evt in events: 64 | pg.event.post(event_obj[evt]) 65 | evts = [pg.event.wait() for i in range(len(events))] 66 | assert [e.type for e in evts] == events 67 | assert pg.event.get() == [] 68 | if env['platform'] != 'js': #waiting not implemented 69 | pg.time.set_timer(events[0], 30) 70 | evt = pg.event.wait() 71 | assert evt.type == events[0] 72 | pg.time.set_timer(events[0], 0) 73 | 74 | 75 | def test_event_peek(): 76 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.USEREVENT] 77 | event_obj = {} 78 | for evt in events: 79 | event_obj[evt] = pg.event.Event(evt) 80 | pg.event.clear() 81 | for evt in events: 82 | assert pg.event.peek(evt) == False 83 | pg.event.post(event_obj[evt]) 84 | assert pg.event.peek(evt) == True 85 | 86 | 87 | def test_event_clear(): 88 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.USEREVENT] 89 | event_obj = {} 90 | for evt in events: 91 | event_obj[evt] = pg.event.Event(evt) 92 | pg.event.clear() 93 | for evt in events: 94 | pg.event.post(event_obj[evt]) 95 | pg.event.clear() 96 | assert pg.event.get() == [] 97 | for evt in events: 98 | pg.event.post(event_obj[evt]) 99 | pg.event.clear(events[0]) 100 | evts = pg.event.get() 101 | assert [e.type for e in evts] == events[1:] 102 | for evt in events: 103 | pg.event.post(event_obj[evt]) 104 | pg.event.clear((events[0], events[1])) 105 | evts = pg.event.get() 106 | assert [e.type for e in evts] == events[2:] 107 | 108 | 109 | def test_event_block(): 110 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.MOUSEMOTION] 111 | event_obj = {} 112 | for evt in events: 113 | event_obj[evt] = pg.event.Event(evt) 114 | for evt in events: 115 | pg.event.post(event_obj[evt]) 116 | pg.event.set_blocked(events[2]) 117 | pg.event.clear() 118 | for evt in events: 119 | pg.event.post(event_obj[evt]) 120 | evts = pg.event.get() 121 | assert [e.type for e in evts] == events[:2] 122 | pg.event.set_allowed(None) 123 | pg.event.clear() 124 | for evt in events: 125 | pg.event.post(event_obj[evt]) 126 | evts = pg.event.get() 127 | assert [e.type for e in evts] == events 128 | 129 | 130 | def test_event_post(): 131 | events = [pg.KEYDOWN, pg.MOUSEBUTTONDOWN, pg.USEREVENT] 132 | event_obj = {} 133 | for evt in events: 134 | event_obj[evt] = pg.event.Event(evt) 135 | pg.event.clear() 136 | for evt in events: 137 | pg.event.post(event_obj[evt]) 138 | evts = pg.event.get() 139 | assert [e.type for e in evts if e.type in events] == events 140 | evt_obj = pg.event.Event(pg.USEREVENT,{'x':1,'y':2,'z':3}) 141 | pg.event.post(evt_obj) 142 | evts = pg.event.get() 143 | e = [ev for ev in evts if ev.type==pg.USEREVENT][0] 144 | assert (e.type==pg.USEREVENT and e.x==1 and e.y==2 and e.z==3) 145 | 146 | -------------------------------------------------------------------------------- /test/libtest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Libtest 3 | 4 | Check doc/libtest.txt for information. 5 | 6 | pyjsmode: 7 | pyjsbuild -S: opt=False/attr=True 8 | pyjsbuild -O: opt=True/attr=False 9 | pyjsbuild -O descriptor-proto/operator-funcs: opt=True/attr=True 10 | """ 11 | 12 | import os, sys 13 | 14 | if os.name in ('posix', 'nt', 'os2', 'ce', 'riscos'): 15 | os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" 16 | import pygame as pg 17 | platform = 'pc' 18 | executor = 'python' 19 | library = 'pygame' 20 | elif os.name == 'java': 21 | import pyj2d as pg 22 | platform = 'jvm' 23 | executor = 'jython' 24 | library = 'pyj2d' 25 | else: 26 | import pyjsdl as pg 27 | platform = 'js' 28 | executor = 'pyjs' 29 | library = 'pyjsdl' 30 | 31 | from test import surface_test 32 | from test import rect_test 33 | from test import draw_test 34 | from test import transform_test 35 | from test import surfarray_test 36 | from test import mask_test 37 | from test import color_test 38 | from test import cursor_test 39 | from test import sprite_test 40 | from test import event_test 41 | from test import time_test 42 | from test import vector_test 43 | 44 | 45 | if executor in ('python', 'jython', 'pyjs'): 46 | has_assert = True 47 | if hasattr('', 'format'): 48 | _name = lambda f: f.__name__ 49 | _str = lambda n, t, r: 'Test {} {} {}'.format(n, _name(t), r) 50 | else: 51 | _name = lambda f: f.__name__ 52 | _str = lambda n, t, r: 'Test %3d %-30s %10s' % (n, _name(t), r) 53 | 54 | 55 | def set_pyjsmode(): 56 | #pyjsbuild -S: opt=False/attr=True 57 | #pyjsbuild -O: opt=True/attr=False 58 | #pyjsbuild -O descriptor-proto/operator-funcs: opt=True/attr=True 59 | if executor == 'pyjs': 60 | pyjs_opt = pg.env.pyjs_mode.optimized 61 | pyjs_attr = pg.env.pyjs_mode.getattr_call 62 | else: 63 | pyjs_opt = False 64 | pyjs_attr = True 65 | return pyjs_opt, pyjs_attr 66 | 67 | 68 | class Log: 69 | 70 | def __init__(self): 71 | self._log = self._set_log() 72 | self._log_text = [] 73 | 74 | def write(self, text): 75 | if self._log: 76 | self._log_text.append(text + '\n') 77 | self._log.setText(''.join(self._log_text)) 78 | else: 79 | print(text) 80 | 81 | def _set_log(self): 82 | if platform == 'js': 83 | try: 84 | pg.display.textbox_init() 85 | log = pg.display.textarea 86 | log.resize(400, 500) 87 | log.toggle() 88 | except: 89 | log = None 90 | else: 91 | log = None 92 | return log 93 | 94 | 95 | lib_tests = [surface_test, 96 | rect_test, 97 | draw_test, 98 | transform_test, 99 | surfarray_test, 100 | mask_test, 101 | color_test, 102 | cursor_test, 103 | sprite_test, 104 | event_test, 105 | time_test, 106 | vector_test] 107 | 108 | 109 | lib_test_name = {'surface_test': surface_test, 110 | 'rect_test': rect_test, 111 | 'draw_test': draw_test, 112 | 'transform_test': transform_test, 113 | 'surfarray_test': surfarray_test, 114 | 'mask_test': mask_test, 115 | 'color_test': color_test, 116 | 'cursor_test': cursor_test, 117 | 'sprite_test': sprite_test, 118 | 'event_test': event_test, 119 | 'time_test': time_test, 120 | 'vector_test': vector_test} 121 | 122 | 123 | env = {} 124 | log = None 125 | tests = [] 126 | test_num = -1 127 | tests_failed = [] 128 | tests_success = True 129 | tests_skipped = False 130 | test_repeat = False 131 | catch_exc = True 132 | 133 | 134 | def tests_init(): 135 | global log 136 | pg.init() 137 | width, height = 20,20 138 | display = pg.display.set_mode((width, height)) 139 | surface = pg.Surface((width, height)) 140 | pyjs_opt, pyjs_attr = set_pyjsmode() 141 | log = Log() 142 | env['pg'] = pg 143 | env['platform'] = platform 144 | env['executor'] = executor 145 | env['library'] = library 146 | env['log'] = log 147 | env['pyjs_opt'] = pyjs_opt 148 | env['pyjs_attr'] = pyjs_attr 149 | env['display'] = display 150 | env['surface'] = surface 151 | env['width'] = width 152 | env['height'] = height 153 | for tst in lib_tests: 154 | test_list = tst.init(env) 155 | tests.extend(test_list) 156 | 157 | 158 | def run_test(): 159 | global tests_success, tests_failed, tests_skipped, test_repeat 160 | result = 'passed' 161 | try: 162 | ret = tests[test_num]() 163 | if ret is not None: 164 | if ret: 165 | test_repeat = True 166 | return 167 | else: 168 | test_repeat = False 169 | except AssertionError: 170 | result = 'failed' 171 | tests_success = False 172 | test_repeat = False 173 | tests_failed.append(test_num) 174 | except NotImplementedError: 175 | result = 'skipped' 176 | tests_skipped = True 177 | test_repeat = False 178 | except: 179 | result = 'error' 180 | tests_success = False 181 | test_repeat = False 182 | if test_num not in tests_failed: 183 | tests_failed.append(test_num) 184 | if not catch_exc: 185 | log.write(_str(test_num, tests[test_num], result)) 186 | raise 187 | log.write(_str(test_num, tests[test_num], result)) 188 | 189 | 190 | def run_tests(): 191 | global test_num 192 | while True: 193 | if test_num < len(tests)-1: 194 | test_num += 1 195 | run_test() 196 | else: 197 | break 198 | test_complete() 199 | 200 | 201 | def run_tests_js(): 202 | global test_num 203 | if test_num < len(tests)-1: 204 | if not test_repeat: 205 | test_num += 1 206 | run_test() 207 | else: 208 | test_complete() 209 | 210 | 211 | def test_complete(): 212 | if tests_success: 213 | log.write('Tests passed.') 214 | else: 215 | log.write('Tests that failed: ' + str(tests_failed)) 216 | if not tests_success or tests_skipped: 217 | log.write('\nCheck doc/libtest.txt for information.') 218 | pg.quit() 219 | 220 | 221 | def main(tests=None, catch_exception=None): 222 | if tests is not None: 223 | if isinstance(tests, list) and len(tests) > 0: 224 | lib_tests[:] = [] 225 | for test in tests: 226 | _test = lib_test_name[test] 227 | lib_tests.append(_test) 228 | if catch_exception is not None: 229 | if catch_exception in (True, False): 230 | global catch_exc 231 | catch_exc = catch_exception 232 | tests_init() 233 | if env['platform'] in ('pc', 'jvm'): 234 | run_tests() 235 | else: 236 | if has_assert: 237 | pg.setup(run_tests_js) 238 | else: 239 | log.write('\nCheck doc/libtest.txt for information.') 240 | 241 | 242 | if __name__ == '__main__': 243 | main() 244 | 245 | -------------------------------------------------------------------------------- /test/mask_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_mask, 10 | test_mask_from_surface, 11 | test_mask_from_threshold] 12 | return tests 13 | 14 | 15 | def test_mask(): 16 | surface = pg.Surface((15,10),pg.SRCALPHA) 17 | pg.draw.rect(surface, (10,20,30), (0,0,4,3)) 18 | mask = pg.mask.from_surface(surface) 19 | assert mask.get_size() == (15,10) 20 | assert mask.count() == 12 21 | assert mask.get_at((0,0)) == 1 22 | assert mask.get_at((4,0)) == 0 23 | mask.set_at((4,0)) 24 | assert mask.get_at((4,0)) == 1 25 | assert mask.count() == 13 26 | assert bool(mask.overlap(mask, (0,0))) == True 27 | assert bool(mask.overlap(mask, (2,2))) == True 28 | assert bool(mask.overlap(mask, (5,5))) == False 29 | assert bool(mask.overlap(mask, (5,0))) == False 30 | assert bool(mask.overlap(mask, (0,5))) == False 31 | assert mask.get_at((8,0)) == 0 32 | mask.fill() 33 | assert mask.get_at((8,0)) == 1 34 | assert mask.count() == 150 35 | mask.clear() 36 | assert mask.get_at((8,0)) == 0 37 | assert mask.count() == 0 38 | mask.invert() 39 | assert mask.get_at((8,0)) == 1 40 | assert mask.count() == 150 41 | 42 | 43 | def test_mask_from_surface(): 44 | surface = pg.Surface((15,10),pg.SRCALPHA) 45 | pg.draw.rect(surface, (10,20,30), (0,0,4,3)) 46 | mask = pg.mask.from_surface(surface) 47 | assert mask.get_at((0,0)) == 1 48 | assert mask.get_at((3,0)) == 1 49 | assert mask.get_at((4,0)) == 0 50 | assert mask.get_at((0,1)) == 1 51 | assert mask.get_at((3,1)) == 1 52 | assert mask.get_at((4,1)) == 0 53 | assert mask.count() == 12 54 | mask = pg.mask.from_surface(surface,254) 55 | assert mask.get_at((0,0)) == 1 56 | assert mask.get_at((3,0)) == 1 57 | assert mask.get_at((4,0)) == 0 58 | assert mask.count() == 12 59 | mask = pg.mask.from_surface(surface,255) 60 | assert mask.get_at((0,0)) == 0 61 | assert mask.get_at((3,0)) == 0 62 | assert mask.get_at((4,0)) == 0 63 | assert mask.count() == 0 64 | 65 | 66 | def test_mask_from_threshold(): 67 | surface = pg.Surface((15,10),pg.SRCALPHA) 68 | pg.draw.rect(surface, (50,100,150), (0,0,4,3)) 69 | mask = pg.mask.from_threshold(surface, (50,100,150), (1,1,1,255)) 70 | assert mask.get_at((0,0)) == 1 71 | assert mask.get_at((3,0)) == 1 72 | assert mask.get_at((4,0)) == 0 73 | assert mask.get_at((0,1)) == 1 74 | assert mask.get_at((3,1)) == 1 75 | assert mask.get_at((4,1)) == 0 76 | assert mask.count() == 12 77 | mask = pg.mask.from_threshold(surface, (50,100,150), (1,1,0,255)) 78 | if env['platform'] in ('jvm', 'js'): #pg diff? 79 | assert mask.get_at((0,0)) == 1 80 | assert mask.get_at((3,0)) == 1 81 | assert mask.get_at((4,0)) == 0 82 | assert mask.count() == 12 83 | else: 84 | assert mask.count() == 0 85 | mask = pg.mask.from_threshold(surface, (50,100,150), (0,0,0,254)) 86 | if env['platform'] in ('jvm', 'js'): #pg diff? 87 | assert mask.get_at((0,0)) == 1 88 | assert mask.get_at((3,0)) == 1 89 | assert mask.get_at((4,0)) == 0 90 | assert mask.count() == 12 91 | else: 92 | assert mask.count() == 0 93 | mask = pg.mask.from_threshold(surface, (50,100,150)) 94 | if env['platform'] in ('jvm' 'js'): #pg diff? 95 | assert mask.get_at((0,0)) == 1 96 | assert mask.get_at((3,0)) == 1 97 | assert mask.get_at((4,0)) == 0 98 | assert mask.get_at((0,1)) == 1 99 | assert mask.get_at((3,1)) == 1 100 | assert mask.get_at((4,1)) == 0 101 | assert mask.count() == 12 102 | else: 103 | assert mask.count() == 0 104 | 105 | -------------------------------------------------------------------------------- /test/rect_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_rect_constructor, 10 | test_rect_get, 11 | test_rect_comparison, 12 | test_rect_copy, 13 | test_rect_move, 14 | test_rect_inflate, 15 | test_rect_clip, 16 | test_rect_union, 17 | test_rect_collidepoint, 18 | test_rect_colliderect, 19 | test_rect_collidelist] 20 | return tests 21 | 22 | 23 | def test_rect_constructor(): 24 | rect = pg.Rect(0,0,10,10) 25 | assert (rect.x,rect.y,rect.width,rect.height) == (0,0,10,10) 26 | rect = pg.Rect((0,0),(10,10)) 27 | assert (rect.x,rect.y,rect.width,rect.height) == (0,0,10,10) 28 | rect = pg.Rect((0,0,10,10)) 29 | assert (rect.x,rect.y,rect.width,rect.height) == (0,0,10,10) 30 | rect = pg.Rect(rect) 31 | assert (rect.x,rect.y,rect.width,rect.height) == (0,0,10,10) 32 | obj = pg.sprite.Sprite() 33 | obj.rect = rect 34 | rect = pg.Rect(obj) 35 | assert (rect.x,rect.y,rect.width,rect.height) == (0,0,10,10) 36 | assert (rect[0],rect[1],rect[2],rect[3]) == (0,0,10,10) 37 | 38 | 39 | def test_rect_get(): 40 | rect = pg.Rect(0,0,10,10) 41 | rect.x,rect.y,rect.width,rect.height = 10,10,100,100 42 | assert (rect.x,rect.y,rect.width,rect.height) == (10,10,100,100) 43 | rect[0],rect[1],rect[2],rect[3] = 20,20,200,200 44 | assert (rect[0],rect[1],rect[2],rect[3]) == (20,20,200,200) 45 | r = pg.Rect(25,25,40,40) 46 | r.x = 30 47 | assert r.x==30 and (r.x,r.y,r.width,r.height)==(30,25,40,40) 48 | r.y = 40 49 | assert r.y==40 and (r.x,r.y,r.width,r.height)==(30,40,40,40) 50 | r.width = 50 51 | assert r.width==50 and (r.x,r.y,r.width,r.height)==(30,40,50,40) 52 | r.height = 60 53 | assert r.height==60 and (r.x,r.y,r.width,r.height)==(30,40,50,60) 54 | if not (env['pyjs_opt'] and not env['pyjs_attr']): 55 | #pyjs -O __getattr__/__setattr__ not called, 56 | #unless --enable-descriptor-proto option. 57 | r = pg.Rect(25,25,40,40) 58 | r.center = (10,20) 59 | assert r.center==(10,20) and (r.x,r.y,r.width,r.height)==(-10,0,40,40) 60 | r.centerx = 30 61 | assert r.centerx==30 and (r.x,r.y,r.width,r.height)==(10,0,40,40) 62 | r.centery = 40 63 | assert r.centery==40 and (r.x,r.y,r.width,r.height)==(10,20,40,40) 64 | r.top = 50 65 | assert r.top==50 and (r.x,r.y,r.width,r.height)==(10,50,40,40) 66 | r.left = 60 67 | assert r.left==60 and (r.x,r.y,r.width,r.height)==(60,50,40,40) 68 | r.bottom = 70 69 | assert r.bottom==70 and (r.x,r.y,r.width,r.height)==(60,30,40,40) 70 | r.right = 80 71 | assert r.right==80 and (r.x,r.y,r.width,r.height)==(40,30,40,40) 72 | r.topleft = (90,100) 73 | assert r.topleft==(90,100) and (r.x,r.y,r.width,r.height)==(90,100,40,40) 74 | r.bottomleft = (100,110) 75 | assert r.bottomleft==(100,110) and (r.x,r.y,r.width,r.height)==(100,70,40,40) 76 | r.topright = (120,130) 77 | assert r.topright==(120,130) and (r.x,r.y,r.width,r.height)==(80,130,40,40) 78 | r.bottomright = (140,150) 79 | assert r.bottomright==(140,150) and (r.x,r.y,r.width,r.height)==(100,110,40,40) 80 | r.midtop = (160,170) 81 | assert r.midtop==(160,170) and (r.x,r.y,r.width,r.height)==(140,170,40,40) 82 | r.midleft = (180,190) 83 | assert r.midleft==(180,190) and (r.x,r.y,r.width,r.height)==(180,170,40,40) 84 | r.midbottom = (200,210) 85 | assert r.midbottom==(200,210) and (r.x,r.y,r.width,r.height)==(180,170,40,40) 86 | r.midright = (220,230) 87 | assert r.midright==(220,230) and (r.x,r.y,r.width,r.height)==(180,210,40,40) 88 | r.size = (240,250) 89 | assert r.size==(240,250) and (r.x,r.y,r.width,r.height)==(180,210,240,250) 90 | r.w = 260 91 | assert r.w==260 and (r.x,r.y,r.width,r.height)==(180,210,260,250) 92 | r.h = 270 93 | assert r.h==270 and (r.x,r.y,r.width,r.height)==(180,210,260,270) 94 | r = pg.Rect(25,25,40,40) 95 | setattr(r, 'center', (10,20)) 96 | assert getattr(r, 'center')==(10,20) and (r.x,r.y,r.width,r.height)==(-10,0,40,40) 97 | 98 | 99 | def test_rect_comparison(): 100 | rect = pg.Rect(0,0,10,10) 101 | if env['platform'] != 'js': 102 | #pyjs compares rect==tuple not __eq__ 103 | assert rect == (0,0,10,10) 104 | assert rect != (0,0,100,100) 105 | assert not (rect == (0,0,100,100)) 106 | if not env['pyjs_opt']: 107 | #pyjs -O __eq__ not called 108 | assert rect == pg.Rect(0,0,10,10) 109 | assert rect != pg.Rect(0,0,100,100) 110 | assert not (rect == pg.Rect(0,0,100,100)) 111 | 112 | 113 | def test_rect_copy(): 114 | r1 = pg.Rect(0,0,100,100) 115 | r2 = r1.copy() 116 | if not env['pyjs_opt']: 117 | #pyjs -O __eq__ not called 118 | assert r1 == r2 119 | assert (r1.x,r1.y,r1.width,r1.height) == (r2.x,r2.y,r2.width,r2.height) 120 | 121 | 122 | def test_rect_move(): 123 | r1 = pg.Rect(10,10,100,100) 124 | r2 = r1.move(10,5) 125 | assert (r1.x,r1.y) == (10,10) and (r2.x,r2.y) == (20,15) 126 | r1 = pg.Rect(10,10,100,100) 127 | r2 = r1.move((10,5)) 128 | assert (r1.x,r1.y) == (10,10) and (r2.x,r2.y) == (20,15) 129 | r = pg.Rect(10,10,100,100) 130 | r.move_ip(10,5) 131 | assert (r.x,r.y) == (20,15) 132 | r = pg.Rect(10,10,100,100) 133 | r.move_ip((10,5)) 134 | assert (r.x,r.y) == (20,15) 135 | 136 | 137 | def test_rect_inflate(): 138 | r1 = pg.Rect(10,10,100,100) 139 | r2 = r1.inflate(10,20) 140 | assert (r1.width,r1.height) == (100,100) and (r2.width,r2.height) == (110,120) 141 | assert (r1.x,r1.y) == (10,10) and (r2.x,r2.y) == (5,0) 142 | r1 = pg.Rect(10,10,100,100) 143 | r2 = r1.inflate(-10,-20) 144 | assert (r1.width,r1.height) == (100,100) and (r2.width,r2.height) == (90,80) 145 | assert (r1.x,r1.y) == (10,10) and (r2.x,r2.y) == (15,20) 146 | r1 = pg.Rect(10,10,100,100) 147 | r2 = r1.inflate((10,20)) 148 | assert (r1.width,r1.height) == (100,100) and (r2.width,r2.height) == (110,120) 149 | assert (r1.x,r1.y) == (10,10) and (r2.x,r2.y) == (5,0) 150 | r = pg.Rect(10,10,100,100) 151 | r.inflate_ip(10,20) 152 | assert (r.width,r.height) == (110,120) 153 | assert (r.x,r.y) == (5,0) 154 | r = pg.Rect(10,10,100,100) 155 | r.inflate_ip(-10,-20) 156 | assert (r.width,r.height) == (90,80) 157 | assert (r.x,r.y) == (15,20) 158 | r = pg.Rect(10,10,100,100) 159 | r.inflate_ip((10,20)) 160 | assert (r.width,r.height) == (110,120) 161 | assert (r.x,r.y) == (5,0) 162 | 163 | 164 | def test_rect_clip(): 165 | r1 = pg.Rect(0,0,100,150) 166 | r2 = pg.Rect(50,50,100,150) 167 | r3 = pg.Rect(200,200,50,100) 168 | r = r1.clip(r2) 169 | assert (r.x,r.y,r.width,r.height) == (50,50,50,100) 170 | r = r1.clip(r3) 171 | assert (r.x,r.y,r.width,r.height) == (0,0,0,0) 172 | 173 | 174 | def test_rect_union(): 175 | r1 = pg.Rect(0,0,100,150) 176 | r2 = pg.Rect(50,50,100,150) 177 | r3 = pg.Rect(200,200,50,100) 178 | r4 = pg.Rect(-10,-10,50,100) 179 | r = r1.union(r2) 180 | assert (r.x,r.y,r.width,r.height) == (0,0,150,200) 181 | r = r1.copy() 182 | r.union_ip(r2) 183 | assert (r.x,r.y,r.width,r.height) == (0,0,150,200) 184 | r = r1.unionall([r2,r3,r4]) 185 | assert (r.x,r.y,r.width,r.height) == (-10, -10, 260, 310) 186 | r = r1.copy() 187 | r.unionall_ip([r2,r3,r4]) 188 | assert (r.x,r.y,r.width,r.height) == (-10, -10, 260, 310) 189 | 190 | 191 | def test_rect_collidepoint(): 192 | r = pg.Rect(10,20,100,200) 193 | assert r.collidepoint((30,40)) == True 194 | assert r.collidepoint(50,60) == True 195 | assert r.collidepoint((5,5)) == False 196 | 197 | 198 | def test_rect_colliderect(): 199 | r1 = pg.Rect(0,0,100,150) 200 | r2 = pg.Rect(50,50,100,150) 201 | r3 = pg.Rect(200,200,50,100) 202 | assert r1.colliderect(r2) == True 203 | assert r1.colliderect(r3) == False 204 | 205 | 206 | def test_rect_collidelist(): 207 | r1 = pg.Rect(0,0,100,150) 208 | r2 = pg.Rect(50,50,100,150) 209 | r3 = pg.Rect(200,200,50,100) 210 | r4 = pg.Rect(75,80,50,50) 211 | assert r1.collidelist([r2,r3,r4]) == 0 212 | assert r1.collidelist([r3,r4,r2]) == 1 213 | assert r1.collidelist([r3]) == -1 214 | 215 | -------------------------------------------------------------------------------- /test/sprite_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_sprite, 10 | test_sprite_group] 11 | return tests 12 | 13 | 14 | def test_sprite(): 15 | Sprite = pg.sprite.Sprite 16 | Group = pg.sprite.Group 17 | g = [Group() for i in range(20)] 18 | sx, rx, i = {}, {}, 10 19 | sx[0] = Sprite() 20 | sx[1] = Sprite(*g) 21 | sx[2] = Sprite(g) 22 | sx[3] = Sprite([]) 23 | sx[4] = Sprite(g[0]) 24 | sx[5] = Sprite([g[0],g[1]]) 25 | sx[6] = Sprite([g]) 26 | sx[7] = Sprite([g],g) 27 | sx[8] = Sprite(g); sx[8].remove(g[0]); sx[8].remove([g[0],g[1]],g[10]) 28 | sx[9] = Sprite(g); sx[9].kill() 29 | sx[0+i] = Sprite(); sx[0+i].add() 30 | sx[1+i] = Sprite(); sx[1+i].add(*g) 31 | sx[2+i] = Sprite(); sx[2+i].add(g) 32 | sx[3+i] = Sprite(); sx[3+i].add([]) 33 | sx[4+i] = Sprite(); sx[4+i].add(g[0]) 34 | sx[5+i] = Sprite(); sx[5+i].add([g[0],g[1]]) 35 | sx[6+i] = Sprite(); sx[6+i].add([g]) 36 | sx[7+i] = Sprite(); sx[7+i].add([g],g) 37 | sx[8+i] = Sprite(); sx[8+i].add(g); sx[8+i].remove(g[0]); sx[8+i].remove([g[0],g[1]],g[10]) 38 | sx[9+i] = Sprite(); sx[9+i].add(g); sx[9+i].kill() 39 | rx[0] = rx[0+i] = [ 0, False ] 40 | rx[1] = rx[1+i] = [ 20, True ] 41 | rx[2] = rx[2+i] = [ 20, True ] 42 | rx[3] = rx[3+i] = [ 0, False ] 43 | rx[4] = rx[4+i] = [ 1, True ] 44 | rx[5] = rx[5+i] = [ 2, True ] 45 | rx[6] = rx[6+i] = [ 20, True ] 46 | rx[7] = rx[7+i] = [ 20, True ] 47 | rx[8] = rx[8+i] = [ 17, True ] 48 | rx[9] = rx[9+i] = [ 0, False ] 49 | for i in range(20): 50 | s,r = sx[i],rx[i] 51 | assert len(s.groups()) == r[0] 52 | assert s.alive() == r[1] 53 | 54 | 55 | def test_sprite_group(): 56 | for Group in (pg.sprite.Group, 57 | pg.sprite.RenderUpdates, 58 | pg.sprite.OrderedUpdates, 59 | pg.sprite.LayeredUpdates): 60 | Sprite = pg.sprite.Sprite 61 | s = [Sprite() for i in range(20)] 62 | grp = Group(s) 63 | gx, rx, i = {}, {}, 12 64 | gx[0] = Group() 65 | gx[1] = Group(*s) 66 | gx[2] = Group(s) 67 | gx[3] = Group([]) 68 | gx[4] = Group(s[0]) 69 | gx[5] = Group([s[0],s[1]]) 70 | gx[6] = Group([s]) 71 | gx[7] = Group([s],s) 72 | gx[8] = Group(grp) 73 | gx[9] = Group([grp]) 74 | gx[10] = Group(s); gx[8].remove(s[0]); gx[8].remove([s[0],s[1]],s[10]) 75 | gx[11] = Group(s); gx[9].empty() 76 | gx[0+i] = Group(); gx[0+i].add() 77 | gx[1+i] = Group(); gx[1+i].add(*s) 78 | gx[2+i] = Group(); gx[2+i].add(s) 79 | gx[3+i] = Group(); gx[3+i].add([]) 80 | gx[4+i] = Group(); gx[4+i].add(s[0]) 81 | gx[5+i] = Group(); gx[5+i].add([s[0],s[1]]) 82 | gx[6+i] = Group(); gx[6+i].add([s]) 83 | gx[7+i] = Group(); gx[7+i].add([s],s) 84 | gx[8+i] = Group(); gx[8+i].add(grp) 85 | gx[9+i] = Group(); gx[9+i].add([grp]) 86 | gx[10+i] = Group(s); gx[10+i].add(s); gx[10+i].remove(s[0]); gx[10+i].remove([s[0],s[1]],s[10]) 87 | gx[11+i] = Group(s); gx[11+i].add(s); gx[11+i].empty() 88 | rx[0] = rx[0+i] = [ 0, False, False, False ] 89 | rx[1] = rx[1+i] = [ 20, True, True, True ] 90 | rx[2] = rx[2+i] = [ 20, True, True, True ] 91 | rx[3] = rx[3+i] = [ 0, False, False, False ] 92 | rx[4] = rx[4+i] = [ 1, True, False, False ] 93 | rx[5] = rx[5+i] = [ 2, True, False, False ] 94 | rx[6] = rx[6+i] = [ 20, True, True, True ] 95 | rx[7] = rx[7+i] = [ 20, True, True, True ] 96 | rx[8] = rx[8+i] = [ 20, True, True, True ] 97 | rx[9] = rx[9+i] = [ 20, True, True, True ] 98 | rx[10] = rx[10+i] = [ 17, False, False, True ] 99 | rx[11] = rx[11+i] = [ 0, False, False, False ] 100 | for x in range(i*2): 101 | g,r = gx[i],rx[i] 102 | assert len(g.sprites()) == r[0] 103 | assert g.has(s[0]) == r[1] 104 | assert g.has([s[0],s[1],s[2]]) == r[2] 105 | assert g.has([s[2],s[5]],s[6]) == r[3] 106 | 107 | -------------------------------------------------------------------------------- /test/surface_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | surface = None 4 | width = None 5 | height = None 6 | 7 | 8 | def init(environ): 9 | global env, pg, surface, width, height 10 | env = environ 11 | pg = env['pg'] 12 | surface = env['surface'] 13 | width = env['width'] 14 | height = env['height'] 15 | tests = [test_surface_get_size, 16 | test_surface_get_rect, 17 | test_surface_copy, 18 | test_surface_blit, 19 | test_surface_fill, 20 | test_surface_set_colorkey, 21 | test_surface_get_colorkey, 22 | test_surface_set_at, 23 | test_surface_get_at] 24 | return tests 25 | 26 | 27 | def _color_convert(color): 28 | if len(color) == 4: 29 | r,g,b,a = color[0],color[1],color[2],color[3] 30 | else: 31 | r,g,b,a = color[0],color[1],color[2],255 32 | return r,g,b,a 33 | 34 | 35 | def test_surface_get_size(): 36 | assert surface.get_size() == (width,height) 37 | assert surface.get_width() == width 38 | assert surface.get_height() == height 39 | 40 | 41 | def test_surface_get_rect(): 42 | rect = surface.get_rect() 43 | if env['platform'] != 'js': #pyjs compares rect==tuple not __eq__ 44 | assert rect == (0,0,width,height) 45 | assert (rect.x,rect.y,rect.width,rect.height) == (0,0,width,height) 46 | rect = surface.get_rect(center=(15,15)) 47 | assert (rect.x,rect.y,rect.width,rect.height) == (5,5,width,height) 48 | 49 | 50 | def test_surface_copy(): 51 | new_surface = surface.copy() 52 | assert surface == surface 53 | assert surface != new_surface 54 | assert surface.get_size() == new_surface.get_size() 55 | 56 | 57 | def test_surface_blit(): 58 | new_surface = pg.Surface((5,5)) 59 | surface.fill((0,0,0)) 60 | new_surface.fill((100,100,100)) 61 | rect = surface.blit(new_surface, (1,0)) 62 | if env['executor'] != 'pyjs': 63 | assert surface.get_at((0,0)) == (0,0,0,255) 64 | assert surface.get_at((1,0)) == (100,100,100,255) 65 | else: 66 | if not env['pyjs_opt']: #pyjs -s compares color==tuple not __eq__ 67 | assert surface.get_at((0,0)) == pg.Color(0,0,0,255) 68 | assert surface.get_at((1,0)) == pg.Color(100,100,100,255) 69 | else: #pyjs -O __eq__ ignored 70 | c = surface.get_at((0,0)) 71 | assert (c.r,c.g,c.b,c.a) == (0,0,0,255) 72 | c = surface.get_at((1,0)) 73 | assert (c.r,c.g,c.b,c.a) == (100,100,100,255) 74 | assert (rect.x,rect.y,rect.width,rect.height) == (1,0,5,5) 75 | 76 | 77 | def test_surface_fill(): 78 | color = (255,0,0), (0,255,0,255) 79 | for c in color: 80 | surface.fill((0,0,0)) 81 | surface.fill(pg.Color(c)) 82 | if env['executor'] != 'pyjs': 83 | assert surface.get_at((0,0)) == c 84 | else: 85 | cc = surface.get_at((0,0)) 86 | assert (cc[0],cc[1],cc[2],cc[3]) == _color_convert(c) 87 | 88 | 89 | def test_surface_set_colorkey(): 90 | color = (255,0,0), (0,255,0,255), None 91 | for c in color: 92 | surface.set_colorkey(c) 93 | if surface.get_colorkey(): 94 | if not env['pyjs_opt']: 95 | assert pg.Color(*surface.get_colorkey()) == pg.Color(*c) 96 | else: #pyjs -O no __eq__ call 97 | r,g,b,a = pg.Color(*surface.get_colorkey()) 98 | cr,cg,cb,ca = pg.Color(*c) 99 | assert r==cr and g==cg and b==cb and a==ca 100 | 101 | 102 | def test_surface_get_colorkey(): 103 | surface.fill((0,0,0)) 104 | surface.set_colorkey((0,0,0)) 105 | assert surface.get_colorkey() == (0,0,0,255) 106 | surface.set_colorkey(None) 107 | assert surface.get_colorkey() is None 108 | 109 | 110 | def test_surface_set_at(): 111 | color = (255,0,0), (0,255,0,255) 112 | for c in color: 113 | surface.fill((0,0,0)) 114 | surface.set_at((0,0), c) 115 | if env['executor'] != 'pyjs': 116 | assert surface.get_at((0,0)) == c 117 | else: #pyjs compares color==tuple not __eq__ 118 | cc = surface.get_at((0,0)) 119 | assert (cc.r,cc.g,cc.b,cc.a) == _color_convert(c) 120 | 121 | 122 | def test_surface_get_at(): 123 | color = (0,0,255,255) 124 | surface.fill((0,0,0)) 125 | surface.set_at((0,0), (0,0,255,255)) 126 | if env['executor'] != 'pyjs': 127 | assert surface.get_at((0,0)) == (0,0,255,255) 128 | assert surface.get_at((0,0)) == (0,0,255) 129 | else: #pyjs compares color==tuple not __eq__ 130 | cc = surface.get_at((0,0)) 131 | assert (cc.r,cc.g,cc.b,cc.a) == (0,0,255,255) 132 | 133 | -------------------------------------------------------------------------------- /test/surfarray_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_surfarray_blit_array, 10 | test_surfarray_make_surface, 11 | test_surfarray_array2d, 12 | test_surfarray_array3d, 13 | test_surfarray_array_alpha] 14 | return tests 15 | 16 | 17 | def test_surfarray_blit_array(): 18 | if env['platform'] == 'jvm': 19 | try: 20 | pg.surfarray._init() 21 | except ImportError: 22 | raise NotImplementedError 23 | surface = pg.Surface((15,10)) 24 | surface.fill((0,0,0)) 25 | array2d = pg.surfarray.array2d(surface) 26 | surface.fill((255,0,0)) 27 | if env['executor'] != 'pyjs': 28 | assert surface.get_at((0,0)) == (255,0,0,255) 29 | else: 30 | c = surface.get_at((0,0)) 31 | assert (c.r,c.g,c.b,c.a) == (255,0,0,255) 32 | pg.surfarray.blit_array(surface, array2d) 33 | if env['executor'] != 'pyjs': 34 | assert surface.get_at((0,0)) == (0,0,0,255) 35 | else: 36 | c = surface.get_at((0,0)) 37 | assert (c.r,c.g,c.b,c.a) == (0,0,0,255) 38 | surface = pg.Surface((15,10)) 39 | surface.fill((0,0,0)) 40 | array3d = pg.surfarray.array3d(surface) 41 | surface.fill((255,0,0)) 42 | if env['executor'] != 'pyjs': 43 | assert surface.get_at((0,0)) == (255,0,0,255) 44 | else: 45 | c = surface.get_at((0,0)) 46 | assert (c.r,c.g,c.b,c.a) == (255,0,0,255) 47 | pg.surfarray.blit_array(surface, array3d) 48 | if env['executor'] != 'pyjs': 49 | assert surface.get_at((0,0)) == (0,0,0,255) 50 | else: 51 | c = surface.get_at((0,0)) 52 | assert (c.r,c.g,c.b,c.a) == (0,0,0,255) 53 | 54 | 55 | def test_surfarray_make_surface(): 56 | if env['platform'] == 'jvm': 57 | try: 58 | pg.surfarray._init() 59 | except ImportError: 60 | raise NotImplementedError 61 | surface = pg.Surface((15,10)) 62 | surface.fill((255,0,0)) 63 | if env['platform'] in ('jvm', 'js'): 64 | array2d = pg.surfarray.array2d(surface) 65 | surface2d = pg.surfarray.make_surface(array2d) 66 | if env['executor'] != 'pyjs': 67 | assert surface2d.get_size() == (15,10) 68 | assert surface2d.get_at((0,0)) == (255,0,0,255) 69 | else: 70 | c = surface.get_at((0,0)) 71 | assert (c.r,c.g,c.b,c.a) == (255,0,0,255) 72 | array3d = pg.surfarray.array3d(surface) 73 | surface3d = pg.surfarray.make_surface(array3d) 74 | assert surface3d.get_size() == (15,10) 75 | if env['executor'] != 'pyjs': 76 | assert surface3d.get_at((0,0)) == (255,0,0,255) 77 | else: 78 | c = surface.get_at((0,0)) 79 | assert (c.r,c.g,c.b,c.a) == (255,0,0,255) 80 | 81 | 82 | def test_surfarray_array2d(): 83 | if env['platform'] == 'jvm': 84 | try: 85 | pg.surfarray._init() 86 | except ImportError: 87 | raise NotImplementedError 88 | surface = pg.Surface((15,10)) 89 | surface.fill((0,0,0)) 90 | array = pg.surfarray.array2d(surface) 91 | for i in range(10): 92 | array[0,i] = 255 93 | assert array[0,0] == 255 94 | assert array[0,1]>>24 & 0xff == 0 95 | if env['platform'] == 'jvm': #array has alpha 96 | assert array[1,0]>>24 & 0xff == 255 97 | surface2 = pg.Surface((15,10), pg.SRCALPHA) 98 | array2 = pg.surfarray.array2d(surface2) 99 | for i in range(10): 100 | array2[0,i] = 255 101 | assert array2[0,0] == 255 102 | assert array2[1,0] == 0 103 | if env['platform'] == 'js': 104 | array = pg.surfarray.array2d(surface, True) 105 | for i in range(10): 106 | array[0,i] = 255 107 | assert array[0,0] == 255 108 | surface2 = pg.Surface((15,10), pg.SRCALPHA) 109 | array2 = pg.surfarray.array2d(surface2, True) 110 | for i in range(10): 111 | array2[0,i] = 255 112 | assert array2[0,0] == 255 113 | assert array2[1,0] == 0 114 | 115 | 116 | def test_surfarray_array3d(): 117 | if env['platform'] == 'jvm': 118 | try: 119 | pg.surfarray._init() 120 | except: 121 | raise NotImplementedError 122 | surface = pg.Surface((15,10)) 123 | surface.fill((0,0,0)) 124 | array = pg.surfarray.array3d(surface) 125 | if env['platform'] != 'js': 126 | assert array.shape == (15,10,3) 127 | else: 128 | if not env['pyjs_opt']: 129 | assert array.shape == (10,15,4) 130 | else: 131 | assert array.getshape() == (10,15,4) 132 | for i in range(10): 133 | array[0,i] = (0,0,255) 134 | assert array[0,0,2] == 255 135 | assert array[1,0,2] == 0 136 | if env['platform'] == 'js': 137 | array = pg.surfarray.array3d(surface, True) 138 | if not env['pyjs_opt']: 139 | assert array.shape == (15,10,3) 140 | else: 141 | assert array.getshape() == (15,10,3) 142 | for i in range(10): 143 | array[0,i] = (0,0,255) 144 | assert array[0,0,2] == 255 145 | assert array[1,0,2] == 0 146 | 147 | 148 | def test_surfarray_array_alpha(): 149 | if env['platform'] == 'jvm': 150 | try: 151 | pg.surfarray._init() 152 | except ImportError: 153 | raise NotImplementedError 154 | surface = pg.Surface((15,10)) 155 | surface.fill((0,0,0)) 156 | array = pg.surfarray.array_alpha(surface) 157 | if env['platform'] != 'js': 158 | assert array.shape == (15,10) 159 | else: 160 | if not env['pyjs_opt']: 161 | assert array.shape == (10,15,4) 162 | else: 163 | assert array.getshape() == (10,15,4) 164 | assert array[1,1] & 0xff == 255 165 | surface2 = pg.Surface((15,10),pg.SRCALPHA) 166 | array2 = pg.surfarray.array_alpha(surface2) 167 | for i in range(10): 168 | array2[0,i] = 255 169 | assert array2[0,0] & 0xff == 255 170 | assert array2[1,0] & 0xff == 0 171 | if env['platform'] == 'js': 172 | array = pg.surfarray.array_alpha(surface) 173 | assert array[1,1] & 0xff == 255 174 | surface2 = pg.Surface((15,10),pg.SRCALPHA) 175 | array2 = pg.surfarray.array_alpha(surface2) 176 | for i in range(10): 177 | array2[0,i] = 255 178 | assert array2[0,0] & 0xff == 255 179 | assert array2[1,0] & 0xff == 0 180 | 181 | -------------------------------------------------------------------------------- /test/time_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | wait = 0 4 | 5 | 6 | def init(environ): 7 | global env, pg 8 | env = environ 9 | pg = env['pg'] 10 | tests = [test_time_delay, 11 | test_time_wait, 12 | test_time_timer] 13 | return tests 14 | 15 | 16 | def test_time_delay(): 17 | _time = 30 18 | t = pg.time.get_ticks() 19 | pg.time.delay(_time) 20 | assert (pg.time.get_ticks()-t) >= _time 21 | 22 | 23 | def test_time_wait(): 24 | global wait 25 | _time = 30 26 | if env['platform'] != 'js': 27 | t = pg.time.get_ticks() 28 | pg.time.wait(_time) 29 | assert (pg.time.get_ticks()-t) >= _time 30 | else: 31 | if not wait: 32 | wait = pg.time.get_ticks() 33 | pg.time.wait(_time) 34 | return True 35 | else: 36 | assert (pg.time.get_ticks()-wait) >= _time 37 | wait = 0 38 | return False 39 | 40 | 41 | def test_time_timer(): 42 | global wait 43 | _time = 30 44 | event = pg.USEREVENT 45 | if env['platform'] != 'js': 46 | t = pg.time.get_ticks() 47 | pg.event.clear() 48 | pg.time.set_timer(event, _time) 49 | evt = pg.event.wait() 50 | pg.time.set_timer(event, 0) 51 | assert evt.type == event 52 | assert (pg.time.get_ticks()-t) >= _time 53 | else: 54 | if not wait: 55 | wait = pg.time.get_ticks() 56 | pg.event.clear() 57 | pg.time.set_timer(event, _time) 58 | pg.time.wait(_time) 59 | return True 60 | else: 61 | evt = pg.event.get()[0] 62 | pg.time.set_timer(event, 0) 63 | assert evt.type == event 64 | assert (pg.time.get_ticks()-wait) >= _time 65 | wait = 0 66 | return False 67 | 68 | -------------------------------------------------------------------------------- /test/transform_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | surface = None 4 | width = None 5 | height = None 6 | 7 | 8 | def init(environ): 9 | global env, pg, surface, width, height 10 | env = environ 11 | pg = env['pg'] 12 | surface = env['surface'] 13 | width = env['width'] 14 | height = env['height'] 15 | tests = [test_transform_rotate, 16 | test_transform_rotozoom, 17 | test_transform_scale, 18 | test_transform_flip] 19 | return tests 20 | 21 | 22 | def test_transform_rotate(): 23 | surface.fill((0,0,0)) 24 | surface.fill((255,0,0), (0,0,width//2,height)) 25 | surf = pg.transform.rotate(surface, 180) 26 | assert surf.get_size() == (width, height) 27 | assert surf.get_at((5,5)).r == 0 and surf.get_at((width-5,5)).r == 255 28 | 29 | 30 | def test_transform_rotozoom(): 31 | surface.fill((0,0,0)) 32 | surface.fill((255,0,0), (0,0,width//2,height)) 33 | surf = pg.transform.rotozoom(surface, 180, 2.0) 34 | assert int(surf.get_width()/width) == 2 and int(surf.get_height()/height) == 2 35 | assert surf.get_at((5,5)).r == 0 and surf.get_at((width*2-5,5)).r == 255 36 | 37 | 38 | 39 | def test_transform_scale(): 40 | surface.fill((0,0,0)) 41 | surface.fill((255,0,0), (0,0,width//2,height)) 42 | size = (width*2, height*2) 43 | surf = pg.transform.scale(surface, size) 44 | assert int(surf.get_width()/width) == 2 and int(surf.get_height()/height) == 2 45 | assert surf.get_at((5,5)).r == 255 and surf.get_at((width*2-5,5)).r == 0 46 | surf = pg.transform.smoothscale(surface, size) 47 | assert int(surf.get_width()/width) == 2 and int(surf.get_height()/height) == 2 48 | assert surf.get_at((5,5)).r == 255 and surf.get_at((width*2-5,5)).r == 0 49 | surf = pg.transform.scale2x(surface) 50 | assert int(surf.get_width()/width) == 2 and int(surf.get_height()/height) == 2 51 | assert surf.get_at((5,5)).r == 255 and surf.get_at((width*2-5,5)).r == 0 52 | 53 | 54 | def test_transform_flip(): 55 | surface.fill((0,0,0)) 56 | surface.fill((255,0,0), (0,0,width//2,height)) 57 | surf = pg.transform.flip(surface, True, False) 58 | assert surf.get_size() == (width, height) 59 | assert surf.get_at((5,5)).r == 0 and surf.get_at((width-5,5)).r == 255 60 | surf = pg.transform.flip(surface, False, True) 61 | assert surf.get_size() == (width, height) 62 | assert surf.get_at((5,5)).r == 255 and surf.get_at((width-5,5)).r == 0 63 | surf = pg.transform.flip(surface, True, True) 64 | assert surf.get_size() == (width, height) 65 | assert surf.get_at((5,5)).r == 0 and surf.get_at((width-5,5)).r == 255 66 | 67 | -------------------------------------------------------------------------------- /test/vector_test.py: -------------------------------------------------------------------------------- 1 | env = None 2 | pg = None 3 | 4 | 5 | def init(environ): 6 | global env, pg 7 | env = environ 8 | pg = env['pg'] 9 | tests = [test_vector_constructor, 10 | test_vector_update, 11 | test_vector_get, 12 | test_vector_operator, 13 | test_vector_magnitude, 14 | test_vector_direction, 15 | test_vector_dot, 16 | test_vector_cross, 17 | test_vector_lerp] 18 | return tests 19 | 20 | 21 | def _rd(val): 22 | return round(val, 3) 23 | 24 | 25 | def test_vector_constructor(): 26 | Vector2 = pg.Vector2 27 | v = Vector2(2.2, 3.0) 28 | assert v.x == 2.2 and v.y == 3.0 29 | v = Vector2((2.2, 3.0)) 30 | assert v.x == 2.2 and v.y == 3.0 31 | v = Vector2(v) 32 | assert v.x == 2.2 and v.y == 3.0 33 | 34 | 35 | def test_vector_update(): 36 | Vector2 = pg.Vector2 37 | v = Vector2(0.0, 0.0) 38 | v.update(2.2, 3.0) 39 | assert v.x == 2.2 and v.y == 3.0 40 | v.update((3.2, 4.0)) 41 | assert v.x == 3.2 and v.y == 4.0 42 | v.update(v) 43 | assert v.x == 3.2 and v.y == 4.0 44 | 45 | 46 | def test_vector_get(): 47 | Vector2 = pg.Vector2 48 | v = Vector2(0.0, 0.0) 49 | assert v.x == 0.0 and v.y == 0.0 50 | assert v[0] == 0.0 and v[1] == 0.0 51 | v.x = 1.0 52 | v.y = 1.0 53 | assert v.x == 1.0 and v.y == 1.0 54 | assert v[0] == 1.0 and v[1] == 1.0 55 | v[0] = 2.0 56 | v[1] = 2.0 57 | assert v.x == 2.0 and v.y == 2.0 58 | assert v[0] == 2.0 and v[1] == 2.0 59 | x, y = v[0], v[1] 60 | assert x == 2.0 and y == 2.0 61 | x, y = v 62 | assert x == 2.0 and y == 2.0 63 | vl = [] 64 | for _v in v: 65 | vl.append(_v) 66 | assert vl[0] == 2.0 and vl[1] == 2.0 67 | 68 | 69 | def test_vector_operator(): 70 | Vector2 = pg.Vector2 71 | v1 = Vector2(2.2, 3.0) 72 | v2 = Vector2(3.0, 4.0) 73 | if not (env['pyjs_opt'] and not env['pyjs_attr']): 74 | v = v1 + v2 75 | assert v.x == 5.2 and v.y == 7.0 76 | v = v1 + (5.0, 5.0) 77 | assert v.x == 7.2 and v.y == 8.0 78 | v = v1 - (1.0, 1.0) 79 | assert _rd(v.x) == 1.2 and v.y == 2.0 80 | v += v1 81 | assert _rd(v.x) == 3.4 and v.y == 5.0 82 | v -= (1.0, 1.0) 83 | assert _rd(v.x) == 2.4 and v.y == 4.0 84 | v = v1 * v2 85 | assert v == 18.6 86 | v = v1 * (3.0, 4.0) 87 | assert v == 18.6 88 | else: 89 | v = Vector2(v1) 90 | assert v.x == 2.2 and v.y == 3.0 91 | v.x = v.x + 1.0 92 | v.y = v.y + 2.0 93 | assert v.x == 3.2 and v.y == 5.0 94 | v.x += 0.5 95 | v.y -= 0.5 96 | assert v.x == 3.7 and v.y == 4.5 97 | if not (env['pyjs_opt'] and not env['pyjs_attr']): 98 | #-S / -O --enable-descriptor-proto --enable-operator-funcs 99 | #unable cmp vector to tuple 100 | el = v1.elementwise() 101 | v = el + v2 102 | assert v.x == 5.2 and v.y == 7.0 103 | v1 = Vector2(2.0, 3.0) 104 | v2 = Vector2(3.0, 4.0) 105 | vx = v1 + v2 106 | if not env['pyjs_opt']: 107 | assert vx == Vector2(5.0, 7.0) 108 | else: #__eq__ not called 109 | assert (vx.x, vx.y) == (5.0, 7.0) 110 | if not env['pyjs_opt']: 111 | assert (v1 + v2) == Vector2(5.0, 7.0) 112 | assert (v1 + (3.0,4.0)) == Vector2(5.0, 7.0) 113 | assert (v1 - v2) == Vector2(-1.0, -1.0) 114 | assert (v1 - (3.0,4.0)) == Vector2(-1.0, -1.0) 115 | assert (v1 * v2) == 18.0 116 | assert (v1 * (3.0,4.0)) == 18.0 117 | assert (v1 * 2) == Vector2(4.0, 6.0) 118 | assert (v1 / 2) == Vector2(1.0, 1.5) 119 | assert (v1 // 2) == Vector2(1.0, 1.0) 120 | if env['platform'] != 'js': ## 121 | assert ((2.0,3.0) + v2) == Vector2(5.0, 7.0) 122 | assert ((2.0,3.0) - v2) == Vector2(-1.0, -1.0) 123 | assert ((2.0,3.0) * v2) == 18.0 124 | assert (2 * v2) == Vector2(6.0, 8.0) 125 | v = Vector2(v1) 126 | v += v2 127 | assert v == Vector2(5.0, 7.0) 128 | v = Vector2(v1) 129 | v += (3.0,4.0) 130 | assert v == Vector2(5.0, 7.0) 131 | v = Vector2(v1) 132 | v -= v2 133 | assert v == Vector2(-1.0, -1.0) 134 | v = Vector2(v1) 135 | v -= (3.0,4.0) 136 | assert v == Vector2(-1.0, -1.0) 137 | v = Vector2(v1) 138 | v *= v2 139 | assert v == 18.0 140 | v = Vector2(v1) 141 | v *= (3.0,4.0) 142 | assert v == 18.0 143 | v = Vector2(v1) 144 | v *= 2 145 | assert v == Vector2(4.0, 6.0) 146 | v = Vector2(v1) 147 | v /= 2 148 | assert v == Vector2(1.0, 1.5) 149 | v = Vector2(v1) 150 | v //= 2 151 | assert v == Vector2(1.0, 1.0) 152 | v = Vector2(v1) 153 | el = v.elementwise() 154 | assert (el + v2) == Vector2(5.0, 7.0) 155 | assert (el - v2) == Vector2(-1.0, -1.0) 156 | assert (el * v2) == Vector2(6.0, 12.0) 157 | vx = (el / v2) 158 | assert (_rd(vx[0]), _rd(vx[1])) == (0.667, 0.75) 159 | assert (el // v2) == Vector2(0.0, 0.0) 160 | assert (v2 + el) == Vector2(5.0, 7.0) 161 | assert (v2 - el) == Vector2(1.0, 1.0) 162 | assert (v2 * el) == Vector2(6.0, 12.0) 163 | vx = (v2 / el) 164 | assert (_rd(vx[0]), _rd(vx[1])) == (1.5, 1.333) 165 | assert (v2 // el) == Vector2(1.0, 1.0) 166 | assert (el + (3.0,4.0)) == Vector2(5.0, 7.0) 167 | assert (el - (3.0,4.0)) == Vector2(-1.0, -1.0) 168 | assert (el * (3.0,4.0)) == Vector2(6.0, 12.0) 169 | vx = (el / (3.0,4.0)) 170 | assert (_rd(vx[0]), _rd(vx[1])) == (0.667, 0.75) 171 | assert (el // (3.0,4.0)) == Vector2(0.0, 0.0) 172 | if env['platform'] != 'js': 173 | assert ((3.0,4.0) + el) == Vector2(5.0, 7.0) 174 | assert ((3.0,4.0) - el) == Vector2(1.0, 1.0) 175 | assert ((3.0,4.0) * el) == Vector2(6.0, 12.0) 176 | vx = ((3.0,4.0) / el) 177 | assert (_rd(vx[0]), _rd(vx[1])) == (1.5, 1.333) 178 | assert ((3.0,4.0) // el) == (1.0, 1.0) 179 | assert (el + 2) == Vector2(4.0, 5.0) 180 | assert (el - 2) == Vector2(0.0, 1.0) 181 | assert (el * 2) == Vector2(4.0, 6.0) 182 | assert (el / 2) == Vector2(1.0, 1.5) 183 | assert (el // 2) == Vector2(1.0, 1.0) 184 | assert (2 + el) == Vector2(4.0, 5.0) 185 | assert (2 - el) == Vector2(0.0, -1.0) 186 | assert (2 * el) == Vector2(4.0, 6.0) 187 | vx = (2 / el) 188 | assert (_rd(vx[0]), _rd(vx[1])) == (1.0, 0.667) 189 | assert (2 // el) == Vector2(1.0, 0.0) 190 | assert (v1 == v1) == True 191 | assert (v1 == v2) == False 192 | assert (v1 != v1) == False 193 | assert (v1 != v2) == True 194 | assert (v1 == Vector2(2.0,3.0)) == True 195 | assert (v1 == Vector2(3.0,4.0)) == False 196 | assert (v1 != Vector2(2.0,3.0)) == False 197 | assert (v1 != Vector2(3.0,4.0)) == True 198 | assert (v1 == 2) == False 199 | assert (v1 != 2) == True 200 | assert (el == el) == True 201 | assert (el != el) == False 202 | if env['platform'] != 'js': ##pyjs cmp obj identity 203 | assert (el == v1) == True 204 | assert (el == v2) == False 205 | assert (el != v1) == False 206 | assert (el != v2) == True 207 | assert (el == (2.0,3.0)) == True 208 | assert (el == (3.0,4.0)) == False 209 | assert (el != (2.0,3.0)) == False 210 | assert (el != (3.0,4.0)) == True 211 | 212 | def test_vector_magnitude(): 213 | Vector2 = pg.Vector2 214 | v1 = Vector2(2.2, 3.0) 215 | v2 = Vector2(6.0, 8.0) 216 | if env['platform'] != 'js': 217 | assert _rd(v1.magnitude()) == _rd(v1.length()) == 3.720 218 | else: #length is js keyword 219 | assert _rd(v1.magnitude()) == 3.720 220 | assert _rd(v1.magnitude_squared()) == _rd(v1.length_squared()) == 13.84 221 | v = v1.normalize() 222 | assert _rd(v.x) == 0.591 and _rd(v.y) == 0.806 223 | v = Vector2(v1) 224 | v.normalize_ip() 225 | assert _rd(v.x) == 0.591 and _rd(v.y) == 0.806 226 | assert v.is_normalized() == True 227 | v.scale_to_length(10) 228 | assert _rd(v.x) == 5.914 and _rd(v.y) == 8.064 229 | assert _rd(v1.distance_to(v2)) == 6.28 230 | assert _rd(v1.distance_squared_to(v2)) == 39.44 231 | 232 | 233 | def test_vector_direction(): 234 | Vector2 = pg.Vector2 235 | v1 = Vector2(2.0, 3.0) 236 | v2 = Vector2(6.0, 8.0) 237 | v3 = Vector2(0.0, 5.0) 238 | v4 = Vector2(0.0, -5.0) 239 | assert _rd(v1.angle_to(v2)) == -3.180 240 | assert _rd(v3.angle_to(v4)) == -180.0 241 | v = v1.rotate(30) 242 | assert _rd(v.x) == 0.232 and _rd(v.y) == 3.598 243 | # if env['platform'] in ('jvm', 'js'): 244 | v = v1.rotate_rad(1.0) 245 | assert _rd(v.x) == -1.444 and _rd(v.y) == 3.304 246 | v = Vector2(v1) 247 | v.rotate_ip(30) 248 | assert _rd(v.x) == 0.232 and _rd(v.y) == 3.598 249 | # if env['platform'] in ('jvm', 'js'): 250 | v = Vector2(v1) 251 | v.rotate_ip_rad(1.0) 252 | assert _rd(v.x) == -1.444 and _rd(v.y) == 3.304 253 | v = v1.reflect(v2) 254 | assert _rd(v.x) == -2.32 and _rd(v.y) == -2.76 255 | v = Vector2(v1) 256 | v.reflect_ip(v2) 257 | assert _rd(v.x) == -2.32 and _rd(v.y) == -2.76 258 | r = v1.as_polar() 259 | assert _rd(r[0]) == 3.606 and _rd(r[1]) == 56.310 260 | v = Vector2(v1) 261 | v.from_polar((3.606, 56.310)) 262 | assert _rd(v.x) == 2.0 and _rd(v.y) == 3.0 263 | 264 | 265 | def test_vector_dot(): 266 | Vector2 = pg.Vector2 267 | v1 = Vector2(2.0, 3.0) 268 | v2 = Vector2(2.0, 0.0) 269 | v3 = Vector2(0.0, 3.0) 270 | v4 = Vector2(-6.0, -8.0) 271 | v5 = Vector2(3.0, 4.0) 272 | assert v1.dot(v2) == 4.0 273 | assert v1.dot(v3) == 9.0 274 | assert v1.dot(v4) == -36.0 275 | assert v1.dot(v5) == 18.0 276 | if not (env['pyjs_opt'] and not env['pyjs_attr']): 277 | assert (v1 * v2) == 4.0 278 | assert (v1 * v3) == 9.0 279 | assert (v1 * v4) == -36.0 280 | assert (v1 * v5) == 18.0 281 | 282 | 283 | def test_vector_cross(): 284 | Vector2 = pg.Vector2 285 | v1 = Vector2(2.0, 3.0) 286 | v2 = Vector2(2.0, 0.0) 287 | v3 = Vector2(0.0, 3.0) 288 | v4 = Vector2(-6.0, -8.0) 289 | v5 = Vector2(3.0, 4.0) 290 | assert v1.cross(v2) == -6.0 291 | assert v1.cross(v3) == 6.0 292 | assert v1.cross(v4) == 2.0 293 | assert v1.cross(v5) == -1.0 294 | 295 | 296 | def test_vector_lerp(): 297 | Vector2 = pg.Vector2 298 | v1 = Vector2(2.0, 3.0) 299 | v2 = Vector2(6.0, 8.0) 300 | v = v1.lerp(v2, 0.0) 301 | assert _rd(v.x) == 2.0 and _rd(v.y) == 3.0 302 | v = v1.lerp(v2, 0.1) 303 | assert _rd(v.x) == 2.4 and _rd(v.y) == 3.5 304 | v = v1.lerp(v2, 0.5) 305 | assert _rd(v.x) == 4.0 and _rd(v.y) == 5.5 306 | v = v1.lerp(v2, 0.9) 307 | assert _rd(v.x) == 5.6 and _rd(v.y) == 7.5 308 | v = v1.lerp(v2, 1.0) 309 | assert _rd(v.x) == 6.0 and _rd(v.y) == 8.0 310 | v = v1.slerp(v2, 0.0) 311 | assert _rd(v.x) == 2.0 and _rd(v.y) == 3.0 312 | v = v1.slerp(v2, 0.1) 313 | assert _rd(v.x) == 2.374 and _rd(v.y) == 3.519 314 | v = v1.slerp(v2, 0.5) 315 | assert _rd(v.x) == 3.929 and _rd(v.y) == 5.553 316 | v = v1.slerp(v2, 0.9) 317 | assert _rd(v.x) == 5.575 and _rd(v.y) == 7.519 318 | v = v1.slerp(v2, 1.0) 319 | assert _rd(v.x) == 6.0 and _rd(v.y) == 8.0 320 | v = v1.slerp(v2, -0.5) 321 | assert _rd(v.x) == -3.929 and _rd(v.y) == -5.553 322 | v = v1.slerp(v2, -1.0) 323 | assert _rd(v.x) == 6.0 and _rd(v.y) == 8.0 324 | v = v1.slerp(v1, 0.5) 325 | assert _rd(v.x) == 2.0 and _rd(v.y) == 3.0 326 | if not (env['pyjs_opt'] and not env['pyjs_attr']): 327 | v = v1.slerp(v1*2.0, 0.5) 328 | assert _rd(v.x) == 3.0 and _rd(v.y) == 4.5 329 | v = v1.slerp(Vector2(-6.0, -8.0), 0.5) 330 | assert _rd(v.x) == -5.553 and _rd(v.y) == 3.929 331 | v = Vector2(5.0, 5.0).slerp(Vector2(-5.0, 5.0), 0.2) 332 | assert _rd(v.x) == 3.210 and _rd(v.y) == 6.300 333 | 334 | --------------------------------------------------------------------------------