├── tests ├── __init__.py └── test_value.py ├── sciter ├── capi │ ├── __init__.py │ ├── sctiscript.py │ ├── scgraphics.py │ ├── screquest.py │ ├── scmsg.py │ ├── scvalue.py │ ├── sctypes.py │ ├── scdom.py │ ├── scdef.py │ ├── scbehavior.py │ └── scapi.py ├── error.py ├── window.py ├── __init__.py ├── host.py ├── platform.py ├── event.py └── value.py ├── setup.cfg ├── examples ├── icon.png ├── archive.zip ├── portable.zip ├── minimal.py ├── insert.py ├── download.py ├── handlers.htm ├── pysciter.py ├── minimal.htm ├── handlers.py ├── plain-win.py └── pysciter.htm ├── .gitignore ├── .editorconfig ├── .github └── workflows │ ├── pylint.yml │ ├── pysciter-tis.yml │ └── pysciter-js.yml ├── LICENSE ├── .appveyor.yml ├── .travis.yml ├── setup.py └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sciter/capi/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 160 3 | ignore = F403,F405,N801,E123,D203 4 | -------------------------------------------------------------------------------- /examples/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sciter-sdk/pysciter/HEAD/examples/icon.png -------------------------------------------------------------------------------- /examples/archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sciter-sdk/pysciter/HEAD/examples/archive.zip -------------------------------------------------------------------------------- /examples/portable.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sciter-sdk/pysciter/HEAD/examples/portable.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env*/* 2 | **/__pycache__/* 3 | *.pyc 4 | *.egg-info/* 5 | /dist/* 6 | /build/* 7 | PySciter.pyproj 8 | PySciter.sln 9 | /.vs/ 10 | /.vscode/ 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | # 4 space indentation 8 | [*.py] 9 | indent_style = space 10 | indent_size = 4 11 | 12 | -------------------------------------------------------------------------------- /examples/minimal.py: -------------------------------------------------------------------------------- 1 | """Minimalistic PySciter sample for Windows.""" 2 | 3 | import sciter 4 | 5 | if __name__ == '__main__': 6 | sciter.runtime_features(file_io=True, allow_sysinfo=True) 7 | 8 | frame = sciter.Window(ismain=True, uni_theme=True) 9 | frame.minimal_menu() 10 | frame.load_file("examples/minimal.htm") 11 | frame.run_app() 12 | -------------------------------------------------------------------------------- /sciter/capi/sctiscript.py: -------------------------------------------------------------------------------- 1 | """TIScript Virtual Machine Runtime. 2 | 3 | Incomplete. 4 | """ 5 | import ctypes 6 | 7 | HVM = ctypes.c_void_p 8 | value = ctypes.c_uint64 9 | 10 | 11 | class tiscript_native_interface(ctypes.Structure): 12 | """.""" 13 | _fields_ = [ 14 | ("create_vm", ctypes.c_void_p), 15 | # TODO: rest of TIScript API 16 | ] 17 | -------------------------------------------------------------------------------- /examples/insert.py: -------------------------------------------------------------------------------- 1 | """Simple DOM example (Go sciter example port).""" 2 | 3 | import sciter, sys 4 | 5 | if __name__ == "__main__": 6 | frame = sciter.Window(ismain=True, uni_theme=False) 7 | frame.set_title("Inserting example") 8 | 9 | # load simple html 10 | frame.load_html(b"""html""") 11 | 12 | # create div and link as child of root node () 13 | div = sciter.Element.create("div", "hello, world") 14 | 15 | root = frame.get_root() 16 | root.insert(div, 0) 17 | 18 | # show window and run app 19 | frame.run_app() 20 | -------------------------------------------------------------------------------- /sciter/capi/scgraphics.py: -------------------------------------------------------------------------------- 1 | """Sciter's platform independent graphics interface. 2 | 3 | Incomplete. 4 | """ 5 | import enum 6 | 7 | from ctypes import Structure, POINTER, c_void_p 8 | from sciter.capi.sctypes import SCFN, UINT, BOOL 9 | 10 | HGFX = c_void_p 11 | HIMG = c_void_p 12 | HPATH = c_void_p 13 | HTEXT = c_void_p 14 | 15 | 16 | class GRAPHIN_RESULT(enum.IntEnum): 17 | """Result value for Sciter Graphics functions.""" 18 | GRAPHIN_PANIC = -1 19 | GRAPHIN_OK = 0 20 | GRAPHIN_BAD_PARAM = 1 21 | GRAPHIN_FAILURE = 2 22 | GRAPHIN_NOTSUPPORTED = 3 23 | # end 24 | 25 | imageCreate = SCFN(GRAPHIN_RESULT, POINTER(HIMG), UINT, UINT, BOOL) 26 | 27 | 28 | class SciterGraphicsAPI(Structure): 29 | """Sciter Graphics ABI.""" 30 | _fields_ = [ 31 | ("imageCreate", imageCreate), 32 | # TODO: rest of Graphics API 33 | ] 34 | 35 | 36 | LPSciterGraphicsAPI = POINTER(SciterGraphicsAPI) 37 | -------------------------------------------------------------------------------- /sciter/error.py: -------------------------------------------------------------------------------- 1 | """Sciter error classes.""" 2 | 3 | 4 | class SciterError(Exception): 5 | """Base class for Sciter exceptions.""" 6 | pass 7 | 8 | 9 | class ScriptError(SciterError): 10 | """Raised by runtime from calling script when script error occured (e.g. bad syntax).""" 11 | def __init__(self, message, script=None): 12 | super().__init__(self, message.replace("\r", "\n")) 13 | self.message = message 14 | self.script = script 15 | 16 | def __repr__(self): 17 | return '%s("%s") at "%s"' % (type(self).__name__, self.message.replace("\r", "\n").rstrip(), self.script if self.script else "<>") 18 | 19 | def __str__(self): 20 | return type(self).__name__ + ": " + self.message.replace("\r", "\n").rstrip() 21 | pass 22 | 23 | 24 | class ScriptException(ScriptError): 25 | """Raised by script by throwing or returning Error instance.""" 26 | def __init__(self, message, script=None): 27 | super().__init__(message, script) 28 | pass 29 | -------------------------------------------------------------------------------- /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | python-version: ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10"] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install flake8 25 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 26 | 27 | - name: Lint with flake8 28 | run: | 29 | echo stop the build if there are Python syntax errors or undefined names 30 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 31 | 32 | echo exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 33 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 pravic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.6.0.{build} 2 | 3 | branches: 4 | only: 5 | - master 6 | - travis 7 | 8 | image: 9 | - Visual Studio 2019 10 | 11 | environment: 12 | matrix: 13 | # Python versions: 14 | # https://www.appveyor.com/docs/windows-images-software/#python 15 | 16 | - TARGET: Python34 17 | ARCH: 32 18 | 19 | - TARGET: Python34-x64 20 | ARCH: 64 21 | 22 | - TARGET: Python35-x64 23 | ARCH: 64 24 | 25 | - TARGET: Python36-x64 26 | ARCH: 64 27 | 28 | - TARGET: Python37-x64 29 | ARCH: 64 30 | 31 | - TARGET: Python38-x64 32 | ARCH: 64 33 | 34 | - TARGET: Python39-x64 35 | ARCH: 64 36 | 37 | - TARGET: Python310-x64 38 | ARCH: 64 39 | 40 | install: 41 | - cmd: echo Testing sciter%ARCH% with %TARGET%. 42 | - cmd: echo Current directory is %APPVEYOR_BUILD_FOLDER% 43 | - cmd: set PATH=C:\%TARGET%;C:\projects\deps;%PATH%; 44 | - python --version 45 | 46 | - mkdir ..\deps 47 | - curl -so "..\deps\sciter.dll" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x%ARCH%/sciter.dll" 48 | 49 | build_script: 50 | - cmd: cd 51 | - python setup.py install 52 | 53 | test_script: 54 | - cmd: cd 55 | - python tests\test_value.py 56 | -------------------------------------------------------------------------------- /sciter/capi/screquest.py: -------------------------------------------------------------------------------- 1 | """Sciter's get resource request object - represents requests made by Element/View.request() functions. 2 | 3 | Incomplete. 4 | """ 5 | 6 | import enum 7 | from ctypes import Structure, POINTER, c_void_p 8 | 9 | from sciter.capi.sctypes import SCFN 10 | 11 | HREQUEST = c_void_p 12 | 13 | 14 | class REQUEST_RESULT(enum.IntEnum): 15 | """.""" 16 | REQUEST_PANIC = -1 # e.g. not enough memory 17 | REQUEST_OK = 0 18 | REQUEST_BAD_PARAM = 1 # bad parameter 19 | REQUEST_FAILURE = 2 # operation failed, e.g. index out of bounds 20 | REQUEST_NOTSUPPORTED = 3 # the platform does not support requested feature 21 | 22 | 23 | class REQUEST_RQ_TYPE(enum.IntEnum): 24 | """.""" 25 | RRT_GET = 1 26 | RRT_POST = 2 27 | RRT_PUT = 3 28 | RRT_DELETE = 4 29 | 30 | 31 | class REQUEST_STATE(enum.IntEnum): 32 | """.""" 33 | RS_PENDING = 0 34 | RS_SUCCESS = 1 35 | RS_FAILURE = 2 36 | 37 | 38 | class SciterResourceType(enum.IntEnum): 39 | """.""" 40 | RT_DATA_HTML = 0 41 | RT_DATA_IMAGE = 1 42 | RT_DATA_STYLE = 2 43 | RT_DATA_CURSOR = 3 44 | RT_DATA_SCRIPT = 4 45 | RT_DATA_RAW = 5 46 | RT_DATA_FONT = 6 47 | RT_DATA_SOUND = 7 48 | 49 | 50 | RequestUse = SCFN(REQUEST_RESULT, HREQUEST) 51 | RequestUnUse = SCFN(REQUEST_RESULT, HREQUEST) 52 | 53 | 54 | class SciterRequestAPI(Structure): 55 | """Sciter Request ABI.""" 56 | _fields_ = [ 57 | ("RequestUse", RequestUse), 58 | ("RequestUnUse", RequestUnUse), 59 | # TODO: rest of Request API 60 | ] 61 | 62 | LPSciterRequestAPI = POINTER(SciterRequestAPI) 63 | -------------------------------------------------------------------------------- /examples/download.py: -------------------------------------------------------------------------------- 1 | """Download http content (Go sciter example port).""" 2 | 3 | import sciter 4 | 5 | class ContentEventHandler(sciter.EventHandler): 6 | """ event handler.""" 7 | 8 | def document_complete(self): 9 | print("content loaded.") 10 | pass 11 | 12 | def on_data_arrived(self, nm): 13 | print("data arrived, uri:", nm.uri, nm.dataSize, "bytes") 14 | pass 15 | pass 16 | 17 | class Frame(sciter.Window): 18 | def __init__(self): 19 | super().__init__(ismain=True, uni_theme=False, debug=True) 20 | pass 21 | 22 | def on_data_load(self, nm): 23 | # called on every html/img/css/etc resource download request 24 | pass 25 | 26 | def on_data_loaded(self, nm): 27 | # called on every downloaded resource 28 | print("data loaded, uri:", nm.uri, nm.dataSize, "bytes") 29 | pass 30 | 31 | def load(self, url): 32 | self.set_title("Download Element Content") 33 | self.load_html(b'''

Url to load: placed here

''', "/") 34 | 35 | # get root element 36 | root = self.get_root() 37 | 38 | # get span#url and frame#content: 39 | span = root.find_first('#url') 40 | content = root.find_first('#content') 41 | 42 | # replace span text with url provided 43 | text = span.get_text() 44 | span.set_text(url) 45 | print("span:", text) 46 | 47 | # install event handler to content frame to print data_arrived events 48 | self.handler = ContentEventHandler(element=content) 49 | 50 | # make http request to download url and place result as inner of #content 51 | print("load content") 52 | content.request_html(url) 53 | pass 54 | pass 55 | 56 | if __name__ == '__main__': 57 | import sys 58 | 59 | print("Sciter version:", sciter.version(as_str=True)) 60 | 61 | url = "http://httpbin.org/html" if len(sys.argv) < 2 else sys.argv[1] 62 | print(url) 63 | 64 | frame = Frame() 65 | frame.load(url) 66 | frame.expand() 67 | frame.run_app(False) 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Based on the "trust" template v0.1.2 2 | # https://github.com/japaric/trust/tree/v0.1.2 3 | 4 | # Ubuntu versions: 5 | # https://docs.travis-ci.com/user/reference/linux/ 6 | dist: xenial 7 | sudo: false 8 | language: python 9 | 10 | notifications: 11 | email: change 12 | 13 | matrix: 14 | include: 15 | # Python versions: 16 | # https://docs.travis-ci.com/user/languages/python/#python-versions 17 | 18 | - os: linux 19 | python: "3.4" 20 | - os: linux 21 | python: "3.7" 22 | - os: linux 23 | python: "3.8" 24 | - os: linux 25 | python: "3.9" 26 | - os: linux 27 | python: "3.10" 28 | 29 | - os: osx 30 | # python comes with `osx_image`, see 31 | # https://blog.travis-ci.com/2019-08-07-extensive-python-testing-on-travis-ci 32 | language: shell 33 | osx_image: xcode10.2 34 | 35 | - os: osx 36 | language: shell 37 | osx_image: xcode12.2 38 | 39 | branches: 40 | only: 41 | - master 42 | - travis 43 | 44 | addons: 45 | apt: 46 | sources: 47 | - ubuntu-toolchain-r-test 48 | 49 | packages: 50 | - libgtk-3-dev 51 | - libgtk-3-0 52 | - libstdc++-6-pic 53 | 54 | 55 | before_install: 56 | - set -e 57 | - python3 --version 58 | 59 | install: 60 | - export SDK_PATH=https://raw.githubusercontent.com/c-smile/sciter-sdk/master 61 | - if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -so "$TRAVIS_BUILD_DIR/libsciter-gtk.so" $SDK_PATH/bin.lnx/x64/libsciter-gtk.so; fi 62 | - if [ "$TRAVIS_OS_NAME" = "osx" ]; then curl -so "$TRAVIS_BUILD_DIR/libsciter.dylib" $SDK_PATH/bin.osx/libsciter.dylib; fi 63 | 64 | - pip3 install -U pip 65 | - pip3 install -U pytest 66 | 67 | before_script: 68 | - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:$TRAVIS_BUILD_DIR"; fi 69 | - if [ "$TRAVIS_OS_NAME" = "osx" ]; then cp "$TRAVIS_BUILD_DIR/libsciter.dylib" "$TRAVIS_BUILD_DIR/liblibsciter.dylib"; fi 70 | 71 | - if [ "$TRAVIS_OS_NAME" = "linux" ]; then export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$TRAVIS_BUILD_DIR"; fi 72 | 73 | - export PATH="$PATH:$TRAVIS_BUILD_DIR" 74 | - export LIBRARY_PATH="$LIBRARY_PATH:$TRAVIS_BUILD_DIR" 75 | 76 | script: 77 | - python3 setup.py develop 78 | - python3 tests/test_value.py 79 | 80 | after_script: set +e 81 | -------------------------------------------------------------------------------- /examples/handlers.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sciter Testing Page 4 | 28 | 53 | 54 | 55 |

Sciter Testing Page

56 |
57 | 58 | 59 | 60 | 61 | 62 |
63 | 64 | 65 |
66 |
67 |
68 | 69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /.github/workflows/pysciter-tis.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | name: Sciter.TIS 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | - travis 10 | 11 | pull_request: 12 | branches: 13 | - master 14 | 15 | jobs: 16 | test: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [macos-latest, ubuntu-latest, windows-latest] 22 | python-version: ["3.8", "3.9", "3.10"] 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - name: Setup Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v2 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Windows deps 33 | if: runner.os == 'Windows' 34 | # Windows: download sciter library 35 | run: curl -sSLo "%SCITER_DEPS%/sciter.dll" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll" 36 | shell: cmd 37 | env: 38 | SCITER_DEPS: ${{ runner.workspace }} 39 | 40 | - name: Linux deps 41 | if: runner.os == 'Linux' 42 | # Linux: download sciter library && install libgtk-3-dev 43 | run: | 44 | curl -so "$SCITER_DEPS/libsciter-gtk.so" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so" 45 | sudo apt-get update -y && sudo apt-get install libgtk-3-dev libgtk-3-0 -y 46 | env: 47 | SCITER_DEPS: ${{ runner.workspace }} 48 | 49 | - name: macOS deps 50 | if: runner.os == 'macOS' 51 | # OSX: download sciter library 52 | run: | 53 | curl -so "$SCITER_DEPS/libsciter.dylib" "https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib" 54 | env: 55 | SCITER_DEPS: ${{ runner.workspace }} 56 | 57 | - name: Install dependencies 58 | shell: bash 59 | run: | 60 | python -m pip install --upgrade pip 61 | pip install pytest 62 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 63 | 64 | - name: Test 65 | shell: bash 66 | env: 67 | SCITER_DEPS: ${{ runner.workspace }} 68 | run: | 69 | export PATH="$PATH:$SCITER_DEPS" 70 | pip install . 71 | python tests/test_value.py 72 | -------------------------------------------------------------------------------- /.github/workflows/pysciter-js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | name: Sciter.JS 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | - travis 10 | 11 | pull_request: 12 | branches: 13 | - master 14 | 15 | jobs: 16 | test: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [macos-latest, ubuntu-latest, windows-latest] 22 | python-version: ["3.8", "3.9", "3.10"] 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - name: Setup Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v2 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Windows deps 33 | if: runner.os == 'Windows' 34 | # Windows: download sciter library 35 | run: curl -sSLo "%SCITER_DEPS%/sciter.dll" "https://raw.githubusercontent.com/c-smile/sciter-js-sdk/main/bin/windows/x64/sciter.dll" 36 | shell: cmd 37 | env: 38 | SCITER_DEPS: ${{ runner.workspace }} 39 | 40 | - name: Linux deps 41 | if: runner.os == 'Linux' 42 | # Linux: download sciter library && install libgtk-3-dev 43 | run: | 44 | curl -so "$SCITER_DEPS/libsciter-gtk.so" "https://raw.githubusercontent.com/c-smile/sciter-js-sdk/main/bin/linux/x64/libsciter-gtk.so" 45 | sudo apt-get update -y && sudo apt-get install libgtk-3-dev libgtk-3-0 -y 46 | env: 47 | SCITER_DEPS: ${{ runner.workspace }} 48 | 49 | - name: macOS deps 50 | if: runner.os == 'macOS' 51 | # OSX: download sciter library 52 | run: | 53 | curl -so "$SCITER_DEPS/libsciter.dylib" "https://raw.githubusercontent.com/c-smile/sciter-js-sdk/main/bin/macosx/libsciter.dylib" 54 | env: 55 | SCITER_DEPS: ${{ runner.workspace }} 56 | 57 | - name: Install dependencies 58 | shell: bash 59 | run: | 60 | python -m pip install --upgrade pip 61 | pip install pytest 62 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 63 | 64 | - name: Test 65 | shell: bash 66 | env: 67 | SCITER_DEPS: ${{ runner.workspace }} 68 | run: | 69 | export PATH="$PATH:$SCITER_DEPS" 70 | pip install . 71 | python tests/test_value.py 72 | -------------------------------------------------------------------------------- /examples/pysciter.py: -------------------------------------------------------------------------------- 1 | """More complex PySciter sample.""" 2 | 3 | import sciter 4 | 5 | # main frame 6 | class Frame(sciter.Window): 7 | def __init__(self): 8 | super().__init__(ismain=True, uni_theme=True) 9 | pass 10 | 11 | def on_subscription(self, groups): 12 | # subscribing only for scripting calls and document events 13 | from sciter.event import EVENT_GROUPS 14 | return EVENT_GROUPS.HANDLE_BEHAVIOR_EVENT | EVENT_GROUPS.HANDLE_SCRIPTING_METHOD_CALL | EVENT_GROUPS.HANDLE_METHOD_CALL 15 | 16 | def on_script_call(self, name, args): 17 | # script calls 18 | print(name, "called from script") 19 | return self.dispatch(name, args) 20 | 21 | 22 | ## @name The following functions are called from scripts: 23 | @sciter.script 24 | def PythonCall(self, arg): 25 | return "Pythonic window (%s)" % str(arg) 26 | 27 | @sciter.script 28 | def GetNativeApi(self): 29 | 30 | def on_add(a, b): 31 | return a + b 32 | 33 | def on_sub(a, b): 34 | raise Exception("sub(%d,%d) raised exception" % (a, b)) 35 | 36 | api = { 'add': on_add, # plain function 37 | 'sub': on_sub, # raised exception will propagated to script 38 | 'mul': lambda a,b: a * b, # lambdas support 39 | } 40 | return api 41 | 42 | @sciter.script 43 | def ScriptCallTest(self): 44 | print("calling 'hello'") 45 | answer = self.call_function('hello', "hello, python") 46 | print("call answer: ", answer) 47 | 48 | print("get and call 'hello'") 49 | answer = self.eval_script('hello') 50 | answer = answer.call('argument', name='on_script_call') 51 | print("get answer: ", answer) 52 | 53 | print("eval 'hello'") 54 | answer = self.eval_script('hello("42");') 55 | print("eval answer: ", answer) 56 | 57 | try: 58 | print("\ncalling 'raise_error'") 59 | answer = self.call_function('raise_error', 17, '42', False) 60 | print("expected ScriptError") 61 | except sciter.ScriptError as e: 62 | print("answer: ", str(e)) 63 | 64 | 65 | try: 66 | print("\nget and call 'raise_error'") 67 | answer = self.eval_script('raise_error') 68 | answer = answer.call('argument', name='on_script_call') 69 | print("expected ScriptError") 70 | except sciter.ScriptError as e: 71 | print("answer: ", str(e)) 72 | 73 | try: 74 | print("\ncalling unexisting function") 75 | answer = self.call_function('raise_error2') 76 | print("expected ScriptError") 77 | except sciter.ScriptError as e: 78 | print("answer: ", str(e)) 79 | return True 80 | 81 | @sciter.script(convert=True, threading=True) 82 | def AsyncThread(self, a: int, b: int) -> int: 83 | print("Handler.AsyncThread(%s, %s) for %s" % (repr(a), repr(b), repr(self))) 84 | print("sleeping for 10 s") 85 | import time 86 | time.sleep(10) 87 | print("resume") 88 | return a + b 89 | 90 | @sciter.script(convert=True, promise=True) 91 | def AsyncTask(self, a: int, b: int) -> int: 92 | print("Handler.AsyncTask(%s, %s) for %s" % (repr(a), repr(b), repr(self))) 93 | print("sleeping for 10 s") 94 | import time 95 | time.sleep(10) 96 | print("resume") 97 | return a + b 98 | pass 99 | ## @} 100 | 101 | # end 102 | 103 | 104 | if __name__ == '__main__': 105 | sciter.runtime_features(allow_sysinfo=True, file_io=True) 106 | 107 | import os 108 | htm = os.path.join(os.path.dirname(__file__), 'pysciter.htm') 109 | frame = Frame() 110 | frame.set_dispatch_options(raw_handlers=False) 111 | frame.load_file(htm) 112 | frame.run_app() 113 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | try: 5 | from setuptools import setup 6 | except ImportError: 7 | from distutils.core import setup 8 | 9 | config = { 10 | 'name': 'PySciter', 11 | 'author': 'pravic', 12 | 'author_email': 'ehysta@gmail.com', 13 | 'description': 'Python bindings for the Sciter - Embeddable HTML/CSS/script engine (cross-platform desktop GUI toolkit).', 14 | 'url': 'https://github.com/sciter-sdk/pysciter/', 15 | 'download_url': 'https://github.com/sciter-sdk/pysciter/releases', 16 | 'bugtrack_url': 'https://github.com/sciter-sdk/pysciter/issues', 17 | 'version': '0.6.9', 18 | 'platforms': ['Windows', 'Linux', 'MacOS X', ], 19 | 'packages': ['sciter', 'sciter.capi'], 20 | 'install_requires': [''], 21 | 'scripts': [], 22 | 'keywords': ['gui', 'sciter', 'javascript', 'tiscript', 'htmlayout', 'html', 'css', 'web', 'cross-platform', ], 23 | 'license': 'MIT', 24 | 'classifiers': [ 25 | 'Development Status :: 5 - Production/Stable', 26 | 'Intended Audience :: Developers', 27 | 'Operating System :: OS Independent', 28 | 'Operating System :: Microsoft :: Windows :: Windows XP', 29 | 'Operating System :: Microsoft :: Windows :: Windows Vista', 30 | 'Operating System :: Microsoft :: Windows :: Windows 7', 31 | 'Operating System :: Microsoft :: Windows :: Windows 10', 32 | 'Operating System :: MacOS :: MacOS X', 33 | 'Operating System :: POSIX :: Linux', 34 | 'Environment :: MacOS X', 35 | 'Environment :: Win32 (MS Windows)', 36 | 'Environment :: X11 Applications :: GTK', 37 | 'Environment :: Web Environment', 38 | 'Programming Language :: C++', 39 | 'Programming Language :: JavaScript', 40 | 'Programming Language :: Python', 41 | 'Programming Language :: Python :: 3', 42 | 'Topic :: Desktop Environment', 43 | 'Topic :: Software Development', 44 | 'Topic :: Software Development :: Libraries :: Application Frameworks', 45 | 'Topic :: Software Development :: Libraries :: Python Modules', 46 | 'Topic :: Software Development :: User Interfaces', 47 | 'Topic :: Software Development :: Widget Sets', 48 | ], 49 | 'long_description': """ 50 | Introduction 51 | ============ 52 | Sciter (https://sciter.com) is an embeddable HTML/CSS/script engine with GPU accelerated rendering for desktop application UI. 53 | It's a compact, single dll/dylib/so file (4-8 mb), engine without any additional dependencies. 54 | 55 | Sciter uses Direct2D GPU accelerated graphics on modern Windows versions and GDI+ on XP. 56 | On OS X, it uses standard CoreGraphics primitives, while the Linux version uses Cairo. 57 | 58 | Sciter uses HTML5 set of elements, implements CSS level 2.1 in full, plus the most popular features of CSS level 3. 59 | It also contains custom CSS extensions that are required to support desktop UI cases. 60 | For example, flex units and various layout managers. 61 | 62 | Check the `screenshot gallery `_ of the desktop UI examples. 63 | 64 | 65 | Installation 66 | ============ 67 | 68 | For installation instructions and usage examples please refer to `github project page `_. 69 | 70 | 71 | Compatibility 72 | ============= 73 | 74 | PySciter requires Python 3.x. 75 | 76 | Sciter works on: 77 | 78 | - Microsoft Windows XP and above (x86/x64) 79 | - macOS v 10.7 and above (64-bit) 80 | - Linux/GTK (GTK v 3.0 and above, 64-bit only) 81 | - Raspberry Pi 82 | 83 | 84 | Feedback and getting involved 85 | ============================= 86 | - PySciter Code Repository: https://github.com/sciter-sdk/pysciter 87 | - Issue tracker: https://github.com/sciter-sdk/pysciter/issues 88 | - Sciter official website: https://sciter.com 89 | - Sciter forum: https://sciter.com/forums/ 90 | - Sciter SDK: https://github.com/c-smile/sciter-sdk 91 | 92 | """, 93 | } 94 | 95 | setup(**config) 96 | -------------------------------------------------------------------------------- /sciter/capi/scmsg.py: -------------------------------------------------------------------------------- 1 | """Message definitions to be passed to the SciterProcX function. 2 | 3 | """ 4 | import enum 5 | 6 | from ctypes import Structure, Union, c_void_p 7 | from sciter.capi.sctypes import UINT, BOOL, HDC, POINT 8 | from sciter.capi.scdef import ELEMENT_BITMAP_RECEIVER 9 | from sciter.capi.scdom import HELEMENT 10 | from sciter.capi.scbehavior import MOUSE_BUTTONS, MOUSE_EVENTS, KEYBOARD_STATES 11 | 12 | class SCITER_X_MSG_CODE(enum.IntEnum): 13 | """SCITER_X_MSG message/function identifier.""" 14 | SXM_CREATE = 0 15 | SXM_DESTROY = 1 16 | SXM_SIZE = 2 17 | SXM_PAINT = 3 18 | SXM_RESOLUTION = 4 19 | SXM_HEARTBIT = 5 20 | SXM_MOUSE = 6 21 | SXM_KEY = 7 22 | SXM_FOCUS = 8 23 | # end 24 | 25 | 26 | class SCITER_X_MSG(Structure): 27 | """Common header of message structures passed to SciterProcX.""" 28 | _fields_ = [ 29 | ("msg", UINT), # SCITER_X_MSG_CODE 30 | ] 31 | 32 | 33 | class SCITER_X_MSG_CREATE(Structure): 34 | """Create event passed to Sciter.""" 35 | _fields_ = [ 36 | ("header", SCITER_X_MSG), 37 | ("backend", UINT), 38 | ("transparent", BOOL), 39 | ] 40 | 41 | 42 | class SCITER_X_MSG_DESTROY(Structure): 43 | """Destroy event passed to Sciter.""" 44 | _fields_ = [ 45 | ("header", SCITER_X_MSG), 46 | ] 47 | 48 | 49 | class SCITER_X_MSG_SIZE(Structure): 50 | _fields_ = [ 51 | ("header", SCITER_X_MSG), 52 | ("width", UINT), 53 | ("height", UINT), 54 | ] 55 | 56 | class SCITER_X_MSG_RESOLUTION(Structure): 57 | _fields_ = [ 58 | ("header", SCITER_X_MSG), 59 | ("pixelsPerInch", UINT), 60 | ] 61 | 62 | class SCITER_X_MSG_MOUSE(Structure): 63 | _fields_ = [ 64 | ("header", SCITER_X_MSG), 65 | #("button", UINT), # this field has been reordered in 4.4.0.3 66 | ("event", UINT), # MOUSE_EVENTS 67 | ("button", UINT), # MOUSE_BUTTONS 68 | ("modifiers", UINT), # KEYBOARD_STATES 69 | ("pos", POINT), 70 | ] 71 | 72 | class SCITER_X_MSG_KEY(Structure): 73 | _fields_ = [ 74 | ("header", SCITER_X_MSG), 75 | #("button", UINT), # this field has been reordered in 4.4.0.3 76 | ("event", UINT), # MOUSE_EVENTS 77 | ("code", UINT), # key scan code 78 | ("modifiers", UINT), # KEYBOARD_STATES 79 | ] 80 | 81 | class SCITER_X_MSG_FOCUS(Structure): 82 | _fields_ = [ 83 | ("header", SCITER_X_MSG), 84 | ("got", BOOL), 85 | ] 86 | 87 | class SCITER_X_MSG_HEARTBIT(Structure): 88 | _fields_ = [ 89 | ("header", SCITER_X_MSG), 90 | ("time", UINT), 91 | 92 | ] 93 | 94 | class SCITER_PAINT_TARGET_TYPE(enum.IntEnum): 95 | SPT_DEFAULT = 0 # default rendering target - window surface 96 | SPT_RECEIVER = 1 # target::receiver fields are valid 97 | SPT_DC = 2 # target::hdc is valid 98 | SPT_OPENGL = 3 # target is not used - caller shall set current context on its side 99 | SPT_OPENGLES = 4 # target is not used - caller shall set current context on its side 100 | 101 | 102 | class SCITER_X_MSG_PAINT_RECEIVER(Structure): 103 | _fields_ = [ 104 | ("param", c_void_p), 105 | ("callback", ELEMENT_BITMAP_RECEIVER), 106 | ] 107 | 108 | 109 | class SCITER_X_MSG_PAINT_TARGET(Union): 110 | _fields_ = [ 111 | ("hdc", HDC), 112 | ("receiver", SCITER_X_MSG_PAINT_RECEIVER), 113 | ] 114 | 115 | 116 | class SCITER_X_MSG_PAINT(Structure): 117 | _fields_ = [ 118 | ("header", SCITER_X_MSG), 119 | ("element", HELEMENT), # layer #HELEMENT, can be NULL if whole tree (document) needs to be rendered. 120 | ("isFore", BOOL), # if element is not null tells if that element is fore-layer. 121 | ("targetType", UINT), # one of SCITER_PAINT_TARGET_TYPE values. 122 | ("target", SCITER_X_MSG_PAINT_TARGET) 123 | ] 124 | -------------------------------------------------------------------------------- /sciter/window.py: -------------------------------------------------------------------------------- 1 | """High level window wrapper.""" 2 | 3 | import sciter.capi.scdef 4 | import sciter.capi.sctypes 5 | import sciter.host 6 | import sciter.event 7 | import sciter.platform 8 | 9 | _api = sciter.SciterAPI() 10 | 11 | 12 | class Window(sciter.platform.BaseWindow, sciter.host.Host, sciter.event.EventHandler): 13 | """Basic Sciter window.""" 14 | 15 | def __init__(self, ismain=False, ispopup=False, ischild=False, resizeable=True, parent=None, uni_theme=False, debug=True, pos=None, size=None, subscription=None): 16 | """Create a new window and setup the sciter and dom callbacks.""" 17 | super().__init__() 18 | from sciter.capi.scdef import SCITER_CREATE_WINDOW_FLAGS 19 | 20 | flags = SCITER_CREATE_WINDOW_FLAGS.SW_CONTROLS 21 | if resizeable: 22 | flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_RESIZEABLE 23 | if ismain: 24 | flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_MAIN | SCITER_CREATE_WINDOW_FLAGS.SW_TITLEBAR 25 | elif ispopup: 26 | flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_POPUP 27 | elif ischild: 28 | flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_CHILD 29 | 30 | if uni_theme: 31 | _api.SciterSetOption(None, sciter.capi.scdef.SCITER_RT_OPTIONS.SCITER_SET_UX_THEMING, True) 32 | 33 | if debug: 34 | flags = flags | SCITER_CREATE_WINDOW_FLAGS.SW_ENABLE_DEBUG 35 | 36 | # New windows can be inspectable. 37 | # Debug messages will be printed always. 38 | # If you need to disable the debug output, 39 | # either call `frame.setup_debug(debug_output=False)` 40 | # or override `Host.on_debug_output`. 41 | self.setup_debug(debug_windows=debug, debug_output=True) 42 | 43 | self.window_flags = flags 44 | self._title_changed = False 45 | 46 | rect = sciter.capi.sctypes.RECT() 47 | if pos is not None: 48 | rect.left = pos[0] 49 | rect.top = pos[1] 50 | if size is None: 51 | raise ValueError("`size` is required if `pos` is provided!") 52 | if size is not None: 53 | rect.right = rect.left + size[0] 54 | rect.bottom = rect.top + size[1] 55 | if not pos and not size: 56 | rect = None 57 | 58 | self.hwnd = self._create(flags, rect=rect, parent=parent) 59 | if not self.hwnd: 60 | raise sciter.SciterError("Could not create window") 61 | 62 | self.setup_callback(self.hwnd) 63 | self.attach(window=self.hwnd, subscription=subscription) 64 | pass 65 | 66 | def collapse(self, hide=False): 67 | """Minimize or hide window.""" 68 | return super().collapse(hide) 69 | 70 | def expand(self, maximize=False): 71 | """Show or maximize window.""" 72 | return super().expand(maximize) 73 | 74 | def dismiss(self): 75 | """Close window.""" 76 | return super().dismiss() 77 | 78 | def set_title(self, title: str): 79 | """Set native window title.""" 80 | self._title_changed = True 81 | return super().set_title(title) 82 | 83 | def get_title(self): 84 | """Get native window title.""" 85 | return super().get_title() 86 | 87 | def minimal_menu(self): 88 | """Construct a minimal menu with a Quit item. Vital for macOS.""" 89 | return super().minimal_menu() 90 | 91 | def run_app(self, show=True): 92 | """Show window and run the main app message loop until window been closed.""" 93 | if show: 94 | self.expand() 95 | ret = super().run_app() 96 | return ret 97 | 98 | def quit_app(self, code=0): 99 | """Post quit message.""" 100 | return super().quit_app(code) 101 | 102 | # overrideable 103 | def _document_ready(self, target): 104 | # Set window title based on content, if any 105 | if self._title_changed: 106 | return 107 | root = sciter.Element(target) 108 | title = root.find_first('html > head > title') 109 | if title: 110 | self.set_title(title.get_text()) 111 | pass 112 | 113 | pass 114 | -------------------------------------------------------------------------------- /examples/minimal.htm: -------------------------------------------------------------------------------- 1 | <html window-icon="icon.png"> 2 | <head> 3 | <title>Minimalistic Sciter demo 4 | 24 | 72 | 117 | 118 | 119 | 120 |

Minimal Sciter Application

121 |

Sciter version x.x.x rev N.

122 |

Running on machine.

123 | 124 | 125 | 126 | 127 |
    128 |
129 | 130 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /sciter/capi/scvalue.py: -------------------------------------------------------------------------------- 1 | """Sciter value, native C interface.""" 2 | 3 | import ctypes 4 | import enum 5 | 6 | FLOAT_VALUE = ctypes.c_double 7 | 8 | 9 | class SCITER_VALUE(ctypes.Structure): 10 | """Sciter value descriptor.""" 11 | _fields_ = [ 12 | ("t", ctypes.c_uint32), 13 | ("u", ctypes.c_uint32), 14 | ("d", ctypes.c_uint64), 15 | ] 16 | 17 | PSCITER_VALUE = ctypes.POINTER(SCITER_VALUE) 18 | 19 | 20 | class VALUE_RESULT(enum.IntEnum): 21 | """Value functions result codes.""" 22 | HV_OK_TRUE = -1 23 | HV_OK = 0 24 | HV_BAD_PARAMETER = 1 25 | HV_INCOMPATIBLE_TYPE = 2 26 | 27 | 28 | class VALUE_TYPE(enum.IntEnum): 29 | """Sciter value types.""" 30 | T_UNDEFINED = 0 31 | T_NULL = 1 32 | T_BOOL = 2 33 | T_INT = 3 34 | T_FLOAT = 4 35 | T_STRING = 5 36 | T_DATE = 6 # INT64 - contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC), a.k.a. FILETIME on Windows 37 | T_CURRENCY = 7 # INT64 - 14.4 fixed number. E.g. dollars = int64 / 10000; 38 | T_LENGTH = 8 # length units, value is int or float, units are VALUE_UNIT_TYPE 39 | T_ARRAY = 9 40 | T_MAP = 10 41 | T_FUNCTION = 11 42 | T_BYTES = 12 # sequence of bytes - e.g. image data 43 | T_OBJECT = 13 # scripting object proxy (TISCRIPT/SCITER) 44 | T_DOM_OBJECT = 14 # DOM object (CSSS!), use get_object_data to get HELEMENT 45 | T_RESOURCE = 15 # 46 | T_RANGE = 16 # integer range N..M 47 | T_DURATION = 17 # time duration in seconds, stored as float 48 | T_ANGLE = 18 # angle value in radians, stored as float 49 | T_COLOR = 19 # color value, stored as 0xAABBGGRR integer 50 | 51 | 52 | class VALUE_UNIT_TYPE(enum.IntEnum): 53 | """Sciter length value subtype.""" 54 | UT_NONE = 0 55 | UT_EM = 1 # height of the element's font. 56 | UT_EX = 2 # height of letter 'x' 57 | UT_PR = 3 # % 58 | UT_SP = 4 # %% "springs", a.k.a. flex units 59 | reserved1 = 5 60 | reserved2 = 6 61 | UT_PX = 7 # pixels 62 | UT_IN = 8 # inches (1 inch = 2.54 centimeters). 63 | UT_CM = 9 # centimeters. 64 | UT_MM = 10 # millimeters. 65 | UT_PT = 11 # points (1 point = 1/72 inches). 66 | UT_PC = 12 # picas (1 pica = 12 points). 67 | UT_DIP = 13 68 | reserved3 = 14 69 | reserved4 = 15 70 | UT_URL = 22 # url in string 71 | 72 | 73 | class VALUE_UNIT_TYPE_DATE(enum.IntEnum): 74 | """Sciter date subtype.""" 75 | DT_HAS_DATE = 0x01 # date contains date portion 76 | DT_HAS_TIME = 0x02 # date contains time portion HH:MM 77 | DT_HAS_SECONDS = 0x04 # date contains time and seconds HH:MM:SS 78 | DT_UTC = 0x10 # T_DATE is known to be UTC. Otherwise it is local date/time 79 | 80 | 81 | class VALUE_UNIT_TYPE_OBJECT(enum.IntEnum): 82 | """Sciter object subtype.""" 83 | UT_OBJECT_ARRAY = 0 # type T_OBJECT of type Array 84 | UT_OBJECT_OBJECT = 1 # type T_OBJECT of type Object 85 | UT_OBJECT_CLASS = 2 # type T_OBJECT of type Class (class or namespace) 86 | UT_OBJECT_NATIVE = 3 # type T_OBJECT of native Type with data slot (LPVOID) 87 | UT_OBJECT_FUNCTION= 4 # type T_OBJECT of type Function 88 | UT_OBJECT_ERROR = 5 # type T_OBJECT of type Error 89 | 90 | 91 | class VALUE_UNIT_TYPE_STRING(enum.IntEnum): 92 | """Sciter string subtype.""" 93 | UT_STRING_STRING = 0 # string 94 | UT_STRING_ERROR = 1 # is an error string 95 | UT_STRING_SECURE = 2 # secure string ("wiped" on destroy) 96 | UT_STRING_FILE = 0xfffe # 97 | UT_STRING_SYMBOL = 0xffff # symbol in tiscript sense 98 | 99 | 100 | class VALUE_STRING_CVT_TYPE(enum.IntEnum): 101 | """Value to string conversion method.""" 102 | CVT_SIMPLE = 0 # simple conversion of terminal values 103 | CVT_JSON_LITERAL = 1 # json literal parsing/emission 104 | CVT_JSON_MAP = 2 # json parsing/emission, it parses as if token '{' already recognized 105 | CVT_XJSON_LITERAL = 3 # x-json parsing/emission, date is emitted as ISO8601 date literal, currency is emitted in the form DDDD$CCC 106 | -------------------------------------------------------------------------------- /examples/handlers.py: -------------------------------------------------------------------------------- 1 | """Sciter handlers sample (Go examples port).""" 2 | 3 | import sciter 4 | 5 | 6 | class RootEventHandler(sciter.EventHandler): 7 | def __init__(self, el, frame): 8 | super().__init__(element=el) 9 | self.parent = frame 10 | pass 11 | 12 | def on_event(self, source, target, code, phase, reason): 13 | he = sciter.Element(source) 14 | #print("-> event:", code, phase, he) 15 | pass 16 | 17 | @sciter.script("mcall") 18 | def method_call(self, *args): 19 | # 20 | # `root.mcall()` (see handlers.htm) calls behavior method of the root dom element (native equivalent is `Element.call_method()`), 21 | # so we need to attach a "behavior" to that element to catch and handle such calls. 22 | # Also it can be handled at script by several ways: 23 | # * `behavior` - Element subclassing with full control 24 | # * `aspect` - provides partial handling by attaching a single function to the dom element 25 | # * manually attaching function to Element via code like `root.mcall = function(args..) {};` 26 | # 27 | print("->mcall args:", "\t".join(map(str, args))) 28 | # explicit null for example, in other cases you can return any python object like None or True 29 | return sciter.Value.null() 30 | 31 | pass 32 | 33 | 34 | class Frame(sciter.Window): 35 | 36 | def __init__(self): 37 | super().__init__(ismain=True, uni_theme=False, debug=False) 38 | self.set_dispatch_options(enable=True, require_attribute=False) 39 | pass 40 | 41 | def test_call(self): 42 | # test sciter call 43 | v = self.call_function('gFunc', "kkk", 555) 44 | print("sciter call successfully:", v) 45 | 46 | # test method call 47 | root = self.get_root() 48 | v = root.call_method('mfn', "method call", 10300) 49 | print("method call successfully:", v) 50 | 51 | # test function call 52 | v = root.call_function('gFunc', "function call", 10300) 53 | print("function call successfully:", v) 54 | pass 55 | 56 | # Functions called from script: 57 | 58 | #@sciter.script - optional attribute here because of self.set_dispatch_options() 59 | def kkk(self): 60 | print("kkk called!") 61 | def fn(*args): 62 | print("%d: %s" % ( len(args), ",".join(map(str, args)) )) 63 | return "native functor called" 64 | rv = {} 65 | rv['num'] = 1000 66 | rv['str'] = "a string" 67 | rv['f'] = fn 68 | return rv 69 | 70 | @sciter.script 71 | def sumall(self, *args): 72 | sum = 0 73 | for v in args: 74 | sum += v 75 | return sum 76 | 77 | @sciter.script("gprintln") 78 | def gprint(self, *args): 79 | print("->", " ".join(map(str, args))) 80 | pass 81 | 82 | def on_load_data(self, nm): 83 | print("loading", nm.uri) 84 | pass 85 | 86 | def on_data_loaded(self, nm): 87 | print("loaded ", nm.uri) 88 | pass 89 | 90 | def on_event(self, source, target, code, phase, reason): 91 | # events from html controls (behaviors) 92 | he = sciter.Element(source) 93 | #print(".. event:", code, phase) 94 | 95 | # TODO: following statement looks ugly. 96 | # Guess it wasn't a nice idea to split event mask to separate code and phase values 97 | # Or we may pack all event arguments to single object (dict) to eliminate such parameters bloat 98 | # 99 | if code == sciter.event.BEHAVIOR_EVENTS.BUTTON_CLICK and phase == sciter.event.PHASE_MASK.SINKING and he.test('#native'): 100 | print("native button clicked!") 101 | return True 102 | pass 103 | 104 | pass 105 | 106 | if __name__ == "__main__": 107 | print("Sciter version:", sciter.version(as_str=True)) 108 | 109 | # create window 110 | frame = Frame() 111 | 112 | # enable debug only for this window 113 | frame.setup_debug() 114 | 115 | # load file 116 | frame.load_file("examples/handlers.htm") 117 | #frame.load_html(b"""""") 118 | 119 | # install additional handler 120 | ev2 = RootEventHandler(frame.get_root(), frame) 121 | 122 | frame.test_call() 123 | 124 | frame.run_app() 125 | -------------------------------------------------------------------------------- /examples/plain-win.py: -------------------------------------------------------------------------------- 1 | """Sciter sample for Win32 API.""" 2 | 3 | # sciter import 4 | import sciter 5 | from sciter import sapi 6 | from sciter.capi.scdef import * 7 | 8 | # ctypes import 9 | from ctypes import * 10 | from ctypes.wintypes import * 11 | 12 | # defs 13 | WS_EX_APPWINDOW = 0x40000 14 | WS_OVERLAPPEDWINDOW = 0xcf0000 15 | WS_CAPTION = 0xc00000 16 | 17 | SW_SHOWNORMAL = 1 18 | SW_SHOW = 5 19 | 20 | CS_HREDRAW = 2 21 | CS_VREDRAW = 1 22 | 23 | CW_USEDEFAULT = 0x80000000 24 | 25 | WM_DESTROY = 2 26 | 27 | WHITE_BRUSH = 0 28 | 29 | IDC_ARROW = 31514 30 | 31 | WNDPROCTYPE = WINFUNCTYPE(LRESULT, HWND, c_uint, WPARAM, LPARAM) 32 | 33 | 34 | class WNDCLASSEX(Structure): 35 | _fields_ = [ 36 | ("cbSize", c_uint), 37 | ("style", c_uint), 38 | ("lpfnWndProc", WNDPROCTYPE), 39 | ("cbClsExtra", c_int), 40 | ("cbWndExtra", c_int), 41 | ("hInstance", HANDLE), 42 | ("hIcon", HANDLE), 43 | ("hCursor", HANDLE), 44 | ("hBrush", HANDLE), 45 | ("lpszMenuName", LPCWSTR), 46 | ("lpszClassName", LPCWSTR), 47 | ("hIconSm", HANDLE)] 48 | 49 | 50 | def on_load_data(ld): 51 | """Custom documents loader, just for example.""" 52 | uri = ld.uri 53 | uri = uri 54 | return 0 55 | 56 | 57 | def on_create_behavior(ld): 58 | """Custom behavior factory, just for example.""" 59 | name = ld.behaviorName 60 | name = name 61 | return 0 62 | 63 | 64 | def on_sciter_callback(pld, param): 65 | """Sciter notifications callback.""" 66 | ld = pld.contents 67 | if ld.code == SciterNotification.SC_LOAD_DATA: 68 | return on_load_data(cast(pld, POINTER(SCN_LOAD_DATA)).contents) 69 | elif ld.code == SciterNotification.SC_ATTACH_BEHAVIOR: 70 | return on_create_behavior(cast(pld, POINTER(SCN_ATTACH_BEHAVIOR)).contents) 71 | return 0 72 | 73 | 74 | def on_wnd_message(hWnd, Msg, wParam, lParam): 75 | """WindowProc Function.""" 76 | handled = BOOL(0) 77 | lr = sapi.SciterProcND(hWnd, Msg, wParam, lParam, byref(handled)) 78 | if handled: 79 | return lr 80 | 81 | if Msg == WM_DESTROY: 82 | windll.user32.PostQuitMessage(0) 83 | return 0 84 | 85 | try: 86 | return windll.user32.DefWindowProcW(hWnd, Msg, wParam, lParam) 87 | except: 88 | import traceback 89 | etype, evalue, estack = sys.exc_info() 90 | print("WndProc exception: %X, 0x%04X, 0x%X, 0x%X" % (hWnd, Msg, wParam, lParam)) 91 | traceback.print_exception(etype, evalue, estack) 92 | return 0 93 | 94 | 95 | def main(): 96 | clsname = sapi.SciterClassName() 97 | sciter.runtime_features(allow_sysinfo=True) 98 | 99 | title = u"Win32 Sciter" 100 | clsname = u"PySciter" 101 | 102 | windll.user32.DefWindowProcW.argtypes = [HWND, c_uint, WPARAM, LPARAM] 103 | windll.user32.DefWindowProcW.restype = LRESULT 104 | 105 | WndProc = WNDPROCTYPE(on_wnd_message) 106 | wndClass = WNDCLASSEX() 107 | wndClass.cbSize = sizeof(WNDCLASSEX) 108 | wndClass.style = CS_HREDRAW | CS_VREDRAW 109 | wndClass.lpfnWndProc = WndProc 110 | wndClass.cbClsExtra = 0 111 | wndClass.cbWndExtra = 0 112 | wndClass.hInstance = windll.kernel32.GetModuleHandleW(0) 113 | wndClass.hIcon = 0 114 | wndClass.hCursor = windll.user32.LoadCursorW(0, IDC_ARROW) 115 | wndClass.hBrush = windll.gdi32.GetStockObject(WHITE_BRUSH) 116 | wndClass.lpszMenuName = 0 117 | wndClass.lpszClassName = clsname 118 | wndClass.hIconSm = 0 119 | 120 | if not windll.user32.RegisterClassExW(byref(wndClass)): 121 | err = windll.kernel32.GetLastError() 122 | print('Failed to register window: ', err) 123 | exit(0) 124 | 125 | hWnd = windll.user32.CreateWindowExW(0, clsname, title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, 0, 0, 0, 0) 126 | if not hWnd: 127 | err = windll.kernel32.GetLastError() 128 | print('Failed to create window: ', err) 129 | exit(0) 130 | 131 | scproc = SciterHostCallback(on_sciter_callback) 132 | sapi.SciterSetCallback(hWnd, scproc, None) 133 | 134 | url = u"examples/minimal.htm" 135 | sapi.SciterLoadFile(hWnd, url) 136 | 137 | windll.user32.ShowWindow(hWnd, SW_SHOW) 138 | windll.user32.UpdateWindow(hWnd) 139 | 140 | msg = MSG() 141 | lpmsg = pointer(msg) 142 | 143 | print('Entering message loop') 144 | while windll.user32.GetMessageW(lpmsg, 0, 0, 0) != 0: 145 | windll.user32.TranslateMessage(lpmsg) 146 | windll.user32.DispatchMessageW(lpmsg) 147 | 148 | print('Quit.') 149 | 150 | if __name__ == '__main__': 151 | main() 152 | -------------------------------------------------------------------------------- /sciter/__init__.py: -------------------------------------------------------------------------------- 1 | """Sciter bindings for Python. 2 | 3 | Read about library at github: https://github.com/sciter-sdk/pysciter. 4 | 5 | This component uses Sciter Engine, 6 | copyright Terra Informatica Software, Inc. 7 | (http://terrainformatica.com/). 8 | 9 | :license: MIT 10 | 11 | Bindings library licensed under [MIT license](https://opensource.org/licenses/MIT). 12 | Sciter Engine has the [own license terms](https://sciter.com/prices/) 13 | and [end used license agreement](https://github.com/c-smile/sciter-sdk/blob/master/license.htm) 14 | for SDK usage. 15 | 16 | """ 17 | 18 | from .capi.scapi import SciterAPI 19 | from .capi.sctypes import SCITER_WIN, SCITER_OSX, SCITER_LNX 20 | from .capi.scdef import SCITER_RT_OPTIONS 21 | 22 | from .value import value as Value 23 | from .window import Window 24 | from .dom import Element 25 | from .event import EventHandler 26 | from .error import SciterError, ScriptError, ScriptException 27 | 28 | sapi = api = SciterAPI() 29 | gapi = sapi.GetSciterGraphicsAPI if sapi else None 30 | rapi = sapi.GetSciterRequestAPI if sapi else None 31 | 32 | 33 | def version(as_str=False): 34 | """Return version of Sciter engine as (3,3,1,7) tuple or '3.3.1.7' string.""" 35 | high = api.SciterVersion(True) 36 | low = api.SciterVersion(False) 37 | ver = (high >> 16, high & 0xFFFF, low >> 16, low & 0xFFFF) 38 | return ".".join(map(str, ver)) if as_str else ver 39 | 40 | 41 | def version_num(): 42 | """Return version of Sciter engine as 0x03030107 number.""" 43 | # However, `4.0.2.5257` can't be represented as a 32-bit number, we return `0x04_00_02_00` instead. 44 | a, b, c, _ = version() 45 | return (a << 24) | (b << 16) | (c << 8) | (0) 46 | 47 | def api_version(): 48 | """Return Sciter API version number, since 4.4.0.3.""" 49 | # `0x0000_0001` in regular builds 50 | # `0x0001_0001` in windowless versions. 51 | return api.version 52 | 53 | def is_windowless(): 54 | """Returns True for windowless builds.""" 55 | return api_version() >= 0x00010001 56 | 57 | def set_option(option, value): 58 | """Set various sciter engine global options, see the SCITER_RT_OPTIONS.""" 59 | ok = api.SciterSetOption(None, option, value) 60 | if not ok: 61 | raise SciterError("Could not set option " + str(option) + "=" + str(value)) 62 | return True 63 | 64 | def runtime_features(file_io=True, socket_io=True, allow_eval=True, allow_sysinfo=True): 65 | """Set runtime features that have been disabled by default since 4.2.5.0""" 66 | from .capi.scdef import SCRIPT_RUNTIME_FEATURES 67 | flags = 0 68 | if file_io: 69 | flags += SCRIPT_RUNTIME_FEATURES.ALLOW_FILE_IO 70 | if socket_io: 71 | flags += SCRIPT_RUNTIME_FEATURES.ALLOW_SOCKET_IO 72 | if allow_eval: 73 | flags += SCRIPT_RUNTIME_FEATURES.ALLOW_EVAL 74 | if allow_sysinfo: 75 | flags += SCRIPT_RUNTIME_FEATURES.ALLOW_SYSINFO 76 | return set_option(SCITER_RT_OPTIONS.SCITER_SET_SCRIPT_RUNTIME_FEATURES, flags) 77 | 78 | def script(name=None, convert=True, safe=True, threading=False, promise=False): 79 | """Annotation decorator for the functions that called from script.""" 80 | # @script def -> script(def) 81 | # @script('name') def -> script(name)(def) 82 | 83 | # `convert`: Convert Sciter values to Python types 84 | # `safe`: Pass exceptions to Sciter or ignore them 85 | # `threading`: Call the handler in a separate thread (concurrent.futures.ThreadPoolExecutor) 86 | # `promise`: Call the handler in a separate thread as a promise 87 | if threading and promise: 88 | raise SciterError("Don't mix `threading` and `promise` in @script") 89 | 90 | def decorator(func): 91 | attr = True if name is None else name 92 | func._from_sciter = attr 93 | func._sciter_cfg = dict(name=name, convert=convert, safe=safe, threading=threading, promise=promise) 94 | return func 95 | 96 | # script('name') 97 | if name is None or isinstance(name, str): 98 | return decorator 99 | 100 | # script(def) 101 | func = name 102 | name = None 103 | return decorator(func) 104 | 105 | def async_script(name=None, convert=True, safe=True): 106 | """Annotation decorator for async functions that called from script.""" 107 | # @async_script def -> async_script(def) 108 | # @async_script('name') def -> async_script(name)(def) 109 | 110 | # `convert`: Convert Sciter values to Python types 111 | # `safe`: Pass exceptions to Sciter or ignore them 112 | # `promise`: Call the handler in a separate thread as a promise (always true) 113 | 114 | def decorator(func): 115 | attr = True if name is None else name 116 | func._from_sciter = attr 117 | func._sciter_cfg = dict(name=name, convert=convert, safe=safe, threading=False, promise=True) 118 | return func 119 | 120 | # async_script('name') 121 | if name is None or isinstance(name, str): 122 | return decorator 123 | 124 | # async_script(def) 125 | func = name 126 | name = None 127 | return decorator(func) 128 | -------------------------------------------------------------------------------- /examples/pysciter.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | PySciter sample 4 | 16 | 103 | 146 | 147 | 148 | 149 |

Pythonic Sciter Application

150 |

Sciter version x.x.x rev N.

151 |

Running on machine

152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /sciter/capi/sctypes.py: -------------------------------------------------------------------------------- 1 | """Sciter platform-dependent types.""" 2 | 3 | import sys 4 | import ctypes 5 | 6 | from ctypes import (POINTER, 7 | c_char, c_byte, c_ubyte, 8 | c_void_p, c_char_p, 9 | c_int32, c_uint32, c_int64, c_uint64, 10 | c_longlong, c_ulonglong, c_double, 11 | sizeof, c_size_t, c_ssize_t) 12 | 13 | 14 | # 'win32', 'darwin', 'linux' 15 | SCITER_OS = sys.platform 16 | SCITER_WIN = SCITER_OS == 'win32' 17 | SCITER_OSX = SCITER_OS == 'darwin' 18 | SCITER_LNX = SCITER_OS == 'linux' 19 | 20 | 21 | def utf16tostr(addr, size=-1): 22 | """Read UTF-16 string from memory and encode as python string.""" 23 | if addr is None: 24 | return None 25 | 26 | cb = size if size > 0 else 32 27 | bstr = ctypes.string_at(addr, cb) 28 | if size >= 0: 29 | return bstr.decode('utf-16le') 30 | 31 | # lookup zero char 32 | chunks = [] 33 | while True: 34 | found = cb 35 | for i in range(0, cb, 2): 36 | c = bstr[i] 37 | if c == 0x00: 38 | found = i 39 | break 40 | pass 41 | assert found % 2 == 0, "truncated string with len " + str(found) 42 | chunks.append(bstr[0:found].decode('utf-16le')) 43 | if found != cb: 44 | break 45 | addr = addr + cb 46 | bstr = ctypes.string_at(addr, cb) 47 | continue 48 | return "".join(chunks) 49 | 50 | 51 | class c_utf16_p(ctypes.c_char_p): 52 | """A ctypes wrapper for UTF-16 string pointer.""" 53 | # Taken from https://stackoverflow.com/a/35507014/736762, thanks to @eryksun. 54 | def __init__(self, value=None): 55 | super(c_utf16_p, self).__init__() 56 | if value is not None: 57 | self.value = value 58 | 59 | @property 60 | def value(self, 61 | c_void_p=ctypes.c_void_p): 62 | addr = c_void_p.from_buffer(self).value 63 | return utf16tostr(addr) 64 | 65 | @value.setter 66 | def value(self, value, 67 | c_char_p=ctypes.c_char_p): 68 | value = value.encode('utf-16le') + b'\x00' 69 | c_char_p.value.__set__(self, value) 70 | 71 | @classmethod 72 | def from_param(cls, obj): 73 | if isinstance(obj, str): 74 | obj = obj.encode('utf-16le') + b'\x00' 75 | return super(c_utf16_p, cls).from_param(obj) 76 | 77 | @classmethod 78 | def _check_retval_(cls, result): 79 | return result.value 80 | pass 81 | 82 | 83 | class UTF16LEField(object): 84 | """Structure member wrapper for UTF-16 string pointers.""" 85 | # Taken from https://stackoverflow.com/a/35507014/736762, thanks to @eryksun. 86 | def __init__(self, name): 87 | self.name = name 88 | 89 | def __get__(self, obj, cls, 90 | c_void_p=ctypes.c_void_p, 91 | addressof=ctypes.addressof): 92 | field_addr = addressof(obj) + getattr(cls, self.name).offset 93 | addr = c_void_p.from_address(field_addr).value 94 | return utf16tostr(addr) 95 | 96 | def __set__(self, obj, value): 97 | value = value.encode('utf-16le') + b'\x00' 98 | setattr(obj, self.name, value) 99 | pass 100 | 101 | 102 | if SCITER_WIN: 103 | # sciter.dll since 4.0.0.0 104 | SCITER_DLL_NAME = "sciter" 105 | SCITER_DLL_EXT = ".dll" 106 | 107 | SCFN = ctypes.WINFUNCTYPE 108 | SC_CALLBACK = ctypes.WINFUNCTYPE 109 | 110 | HWINDOW = c_void_p # HWND 111 | HDC = c_void_p # HDC 112 | 113 | BOOL = c_int32 114 | LPCWSTR = LPWSTR = ctypes.c_wchar_p 115 | 116 | ID2D1RenderTarget = c_void_p 117 | ID2D1Factory = c_void_p 118 | IDWriteFactory = c_void_p 119 | 120 | IDXGISwapChain = c_void_p 121 | IDXGISurface = c_void_p 122 | 123 | elif SCITER_OSX: 124 | # sciter-osx-32 since 3.3.1.8 125 | # libsciter since 4.4.6.3 126 | SCITER_DLL_NAME = "libsciter" 127 | SCITER_DLL_EXT = ".dylib" 128 | 129 | SCFN = ctypes.CFUNCTYPE 130 | SC_CALLBACK = ctypes.CFUNCTYPE 131 | 132 | HWINDOW = c_void_p # NSView* 133 | HDC = c_void_p # CGContextRef 134 | 135 | BOOL = c_byte 136 | LPCWSTR = LPWSTR = c_utf16_p 137 | 138 | elif SCITER_LNX: 139 | # libsciter since 3.3.1.7 140 | # libsciter-gtk.so instead of libsciter-gtk-64.so since 4.1.4 141 | SCITER_DLL_NAME = "libsciter-gtk" 142 | SCITER_DLL_EXT = ".so" 143 | 144 | SCFN = ctypes.CFUNCTYPE 145 | SC_CALLBACK = ctypes.CFUNCTYPE 146 | 147 | HWINDOW = c_void_p # GtkWidget* 148 | HDC = c_void_p # cairo_t 149 | 150 | BOOL = c_byte 151 | LPCWSTR = LPWSTR = c_utf16_p 152 | 153 | 154 | # Common types 155 | 156 | VOID = None 157 | nullptr = POINTER(c_int32)() 158 | 159 | BYTE = c_byte 160 | INT = c_int32 161 | UINT = c_uint32 162 | INT64 = c_int64 163 | tiscript_value = c_uint64 164 | 165 | # must be pointer-wide 166 | # WPARAM is defined as UINT_PTR (unsigned type) 167 | # LPARAM is defined as LONG_PTR (signed type) 168 | WPARAM = c_size_t 169 | LPARAM = c_ssize_t 170 | 171 | UINT_PTR = c_size_t 172 | LRESULT = c_ssize_t 173 | 174 | PBOOL = LPBOOL = POINTER(BOOL) 175 | LPCBYTE = c_char_p 176 | LPCSTR = LPSTR = c_char_p 177 | LPCVOID = LPVOID = c_void_p 178 | LPUINT = POINTER(UINT) 179 | 180 | 181 | class RECT(ctypes.Structure): 182 | """Rectangle coordinates structure.""" 183 | _fields_ = [("left", c_int32), 184 | ("top", c_int32), 185 | ("right", c_int32), 186 | ("bottom", c_int32)] 187 | tagRECT = _RECTL = RECTL = RECT 188 | PRECT = LPRECT = POINTER(RECT) 189 | 190 | 191 | class POINT(ctypes.Structure): 192 | """Point coordinates structure.""" 193 | _fields_ = [("x", c_int32), 194 | ("y", c_int32)] 195 | tagPOINT = _POINTL = POINTL = POINT 196 | PPOINT = LPPOINT = POINTER(POINT) 197 | 198 | 199 | class SIZE(ctypes.Structure): 200 | """SIZE structure for width and height.""" 201 | _fields_ = [("cx", c_int32), 202 | ("cy", c_int32)] 203 | tagSIZE = SIZEL = SIZE 204 | PSIZE = LPSIZE = POINTER(SIZE) 205 | 206 | 207 | class MSG(ctypes.Structure): 208 | """MSG structure for windows message queue.""" 209 | _fields_ = [("hWnd", HWINDOW), 210 | ("message", c_uint32), 211 | ("wParam", WPARAM), 212 | ("lParam", LPARAM), 213 | ("time", c_uint32), 214 | ("pt", POINT)] 215 | 216 | PMSG = LPMSG = POINTER(MSG) 217 | -------------------------------------------------------------------------------- /sciter/capi/scdom.py: -------------------------------------------------------------------------------- 1 | """DOM access methods, C interface.""" 2 | 3 | import enum 4 | import ctypes 5 | 6 | from sciter.capi.sctypes import INT, UINT, LPCWSTR, UTF16LEField 7 | 8 | HELEMENT = ctypes.c_void_p 9 | HNODE = ctypes.c_void_p 10 | HRANGE = ctypes.c_void_p 11 | HSARCHIVE = ctypes.c_void_p 12 | 13 | 14 | class SCDOM_RESULT(enum.IntEnum): 15 | """Result value for Sciter DOM functions.""" 16 | 17 | # function completed successfully 18 | SCDOM_OK = 0 19 | 20 | # invalid HWINDOW 21 | SCDOM_INVALID_HWND = 1 22 | 23 | # invalid HELEMENT 24 | SCDOM_INVALID_HANDLE = 2 25 | 26 | # attempt to use HELEMENT which is not marked by Sciter_UseElement() 27 | SCDOM_PASSIVE_HANDLE = 3 28 | 29 | # parameter is invalid, e.g. pointer is null 30 | SCDOM_INVALID_PARAMETER = 4 31 | 32 | # operation failed, e.g. invalid html in SciterSetElementHtml() 33 | SCDOM_OPERATION_FAILED = 5 34 | 35 | SCDOM_OK_NOT_HANDLED = (-1) 36 | # end 37 | 38 | 39 | class HPOSITION(ctypes.Structure): 40 | """.""" 41 | _fields_ = [ 42 | ("hn", HNODE), 43 | ("pos", INT), 44 | ] 45 | 46 | 47 | class METHOD_PARAMS(ctypes.Structure): 48 | """.""" 49 | _fields_ = [("methodID", UINT), ] 50 | 51 | 52 | class REQUEST_PARAM(ctypes.Structure): 53 | """.""" 54 | _fields_ = [ 55 | ("_name", LPCWSTR), 56 | ("_value", LPCWSTR), 57 | ] 58 | name = UTF16LEField('_name') 59 | value = UTF16LEField('_value') 60 | 61 | 62 | class NODE_TYPE(enum.IntEnum): 63 | """.""" 64 | NT_ELEMENT = 0 65 | NT_TEXT = 1 66 | NT_COMMENT = 2 67 | 68 | 69 | class NODE_INS_TARGET(enum.IntEnum): 70 | """.""" 71 | NIT_BEFORE = 0 72 | NIT_AFTER = 1 73 | NIT_APPEND = 2 74 | NIT_PREPEND = 3 75 | 76 | 77 | class ELEMENT_AREAS(enum.IntEnum): 78 | """Bounding rectangle of the element.""" 79 | 80 | ROOT_RELATIVE = 0x01 # - or this flag if you want to get HTMLayout window relative coordinates, 81 | SELF_RELATIVE = 0x02 # - "or" this flag if you want to get coordinates relative to the origin 82 | CONTAINER_RELATIVE = 0x03 # - position inside immediate container. 83 | VIEW_RELATIVE = 0x04 # - position relative to view - HTMLayout window 84 | 85 | CONTENT_BOX = 0x00 # content (inner) box 86 | PADDING_BOX = 0x10 # content + paddings 87 | BORDER_BOX = 0x20 # content + paddings + border 88 | MARGIN_BOX = 0x30 # content + paddings + border + margins 89 | 90 | BACK_IMAGE_AREA = 0x40 # relative to content origin - location of background image (if it set no-repeat) 91 | FORE_IMAGE_AREA = 0x50 # relative to content origin - location of foreground image (if it set no-repeat) 92 | 93 | SCROLLABLE_AREA = 0x60 # scroll_area - scrollable area in content box 94 | 95 | 96 | class SCITER_SCROLL_FLAGS(enum.IntEnum): 97 | """.""" 98 | SCROLL_TO_TOP = 0x01 99 | SCROLL_SMOOTH = 0x10 100 | 101 | 102 | class SET_ELEMENT_HTML(enum.IntEnum): 103 | """.""" 104 | SIH_REPLACE_CONTENT = 0 # replace content of the element 105 | SIH_INSERT_AT_START = 1 # insert html before first child of the element 106 | SIH_APPEND_AFTER_LAST = 2 # insert html after last child of the element 107 | SOH_REPLACE = 3 # replace element by html, a.k.a. element.outerHtml = "something" 108 | SOH_INSERT_BEFORE = 4 # insert html before the element 109 | SOH_INSERT_AFTER = 5 # insert html after the element 110 | 111 | 112 | class ELEMENT_STATE_BITS(enum.IntEnum): 113 | """Runtime DOM element state.""" 114 | STATE_LINK = 0x00000001 115 | STATE_HOVER = 0x00000002 116 | STATE_ACTIVE = 0x00000004 117 | STATE_FOCUS = 0x00000008 118 | STATE_VISITED = 0x00000010 119 | STATE_CURRENT = 0x00000020 # current (hot) item 120 | STATE_CHECKED = 0x00000040 # element is checked (or selected) 121 | STATE_DISABLED = 0x00000080 # element is disabled 122 | STATE_READONLY = 0x00000100 # readonly input element 123 | STATE_EXPANDED = 0x00000200 # expanded state - nodes in tree view 124 | STATE_COLLAPSED = 0x00000400 # collapsed state - nodes in tree view - mutually exclusive with 125 | STATE_INCOMPLETE = 0x00000800 # one of fore/back images requested but not delivered 126 | STATE_ANIMATING = 0x00001000 # is animating currently 127 | STATE_FOCUSABLE = 0x00002000 # will accept focus 128 | STATE_ANCHOR = 0x00004000 # anchor in selection (used with current in selects) 129 | STATE_SYNTHETIC = 0x00008000 # this is a synthetic element - don't emit it's head/tail 130 | STATE_OWNS_POPUP = 0x00010000 # this is a synthetic element - don't emit it's head/tail 131 | STATE_TABFOCUS = 0x00020000 # focus gained by tab traversal 132 | STATE_EMPTY = 0x00040000 # empty - element is empty (text.size() == 0 && subs.size() == 0) 133 | # if element has behavior attached then the behavior is responsible for the value of this flag. 134 | STATE_BUSY = 0x00080000 # busy; loading 135 | 136 | STATE_DRAG_OVER = 0x00100000 # drag over the block that can accept it (so is current drop target). Flag is set for the drop target block 137 | STATE_DROP_TARGET = 0x00200000 # active drop target. 138 | STATE_MOVING = 0x00400000 # dragging/moving - the flag is set for the moving block. 139 | STATE_COPYING = 0x00800000 # dragging/copying - the flag is set for the copying block. 140 | STATE_DRAG_SOURCE = 0x01000000 # element that is a drag source. 141 | STATE_DROP_MARKER = 0x02000000 # element is drop marker 142 | 143 | STATE_PRESSED = 0x04000000 # pressed - close to active but has wider life span - e.g. in MOUSE_UP it 144 | # is still on; so behavior can check it in MOUSE_UP to discover CLICK condition. 145 | STATE_POPUP = 0x08000000 # this element is out of flow - popup 146 | 147 | STATE_IS_LTR = 0x10000000 # the element or one of its containers has dir=ltr declared 148 | STATE_IS_RTL = 0x20000000 # the element or one of its containers has dir=rtl declared 149 | 150 | 151 | class REQUEST_TYPE(enum.IntEnum): 152 | """.""" 153 | GET_ASYNC = 0 154 | POST_ASYNC = 1 155 | GET_SYNC = 2 156 | POST_SYNC = 3 157 | 158 | 159 | class CTL_TYPE(enum.IntEnum): 160 | """DOM control type. Note: it was changed in 4.0.3.""" 161 | CTL_NO = 0 # This dom element has no behavior at all. 162 | CTL_UNKNOWN = 1 # This dom element has behavior but its type is unknown. 163 | CTL_EDIT = 2 # Single line edit box. 164 | CTL_NUMERIC = 3 # Numeric input with optional spin buttons. 165 | CTL_CLICKABLE = 4 # toolbar button, behavior:clickable. 166 | CTL_BUTTON = 5 # Command button. 167 | CTL_CHECKBOX = 6 # CheckBox (button). 168 | CTL_RADIO = 7 # OptionBox (button). 169 | CTL_SELECT_SINGLE = 8 # Single select, ListBox or TreeView. 170 | CTL_SELECT_MULTIPLE = 9 # Multiselectable select, ListBox or TreeView. 171 | CTL_DD_SELECT = 10 # Dropdown single select. 172 | CTL_TEXTAREA = 11 # Multiline TextBox. 173 | CTL_HTMLAREA = 12 # HTML selection behavior. 174 | CTL_PASSWORD = 13 # Password input element. 175 | CTL_PROGRESS = 14 # Progress element. 176 | CTL_SLIDER = 15 # Slider input element. 177 | CTL_DECIMAL = 16 # Decimal number input element. 178 | CTL_CURRENCY = 17 # Currency input element. 179 | CTL_SCROLLBAR = 18 180 | CTL_LIST = 19 181 | CTL_RICHTEXT = 20 182 | CTL_CALENDAR = 21 183 | CTL_DATE = 22 184 | CTL_TIME = 23 185 | CTL_FILE = 24 # file input element. 186 | CTL_PATH = 25 # path input element. 187 | 188 | CTL_LAST_INPUT = 26 189 | 190 | CTL_HYPERLINK = CTL_LAST_INPUT 191 | CTL_FORM = 27 192 | 193 | CTL_MENUBAR = 28 194 | CTL_MENU = 29 195 | CTL_MENUBUTTON = 30 196 | 197 | CTL_FRAME = 31 198 | CTL_FRAMESET = 32 199 | 200 | CTL_TOOLTIP = 33 201 | 202 | CTL_HIDDEN = 34 203 | CTL_URL = 35 # URL input element. 204 | CTL_TOOLBAR = 36 205 | 206 | CTL_WINDOW = 37 # has HWND attached to it 207 | 208 | CTL_LABEL = 38 209 | CTL_IMAGE = 39 # image/video object. 210 | CTL_PLAINTEXT = 40 # Multiline TextBox + colorizer. 211 | -------------------------------------------------------------------------------- /sciter/capi/scdef.py: -------------------------------------------------------------------------------- 1 | """Common Sciter declarations.""" 2 | 3 | import enum 4 | 5 | from ctypes import * 6 | 7 | from sciter.capi.sctypes import * 8 | from sciter.capi.scdom import HELEMENT 9 | from sciter.capi.screquest import HREQUEST 10 | from sciter.capi.scvalue import PSCITER_VALUE 11 | 12 | 13 | class LOAD_RESULT(enum.IntEnum): 14 | """.""" 15 | LOAD_OK = 0 # do default loading if data not set 16 | LOAD_DISCARD = 1 # discard request completely 17 | LOAD_DELAYED = 2 # data will be delivered later by the host application. 18 | 19 | LOAD_MYSELF = 3 # Use sciter-x-request.h[pp] API functions with SCN_LOAD_DATA::requestId handle. 20 | 21 | 22 | class SciterNotification(enum.IntEnum): 23 | """.""" 24 | SC_LOAD_DATA = 0x01 25 | SC_DATA_LOADED = 0x02 26 | SC_ATTACH_BEHAVIOR = 0x04 27 | SC_ENGINE_DESTROYED = 0x05 28 | SC_POSTED_NOTIFICATION = 0x06 29 | SC_GRAPHICS_CRITICAL_FAILURE = 0x07 30 | SC_KEYBOARD_REQUEST = 0x08 31 | SC_INVALIDATE_RECT = 0x09 32 | 33 | 34 | class SCITER_RT_OPTIONS(enum.IntEnum): 35 | """Sciter engine options (global or per-window).""" 36 | SCITER_SMOOTH_SCROLL = 1 # value: TRUE - enable, value: FALSE - disable, enabled by default 37 | SCITER_CONNECTION_TIMEOUT = 2 # global; value: milliseconds, connection timeout of http client 38 | SCITER_HTTPS_ERROR = 3 # global; value: 0 - drop connection, 1 - use builtin dialog, 2 - accept connection silently 39 | SCITER_FONT_SMOOTHING = 4 # value: 0 - system default, 1 - no smoothing, 2 - std smoothing, 3 - clear type 40 | 41 | SCITER_TRANSPARENT_WINDOW = 6 # Windows Aero support, value: 42 | # 0 - normal drawing, 43 | # 1 - window has transparent background after calls DwmExtendFrameIntoClientArea() or DwmEnableBlurBehindWindow(). 44 | SCITER_SET_GPU_BLACKLIST = 7 # global; 45 | # value = LPCBYTE, json - GPU black list, see: gpu-blacklist.json resource. 46 | SCITER_SET_SCRIPT_RUNTIME_FEATURES = 8, # global or window; value - combination of SCRIPT_RUNTIME_FEATURES flags. 47 | SCITER_SET_GFX_LAYER = 9 # global; value - GFX_LAYER 48 | SCITER_SET_DEBUG_MODE = 10 # global or window; value - TRUE/FALSE 49 | SCITER_SET_UX_THEMING = 11 # global; value - BOOL, TRUE - the engine will use "unisex" theme that is common for all platforms. 50 | # That UX theme is not using OS primitives for rendering input elements. Use it if you want exactly 51 | # the same (modulo fonts) look-n-feel on all platforms. 52 | SCITER_ALPHA_WINDOW = 12 # hWnd, value - TRUE/FALSE - window uses per pixel alpha (e.g. WS_EX_LAYERED/UpdateLayeredWindow() window) 53 | SCITER_SET_INIT_SCRIPT = 13 # hWnd - N/A , value LPCSTR - UTF-8 encoded script source to be loaded into each view before any other script execution. 54 | 55 | 56 | class SCRIPT_RUNTIME_FEATURES(enum.IntEnum): 57 | ALLOW_FILE_IO = 0x00000001 58 | ALLOW_SOCKET_IO = 0x00000002 59 | ALLOW_EVAL = 0x00000004 60 | ALLOW_SYSINFO = 0x00000008 61 | 62 | 63 | class GFX_LAYER(enum.IntEnum): 64 | AUTO = 0xFFFF 65 | CPU = 1 66 | 67 | if SCITER_WIN: 68 | GDI = 1 69 | elif SCITER_LNX: 70 | CG = 1 71 | elif SCITER_OSX: 72 | CAIRO = 1 73 | 74 | if SCITER_WIN: 75 | WARP = 2 76 | D2D = 3 77 | 78 | SKIA_CPU = 4 79 | SKIA_OPENGL = 5 80 | 81 | 82 | class OUTPUT_SUBSYTEMS(enum.IntEnum): 83 | DOM = 0 # html parser & runtime 84 | CSSS = 1 # csss! parser & runtime 85 | CSS = 2 # css parser 86 | TIS = 3 # TIS parser & runtime 87 | 88 | 89 | class OUTPUT_SEVERITY(enum.IntEnum): 90 | INFO = 0 91 | WARNING = 1 92 | ERROR = 2 93 | 94 | 95 | class SCITER_CREATE_WINDOW_FLAGS(enum.IntEnum): 96 | SW_CHILD = (1 << 0) # child window only, if this flag is set all other flags ignored 97 | SW_TITLEBAR = (1 << 1) # toplevel window, has titlebar 98 | SW_RESIZEABLE = (1 << 2) # has resizeable frame 99 | SW_TOOL = (1 << 3) # is tool window 100 | SW_CONTROLS = (1 << 4) # has minimize / maximize buttons 101 | SW_GLASSY = (1 << 5) # glassy window - supports "Acrylic" on Windows and "Vibrant" on MacOS. 102 | SW_ALPHA = (1 << 6) # transparent window ( e.g. WS_EX_LAYERED on Windows ) 103 | SW_MAIN = (1 << 7) # main window of the app, will terminate the app on close 104 | SW_POPUP = (1 << 8) # the window is created as topmost window. 105 | SW_ENABLE_DEBUG = (1 << 9) # make this window inspector ready 106 | SW_OWNS_VM = (1 << 10) # it has its own script VM 107 | 108 | 109 | class SCITER_CALLBACK_NOTIFICATION(Structure): 110 | """.""" 111 | _fields_ = [ 112 | ("code", c_uint), 113 | ("hwnd", HWINDOW), 114 | ] 115 | 116 | 117 | class SCN_LOAD_DATA(Structure): 118 | """.""" 119 | _fields_ = [ 120 | ("code", c_uint), 121 | ("hwnd", HWINDOW), 122 | ("_uri", LPCWSTR), 123 | ("outData", LPCBYTE), 124 | ("outDataSize", UINT), 125 | ("dataType", UINT), 126 | ("requestId", HREQUEST), 127 | ("principal", HELEMENT), 128 | ("initiator", HELEMENT), 129 | ] 130 | uri = UTF16LEField('_uri') 131 | 132 | 133 | class SCN_DATA_LOADED(Structure): 134 | """.""" 135 | _fields_ = [ 136 | ("code", c_uint), 137 | ("hwnd", HWINDOW), 138 | ("_uri", LPCWSTR), 139 | ("data", LPCBYTE), 140 | ("dataSize", UINT), 141 | ("dataType", UINT), 142 | ("status", UINT), 143 | ] 144 | uri = UTF16LEField('_uri') 145 | 146 | 147 | class SCN_ATTACH_BEHAVIOR(Structure): 148 | """.""" 149 | _fields_ = [ 150 | ("code", c_uint), 151 | ("hwnd", HWINDOW), 152 | ("element", HELEMENT), 153 | ("behaviorName", LPCSTR), 154 | ("elementProc", c_void_p), 155 | ("elementTag", LPVOID), 156 | ] 157 | 158 | class SCN_KEYBOARD_REQUEST(Structure): 159 | """.""" 160 | _fields_ = [ 161 | ("code", c_uint), 162 | ("hwnd", HWINDOW), 163 | ("keyboardMode", c_uint) 164 | ] 165 | 166 | class SCN_INVALIDATE_RECT(Structure): 167 | """.""" 168 | _fields_ = [ 169 | ("code", c_uint), 170 | ("hwnd", HWINDOW), 171 | ("invalidRect", RECT) 172 | ] 173 | 174 | 175 | LPSCITER_CALLBACK_NOTIFICATION = POINTER(SCITER_CALLBACK_NOTIFICATION) 176 | SciterHostCallback = SC_CALLBACK(UINT, LPSCITER_CALLBACK_NOTIFICATION, LPVOID) 177 | 178 | if SCITER_WIN: 179 | SciterWindowDelegate = SC_CALLBACK(LRESULT, HWINDOW, UINT, WPARAM, LPARAM, LPVOID, PBOOL) 180 | else: 181 | SciterWindowDelegate = c_void_p 182 | 183 | DEBUG_OUTPUT_PROC = SC_CALLBACK(VOID, LPVOID, UINT, UINT, LPCWSTR, UINT) 184 | 185 | LPCSTR_RECEIVER = SC_CALLBACK(VOID, LPCSTR, UINT, LPVOID) 186 | LPCWSTR_RECEIVER = SC_CALLBACK(VOID, LPCWSTR, UINT, LPVOID) 187 | LPCBYTE_RECEIVER = SC_CALLBACK(VOID, LPCBYTE, UINT, LPVOID) 188 | 189 | SciterElementCallback = SC_CALLBACK(BOOL, HELEMENT, LPVOID) 190 | 191 | ElementEventProc = SC_CALLBACK(BOOL, LPVOID, HELEMENT, UINT, LPVOID) 192 | 193 | ELEMENT_COMPARATOR = SC_CALLBACK(INT, HELEMENT, HELEMENT, LPVOID) 194 | 195 | KeyValueCallback = SC_CALLBACK(BOOL, LPVOID, PSCITER_VALUE, PSCITER_VALUE) 196 | 197 | NATIVE_FUNCTOR_INVOKE = CFUNCTYPE(VOID, LPVOID, UINT, PSCITER_VALUE, PSCITER_VALUE) 198 | NATIVE_FUNCTOR_RELEASE = CFUNCTYPE(VOID, LPVOID) 199 | 200 | ELEMENT_BITMAP_RECEIVER = SC_CALLBACK(VOID, LPCBYTE, INT, INT, UINT, UINT, LPVOID) 201 | 202 | 203 | class StringReceiver(): 204 | """LPCWSTR_RECEIVER wrapper.""" 205 | 206 | def __init__(self, string_type: str): 207 | """Construct callback by one of 'char', 'wchar' or 'byte' string type.""" 208 | self.text = None 209 | if string_type == 'char': 210 | self.cb = LPCSTR_RECEIVER(self._a2s) 211 | elif string_type == 'byte': 212 | self.cb = LPCBYTE_RECEIVER(self._b2s) 213 | elif string_type == 'wchar': 214 | self.cb = LPCWSTR_RECEIVER(self._w2s) 215 | else: 216 | raise ValueError("Unknown callback type. Use one of 'char', 'byte' or 'wchar'.") 217 | self._as_parameter_ = self.cb 218 | pass 219 | 220 | def _w2s(self, sz, n, ctx): 221 | # wchar_t 222 | self.text = sz if SCITER_WIN else sz.value 223 | pass 224 | 225 | def _a2s(self, sz, n, ctx): 226 | # char 227 | self.text = sz.decode('utf-8') 228 | pass 229 | 230 | def _b2s(self, sz, n, ctx): 231 | # byte 232 | self.text = sz.decode('utf-8') 233 | pass 234 | pass 235 | -------------------------------------------------------------------------------- /sciter/host.py: -------------------------------------------------------------------------------- 1 | """Sciter host application helpers.""" 2 | 3 | import ctypes 4 | import os.path 5 | 6 | from sciter.capi.scdef import * 7 | from sciter.capi.sctypes import HWINDOW 8 | 9 | import sciter 10 | import sciter.dom 11 | 12 | import sys 13 | 14 | _api = sciter.SciterAPI() 15 | 16 | 17 | class Host(): 18 | """Standard implementation of SCITER_CALLBACK_NOTIFY handler.""" 19 | 20 | def __init__(self): 21 | """.""" 22 | super().__init__() 23 | self.hwnd = None 24 | self.root = None 25 | pass 26 | 27 | def __call__(self, name, *args): 28 | """Alias for self.call_function().""" 29 | return self.call_function(name, *args) 30 | 31 | def setup_callback(self, hwnd): 32 | """Set callback for sciter engine events.""" 33 | if not hwnd: 34 | raise ValueError("Invalid window handle provided.") 35 | self.hwnd = hwnd 36 | self.root = self.get_root() # if called on existing document 37 | self._sciter_handler_proc = SciterHostCallback(self.handle_notification) 38 | _api.SciterSetCallback(hwnd, self._sciter_handler_proc, ctypes.c_void_p(0)) 39 | pass 40 | 41 | def setup_debug(self, hwnd=None, debug_windows=True, debug_output=True): 42 | """Setup debug output function for specific window or globally.""" 43 | ok = _api.SciterSetOption(hwnd, SCITER_RT_OPTIONS.SCITER_SET_DEBUG_MODE, debug_windows) 44 | if not ok: 45 | raise sciter.SciterError("Could not set debug mode") 46 | self._sciter_debug_proc = DEBUG_OUTPUT_PROC(self.on_debug_output) 47 | if debug_output: 48 | _api.SciterSetupDebugOutput(hwnd, None, self._sciter_debug_proc) 49 | else: 50 | _api.SciterSetupDebugOutput(hwnd, None, DEBUG_OUTPUT_PROC(0)) 51 | pass 52 | 53 | def set_option(self, option, value): 54 | """Set various sciter engine options, see the SCITER_RT_OPTIONS.""" 55 | hwnd = self.hwnd 56 | if option in (SCITER_RT_OPTIONS.SCITER_SET_GPU_BLACKLIST, SCITER_RT_OPTIONS.SCITER_SET_GFX_LAYER, SCITER_RT_OPTIONS.SCITER_SET_UX_THEMING): 57 | hwnd = None 58 | ok = _api.SciterSetOption(hwnd, option, value) 59 | if not ok: 60 | raise sciter.SciterError("Could not set option " + str(option) + "=" + str(value)) 61 | return self 62 | 63 | def set_home_url(self, url: str): 64 | """Set sciter window home url.""" 65 | ok = _api.SciterSetHomeURL(self.hwnd, url) 66 | if not ok: 67 | raise sciter.SciterError("Could not set home url " + str(url)) 68 | return self 69 | 70 | def set_media_type(self, media_type: str): 71 | """Set media type of this sciter instance.""" 72 | ok = _api.SciterSetMediaType(self.hwnd, media_type) 73 | if not ok: 74 | raise sciter.SciterError("Could not set media type " + str(media_type)) 75 | return self 76 | 77 | def set_media_vars(self, media: dict): 78 | """Set media variables of this sciter instance.""" 79 | v = sciter.Value(media) 80 | ok = _api.SciterSetMediaVars(self.hwnd, v) 81 | if not ok: 82 | raise sciter.SciterError("Could not set media vars " + str(media)) 83 | return self 84 | 85 | def set_master_css(self, css_str: str, append: bool): 86 | """Set Master style sheet.""" 87 | utf = css_str.encode('utf-8') 88 | if append: 89 | ok = _api.SciterAppendMasterCSS(utf, len(utf)) 90 | else: 91 | ok = _api.SciterSetMasterCSS(utf, len(utf)) 92 | if not ok: 93 | raise sciter.SciterError("Could not set master CSS") 94 | return self 95 | 96 | def set_css(self, css_str: str, base_url: str, media_type: str): 97 | """Set (reset) style sheet of current document.""" 98 | utf = css_str.encode('utf-8') 99 | ok = _api.SciterSetCSS(self.hwnd, utf, len(utf), base_url, media_type) 100 | if not ok: 101 | raise sciter.SciterError("Could not set CSS") 102 | return self 103 | 104 | def get_hwnd(self) -> HWINDOW: 105 | """Get window handle.""" 106 | return self.hwnd 107 | 108 | def load_file(self, uri: str, normalize=True): 109 | """Load HTML document from file.""" 110 | if normalize and "://" not in uri: 111 | uri = "file://" + os.path.abspath(uri).replace("\\", "/") 112 | ok = _api.SciterLoadFile(self.hwnd, uri) 113 | if not ok: 114 | raise sciter.SciterError("Unable to load file " + uri) 115 | self.root = self.get_root() 116 | return self 117 | 118 | def load_html(self, html: bytes, uri=None): 119 | """Load HTML document from memory.""" 120 | if not isinstance(html, bytes): 121 | raise TypeError("html must be a bytes type") 122 | cb = len(html) 123 | pb = ctypes.c_char_p(html) 124 | ok = _api.SciterLoadHtml(self.hwnd, pb, cb, uri) 125 | if not ok: 126 | raise sciter.SciterError("Unable to load html " + str(uri)) 127 | self.root = self.get_root() 128 | return self 129 | 130 | def get_root(self): 131 | """Get window root DOM element.""" 132 | he = sciter.dom.HELEMENT() 133 | ok = _api.SciterGetRootElement(self.hwnd, ctypes.byref(he)) 134 | sciter.dom.Element._throw_if(ok) 135 | return sciter.dom.Element(he) if he else None 136 | 137 | def eval_script(self, script: str, name=None): 138 | """Evaluate script in context of current document.""" 139 | rv = sciter.Value() 140 | ok = _api.SciterEval(self.hwnd, script, len(script), rv) 141 | sciter.Value.raise_from(rv, ok != False, name if name else 'Host.eval') 142 | return rv 143 | 144 | def call_function(self, name: str, *args): 145 | """Call scripting function defined in the global namespace.""" 146 | rv = sciter.Value() 147 | argc, argv, _ = sciter.Value.pack_args(*args) 148 | ok = _api.SciterCall(self.hwnd, name.encode('utf-8'), argc, argv, rv) 149 | sciter.Value.raise_from(rv, ok != False, name) 150 | return rv 151 | 152 | def data_ready(self, uri: str, data: bytes, request_id=None, hwnd=None): 153 | """This function is used as response to SCN_LOAD_DATA request.""" 154 | if not hwnd: 155 | hwnd = self.hwnd 156 | if request_id is not None: 157 | ok = _api.SciterDataReadyAsync(hwnd, uri, data, len(data), request_id) 158 | else: 159 | ok = _api.SciterDataReady(hwnd, uri, data, len(data)) 160 | if not ok: 161 | raise sciter.SciterError("Unable to pass data for " + uri) 162 | pass 163 | 164 | 165 | ## @name following functions can be overloaded 166 | def on_load_data(self, nm: SCN_LOAD_DATA): 167 | """Notifies that Sciter is about to download a referred resource.""" 168 | pass 169 | 170 | def on_data_loaded(self, nm: SCN_DATA_LOADED): 171 | """This notification indicates that external data (for example image) download process completed.""" 172 | pass 173 | 174 | def on_attach_behavior(self, nm: SCN_ATTACH_BEHAVIOR): 175 | """This notification is sent on parsing the document and while processing elements having non empty `style.behavior` attribute value.""" 176 | pass 177 | 178 | def on_debug_output(self, tag, subsystem, severity, text, text_len): 179 | """This output function will be used for reprting problems found while loading html and css documents.""" 180 | sysname = OUTPUT_SUBSYTEMS(subsystem).name.lower() 181 | sevname = OUTPUT_SEVERITY(severity).name.lower() 182 | if not sciter.SCITER_WIN: 183 | text = text.value 184 | message = text.replace("\r", "\n").rstrip() 185 | if message: 186 | destination = sys.stdout if sevname == 'info' else sys.stderr 187 | print("{}:{}: {}".format(sevname, sysname, message), file=destination) 188 | pass 189 | 190 | def handle_notification(self, pnm, param): 191 | """Sciter notification handler.""" 192 | # pylint: disable=assignment-from-none,assignment-from-no-return 193 | # because the `self.on_` methods can be overloaded 194 | rv = 0 195 | nm = pnm.contents 196 | if nm.code == SciterNotification.SC_LOAD_DATA: 197 | rv = self.on_load_data(ctypes.cast(pnm, ctypes.POINTER(SCN_LOAD_DATA)).contents) 198 | elif nm.code == SciterNotification.SC_DATA_LOADED: 199 | rv = self.on_data_loaded(ctypes.cast(pnm, ctypes.POINTER(SCN_DATA_LOADED)).contents) 200 | elif nm.code == SciterNotification.SC_ATTACH_BEHAVIOR: 201 | rv = self.on_attach_behavior(ctypes.cast(pnm, ctypes.POINTER(SCN_ATTACH_BEHAVIOR)).contents) 202 | assert(rv is None or isinstance(rv, int)) 203 | return 0 if rv is None else rv 204 | pass 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python bindings for Sciter 2 | 3 | [![AppVeyor status](https://ci.appveyor.com/api/projects/status/rphv883klffw9em9/branch/master?svg=true)](https://ci.appveyor.com/project/pravic/pysciter) 4 | [![Travis Status](https://www.travis-ci.com/sciter-sdk/pysciter.svg?branch=master)](https://www.travis-ci.com/sciter-sdk/pysciter) 5 | [![PyPI](https://img.shields.io/pypi/v/pysciter.svg)](https://pypi.python.org/pypi/PySciter) 6 | [![License](https://img.shields.io/pypi/l/pysciter.svg)](https://pypi.python.org/pypi/PySciter) 7 | [![Join the forums at https://sciter.com/forums](https://img.shields.io/badge/forum-sciter.com-orange.svg)](https://sciter.com/forums) 8 | 9 | Check [this page](https://sciter.com/developers/sciter-sdk-bindings/) for other language bindings (Delphi / D / Go / .NET / Python / Rust). 10 | 11 | ---- 12 | 13 | 14 | ## Introduction 15 | 16 | Sciter is an embeddable [multiplatform](https://sciter.com/sciter/crossplatform/) HTML/CSS/script engine with GPU accelerated rendering designed to render modern desktop application UI. It's a compact, single dll/dylib/so file (4-8 mb) engine without any additional dependencies. 17 | 18 | 19 | ## Screenshots 20 | 21 | Check the [screenshot gallery](https://github.com/oskca/sciter#sciter-desktop-ui-examples) of the desktop UI examples. 22 | 23 | 24 | ## Description 25 | 26 | Physically Sciter is a mono library which contains: 27 | 28 | * [HTML and CSS](https://sciter.com/developers/for-web-programmers/) rendering engine based on the H-SMILE core used in [HTMLayout](https://terrainformatica.com/a-homepage-section/htmlayout/), 29 | * JavaScript in [Sciter.JS](https://sciter.com/sciter-js-is-the-mainstream-primary-version-of-sciter/), 30 | * JavaScript alike [Scripting engine](https://sciter.com/developers/sciter-docs/) – core of [TIScript](https://sciter.com/developers/for-web-programmers/tiscript-vs-javascript/) which by itself is based on [c-smile](https://c-smile.sourceforge.net/) engine, 31 | * Persistent [Database](https://sciter.com/docs/content/script/Storage.htm) (a.k.a. [JSON DB](https://terrainformatica.com/2006/10/what-the-hell-is-that-json-db/)) based on excellent DB products of [Konstantin Knizhnik](http://garret.ru/databases.html). 32 | * [Graphics](https://sciter.com/docs/content/sciter/Graphics.htm) module that uses native graphics primitives provided by supported platforms: Direct2D on Windows 7 and above, GDI+ on Windows XP, CoreGraphics on MacOS, Cairo on Linux/GTK. Yet there is an option to use built-in [Skia/OpenGL](https://skia.org/) backend on each platform. 33 | * Network communication module, it relies on platform HTTP client primitives and/or [Libcurl](https://curl.haxx.se/). 34 | 35 | 36 | Internally it contains the following modules: 37 | 38 | * **CSS** – CSS parser and the collection of parsed CSS rules, etc. 39 | * **HTML DOM** – HTML parser and DOM tree implementation. 40 | * **layout managers** – collection of various layout managers – text layout, default block layout, flex layouts. Support of positioned floating elements is also here. This module does the layout calculations heavy lifting. This module is also responsible for the rendering of layouts. 41 | * **input behaviors** – a collection of built-in behaviors – code behind "active" DOM elements: ``, `