├── .editorconfig ├── .github ├── funding.yml ├── issue_template.md ├── stale.yml └── workflows │ ├── docs.yaml │ └── stale.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── MANIFEST.in ├── README.md ├── SECURITY.md ├── appveyor.yml ├── docs ├── .nvmrc ├── .vuepress │ ├── client.js │ ├── components │ │ ├── CurrentVersion.vue │ │ └── Features.vue │ ├── config.js │ ├── generate-examples.js │ └── public │ │ ├── 2.4 │ │ ├── 404.html │ │ ├── assets │ │ │ ├── css │ │ │ │ └── 0.styles.e542dfbb.css │ │ │ ├── img │ │ │ │ └── search.83621669.svg │ │ │ └── js │ │ │ │ ├── 10.946430c0.js │ │ │ │ ├── 11.9f37de77.js │ │ │ │ ├── 12.cc649247.js │ │ │ │ ├── 13.94ca571d.js │ │ │ │ ├── 14.a047eef4.js │ │ │ │ ├── 15.788bb271.js │ │ │ │ ├── 16.694894b2.js │ │ │ │ ├── 17.2edfeaf2.js │ │ │ │ ├── 18.7b4619dc.js │ │ │ │ ├── 19.ec102f84.js │ │ │ │ ├── 2.06af3526.js │ │ │ │ ├── 20.b1804ec3.js │ │ │ │ ├── 21.82f7ca23.js │ │ │ │ ├── 22.0e759d36.js │ │ │ │ ├── 23.8e7b242c.js │ │ │ │ ├── 24.720f51e9.js │ │ │ │ ├── 25.3af86868.js │ │ │ │ ├── 26.562c94d4.js │ │ │ │ ├── 27.7c95cefd.js │ │ │ │ ├── 28.d5836b93.js │ │ │ │ ├── 29.528821ab.js │ │ │ │ ├── 3.c462cbf1.js │ │ │ │ ├── 30.e6e32fda.js │ │ │ │ ├── 31.c651468e.js │ │ │ │ ├── 32.6c8529fe.js │ │ │ │ ├── 33.96fe48c6.js │ │ │ │ ├── 34.1503f1f8.js │ │ │ │ ├── 35.2c565ae8.js │ │ │ │ ├── 36.60799f3e.js │ │ │ │ ├── 37.5656488c.js │ │ │ │ ├── 38.30116eb6.js │ │ │ │ ├── 39.8b7a8ec5.js │ │ │ │ ├── 4.cc18902f.js │ │ │ │ ├── 40.14c37596.js │ │ │ │ ├── 5.b85830ed.js │ │ │ │ ├── 6.fe285e19.js │ │ │ │ ├── 7.5eeb60d8.js │ │ │ │ ├── 8.b9ad99cd.js │ │ │ │ ├── 9.6a770a26.js │ │ │ │ └── app.ab883ad2.js │ │ ├── contributing │ │ │ ├── bug_reporting.html │ │ │ ├── development.html │ │ │ ├── documentation.html │ │ │ ├── donating.html │ │ │ └── index.html │ │ ├── examples │ │ │ ├── cef.html │ │ │ ├── change_url.html │ │ │ ├── css_load.html │ │ │ ├── debug.html │ │ │ ├── destroy_window.html │ │ │ ├── fullscreen.html │ │ │ ├── get_current_url.html │ │ │ ├── html_load.html │ │ │ ├── index.html │ │ │ ├── js_api.html │ │ │ ├── js_evaluate.html │ │ │ ├── loading_animation.html │ │ │ ├── localization.html │ │ │ ├── min_size.html │ │ │ ├── multiple_windows.html │ │ │ ├── open_file_dialog.html │ │ │ ├── open_url.html │ │ │ ├── quit_confirm.html │ │ │ ├── save_file_dialog.html │ │ │ ├── toggle_fullscreen.html │ │ │ └── window_title_change.html │ │ ├── guide │ │ │ ├── api.html │ │ │ ├── architecture.html │ │ │ ├── debugging.html │ │ │ ├── freezing.html │ │ │ ├── index.html │ │ │ ├── installation.html │ │ │ ├── renderer.html │ │ │ ├── security.html │ │ │ ├── usage.html │ │ │ └── virtualenv.html │ │ ├── hello │ │ │ ├── index.html │ │ │ └── tachyon.css │ │ ├── index.html │ │ ├── logo.png │ │ └── paypal.png │ │ ├── 3.7 │ │ ├── 404.html │ │ ├── CHANGELOG.html │ │ ├── assets │ │ │ ├── css │ │ │ │ └── 0.styles.28a501df.css │ │ │ ├── img │ │ │ │ ├── pywebview3.5e63e895.png │ │ │ │ └── search.83621669.svg │ │ │ └── js │ │ │ │ ├── 10.f125ec08.js │ │ │ │ ├── 11.81fead78.js │ │ │ │ ├── 12.0a86c191.js │ │ │ │ ├── 13.c09145fc.js │ │ │ │ ├── 14.2a7dc35b.js │ │ │ │ ├── 15.90c9f36a.js │ │ │ │ ├── 16.69b1f4b4.js │ │ │ │ ├── 17.956ed884.js │ │ │ │ ├── 18.bd6678a0.js │ │ │ │ ├── 19.b31e62b7.js │ │ │ │ ├── 2.0ff7ef1d.js │ │ │ │ ├── 20.cebc4a71.js │ │ │ │ ├── 21.d2041966.js │ │ │ │ ├── 22.2a3916f2.js │ │ │ │ ├── 23.712fbf84.js │ │ │ │ ├── 24.f768e66b.js │ │ │ │ ├── 25.69b3e415.js │ │ │ │ ├── 26.6619f84a.js │ │ │ │ ├── 27.e2a4e1bf.js │ │ │ │ ├── 28.abcc819c.js │ │ │ │ ├── 29.c379d080.js │ │ │ │ ├── 3.749f6582.js │ │ │ │ ├── 30.771e8640.js │ │ │ │ ├── 31.547a8393.js │ │ │ │ ├── 32.778a82c7.js │ │ │ │ ├── 33.6ff70f11.js │ │ │ │ ├── 34.ae1b5fb4.js │ │ │ │ ├── 35.c5555b94.js │ │ │ │ ├── 36.d53c471f.js │ │ │ │ ├── 37.bbba2d58.js │ │ │ │ ├── 38.5eb30036.js │ │ │ │ ├── 39.8cac65fe.js │ │ │ │ ├── 4.f2c64e71.js │ │ │ │ ├── 40.53182a9c.js │ │ │ │ ├── 41.19399765.js │ │ │ │ ├── 42.41128068.js │ │ │ │ ├── 43.660f3959.js │ │ │ │ ├── 44.09d5c6f8.js │ │ │ │ ├── 45.4a818eab.js │ │ │ │ ├── 46.d1621897.js │ │ │ │ ├── 47.7f8f8dec.js │ │ │ │ ├── 48.f27d4c39.js │ │ │ │ ├── 49.4189d401.js │ │ │ │ ├── 5.2a771d7d.js │ │ │ │ ├── 50.beff8380.js │ │ │ │ ├── 51.8a918a6c.js │ │ │ │ ├── 52.92d0105f.js │ │ │ │ ├── 53.28701cde.js │ │ │ │ ├── 54.2b15d6fb.js │ │ │ │ ├── 55.d231c742.js │ │ │ │ ├── 56.7e02bbff.js │ │ │ │ ├── 57.61e9e46c.js │ │ │ │ ├── 58.cc70e253.js │ │ │ │ ├── 59.d3f412e7.js │ │ │ │ ├── 6.64e1f3ed.js │ │ │ │ ├── 60.f4fd7728.js │ │ │ │ ├── 61.cd0f1d68.js │ │ │ │ ├── 62.bf000bcc.js │ │ │ │ ├── 63.807edde1.js │ │ │ │ ├── 64.2b6fe843.js │ │ │ │ ├── 7.779cbcff.js │ │ │ │ ├── 8.66fb3acb.js │ │ │ │ ├── 9.84350af9.js │ │ │ │ └── app.10a559eb.js │ │ ├── blog │ │ │ ├── index.html │ │ │ └── pywebview3.html │ │ ├── contributing │ │ │ ├── bug_reporting.html │ │ │ ├── development.html │ │ │ ├── documentation.html │ │ │ ├── donating.html │ │ │ └── index.html │ │ ├── examples │ │ │ ├── cef.html │ │ │ ├── change_url.html │ │ │ ├── close_confirm.html │ │ │ ├── confirmation_dialog.html │ │ │ ├── cookies.html │ │ │ ├── css_load.html │ │ │ ├── debug.html │ │ │ ├── destroy_window.html │ │ │ ├── events.html │ │ │ ├── expose.html │ │ │ ├── frameless.html │ │ │ ├── fullscreen.html │ │ │ ├── get_current_url.html │ │ │ ├── get_elements.html │ │ │ ├── hide_window.html │ │ │ ├── html_load.html │ │ │ ├── index.html │ │ │ ├── js_api.html │ │ │ ├── js_evaluate.html │ │ │ ├── js_evaluate_async.html │ │ │ ├── links.html │ │ │ ├── loading_animation.html │ │ │ ├── localization.html │ │ │ ├── menu.html │ │ │ ├── min_size.html │ │ │ ├── minimize_window.html │ │ │ ├── move_window.html │ │ │ ├── multiple_windows.html │ │ │ ├── on_top.html │ │ │ ├── open_file_dialog.html │ │ │ ├── open_url.html │ │ │ ├── resize_window.html │ │ │ ├── save_file_dialog.html │ │ │ ├── screens.html │ │ │ ├── toggle_fullscreen.html │ │ │ ├── user_agent.html │ │ │ ├── vibrancy.html │ │ │ └── window_title_change.html │ │ ├── guide │ │ │ ├── api.html │ │ │ ├── architecture.html │ │ │ ├── debugging.html │ │ │ ├── freezing.html │ │ │ ├── index.html │ │ │ ├── installation.html │ │ │ ├── interdomain.html │ │ │ ├── renderer.html │ │ │ ├── security.html │ │ │ ├── usage.html │ │ │ └── virtualenv.html │ │ ├── hello │ │ │ ├── index.html │ │ │ └── tachyon.css │ │ ├── index.html │ │ ├── logo-no-text.png │ │ ├── logo.png │ │ ├── paypal.png │ │ ├── screenshots │ │ │ ├── todos-linux.png │ │ │ ├── todos-macos.png │ │ │ └── todos-windows.png │ │ └── windows31.png │ │ ├── download │ │ ├── index.html │ │ └── test-data.bin │ │ ├── github_sponsor_button.png │ │ ├── hello │ │ ├── index.html │ │ └── tachyon.css │ │ ├── light-bg.png │ │ ├── logo-no-text.png │ │ ├── logo.png │ │ ├── opencollective.png │ │ ├── patreon.png │ │ ├── paypal.png │ │ ├── prism.png │ │ ├── screenshots │ │ ├── todos-linux.png │ │ ├── todos-macos.png │ │ └── todos-windows.png │ │ └── windows31.png ├── CHANGELOG.md ├── README.md ├── _redirects ├── api │ └── README.md ├── blog │ ├── README.md │ ├── pywebview3.md │ ├── pywebview3.png │ └── pywebview5.md ├── contributing │ ├── README.md │ ├── bug_reporting.md │ ├── development.md │ ├── documentation.md │ └── donating.md ├── examples │ └── README.md ├── guide │ ├── README.md │ ├── architecture.md │ ├── debugging.md │ ├── dom.md │ ├── faq.md │ ├── freezing.md │ ├── installation.md │ ├── interdomain.md │ ├── security.md │ ├── usage.md │ └── web_engine.md ├── package-lock.json └── package.json ├── examples ├── assets │ ├── cookies.html │ ├── index.html │ ├── video.mp4 │ └── video.webm ├── cef.py ├── change_url.py ├── confirm_close.py ├── confirmation_dialog.py ├── cookies.py ├── debug.py ├── destroy_window.py ├── dom_events.py ├── dom_manipulation.py ├── dom_traversal.py ├── downloads.py ├── drag_drop.py ├── drag_region.py ├── evaluate_js.py ├── evaluate_js_async.py ├── events.py ├── expose.py ├── flask_app │ ├── README │ ├── gui │ │ └── index.html │ ├── requirements.txt │ └── src │ │ ├── backend │ │ ├── app.py │ │ ├── main.py │ │ └── server.py │ │ └── frontend │ │ └── README ├── focus.py ├── frameless.py ├── fullscreen.py ├── get_current_url.py ├── get_elements.py ├── hide_window.py ├── http_server.py ├── icon.py ├── js_api.py ├── links.py ├── load_css.py ├── load_html.py ├── loading_animation.py ├── localhost_ssl.py ├── localization.py ├── menu.py ├── min_size.py ├── move_window.py ├── multiple_servers.py ├── multiple_windows.py ├── on_top.py ├── open_file_dialog.py ├── py2app_setup.py ├── pystray_icon.py ├── qt_test.py ├── remote_debugging.py ├── resize.py ├── run_js.py ├── save_file_dialog.py ├── screens.py ├── settings.py ├── simple_browser.py ├── todos │ ├── README.md │ ├── assets │ │ ├── index.html │ │ ├── logo.jpg │ │ ├── script.js │ │ └── styles.css │ ├── buildozer.spec │ └── main.py ├── toggle_fullscreen.py ├── transparent.py ├── user_agent.py ├── vibrancy.py ├── window_state.py └── window_title_change.py ├── interop ├── android │ ├── .gitattributes │ ├── build.gradle │ ├── lib │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── pywebview │ │ │ ├── EventCallbackWrapper.java │ │ │ ├── JavascriptValueCallback.java │ │ │ ├── JsApiCallbackWrapper.java │ │ │ ├── PyJavascriptInterface.java │ │ │ ├── PyWebChromeClient.java │ │ │ └── PyWebViewClient.java │ └── settings.gradle └── mshtml │ ├── Properties │ └── AssemblyInfo.cs │ ├── README.txt │ ├── WebBrowserInterop.cs │ ├── WebBrowserInterop.csproj │ └── WebBrowserInterop.sln ├── logo ├── banner.png ├── logo-no-text.png ├── logo.pdf ├── logo.png └── logo.svg ├── pyproject.toml ├── requirements.txt ├── tests ├── __init__.py ├── assets │ ├── script.js │ ├── styles.css │ └── test.html ├── conftest.py ├── run.ps1 ├── run.py ├── run.sh ├── run_cef.py ├── run_qt.py ├── test_bg_color.py ├── test_cookies.py ├── test_dom.py ├── test_download.py ├── test_evaluate_js.py ├── test_expose.py ├── test_frameless.py ├── test_fullscreen.py ├── test_get_current_url.py ├── test_get_elements.py ├── test_hide_window.py ├── test_http_server.py ├── test_js_api.py ├── test_load_html.py ├── test_localization.py ├── test_min_size.py ├── test_move_window.py ├── test_multi_window.py ├── test_noresize.py ├── test_on_top.py ├── test_resize.py ├── test_run_js.py ├── test_screens.py ├── test_serialization.py ├── test_set_title.py ├── test_simple_browser.py ├── test_start.py ├── test_toggle_fullscreen.py ├── test_token.py ├── test_url_load.py ├── test_vibrancy.py ├── test_window.py ├── util.py └── util_cocoa.py └── webview ├── __init__.py ├── __pyinstaller ├── __init__.py └── hook-webview.py ├── dom ├── __init__.py ├── classlist.py ├── dom.py ├── element.py ├── event.py └── propsdict.py ├── errors.py ├── event.py ├── guilib.py ├── http.py ├── js ├── api.js ├── customize.js ├── finish.js └── lib │ ├── dom_json.js │ └── polyfill.js ├── lib ├── Microsoft.Web.WebView2.Core.dll ├── Microsoft.Web.WebView2.LICENSE.md ├── Microsoft.Web.WebView2.WinForms.dll ├── WebBrowserInterop.x64.dll ├── WebBrowserInterop.x86.dll ├── pywebview-android.jar └── runtimes │ ├── win-arm64 │ └── native │ │ └── WebView2Loader.dll │ ├── win-x64 │ └── native │ │ └── WebView2Loader.dll │ └── win-x86 │ └── native │ └── WebView2Loader.dll ├── localization.py ├── menu.py ├── platforms ├── __init__.py ├── android.py ├── cef.py ├── cocoa.py ├── edgechromium.py ├── gtk.py ├── mshtml.py ├── qt.py └── winforms.py ├── py.typed ├── screen.py ├── util.py └── window.py /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.py] 10 | indent_size = 4 11 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: r0x0r 4 | patreon: pywebview 5 | open_collective: pywebview 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Specification 2 | - pywebview version: 3 | - operating system: 4 | - web renderer: 5 | 6 | ### Description 7 | 8 | Describe your issue in details. Provide a stand-alone code example that would demonstrate the problem. 9 | 10 | ### Practicalities 11 | 12 | - YES/NO I am willing to work on this issue myself. 13 | 14 | - YES/NO I am prepared to support this issue financially. 15 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 5 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - future 8 | - bug 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: true 18 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | name: Merge Master to Docs on Release 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | 8 | jobs: 9 | merge: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | ref: docs 17 | 18 | - name: Merge master into docs 19 | run: | 20 | git config user.name "GitHub Actions" 21 | git config user.email "actions@example.com" 22 | 23 | git fetch origin master --depth 1 24 | git merge origin/master --no-edit --allow-unrelated-histories 25 | git push origin docs 26 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "30 1 * * *" 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/stale@v3.0.10 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | close-issue-message: 'The message to post on the issue when closing it. If none provided, will not comment when closing an issue.' 17 | close-pr-message: 'The message to post on the pr when closing it. If none provided, will not comment when closing a pull requests.' 18 | days-before-stale: 30 19 | days-before-close: 5 20 | stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' 21 | stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' 22 | stale-issue-label: 'stale' 23 | stale-pr-label: 'stale' 24 | exempt-issue-labels: 'future' 25 | remove-stale-when-updated: true 26 | remove-issue-stale-when-updated: true 27 | remove-pr-stale-when-updated: true 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.spec 3 | *.sh 4 | setup.cfg 5 | pywebview.egg-info 6 | MANIFEST 7 | build 8 | dist 9 | .cache 10 | .idea 11 | .vs 12 | .temp 13 | bin 14 | obj 15 | node_modules 16 | dist 17 | .DS_Store 18 | venv* 19 | .vscode 20 | .pytest_cache 21 | __pycache__ 22 | .python-version 23 | .venv/ 24 | _version.py 25 | docs/examples 26 | /main.py 27 | .buildozer 28 | *.apk 29 | .gradle 30 | gradle* 31 | .pypirc 32 | .env -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.4.0 6 | hooks: 7 | - id: check-added-large-files 8 | - id: check-yaml 9 | - id: double-quote-string-fixer 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | exclude: ^docs/\.vuepress/public/ 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2014-2017, Roman Sirokov 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft windows_interop 2 | include webview/lib/Microsoft.Web.WebView2.LICENSE.md 3 | include webview/js/**/*.js 4 | exclude docs/**/* 5 | exclude examples/**/* 6 | exclude tests/**/* 7 | exclude logo/**/* 8 | exclude appveyor.yml 9 | exclude interop/**/* -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Versions lower than 3.0 are not supported. 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 3.x | :white_check_mark: | 10 | | < 3.0 | :x: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | If you find a vulnerability in the pywebview code, please get in touch via [roman@flowrl.com](mailto:roman@flowrl.com). 15 | Please do not report any vulnerabilities in the node_modules dependencies of the pywebview website. 16 | -------------------------------------------------------------------------------- /docs/.nvmrc: -------------------------------------------------------------------------------- 1 | 22.12.0 2 | -------------------------------------------------------------------------------- /docs/.vuepress/client.js: -------------------------------------------------------------------------------- 1 | import { defineClientConfig } from 'vuepress/client' 2 | import CurrentVersion from './components/CurrentVersion.vue' 3 | import Features from './components/Features.vue' 4 | 5 | export default defineClientConfig({ 6 | enhance({ app }) { 7 | app.component('CurrentVersion', CurrentVersion) 8 | app.component('Features', Features) 9 | }, 10 | }) -------------------------------------------------------------------------------- /docs/.vuepress/components/CurrentVersion.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | -------------------------------------------------------------------------------- /docs/.vuepress/generate-examples.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | function extractDescriptionFromPythonCode(code) { 5 | const match = code.match(/(['"]{3}[\s\S]*?['"]{3})/); 6 | 7 | if (match) { 8 | return match[0].replace(/['"]{3}/g, '').trim(); 9 | } else { 10 | return ''; 11 | } 12 | } 13 | 14 | function removeExtraLineBreaksFromCodeBlock(code) { 15 | return code.replace(/(\r?\n){3,}/g, '\n\n').trim(); 16 | } 17 | 18 | function removeCommentsFromPythonCode(code) { 19 | return code.replace(/(^['"]{3}[\s\S]*?['"]{3})/gm, '').trim(); 20 | } 21 | 22 | function convertToMarkdown(filePath, outputDirectory) { 23 | try { 24 | const pythonCode = fs.readFileSync(filePath, 'utf8'); 25 | const description = extractDescriptionFromPythonCode(pythonCode); 26 | const codeWithoutComments = removeCommentsFromPythonCode(pythonCode); 27 | const codeWithoutExtraLineBreaks = removeExtraLineBreaksFromCodeBlock(codeWithoutComments); 28 | const fileName = path.basename(filePath, '.py'); 29 | const markdownFileName = fileName + '.md'; 30 | const markdownFilePath = path.join(outputDirectory, markdownFileName); 31 | const title = fileName.replace(/_/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase()); 32 | const markdownContent = `# ${title}\n\n${description}\n\n\`\`\`python\n${codeWithoutExtraLineBreaks}\n\`\`\``; 33 | fs.writeFileSync(markdownFilePath, markdownContent); 34 | console.log(`Converted ${filePath} to ${markdownFilePath}`); 35 | } catch (error) { 36 | console.error(`Error converting ${filePath}: ${error.message}`); 37 | } 38 | } 39 | 40 | function generateExamples(directoryPath, outputDirectory) { 41 | if (!fs.existsSync(outputDirectory)) { 42 | fs.mkdirSync(outputDirectory); 43 | } 44 | 45 | fs.readdirSync(directoryPath).forEach(file => { 46 | const filePath = path.join(directoryPath, file); 47 | 48 | if (file.endsWith('.py')) { 49 | convertToMarkdown(filePath, outputDirectory); 50 | } 51 | }); 52 | } 53 | 54 | export { generateExamples }; 55 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/10.946430c0.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[10],{189:function(t,s,a){"use strict";a.r(s);var e=a(0),n=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"use-cef"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#use-cef","aria-hidden":"true"}},[t._v("#")]),t._v(" Use CEF")]),t._v(" "),a("p",[t._v("To use Chrome Embedded Framework on Windows.")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("gui "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'cef'")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'CEF Example'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}],!1,null,null,null);s.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/13.94ca571d.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[13],{205:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"debugging"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#debugging","aria-hidden":"true"}},[t._v("#")]),t._v(" Debugging")]),t._v(" "),e("p",[t._v("To open up debugging console, right click on an element and select Inspect.")]),t._v(" "),e("div",{staticClass:"language-python extra-class"},[e("pre",{pre:!0,attrs:{class:"language-python"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Debug window'")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" debug"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),e("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}],!1,null,null,null);s.default=a.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/15.788bb271.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[15],{184:function(t,s,e){"use strict";e.r(s);var n=e(0),a=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"fullscreen-window"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#fullscreen-window","aria-hidden":"true"}},[t._v("#")]),t._v(" Fullscreen window")]),t._v(" "),e("p",[t._v("Create a fullscreen window.")]),t._v(" "),e("div",{staticClass:"language-python extra-class"},[e("pre",{pre:!0,attrs:{class:"language-python"}},[e("code",[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Create a non-resizable webview window with 800x600 dimensions")]),t._v("\n webview"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Full-screen browser"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"https://pywebview.flowrl.com/hello"')]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n fullscreen"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),e("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}],!1,null,null,null);s.default=a.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/19.ec102f84.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[19],{180:function(t,e,n){"use strict";n.r(e);var a=n(0),r=Object(a.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"javascript-evaluation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#javascript-evaluation","aria-hidden":"true"}},[this._v("#")]),this._v(" Javascript evaluation")]),this._v(" "),e("p",[this._v("Evaluate Javascript from Python code.")]),this._v(" "),e("div",{staticClass:"language-import webview extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("import threading\n\n\ndef evaluate_js():\n result = webview.evaluate_js(\n r\"\"\"\n var h1 = document.createElement('h1')\n var text = document.createTextNode('Hello pywebview')\n h1.appendChild(text)\n document.body.appendChild(h1)\n\n document.body.style.backgroundColor = '#212121'\n document.body.style.color = '#f2f2f2'\n\n // Return user agent\n 'User agent:\\n' + navigator.userAgent;\n \"\"\"\n )\n\n print(result)\n\n\nif __name__ == '__main__':\n t = threading.Thread(target=evaluate_js)\n t.start()\n\n webview.create_window('Run custom JavaScript')\n")])])])])}],!1,null,null,null);e.default=r.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/2.06af3526.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[2],{165:function(t,n,e){},166:function(t,n,e){"use strict";var a=e(165);e.n(a).a},186:function(t,n,e){"use strict";e.r(n);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,n){var e=n.props,a=n.slots;return t("span",{class:["badge",e.type,e.vertical]},e.text||a().default)}},r=(e(166),e(0)),i=Object(r.a)(a,void 0,void 0,!1,null,"099ab69c",null);n.default=i.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/25.3af86868.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[25],{174:function(t,s,a){"use strict";a.r(s);var e=a(0),n=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"open-url"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#open-url","aria-hidden":"true"}},[t._v("#")]),t._v(" Open URL")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Simple browser'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}],!1,null,null,null);s.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/26.562c94d4.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[26],{173:function(t,a,s){"use strict";s.r(a);var n=s(0),e=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"content"},[s("h1",{attrs:{id:"quit-confirmation-dialog"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#quit-confirmation-dialog","aria-hidden":"true"}},[t._v("#")]),t._v(" Quit confirmation dialog")]),t._v(" "),s("div",{staticClass:"language-python extra-class"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Create a standard webview window")]),t._v("\n webview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Confirm Quit Example'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n confirm_quit"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}],!1,null,null,null);a.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/33.96fe48c6.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[33],{203:function(e,t,n){"use strict";n.r(t);var a=n(0),s=Object(a.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"content"},[n("h1",{attrs:{id:"debugging"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#debugging","aria-hidden":"true"}},[e._v("#")]),e._v(" Debugging")]),e._v(" "),n("p",[e._v("To debug Javascript, set the "),n("code",[e._v("debug")]),e._v(" parameter to "),n("code",[e._v("True")])]),e._v(" "),n("div",{staticClass:"language-python extra-class"},[n("pre",{pre:!0,attrs:{class:"language-python"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("import")]),e._v(" webview\n\nwebview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("create_window"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[e._v("'https://pywebview.flowrl.com'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(",")]),e._v(" debug"),n("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[e._v("True")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n")])])]),n("p",[e._v("This will enable web inspector on macOS, GTK and QT (QTWebEngine only). To open the web inspector, right click on the page and select Inspect. Unfortunately the Trident web engine on Windows has no web inspector and currently there is no way to attach an external debugger. The "),n("code",[e._v("debug")]),e._v(" flag enables Javascript error reporting and right-click context menu on Windows.")]),e._v(" "),n("p",[e._v("You can debug Python code normally using your favorite debugger.")])])}],!1,null,null,null);t.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/34.1503f1f8.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[34],{202:function(e,r,t){"use strict";t.r(r);var n=t(0),o=Object(n.a)({},function(){var e=this,r=e.$createElement,t=e._self._c||r;return t("div",{staticClass:"content"},[e._m(0),e._v(" "),t("p",[e._v("To freeze your application use "),t("a",{attrs:{href:"https://py2app.readthedocs.io/en/latest/",target:"_blank",rel:"noopener noreferrer"}},[e._v("py2app"),t("OutboundLink")],1),e._v(" on macOS ans "),t("a",{attrs:{href:"https://www.pyinstaller.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("pyinstaller"),t("OutboundLink")],1),e._v(" on Windows. For a reference setup.py for py2app, look "),t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/blob/master/examples/py2app_setup.py",target:"_blank",rel:"noopener noreferrer"}},[e._v("here"),t("OutboundLink")],1)]),e._v(" "),t("p",[e._v("For Pyinstaller you need to include either "),t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x86.dll",target:"_blank",rel:"noopener noreferrer"}},[e._v("WebBrowserInterop.x86.dll"),t("OutboundLink")],1),e._v(" or "),t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x64.dll",target:"_blank",rel:"noopener noreferrer"}},[e._v("WebBrowserInterop.x64.dll"),t("OutboundLink")],1),e._v(" depending on whether you build against 32-bit or 64-bit Python. The DLLs bundled with "),t("em",[e._v("pywebview")]),e._v(" and are located in the "),t("code",[e._v("site-packages/webview/lib")]),e._v(" directory. There is currently an effort to provide a hook for pyinstaller that would bundle this DLL automatically, but for now you have to resort to this step.")]),e._v(" "),e._m(1)])},[function(){var e=this.$createElement,r=this._self._c||e;return r("h1",{attrs:{id:"freezing"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#freezing","aria-hidden":"true"}},[this._v("#")]),this._v(" Freezing")])},function(){var e=this.$createElement,r=this._self._c||e;return r("p",[r("em",[this._v("TODO: add Linux freezing guide")])])}],!1,null,null,null);r.default=o.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/37.5656488c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[37],{197:function(e,t,r){"use strict";r.r(t);var a=r(0),s=Object(a.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),r("p",[e._v("When using a local web server, you must protect your API from unauthorized access. "),r("a",{attrs:{href:"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)",target:"_blank",rel:"noopener noreferrer"}},[e._v("CSRF attacks"),r("OutboundLink")],1),e._v(" can be a major problem if API is not protected in an adequate matter. Refer to "),r("a",{attrs:{href:"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet",target:"_blank",rel:"noopener noreferrer"}},[e._v("this document"),r("OutboundLink")],1),e._v(" for API securing approaches. A library like "),r("a",{attrs:{href:"https://flask-seasurf.readthedocs.io/en/latest/",target:"_blank",rel:"noopener noreferrer"}},[e._v("flask-seasurf"),r("OutboundLink")],1),e._v(" alongside Flask can be used too.")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"security"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#security","aria-hidden":"true"}},[this._v("#")]),this._v(" Security")])}],!1,null,null,null);t.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/39.8b7a8ec5.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[39],{200:function(t,e,n){"use strict";n.r(e);var s=n(0),i=Object(s.a)({},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[this._m(0),this._v(" "),e("p",[this._v("If you create a virtual environment using the built-in Python on macOS, a pywebview window will have issues with keyboard focus and Cmd+Tab. The issue can be avoided by using other Python installation as described "),e("a",{attrs:{href:"https://virtualenv.pypa.io/en/stable/userguide/#using-virtualenv-without-bin-python",target:"_blank",rel:"noopener noreferrer"}},[this._v("here"),e("OutboundLink")],1),this._v(". For example, Python 3 installed via Homebrew](https://brew.sh) does not")]),this._v(" "),this._m(1)])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"virtual-environment"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#virtual-environment","aria-hidden":"true"}},[this._v("#")]),this._v(" Virtual environment")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("brew "),e("span",{pre:!0,attrs:{class:"token function"}},[this._v("install")]),this._v(" python3\nvirtualenv pywebview_env -p python3\n")])])])}],!1,null,null,null);e.default=i.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/4.cc18902f.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{199:function(e,t,o){"use strict";o.r(t);var a=o(0),n=Object(a.a)({},function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("div",{staticClass:"content"},[o("p",[e._v("Thanks for considering contributing to pywebview.")]),e._v(" "),o("p",[e._v("Pywebview is a small-time project, which gets updated sporadically whenever time permits. Any help is more than appreciated and the best way to contribute is submitting a pull request. Bug fixes are always welcome. If you wish to submit a new feature, please create an issue and discuss it beforehand.")]),e._v(" "),o("p",[e._v("If you found a bug and want to report it, please test it first in a web-browser that is used by default for your operating system to see if the problem is with your code, rather than pywebview. Do not forget to specify on which platform and pywebview version it occurs.")]),e._v(" "),o("p",[e._v("To support pywebview financially, consider becoming a patron of the project. Pywebview has no corporate backing and financial help is welcomed to keep the project alive.")]),e._v(" "),e._m(0),e._v(" "),o("p",[e._v("For other ways to donate refer to the "),o("router-link",{attrs:{to:"/contributing/donating.html"}},[e._v("donation")]),e._v(" page.")],1)])},[function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"center spc-l spc-vertical"},[t("a",{attrs:{href:"https://www.patreon.com/bePatron?u=13226105","data-patreon-widget-type":"become-patron-button"}},[t("img",{attrs:{src:"https://c5.patreon.com/external/logo/become_a_patron_button.png",alt:"Become a Patron!"}})])])}],!1,null,null,null);t.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/40.14c37596.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[40],{167:function(n,w,o){}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/5.b85830ed.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{194:function(e,t,r){"use strict";r.r(t);var i=r(0),s=Object(i.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),r("p",[e._v("If you think you found a bug, verify following steps first")]),e._v(" "),r("ol",[r("li",[e._v("Does the bug occur in a default browser? If so, the problem is with your code, not pywebview")]),e._v(" "),r("li",[e._v("Are you using the latest master? Bug fixes are merged into the master and it may take a while until a new release is deployed to Pypi.")]),e._v(" "),r("li",[e._v("Has it been "),r("a",{attrs:{href:"https://github.com/r0x0r/pywebview/issues",target:"_blank",rel:"noopener noreferrer"}},[e._v("reported"),r("OutboundLink")],1),e._v(" already?")])]),e._v(" "),r("p",[e._v("If you verified all the three points and are sure that the issue is caused by pywebview, feel free to submit a new issue. Please remember to specify under which operating system the bug occurs, as well as with other relevant information. In case of Linux, specify a distro you are using.")])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"bug-reporting"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#bug-reporting","aria-hidden":"true"}},[this._v("#")]),this._v(" Bug reporting")])}],!1,null,null,null);t.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/7.5eeb60d8.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{192:function(t,e,i){"use strict";i.r(e);var n=i(0),a=Object(n.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"documentation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#documentation","aria-hidden":"true"}},[this._v("#")]),this._v(" Documentation")]),this._v(" "),e("p",[this._v("One way to contribute is to improve documentation on this side. Each page has a 'Help us improve this page' link at the bottom of the page. By clicking the link you can create a pull request with your changes. You need a Github account to edit pages.")])])}],!1,null,null,null);e.default=a.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/8.b9ad99cd.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[8],{191:function(t,e,r){"use strict";r.r(e);var a=r(0),n=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("Recurring pledges come perks, like getting email support or featuring your name or logo in the project repository")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),r("p",[r("a",{attrs:{href:"http://bit.ly/2eg2Z5P",target:"_blank",rel:"noopener noreferrer"}},[r("img",{attrs:{src:"/paypal.png",alt:"Paypal"}}),r("OutboundLink")],1)])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"donating"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#donating","aria-hidden":"true"}},[this._v("#")]),this._v(" Donating")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"recurrring-pledge"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#recurrring-pledge","aria-hidden":"true"}},[this._v("#")]),this._v(" Recurrring pledge")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"center spc-l spc-vertical"},[e("a",{attrs:{href:"https://www.patreon.com/bePatron?u=13226105","data-patreon-widget-type":"become-patron-button"}},[e("img",{attrs:{src:"https://c5.patreon.com/external/logo/become_a_patron_button.png",alt:"Become a Patron!"}})])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"one-time-donations"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#one-time-donations","aria-hidden":"true"}},[this._v("#")]),this._v(" One-time donations")])}],!1,null,null,null);e.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/assets/js/9.6a770a26.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[9],{190:function(e,t,r){"use strict";r.r(t);var a=r(0),s=Object(a.a)({},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"content"},[this._m(0),this._v(" "),t("p",[this._v("Here you can find examples demonstrating different aspects of pywebview. For non-trivial examples on how to create a full-grown application refer to the repository.")]),this._v(" "),t("ul",[t("li",[t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/tree/master/examples/todos",target:"_blank",rel:"noopener noreferrer"}},[this._v("Serverless application"),t("OutboundLink")],1)]),this._v(" "),t("li",[t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/tree/master/examples/flask_app",target:"_blank",rel:"noopener noreferrer"}},[this._v("Flask-based application"),t("OutboundLink")],1)])])])},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"examples"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#examples","aria-hidden":"true"}},[this._v("#")]),this._v(" Examples")])}],!1,null,null,null);t.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/2.4/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/2.4/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/2.4/paypal.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/img/pywebview3.5e63e895.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/assets/img/pywebview3.5e63e895.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/10.f125ec08.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[10],{410:function(t,e,a){"use strict";a.r(e);var r=a(58),s=Object(r.a)({},(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"blog"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#blog"}},[t._v("#")]),t._v(" Blog")]),t._v(" "),a("h3",{attrs:{id:"_2019-07-11-introducing-pywebview-3-0"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#_2019-07-11-introducing-pywebview-3-0"}},[t._v("#")]),t._v(" "),a("span",{staticClass:"date"},[t._v("2019-07-11")]),t._v(" Introducing pywebview 3.0")]),t._v(" "),a("p",[a("em",[t._v("pywebview")]),t._v(" has reached version 3.0 and has a number of breaking changes."),a("br"),t._v(" "),a("RouterLink",{attrs:{to:"/blog/pywebview3.html"}},[t._v("Read more")])],1)])}),[],!1,null,null,null);e.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/11.81fead78.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[11],{411:function(t,e,o){"use strict";o.r(e);var a=o(58),n=Object(a.a)({},(function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("p",[t._v("Thanks for considering contributing to pywebview.")]),t._v(" "),o("p",[t._v("Pywebview is a small-time project, which gets updated sporadically whenever time permits. Any help is more than appreciated and the best way to contribute is submitting a pull request. Bug fixes are always welcome. If you wish to submit a new feature, please create an issue and discuss it beforehand.")]),t._v(" "),o("p",[t._v("If you found a bug and want to report it, please test it first in a web-browser that is used by default for your operating system to see if the problem is with your code, rather than pywebview. Do not forget to specify on which platform and pywebview version it occurs.")]),t._v(" "),o("p",[t._v("To support pywebview financially, consider becoming a patron of the project. Pywebview has no corporate backing and financial help is welcomed to keep the project alive.")]),t._v(" "),o("div",{staticClass:"center spc-l spc-vertical"},[o("a",{attrs:{href:"https://www.patreon.com/bePatron?u=13226105","data-patreon-widget-type":"become-patron-button"}},[o("img",{attrs:{src:"https://c5.patreon.com/external/logo/become_a_patron_button.png",alt:"Become a Patron!"}})])]),t._v(" "),o("div",{staticClass:"center spc-l spc-vertical"},[o("a",{attrs:{href:"https://opencollective.com/pywebview/donate",target:"_blank"}},[o("img",{attrs:{src:"https://opencollective.com/pywebview/donate/button@2x.png?color=blue",width:"300"}})])]),t._v(" "),o("p",[t._v("For other ways to donate refer to the "),o("RouterLink",{attrs:{to:"/contributing/donating.html"}},[t._v("donation")]),t._v(" page.")],1)])}),[],!1,null,null,null);e.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/12.0a86c191.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[12],{412:function(e,t,r){"use strict";r.r(t);var s=r(58),i=Object(s.a)({},(function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("h1",{attrs:{id:"bug-reporting"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#bug-reporting"}},[e._v("#")]),e._v(" Bug reporting")]),e._v(" "),r("p",[e._v("If you think you found a bug, verify following steps first")]),e._v(" "),r("ol",[r("li",[e._v("Does the bug occur in a default browser? If so, the problem is with your code, not pywebview")]),e._v(" "),r("li",[e._v("Are you using the latest master? Bug fixes are merged into the master and it may take a while until a new release is deployed to Pypi.")]),e._v(" "),r("li",[e._v("Has it been "),r("a",{attrs:{href:"https://github.com/r0x0r/pywebview/issues",target:"_blank",rel:"noopener noreferrer"}},[e._v("reported"),r("OutboundLink")],1),e._v(" already?")])]),e._v(" "),r("p",[e._v("If you verified all the three points and are sure that the issue is caused by pywebview, feel free to submit a new issue. Please remember to specify under which operating system the bug occurs, as well as with other relevant information. In case of Linux, specify a distro you are using.")])])}),[],!1,null,null,null);t.default=i.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/14.2a7dc35b.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[14],{416:function(t,e,a){"use strict";a.r(e);var n=a(58),o=Object(n.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[e("h1",{attrs:{id:"documentation"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#documentation"}},[this._v("#")]),this._v(" Documentation")]),this._v(" "),e("p",[this._v("One way to contribute is to improve documentation on this side. Each page has a 'Help us improve this page' link at the bottom of the page. By clicking the link you can create a pull request with your changes. You need a Github account to edit pages.")])])}),[],!1,null,null,null);e.default=o.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/15.90c9f36a.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[15],{417:function(t,e,a){"use strict";a.r(e);var r=a(58),n=Object(r.a)({},(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"donating"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#donating"}},[t._v("#")]),t._v(" Donating")]),t._v(" "),a("h2",{attrs:{id:"recurrring-pledge"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#recurrring-pledge"}},[t._v("#")]),t._v(" Recurrring pledge")]),t._v(" "),a("p",[t._v("Recurring pledges come perks, like getting email support or featuring your name or logo in the project repository")]),t._v(" "),a("div",{staticClass:"center spc-l spc-vertical"},[a("a",{attrs:{href:"https://www.patreon.com/bePatron?u=13226105","data-patreon-widget-type":"become-patron-button"}},[a("img",{attrs:{src:"https://c5.patreon.com/external/logo/become_a_patron_button.png",alt:"Become a Patron!"}})])]),t._v(" "),a("div",{staticClass:"center spc-l spc-vertical"},[a("a",{attrs:{href:"https://opencollective.com/pywebview/donate",target:"_blank"}},[a("img",{attrs:{src:"https://opencollective.com/pywebview/donate/button@2x.png?color=blue",width:"300"}})])]),t._v(" "),a("h2",{attrs:{id:"one-time-donations"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#one-time-donations"}},[t._v("#")]),t._v(" One-time donations")]),t._v(" "),a("p",[t._v("We accept donations via Paypal")]),t._v(" "),a("div",{staticClass:"center spc-l spc-vertical"},[a("a",{attrs:{href:"http://bit.ly/2eg2Z5P",target:"_blank"}},[a("img",{attrs:{src:"/paypal.png"}})])])])}),[],!1,null,null,null);e.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/19.b31e62b7.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[19],{418:function(t,a,s){"use strict";s.r(a);var n=s(58),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"quit-confirmation-dialog"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#quit-confirmation-dialog"}},[t._v("#")]),t._v(" Quit confirmation dialog")]),t._v(" "),s("div",{staticClass:"language-python extra-class"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),s("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\nThis example demonstrates a webview window with a quit confirmation dialog.\n"""')]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Create a standard webview window")]),t._v("\n webview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Confirm Close Example'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n confirm_close"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])])])}),[],!1,null,null,null);a.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/22.2a3916f2.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[22],{421:function(t,s,a){"use strict";a.r(s);var n=a(58),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"css-load"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#css-load"}},[t._v("#")]),t._v(" CSS load")]),t._v(" "),a("p",[t._v("Change window background color by loading CSS")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("load_css")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("load_css"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'body { background: red !important; }'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n window "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Load CSS Example'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("load_css"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/23.712fbf84.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[23],{423:function(t,s,n){"use strict";n.r(s);var a=n(58),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"debugging"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#debugging"}},[t._v("#")]),t._v(" Debugging")]),t._v(" "),n("p",[t._v("To open up debugging console, right click on an element and select Inspect.")]),t._v(" "),n("div",{staticClass:"language-python extra-class"},[n("pre",{pre:!0,attrs:{class:"language-python"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Debug window'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("debug"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/27.e2a4e1bf.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[27],{427:function(t,s,a){"use strict";a.r(s);var n=a(58),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"frameless-window"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#frameless-window"}},[t._v("#")]),t._v(" Frameless window")]),t._v(" "),a("p",[t._v("Create a frameless window. The window can be moved around by dragging any point.")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Frameless window'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'http://pywebview.flowrl.com/hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n frameless"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/28.abcc819c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[28],{424:function(t,s,n){"use strict";n.r(s);var a=n(58),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"fullscreen-window"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#fullscreen-window"}},[t._v("#")]),t._v(" Fullscreen window")]),t._v(" "),n("p",[t._v("Create a fullscreen window.")]),t._v(" "),n("div",{staticClass:"language-python extra-class"},[n("pre",{pre:!0,attrs:{class:"language-python"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Full-screen window'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n fullscreen"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/29.c379d080.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[29],{436:function(t,n,s){"use strict";s.r(n);var a=s(58),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"get-current-url"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#get-current-url"}},[t._v("#")]),t._v(" Get current URL")]),t._v(" "),s("p",[t._v("Print current URL after page is loaded.")]),t._v(" "),s("div",{staticClass:"language-python extra-class"},[s("pre",{pre:!0,attrs:{class:"language-python"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("get_current_url")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get_current_url"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n window "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" webview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Get current URL'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("get_current_url"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" window"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);n.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/3.749f6582.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[3],{373:function(t,e,n){},401:function(t,e,n){"use strict";n(373)},466:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,i=e.slots;return t("span",{class:["badge",n.type],style:{verticalAlign:n.vertical}},n.text||i().default)}},r=(n(401),n(58)),p=Object(r.a)(i,void 0,void 0,!1,null,"15b7b770",null);e.default=p.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/36.d53c471f.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[36],{434:function(t,n,a){"use strict";a.r(n);var e=a(58),s=Object(e.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"link-types"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#link-types"}},[t._v("#")]),t._v(" Link types")]),t._v(" "),a("p",[t._v("Demonstrate a difference between different link types")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\nhtml "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v("\"\"\"\n \n \n \n

Links

\n\n

Regular links are opened in the application window.

\n

target='_blank' links are opened in an external browser.

\n\n \n \n\"\"\"")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n window "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Link types'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" html"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("html"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);n.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/4.f2c64e71.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{374:function(t,e,a){},402:function(t,e,a){"use strict";a(374)},406:function(t,e,a){"use strict";a.r(e);var n={name:"CodeBlock",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}},mounted:function(){this.$parent&&this.$parent.loadTabs&&this.$parent.loadTabs()}},i=(a(402),a(58)),s=Object(i.a)(n,(function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"theme-code-block",class:{"theme-code-block__active":this.active}},[this._t("default")],2)}),[],!1,null,"759a7d02",null);e.default=s.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/40.53182a9c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[40],{438:function(t,s,n){"use strict";n.r(s);var a=n(58),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"minimum-window-size"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#minimum-window-size"}},[t._v("#")]),t._v(" Minimum window size")]),t._v(" "),n("p",[t._v("Set minimum window dimensions.")]),t._v(" "),n("div",{staticClass:"language-python extra-class"},[n("pre",{pre:!0,attrs:{class:"language-python"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Create a resizable webview window with minimum size constraints")]),t._v("\n webview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Minimum window size'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n min_size"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("400")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/46.d1621897.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[46],{443:function(t,s,a){"use strict";a.r(s);var n=a(58),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"open-url"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#open-url"}},[t._v("#")]),t._v(" Open URL")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Create a standard webview window")]),t._v("\n window "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Simple browser'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/5.2a771d7d.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{375:function(e,t,a){},403:function(e,t,a){"use strict";a(375)},407:function(e,t,a){"use strict";a.r(t);a(84),a(39),a(7),a(116),a(117);var o={name:"CodeGroup",data:function(){return{codeTabs:[],activeCodeTabIndex:-1}},watch:{activeCodeTabIndex:function(e){this.activateCodeTab(e)}},mounted:function(){this.loadTabs()},methods:{changeCodeTab:function(e){this.activeCodeTabIndex=e},loadTabs:function(){var e=this;this.codeTabs=(this.$slots.default||[]).filter((function(e){return Boolean(e.componentOptions)})).map((function(t,a){return""===t.componentOptions.propsData.active&&(e.activeCodeTabIndex=a),{title:t.componentOptions.propsData.title,elm:t.elm}})),-1===this.activeCodeTabIndex&&this.codeTabs.length>0&&(this.activeCodeTabIndex=0),this.activateCodeTab(0)},activateCodeTab:function(e){this.codeTabs.forEach((function(e){e.elm&&e.elm.classList.remove("theme-code-block__active")})),this.codeTabs[e].elm&&this.codeTabs[e].elm.classList.add("theme-code-block__active")}}},n=(a(403),a(58)),c=Object(n.a)(o,(function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ClientOnly",[a("div",{staticClass:"theme-code-group"},[a("div",{staticClass:"theme-code-group__nav"},[a("ul",{staticClass:"theme-code-group__ul"},e._l(e.codeTabs,(function(t,o){return a("li",{key:t.title,staticClass:"theme-code-group__li"},[a("button",{staticClass:"theme-code-group__nav-tab",class:{"theme-code-group__nav-tab-active":o===e.activeCodeTabIndex},on:{click:function(t){return e.changeCodeTab(o)}}},[e._v("\n "+e._s(t.title)+"\n ")])])})),0)]),e._v(" "),e._t("default"),e._v(" "),e.codeTabs.length<1?a("pre",{staticClass:"pre-blank"},[e._v("// Make sure to add code blocks to your code group")]):e._e()],2)])}),[],!1,null,"deefee04",null);t.default=c.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/51.8a918a6c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[51],{448:function(t,s,a){"use strict";a.r(s);var n=a(58),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"change-user-agent-string"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#change-user-agent-string"}},[t._v("#")]),t._v(" Change user agent string")]),t._v(" "),a("p",[t._v("Change the user-agent of a window. EdgeHTML is not supported.")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" webview\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" __name__ "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'__main__'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_window"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'User Agent Test'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'https://pywebview.flowrl.com/hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n webview"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("start"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("user_agent"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Custom user agent'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/54.2b15d6fb.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[54],{454:function(e,t,o){"use strict";o.r(t);var n=o(58),r=Object(n.a)({},(function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[o("h1",{attrs:{id:"about"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#about"}},[e._v("#")]),e._v(" About")]),e._v(" "),o("p",[o("em",[e._v("pywebview")]),e._v(" is a lightweight cross-platform wrapper around a webview component that allows to display HTML content in its own native GUI window. You may think of as Electron for Python (minus huge executable sizes). It gives you power of web technologies in your desktop application, hiding the fact that GUI is browser based. You can use pywebview either with a lightweight web framework like "),o("a",{attrs:{href:"http://flask.pocoo.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Flask"),o("OutboundLink")],1),e._v(" or "),o("a",{attrs:{href:"http://bottlepy.org/docs/dev/index.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("Bottle"),o("OutboundLink")],1),e._v(" or on its own with a two way bridge between Python and DOM.")]),e._v(" "),o("p",[o("em",[e._v("pywebview")]),e._v(" uses native GUI for creating a web component window: WinForms on Windows, Cocoa on macOS and QT or GTK on Linux. If you choose to freeze your application, pywebview does not bundle a heavy GUI toolkit or web renderer with it keeping the executable size small.")]),e._v(" "),o("p",[o("em",[e._v("pywebview")]),e._v(" is a BSD licensed open source project. It is an independent project with no corporate backing. If you find "),o("em",[e._v("pywebview")]),e._v(" useful, consider supporting it. More donation options are outlined on the "),o("RouterLink",{attrs:{to:"/contributing/donating.html"}},[e._v("Donating")]),e._v(" page.")],1),e._v(" "),o("div",{staticClass:"spc-l spc-bottom center"},[o("a",{attrs:{href:"https://www.patreon.com/bePatron?u=13226105","data-patreon-widget-type":"become-patron-button"}},[o("img",{attrs:{src:"https://c5.patreon.com/external/logo/become_a_patron_button.png",alt:"Become a Patron!"}})])]),e._v(" "),o("div",{staticClass:"center spc-l spc-vertical"},[o("a",{attrs:{href:"https://opencollective.com/pywebview/donate",target:"_blank"}},[o("img",{attrs:{src:"https://opencollective.com/pywebview/donate/button@2x.png?color=blue",width:"300"}})])]),e._v(" "),o("p",[o("em",[e._v("pywebview")]),e._v(" is created by "),o("a",{attrs:{href:"https://github.com/r0x0r/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Roman Sirokov"),o("OutboundLink")],1),e._v(".")])])}),[],!1,null,null,null);t.default=r.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/57.61e9e46c.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[57],{458:function(e,t,n){"use strict";n.r(t);var a=n(58),o=Object(a.a)({},(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[n("h1",{attrs:{id:"debugging"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#debugging"}},[e._v("#")]),e._v(" Debugging")]),e._v(" "),n("p",[e._v("To debug Javascript, set the "),n("code",[e._v("debug")]),e._v(" parameter of "),n("code",[e._v("start")]),e._v(" to "),n("code",[e._v("True")])]),e._v(" "),n("div",{staticClass:"language-python extra-class"},[n("pre",{pre:!0,attrs:{class:"language-python"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[e._v("import")]),e._v(" webview\n\nwebview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("create_window"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[e._v("'https://pywebview.flowrl.com/hello'")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\nwebview"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(".")]),e._v("start"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v("(")]),e._v("debug"),n("span",{pre:!0,attrs:{class:"token operator"}},[e._v("=")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[e._v("True")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[e._v(")")]),e._v("\n")])])]),n("p",[e._v("This will enable web inspector on macOS, GTK and QT (QTWebEngine only). To open the web inspector, right click on the page and select Inspect.")]),e._v(" "),n("p",[e._v("To debug EdgeHTML, you need to install "),n("a",{attrs:{href:"https://www.microsoft.com/en-us/p/microsoft-edge-devtools-preview/9mzbfrmz0mnj",target:"_blank",rel:"noopener noreferrer"}},[e._v("Microsoft Edge DevTools Preview"),n("OutboundLink")],1),e._v(". Launch the application and select your application from the list of running WebViews. The "),n("code",[e._v("debug")]),e._v(" flag also routes "),n("code",[e._v("console.logs")]),e._v(" to the Python console.")]),e._v(" "),n("p",[e._v("There is no way to attach an external debugger to MSHTML. The "),n("code",[e._v("debug")]),e._v(" flag enables Javascript error reporting and right-click context menu on Windows.")])])}),[],!1,null,null,null);t.default=o.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/58.cc70e253.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[58],{460:function(e,r,t){"use strict";t.r(r);var o=t(58),n=Object(o.a)({},(function(){var e=this,r=e.$createElement,t=e._self._c||r;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"freezing"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#freezing"}},[e._v("#")]),e._v(" Freezing")]),e._v(" "),t("h2",{attrs:{id:"macos"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#macos"}},[e._v("#")]),e._v(" macOS")]),e._v(" "),t("p",[e._v("Use "),t("a",{attrs:{href:"https://py2app.readthedocs.io/en/latest/",target:"_blank",rel:"noopener noreferrer"}},[e._v("py2app"),t("OutboundLink")],1),e._v(". For a reference setup.py for py2app, look "),t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/blob/master/examples/py2app_setup.py",target:"_blank",rel:"noopener noreferrer"}},[e._v("here"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("h2",{attrs:{id:"windows"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#windows"}},[e._v("#")]),e._v(" Windows")]),e._v(" "),t("p",[e._v("Use "),t("a",{attrs:{href:"https://www.pyinstaller.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("pyinstaller"),t("OutboundLink")],1),e._v(".")]),e._v(" "),t("p",[e._v("If you are using "),t("em",[e._v("PyInstaller>=3.6")]),e._v(", it should work out of the box as there is hook that takes care of the bundling of necessary dlls. Therefore, this version of PyInstaller is the recommended one.")]),e._v(" "),t("p",[e._v("Should you need to use prior versions of PyInstaller (<=3.5), you will need to bundle the dlls yourself. Either "),t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x86.dll",target:"_blank",rel:"noopener noreferrer"}},[e._v("WebBrowserInterop.x86.dll"),t("OutboundLink")],1),e._v(" or "),t("a",{attrs:{href:"https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x64.dll",target:"_blank",rel:"noopener noreferrer"}},[e._v("WebBrowserInterop.x64.dll"),t("OutboundLink")],1),e._v(" depending on whether you build against 32-bit or 64-bit Python.\nThe DLLs bundled with "),t("em",[e._v("pywebview")]),e._v(" and are located in the "),t("code",[e._v("site-packages/webview/lib")]),e._v(" directory.")]),e._v(" "),t("h2",{attrs:{id:"linux"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#linux"}},[e._v("#")]),e._v(" Linux")]),e._v(" "),t("p",[e._v("Use "),t("a",{attrs:{href:"https://www.pyinstaller.org/",target:"_blank",rel:"noopener noreferrer"}},[e._v("pyinstaller"),t("OutboundLink")],1),e._v(".")])])}),[],!1,null,null,null);r.default=n.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/62.bf000bcc.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[62],{464:function(e,t,r){"use strict";r.r(t);var o=r(58),a=Object(o.a)({},(function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("h1",{attrs:{id:"security"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#security"}},[e._v("#")]),e._v(" Security")]),e._v(" "),r("p",[e._v("When using a local web server, you must protect your API from unauthorized access. "),r("a",{attrs:{href:"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)",target:"_blank",rel:"noopener noreferrer"}},[e._v("CSRF attacks"),r("OutboundLink")],1),e._v(" can be a major problem if API is not protected in an adequate matter. "),r("em",[e._v("pywebview")]),e._v(" generates a session-unique token that is exposed both to Python "),r("code",[e._v("webview.token")]),e._v(" and DOM "),r("code",[e._v("window.pywebview.token")]),e._v(". See "),r("a",{attrs:{href:"https://github.com/r0x0r/pywebview/tree/master/examples/flask_app",target:"_blank",rel:"noopener noreferrer"}},[e._v("Flask app"),r("OutboundLink")],1),e._v(" for an example.")]),e._v(" "),r("p",[e._v("For building a custom solution refer to "),r("a",{attrs:{href:"https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet",target:"_blank",rel:"noopener noreferrer"}},[e._v("this document"),r("OutboundLink")],1),e._v(" for API securing approaches. A library like "),r("a",{attrs:{href:"https://flask-seasurf.readthedocs.io/en/latest/",target:"_blank",rel:"noopener noreferrer"}},[e._v("flask-seasurf"),r("OutboundLink")],1),e._v(" alongside Flask can be used too.")])])}),[],!1,null,null,null);t.default=a.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/64.2b6fe843.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[64],{465:function(e,t,n){"use strict";n.r(t);var a=n(58),r=Object(a.a)({},(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[n("h1",{attrs:{id:"virtual-environment"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#virtual-environment"}},[e._v("#")]),e._v(" Virtual environment")]),e._v(" "),n("p",[e._v("If you create a virtual environment using the built-in Python on macOS, a pywebview window will have issues with keyboard focus and Cmd+Tab. The issue can be avoided by using other Python installation as described "),n("a",{attrs:{href:"https://virtualenv.pypa.io/en/stable/userguide/#using-virtualenv-without-bin-python",target:"_blank",rel:"noopener noreferrer"}},[e._v("here"),n("OutboundLink")],1),e._v(". For example to use Python 3 via "),n("a",{attrs:{href:"https://brew.sh",target:"_blank",rel:"noopener noreferrer"}},[e._v("Homebrew"),n("OutboundLink")],1),e._v(".")]),e._v(" "),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[e._v("brew "),n("span",{pre:!0,attrs:{class:"token function"}},[e._v("install")]),e._v(" python3\nvirtualenv pywebview_env -p python3\n")])])])])}),[],!1,null,null,null);t.default=r.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/assets/js/7.779cbcff.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{405:function(t,e,s){"use strict";s.r(e);var n=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],o={methods:{getMsg:function(){return n[Math.floor(Math.random()*n.length)]}}},i=s(58),h=Object(i.a)(o,(function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"theme-container"},[e("div",{staticClass:"theme-default-content"},[e("h1",[this._v("404")]),this._v(" "),e("blockquote",[this._v(this._s(this.getMsg()))]),this._v(" "),e("RouterLink",{attrs:{to:"/"}},[this._v("\n Take me home.\n ")])],1)])}),[],!1,null,null,null);e.default=h.exports}}]); 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/logo-no-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/logo-no-text.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/paypal.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/screenshots/todos-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/screenshots/todos-linux.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/screenshots/todos-macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/screenshots/todos-macos.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/screenshots/todos-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/screenshots/todos-windows.png -------------------------------------------------------------------------------- /docs/.vuepress/public/3.7/windows31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/3.7/windows31.png -------------------------------------------------------------------------------- /docs/.vuepress/public/download/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Pywebview download test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 | pywebview 18 | 19 |
20 | Documentation 21 | 22 | GitHub 23 | 24 | Report a bug 25 |
26 | 27 |

Test download

28 |
29 | 30 |

31 | Download file 32 |

33 |
34 | 35 | 38 | 39 | 40 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/.vuepress/public/download/test-data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/download/test-data.bin -------------------------------------------------------------------------------- /docs/.vuepress/public/github_sponsor_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/github_sponsor_button.png -------------------------------------------------------------------------------- /docs/.vuepress/public/light-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/light-bg.png -------------------------------------------------------------------------------- /docs/.vuepress/public/logo-no-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/logo-no-text.png -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/opencollective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/opencollective.png -------------------------------------------------------------------------------- /docs/.vuepress/public/patreon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/patreon.png -------------------------------------------------------------------------------- /docs/.vuepress/public/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/paypal.png -------------------------------------------------------------------------------- /docs/.vuepress/public/prism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/prism.png -------------------------------------------------------------------------------- /docs/.vuepress/public/screenshots/todos-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/screenshots/todos-linux.png -------------------------------------------------------------------------------- /docs/.vuepress/public/screenshots/todos-macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/screenshots/todos-macos.png -------------------------------------------------------------------------------- /docs/.vuepress/public/screenshots/todos-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/screenshots/todos-windows.png -------------------------------------------------------------------------------- /docs/.vuepress/public/windows31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/.vuepress/public/windows31.png -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | title: pywebview 4 | tagline: Build GUI for your Python program with JavaScript, HTML, and CSS. 5 | heroImage: logo.png 6 | footer: BSD Licensed | Copyright © 2014–present Roman Sirokov 7 | actions: 8 | - text: Get started 9 | icon: lightbulb 10 | link: /guide/ 11 | - text: API 12 | icon: code 13 | link: /api/ 14 | - text: Examples 15 | icon: star 16 | link: /examples/ 17 | 18 | --- 19 | 20 | 21 | _pywebview_ is a lightweight BSD-licensed cross-platform wrapper around a webview component. _pywebview_ allows to display HTML content in its own native GUI window. It gives you power of web technologies in your desktop application, hiding the fact that GUI is browser based. _pywebview_ ships with a built-in HTTP server, DOM support in Python and window management functionality., 22 | 23 | 24 | ## Install 25 | 26 | ``` bash 27 | pip install pywebview 28 | ``` 29 | 30 | _Depending on a platform you may need to install additional libraries. Refer to the [installation](/guide/installation.html) page for details._ 31 | 32 | ## Hello world 33 | 34 | ``` python 35 | import webview 36 | webview.create_window('Hello world', 'https://pywebview.flowrl.com/') 37 | webview.start() 38 | ``` 39 | 40 | Explore [documentation](/guide/) or [examples](/examples/). If React is your thing, get started right away with [React boilerplate](https://github.com/r0x0r/pywebview-react-boilerplate). 41 | 42 | 43 | 44 | 45 | 46 | ::: center 47 | [![Github Sponsor](/github_sponsor_button.png)](https://github.com/sponsors/r0x0r) 48 | 49 | [![Patreon](/patreon.png)](https://www.patreon.com/bePatron?u=13226105) 50 | 51 | [![Open Collective](/opencollective.png)](https://opencollective.com/pywebview/donate) 52 | ::: -------------------------------------------------------------------------------- /docs/_redirects: -------------------------------------------------------------------------------- 1 | /changelog/ /CHANGELOG.html -------------------------------------------------------------------------------- /docs/blog/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Blog 3 | 4 | 5 | ### 2024-03-08 pywebview 5 6 | 7 | _pywebview_ 5 has landed with Android support and DOM.
8 | [Read more](/blog/pywebview5.html) 9 | 10 | 11 | ### 2019-07-11 Introducing pywebview 3.0 12 | 13 | _pywebview_ has reached version 3.0 and has a number of breaking changes.
14 | [Read more](/blog/pywebview3.html) 15 | -------------------------------------------------------------------------------- /docs/blog/pywebview3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/docs/blog/pywebview3.png -------------------------------------------------------------------------------- /docs/contributing/README.md: -------------------------------------------------------------------------------- 1 | Thanks for considering contributing to pywebview. 2 | 3 | _pywebview_ is a small-time project, which gets updated sporadically whenever time permits. Any help is appreciated and the best way to contribute is submitting a pull request. Bug fixes are always welcome. If you wish to submit a new feature, please create an issue and discuss it beforehand. 4 | 5 | If you found a bug and want to report it, please test it first in a web-browser that is used by default for your operating system to see if the problem is with your code, rather than pywebview. Do not forget to specify on which platform and pywebview version it occurs. 6 | 7 | To support pywebview financially, consider sponsoring the project. Pywebview has no corporate backing and financial help is welcomed to keep the project alive. 8 | 9 |
10 | 11 | Become a Patron! 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | For other ways to donate refer to the [donation](/contributing/donating.html) page. 24 | -------------------------------------------------------------------------------- /docs/contributing/bug_reporting.md: -------------------------------------------------------------------------------- 1 | # Bug reporting 2 | 3 | If you think you found a bug, verify following steps first 4 | 5 | 1. Does the bug occur in a default browser? If so, the problem is with your code, not pywebview 6 | 2. Are you using the latest master? Bug fixes are merged into the master and it may take a while until a new release is deployed to Pypi. 7 | 3. Has it been [reported](https://github.com/r0x0r/pywebview/issues) already? 8 | 9 | If you verified all the three points and are sure that the issue is caused by pywebview, feel free to submit a new issue. Please remember to specify under which operating system the bug occurs, as well as with other relevant information. In case of Linux, specify a distro you are using. 10 | -------------------------------------------------------------------------------- /docs/contributing/documentation.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | One way to contribute is to improve documentation on this side. Each page has a 'Help us improve this page' link at the bottom of the page. By clicking the link you can create a pull request with your changes. You need a Github account to edit pages. 4 | -------------------------------------------------------------------------------- /docs/contributing/donating.md: -------------------------------------------------------------------------------- 1 | # Donating 2 | 3 | 4 | ## Recurrring pledge 5 | 6 | Recurring pledges come perks, like getting email support or featuring your name or logo in the project repository 7 | 8 |
9 | 10 | Become a Patron! 11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 21 | ## One-time donations 22 | 23 | We accept donations via Paypal 24 | 25 |
26 | 27 | 28 | 29 |
30 | -------------------------------------------------------------------------------- /docs/examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | You can find examples demonstrating features of _pywebview_ in the sidebar. 4 | 5 | Below there are a couple of non-trivial examples that demonstrate an application architecture. 6 | 7 | ## React Boilerplate 8 | 9 | [React boilerplate with parcel-bundler](https://github.com/r0x0r/pywebview-react-boilerplate). A complete React-based boilerplate with installation, usage and building taken care of out of the box. 10 | 11 | [React boilerplate with create-react-app](https://github.com/dzc0d3r/pywebview-react-boilerplate/). A complete React-based boilerplate with installation, usage and building taken care of out of the box. 12 | 13 | 14 | ## Serverless application 15 | 16 | [Serverless application](https://github.com/r0x0r/pywebview/tree/docs/examples/todos) 17 | 18 | A simple todo application that uses serverless architecture. Communication between frontend and backend is provided by built-in API. 19 | 20 | 34 | 35 | ## HTTP server application 36 | 37 | [Flask-based application](https://github.com/r0x0r/pywebview/tree/docs/examples/flask_app) 38 | 39 | In this example communication between frontend and backend is facilitated by a Flask server. 40 | -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | _pywebview_ is a lightweight native webview wrapper that allows to display HTML content in its own native GUI window. It gives you power of web technologies in your desktop application, hiding the fact that GUI is browser based. _pywebview_ ships with a built-in HTTP server, DOM support in Python and window management functionality. 4 | 5 | _pywebview_ is available for Windows, macOS, Linux (GTK or QT) and Android. It uses native GUI for creating a web component window: WinForms on Windows, Cocoa on macOS and QT or GTK on Linux. If you choose to freeze your application, _pywebview_ does not bundle a heavy GUI toolkit or web renderer with it keeping the executable size small. 6 | 7 | _pywebview_ provides advanced features like window manipulation functionality, event system, built-in HTTP server, native GUI elements like application menu and various dialogs, two way communication between Javascript ↔ Python and DOM support. 8 | 9 | _pywebview_ is created by [Roman Sirokov](https://github.com/r0x0r/). 10 | 11 | ## Install 12 | 13 | Generally, you should be able to install _pywebview_ with 14 | 15 | ``` bash 16 | pip install pywebview 17 | ``` 18 | 19 | Although on some Linux platforms you may need to install additional libraries. Refer to the [installation](/guide/installation.html) page for details. 20 | 21 | ## Develop 22 | 23 | Read the basic concepts in [Usage](/guide/usage), dive into [application architecture](/guide/architecture). Explore [API](/guide/api) and check various [examples](/examples) 24 | 25 | ## Contribute 26 | 27 | Checkout out [contributing guidelines](/contributing/) 28 | 29 | ## Support the project 30 | 31 | If you find _pywebview_ useful, please support it. 32 | 33 | ::: center 34 | [![Github Sponsor](/github_sponsor_button.png)](https://github.com/sponsors/r0x0r) 35 | 36 | [![Patreon](/patreon.png)](https://www.patreon.com/bePatron?u=13226105) 37 | 38 | [![Open Collective](/opencollective.png)](https://opencollective.com/pywebview/donate) 39 | ::: -------------------------------------------------------------------------------- /docs/guide/architecture.md: -------------------------------------------------------------------------------- 1 | # Application architecture 2 | 3 | There are several way to build your application using _pywebview_: 4 | 5 | ## Pure web server 6 | 7 | - The most simple case is pointing to a url. This requires a running web server either remotely or locally 8 | 9 | ``` python 10 | webview.create_window('Simple browser', 'https://pywebview.flowrl.com') 11 | webview.start() 12 | ``` 13 | 14 | If you point to a local web server, you can start an external HTTP server in a background thread manually and or giving a WSGIRef server instance to the url parameter. 15 | 16 | ``` python 17 | server = Flask(__name__, static_folder='.', template_folder='.') 18 | webview.create_window('My first pywebview application', server) 19 | webview.start() 20 | ``` 21 | 22 | See a complete example [Flask-based application](https://github.com/r0x0r/pywebview/tree/master/examples/flask_app) 23 | 24 | When using a local web server, you should protect your API calls against CSRF attacks. See [security](/guide/security.html) for more information. 25 | 26 | While the `file://` protocol is possible, its use is discouraged as it comes with a number of inherit limitations and is not well supported. 27 | 28 | ## JS API with internal HTTP server 29 | 30 | Another approach is using JS API bridge and serving static content with a built-in HTTP server. JS API bridge allows communication between Python and Javascript domains without a web server. Thje bridge can be created either with `create_window(..., js_api=Api())` or `window.expose` function. To serve static contents, set entrypoint url to a local relative path. This will start a built-in HTTP server automatically. For more details on communication between Python and Javascript refer to [interdomain communication](/guide/interdomain.html). See an example [serverless application](https://github.com/r0x0r/pywebview/tree/master/examples/todos) for a complete implementation. 31 | 32 | ## Serverless 33 | 34 | - Finally you can do without a web server altogther by loading HTML using `webview.create_window(...html='')` or `window.load_html`. This approach has got limitations though, as file system does not exist in the context of the loaded page. Images and other assets can be loaded only inline using Base64. 35 | -------------------------------------------------------------------------------- /docs/guide/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | To debug Javascript, set `webview.start(debug=True)` 4 | 5 | ``` python 6 | import webview 7 | 8 | webview.create_window('Woah dude!', 'https://pywebview.flowrl.com/hello') 9 | webview.start(debug=True) 10 | ``` 11 | 12 | This will enable web inspector on macOS, GTK and QT (QTWebEngine only). To open the web inspector on macOS, right click on the page and select Inspect. To disable auto-opening of DevTools, set `webview.settings['OPEN_DEVTOOLS_IN_DEBUG'] = False` before invoking `webview.start()`. 13 | 14 | Debugging Python code on Android is not possible apart from printing message to `logcat`. Use `adb -s logcat | grep python` for displaying log messages related to Python. Frontend code can be debugged with WebView remote debugging. Refer to [this guide](https://developer.chrome.com/docs/devtools/remote-debugging/webviews/) for details. 15 | 16 | Remote debugging is supported with the `edgechromium` renderer. To take remote debugging into use set `webview.settings['REMOTE_DEBUGGING_PORT']` to the port number you wish to run a debugger on. 17 | 18 | There is no way to attach an external debugger to MSHTML. The `debug` flag enables Javascript error reporting and right-click context menu. 19 | -------------------------------------------------------------------------------- /docs/guide/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## How do I set an application icon? 4 | 5 | For macOS, Windows, and Android, the application icon is set via a bundler and embedded in the resulting executable. For GTK and QT, you can set the application icon using `webview.start(icon=icon_path)`, but you might need some additional adjustments to get your icon visible depending on the window manager you use. 6 | 7 | ## Why does _pywebview_ have to run on a main thread? 8 | 9 | This is dictated by underlying GUI libraries _pywebview_ is based on. GUI loop is expected to run on a main thread. While some libraries allow the GUI to be run in a sub-thread, Cocoa has a strict requirement regarding the main thread. If you need your logic to run in a main thread, use the `multiprocessing` module. 10 | 11 | ## webview has no attribute create_window 12 | 13 | You probably have a file named `webview.py` in the current directory. Renaming it to something else should fix the problem. 14 | 15 | ## What renderer is used? 16 | 17 | Set `PYWEBVIEW_LOG=debug` environment variable before running your programme. It will display used renderer in the first line of the program output. See available renderers [here](/guide/renderer) 18 | 19 | ## Terminal window receives key events on macOS 20 | 21 | If you create a virtual environment using the built-in Python on macOS, a pywebview window will have issues with keyboard focus and Cmd+Tab. The issue can be avoided by using other Python installation. For example to use Python 3 via [Homebrew](https://brew.sh). 22 | 23 | ``` bash 24 | brew install python3 25 | virtualenv pywebview_env -p python3 26 | ``` 27 | 28 | ## Frozen executable is too big 29 | 30 | Big executable size is caused by packager picking up unnecessary dependencies. For example if you have `PyQt` installed but use Winforms on Windows, Pyinstaller will bundle both frameworks. To avoid this in Pyinstaller, use `--exclude-module` option to explicitly omit the module. 31 | -------------------------------------------------------------------------------- /docs/guide/freezing.md: -------------------------------------------------------------------------------- 1 | # Freezing 2 | 3 | ## Android 4 | 5 | pywebview is designed to be built with [buildozer](https://buildozer.readthedocs.io/en/latest/). You need to include following lines in your `buildozer.spec` to bundle pywebview correctly 6 | 7 | ``` ini 8 | requirements = python3,kivy,pywebview 9 | android.add_jars = 10 | ``` 11 | 12 | `pywebview-android.jar` is shipped with `pywebview` and can be found under `site-packages/pywebview/lib`. To get its full path type 13 | 14 | ``` python 15 | from webview import util 16 | print(util.android_jar_path()) 17 | ``` 18 | 19 | You can see a sample `bulldozer.spec` [here](https://github.com/r0x0r/pywebview/blob/master/examples/todos/bulldozer.spec) 20 | 21 | ## macOS 22 | 23 | Use [py2app](https://py2app.readthedocs.io/en/latest/). For a reference setup.py for py2app, look [here](https://github.com/r0x0r/pywebview/blob/master/examples/py2app_setup.py). 24 | 25 | ## Windows / Linux 26 | 27 | Use [pyinstaller](https://www.pyinstaller.org/). Pyinstaller picks all the dependencies found in `pywebview`, even if you don't use them. So for example if you have `PyQt` installed, but use `EdgeChromium` renderer on Windows, pyinstaller will bundle `PyQT` all the same. To prevent that you might want to add unwanted dependencies to `excludes` in your spec file. 28 | 29 | [nuitka](http://nuitka.net/) can be used for freezing as well. You may want to use `--nofollow-import-to` to exclude unwanted dependencies. 30 | -------------------------------------------------------------------------------- /docs/guide/interdomain.md: -------------------------------------------------------------------------------- 1 | # Javascript–Python bridge 2 | 3 | _pywebview_ offers two-way communication between Javascript and Python, enabling interaction between the two languages without a HTTP server. 4 | 5 | ## Run Javascript from Python 6 | 7 | `window.evaluate_js(code, callback=None)` allows you to execute arbitrary Javascript code with a last value returned synchronously. If callback function is supplied, then promises are resolved and the callback function is called with the result as a parameter. Javascript types are converted to Python types, eg. JS objects to dicts, arrays to lists, undefined to None. If executed Javascript code results in an error, the error is rethrown as a `webview.util.JavascriptException` in Python. `evaluate_js` wraps Javascript code in a helper wrapper and executes it using `eval`. 8 | 9 | [See example](/examples/evaluate_js.html) 10 | 11 | `window.run_js(code)` executes Javascript code as is without any wrapper code. `run_js` does not return a result or handle exceptions. This can be useful in scenarios, where you need to execute Javascript code with the `unsafe-eval` CSP policy set. 12 | 13 | ## Run Python from Javascript 14 | 15 | Executing Python functions from Javascript can be done with two different mechanisms. 16 | 17 | - by exposing an instance of a Python class to the `js_api` parameter of `create_window`. All the callable methods of the class will be exposed to the JS domain as `pywebview.api.method_name` with correct parameter signatures. Method name must not start with an underscore. Nested classes are allowed and are converted into a nested objects in Javascript. Class attributes starting with an underscore are not exposed. See an [example](/examples/js_api.html). 18 | 19 | - by passing your function(s) to window object's `expose(func)`. This will expose a function or functions to the JS domain as `pywebview.api.func_name`. Unlike JS API, `expose` allows to expose functions also at the runtime. If there is a name clash between JS API and exposed functions, the latter takes precedence. See an [example](/examples/expose.html). 20 | 21 | Exposed function returns a promise that is resolved to its result value. Exceptions are rejected and encapsulated inside a Javascript `Error` object. Stacktrace is available via `error.stack`. Exposed functions are executed in separate threads and are not thread-safe. 22 | 23 | `window.pywebview.api` is not guaranteed to be available on the `window.onload` event. Subscribe to the `window.pywebviewready` event instead to make sure that `window.pywebview.api` is ready. 24 | 25 | [See example](/examples/js_api.html). 26 | -------------------------------------------------------------------------------- /docs/guide/security.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | It is advisable to enable SSL for local HTTP server. To accomplish this, simply start the application with the `ssl` paramater set to True `webview.start(ssl=True)`. You need to have `cryptography` pip dependency installed in order to use `ssl`. It is not installed by default. 4 | 5 | If you employ a REST API, [CSRF attacks](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)) can be a major concern. _pywebview_ mitigates this risk by generating a session-unique token that is accessible in Python as `webview.token` and in JavaScript as `window.pywebview.token`. For more information on securing APIs, refer to the [CSRF Prevention Cheat Sheet](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_\(CSRF\)_Prevention_Cheat_Sheet). You can also see a practical example in the [Flask app](https://github.com/r0x0r/pywebview/tree/master/examples/flask_app). 6 | -------------------------------------------------------------------------------- /docs/guide/web_engine.md: -------------------------------------------------------------------------------- 1 | # Web engine 2 | 3 | The following renderers are used on each platform 4 | 5 | | Platform | Code | Renderer | Provider | Browser compatibility | 6 | |----------|--------------|----------|---------------------------------------------------|-----------------------| 7 | | Android | | WebKit | | Ever-green Chromium | 8 | | GTK | gtk | WebKit | WebKit2 (minimum version >2.2) | | 9 | | macOS | | WebKit | WebKit.WKWebView (bundled with OS) | | 10 | | QT | qt | WebKit | QtWebEngine / QtWebKit | | 11 | | Windows | edgechromium | Chromium | > .NET Framework 4.6.2 and Edge Runtime installed | Ever-green Chromium | 12 | | Windows | cef | CEF | CEF Python | Chrome 66 | 13 | | Windows | mshtml | MSHTML | DEPRECATED Internet Explorer MSHTML | IE11 (Windows 10/8/7) | 14 | 15 | On Windows renderer is chosen in the following order: `edgechromium`, `mshtml`. `mshtml` is the only renderer that is guaranteed to be available on any system. Edge Runtime must be installed in order to use Edge Chromium on Windows. You can download it from [here](https://developer.microsoft.com/en-us/microsoft-edge/webview2/). Distribution guidelines are found [here](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution). 16 | 17 | To change a default renderer set either `PYWEBVIEW_GUI` environment variable or pass the rendered value to `webview.start(gui=code)` function parameter. Check for available values in the Code column from the table above. 18 | 19 | For example to use CEF on Windows 20 | 21 | ``` bash 22 | export PYWEBVIEW_GUI=cef 23 | ``` 24 | 25 | or 26 | 27 | ``` python 28 | import webview 29 | webview.start(gui='cef') 30 | ``` 31 | 32 | If you wish to pass custom settings to CEF, refer to [this example](/examples/cef.html) 33 | 34 | To force QT on Linux systems 35 | 36 | ``` bash 37 | export PYWEBVIEW_GUI=qt 38 | ``` 39 | 40 | or 41 | 42 | ``` python 43 | import webview 44 | webview.start(gui='qt') 45 | ``` 46 | 47 | ## Known issues and limitations 48 | 49 | ## QtWebKit 50 | 51 | * Debugging is not supported 52 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "docs:dev": "vuepress dev --debug .", 4 | "docs:build": "NODE_ENV=production vuepress build . && cp _redirects .vuepress/dist" 5 | }, 6 | "devDependencies": { 7 | "@vuepress/bundler-vite": "^2.0.0-rc.19", 8 | "@vuepress/plugin-medium-zoom": "1.9.10", 9 | "directory-tree": "^3.5.1", 10 | "sass-embedded": "^1.83.0", 11 | "vuepress": "2.0.0-rc.19", 12 | "vuepress-theme-hope": "^2.0.0-rc.66" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/assets/cookies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 |

Cookies Example

20 | 21 |

Cookies:

22 |

Local storage:

23 | 24 |
25 |
26 | 27 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | It works! 6 | 7 | 18 | 19 | 20 | 21 |

Woah dude!

22 | 23 |

It works!
This file is served locally by a built-in HTTP server.

24 | 25 |

Video

26 | 27 | 31 | 32 |

Links

33 | 34 |
Regular links are opened in the application window.
35 |
target='_blank' links are opened in an external browser.
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/assets/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/examples/assets/video.mp4 -------------------------------------------------------------------------------- /examples/assets/video.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/examples/assets/video.webm -------------------------------------------------------------------------------- /examples/cef.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Create a CEF window with custom Chrome settings. Available only on Windows. 5 | """ 6 | 7 | # To pass custom settings to CEF, import and update settings dict 8 | from webview.platforms.cef import browser_settings, settings 9 | 10 | settings.update({'persist_session_cookies': True}) 11 | browser_settings.update({'dom_paste_disabled': False}) 12 | 13 | if __name__ == '__main__': 14 | webview.create_window('CEF browser', 'https://pywebview.flowrl.com/hello') 15 | webview.start(gui='cef') 16 | -------------------------------------------------------------------------------- /examples/change_url.py: -------------------------------------------------------------------------------- 1 | """ 2 | Change URL ten seconds after the first URL is loaded. 3 | """ 4 | 5 | import time 6 | 7 | import webview 8 | 9 | 10 | def change_url(window): 11 | # wait a few seconds before changing url: 12 | time.sleep(10) 13 | 14 | # change url: 15 | window.load_url('https://pywebview.flowrl.com/hello') 16 | 17 | 18 | if __name__ == '__main__': 19 | window = webview.create_window('URL Change Example', 'http://www.google.com') 20 | webview.start(change_url, window) 21 | -------------------------------------------------------------------------------- /examples/confirm_close.py: -------------------------------------------------------------------------------- 1 | """A window with a quit confirmation dialog.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | # Create a standard webview window 7 | webview.create_window( 8 | 'Confirm Quit Example', 'https://pywebview.flowrl.com/hello', confirm_close=True 9 | ) 10 | webview.start() 11 | -------------------------------------------------------------------------------- /examples/confirmation_dialog.py: -------------------------------------------------------------------------------- 1 | """A window with a confirmation dialog.""" 2 | 3 | import webview 4 | 5 | 6 | def open_confirmation_dialog(window): 7 | result = window.create_confirmation_dialog('Question', 'Are you ok with this?') 8 | if result: 9 | print('User clicked OK') 10 | else: 11 | print('User clicked Cancel') 12 | 13 | 14 | if __name__ == '__main__': 15 | window = webview.create_window( 16 | 'Confirmation dialog example', 'https://pywebview.flowrl.com/hello' 17 | ) 18 | webview.start(open_confirmation_dialog, window) 19 | -------------------------------------------------------------------------------- /examples/cookies.py: -------------------------------------------------------------------------------- 1 | """A cookies and local storage example.""" 2 | 3 | import webview 4 | 5 | 6 | def read_cookies(window): 7 | cookies = window.get_cookies() 8 | for c in cookies: 9 | print(c.output()) 10 | 11 | 12 | class Api: 13 | def clearCookies(self): 14 | window.clear_cookies() 15 | print('Cookies cleared') 16 | 17 | if __name__ == '__main__': 18 | window = webview.create_window('Cookie example', 'assets/cookies.html', js_api=Api()) 19 | 20 | # We need to explicitly set a http port to persist cookies between sessions 21 | webview.start(read_cookies, window, private_mode=False, http_server=True, http_port=13377) 22 | -------------------------------------------------------------------------------- /examples/debug.py: -------------------------------------------------------------------------------- 1 | """ 2 | A debug window example that opens DevTools. 3 | """ 4 | 5 | import webview 6 | 7 | if __name__ == '__main__': 8 | webview.create_window('Debug window', 'https://pywebview.flowrl.com/hello') 9 | webview.start(debug=True) 10 | -------------------------------------------------------------------------------- /examples/destroy_window.py: -------------------------------------------------------------------------------- 1 | """ 2 | Programmatically destroy created window after five seconds. 3 | """ 4 | 5 | import time 6 | 7 | import webview 8 | 9 | 10 | def destroy(window): 11 | # show the window for a few seconds before destroying it: 12 | time.sleep(5) 13 | print('Destroying window..') 14 | window.destroy() 15 | print('Destroyed!') 16 | 17 | 18 | if __name__ == '__main__': 19 | window = webview.create_window('Destroy Window Example', 'https://pywebview.flowrl.com/hello') 20 | webview.start(destroy, window) 21 | print('Window is destroyed') 22 | -------------------------------------------------------------------------------- /examples/dom_events.py: -------------------------------------------------------------------------------- 1 | """This example demonstrates how to expose Python functions to the Javascript domain.""" 2 | 3 | import webview 4 | from webview.dom import DOMEventHandler 5 | 6 | window = None 7 | 8 | def click_handler(e): 9 | print(e) 10 | 11 | 12 | def input_handler(e): 13 | print(e['target']['value']) 14 | 15 | 16 | def remove_handlers(scroll_event, click_event, input_event): 17 | scroll_event -= scroll_handler 18 | click_event -= click_handler 19 | input_event -= input_handler 20 | 21 | 22 | def scroll_handler(e): 23 | scroll_top = window.dom.window.node['scrollY'] 24 | print(f'Scroll position {scroll_top}') 25 | 26 | 27 | def link_handler(e): 28 | print(f"Link target is {e['target']['href']}") 29 | 30 | 31 | def bind(window): 32 | window.dom.document.events.scroll += DOMEventHandler(scroll_handler, debounce=100) 33 | 34 | button = window.dom.get_element('#button') 35 | button.events.click += click_handler 36 | 37 | input = window.dom.get_element('#input') 38 | input.events.input += input_handler 39 | 40 | remove_events = window.dom.get_element('#remove') 41 | remove_events.on('click', lambda e: remove_handlers(window.dom.document.events.scroll, button.events.click, input.events.input)) 42 | 43 | link = window.dom.get_element('#link') 44 | link.events.click += DOMEventHandler(link_handler, prevent_default=True) 45 | 46 | 47 | if __name__ == '__main__': 48 | window = webview.create_window( 49 | 'DOM Event Example', html=''' 50 | 51 | 52 | 60 | 61 | 62 |
63 | 64 | 65 | Click me 66 |
67 | 68 | 69 | 70 | ''' 71 | ) 72 | webview.start(bind, window) 73 | -------------------------------------------------------------------------------- /examples/downloads.py: -------------------------------------------------------------------------------- 1 | """ Enable file downloads 2 | """ 3 | 4 | import webview 5 | 6 | if __name__ == '__main__': 7 | # Create a standard webview window 8 | webview.settings['ALLOW_DOWNLOADS'] = True 9 | window = webview.create_window('Simple browser', 'https://pywebview.flowrl.com/download') 10 | webview.start() 11 | -------------------------------------------------------------------------------- /examples/drag_drop.py: -------------------------------------------------------------------------------- 1 | """This example demonstrates how to expose Python functions to the Javascript domain.""" 2 | 3 | import webview 4 | from webview.dom import DOMEventHandler 5 | 6 | 7 | def on_drag(e): 8 | pass 9 | 10 | def on_drop(e): 11 | files = e['dataTransfer']['files'] 12 | if len(files) == 0: 13 | return 14 | 15 | print(f'Event: {e["type"]}. Dropped files:') 16 | 17 | for file in files: 18 | print(file.get('pywebviewFullPath')) 19 | 20 | 21 | def bind(window): 22 | window.dom.document.events.dragenter += DOMEventHandler(on_drag, True, True) 23 | window.dom.document.events.dragstart += DOMEventHandler(on_drag, True, True) 24 | window.dom.document.events.dragover += DOMEventHandler(on_drag, True, True, debounce=500) 25 | window.dom.document.events.drop += DOMEventHandler(on_drop, True, True) 26 | 27 | if __name__ == '__main__': 28 | window = webview.create_window( 29 | 'Drag & drop example', html=''' 30 | 31 | 32 |

Drag files here

33 | 34 | 35 | ''' 36 | ) 37 | webview.start(bind, window) 38 | -------------------------------------------------------------------------------- /examples/drag_region.py: -------------------------------------------------------------------------------- 1 | """ 2 | A user-provided "drag region" to move a frameless window 3 | around, whilst maintaining normal mouse down/move events elsewhere. This roughly 4 | replicates `-webkit-drag-region`. 5 | """ 6 | 7 | import webview 8 | 9 | html = """ 10 | 11 | 20 | 21 | 22 |
Drag me!
23 | 24 | """ 25 | 26 | if __name__ == '__main__': 27 | window = webview.create_window( 28 | 'API example', 29 | html=html, 30 | frameless=True, 31 | easy_drag=False, 32 | ) 33 | webview.start() 34 | -------------------------------------------------------------------------------- /examples/evaluate_js.py: -------------------------------------------------------------------------------- 1 | """Run Javascript code from Python.""" 2 | 3 | import webview 4 | from webview.errors import JavascriptException 5 | 6 | 7 | def evaluate_js(window): 8 | result = window.evaluate_js( 9 | r""" 10 | var h1 = document.createElement('h1') 11 | var text = document.createTextNode('Hello pywebview') 12 | h1.appendChild(text) 13 | document.body.appendChild(h1) 14 | 15 | document.body.style.backgroundColor = '#212121' 16 | document.body.style.color = '#f2f2f2' 17 | 18 | // Return user agent 19 | 'User agent:\n' + navigator.userAgent; 20 | """ 21 | ) 22 | 23 | print(result) 24 | 25 | try: 26 | result = window.evaluate_js('syntaxerror#$%#$') 27 | except JavascriptException as e: 28 | print('Javascript exception occured: ', e) 29 | 30 | 31 | if __name__ == '__main__': 32 | window = webview.create_window('Evaluate JavaScript', html='') 33 | webview.start(evaluate_js, window) 34 | -------------------------------------------------------------------------------- /examples/evaluate_js_async.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Run asynchronous Javascript code and invoke a callback. 5 | """ 6 | 7 | def callback(result): 8 | print(result) 9 | 10 | def evaluate_js_async(window): 11 | window.evaluate_js( 12 | """ 13 | new Promise((resolve, reject) => { 14 | setTimeout(() => { 15 | resolve('Whaddup!'); 16 | }, 300); 17 | }); 18 | """, callback) 19 | 20 | 21 | if __name__ == '__main__': 22 | window = webview.create_window('Run async Javascript', html='') 23 | webview.start(evaluate_js_async, window) 24 | 25 | -------------------------------------------------------------------------------- /examples/events.py: -------------------------------------------------------------------------------- 1 | """Subscribe and unsubscribe to pywebview events.""" 2 | 3 | import webview 4 | 5 | def on_before_show(window): 6 | print('Native window object', window.native) 7 | 8 | 9 | def on_closed(): 10 | print('pywebview window is closed') 11 | 12 | 13 | def on_closing(): 14 | print('pywebview window is closing') 15 | 16 | 17 | def on_shown(): 18 | print('pywebview window shown') 19 | 20 | 21 | def on_minimized(): 22 | print('pywebview window minimized') 23 | 24 | 25 | def on_restored(): 26 | print('pywebview window restored') 27 | 28 | 29 | def on_maximized(): 30 | print('pywebview window maximized') 31 | 32 | 33 | def on_resized(width, height): 34 | print( 35 | 'pywebview window is resized. new dimensions are {width} x {height}'.format( 36 | width=width, height=height 37 | ) 38 | ) 39 | 40 | 41 | # you can supply optional window argument to access the window object event was triggered on 42 | def on_loaded(window): 43 | print('DOM is ready') 44 | 45 | # unsubscribe event listener 46 | window.events.loaded -= on_loaded 47 | window.load_url('https://pywebview.flowrl.com/hello') 48 | 49 | 50 | def on_moved(x, y): 51 | print('pywebview window is moved. new coordinates are x: {x}, y: {y}'.format(x=x, y=y)) 52 | 53 | 54 | if __name__ == '__main__': 55 | window = webview.create_window( 56 | 'Simple browser', 'https://pywebview.flowrl.com/', confirm_close=True 57 | ) 58 | 59 | window.events.closed += on_closed 60 | window.events.closing += on_closing 61 | window.events.before_show += on_before_show 62 | window.events.shown += on_shown 63 | window.events.loaded += on_loaded 64 | window.events.minimized += on_minimized 65 | window.events.maximized += on_maximized 66 | window.events.restored += on_restored 67 | window.events.resized += on_resized 68 | window.events.moved += on_moved 69 | 70 | webview.start() 71 | -------------------------------------------------------------------------------- /examples/expose.py: -------------------------------------------------------------------------------- 1 | """Exposing Python functions to the Javascript domain.""" 2 | 3 | import webview 4 | 5 | 6 | def lol(): 7 | print('LOL') 8 | 9 | 10 | def wtf(): 11 | print('WTF') 12 | 13 | 14 | def echo(arg1, arg2, arg3): 15 | print(arg1) 16 | print(arg2) 17 | print(arg3) 18 | 19 | 20 | def expose(window): 21 | window.expose(echo) # expose a function during the runtime 22 | 23 | window.evaluate_js('pywebview.api.lol()') 24 | window.evaluate_js('pywebview.api.wtf()') 25 | window.evaluate_js('pywebview.api.echo(1, 2, 3)') 26 | 27 | 28 | if __name__ == '__main__': 29 | window = webview.create_window( 30 | 'JS Expose Example', html='

JS API function Expose' 31 | ) 32 | window.expose(lol, wtf) # expose functions beforehand 33 | 34 | webview.start(expose, window) 35 | -------------------------------------------------------------------------------- /examples/flask_app/README: -------------------------------------------------------------------------------- 1 | # Flask app 2 | 3 | This is a sample skeleton of an application using pywebview and Flask. The directory structure as well as some basic boilerplate code is provided. The application entry point is src/backend/main.py. 4 | -------------------------------------------------------------------------------- /examples/flask_app/requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | pywebview 3 | -------------------------------------------------------------------------------- /examples/flask_app/src/backend/app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Application stub 3 | """ 4 | 5 | 6 | def initialize(): 7 | # perform heavy stuff here 8 | return True 9 | 10 | 11 | def do_stuff(): 12 | # do whatever you need to do 13 | response = 'This is response from Python backend' 14 | return response 15 | -------------------------------------------------------------------------------- /examples/flask_app/src/backend/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from contextlib import redirect_stdout 3 | from io import StringIO 4 | 5 | from server import server 6 | 7 | import webview 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | if __name__ == '__main__': 13 | stream = StringIO() 14 | with redirect_stdout(stream): 15 | window = webview.create_window('My first pywebview application', server) 16 | webview.start() 17 | -------------------------------------------------------------------------------- /examples/flask_app/src/frontend/README: -------------------------------------------------------------------------------- 1 | Put here your GUI code here created with your favorite front-end technologies. The backend is configured to look for 2 | frontend code in the `gui` directory of the root directory. So the code put here shall be configured to produce output 3 | to the `gui` directory (e.g. using webpack, gulp or equivalent). Or forget all this and just put your frontend code 4 | directly in the `gui` directory. 5 | -------------------------------------------------------------------------------- /examples/focus.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Create a non-focusable window that can be useful for onscreen floating tools. 5 | """ 6 | 7 | if __name__ == '__main__': 8 | webview.create_window('Nonfocusable window', html='

You shouldnt be able to type into this window...

...but still you can click elements in this window...

', focus=False) 9 | webview.start() 10 | -------------------------------------------------------------------------------- /examples/frameless.py: -------------------------------------------------------------------------------- 1 | """Create a frameless window. The window can be moved around by dragging any point.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | # Create a resizable webview window with minimum size constraints 7 | webview.create_window('Frameless window', 'http://pywebview.flowrl.com/hello', 8 | frameless=True, easy_drag=True) 9 | webview.start() 10 | -------------------------------------------------------------------------------- /examples/fullscreen.py: -------------------------------------------------------------------------------- 1 | """Create a fullscreen window.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | # Create a non-resizable webview window with 800x600 dimensions 7 | webview.create_window( 8 | 'Full-screen window', 'https://pywebview.flowrl.com/hello', fullscreen=True 9 | ) 10 | webview.start() 11 | -------------------------------------------------------------------------------- /examples/get_current_url.py: -------------------------------------------------------------------------------- 1 | """Print current URL after page is loaded.""" 2 | 3 | import webview 4 | 5 | 6 | def get_current_url(window): 7 | print(window.get_current_url()) 8 | 9 | 10 | if __name__ == '__main__': 11 | window = webview.create_window('Get current URL', 'https://pywebview.flowrl.com/hello') 12 | webview.start(get_current_url, window) 13 | -------------------------------------------------------------------------------- /examples/get_elements.py: -------------------------------------------------------------------------------- 1 | """Get DOM elements using selectors.""" 2 | 3 | import webview 4 | 5 | 6 | def get_elements(window): 7 | heading = window.get_elements('#heading') 8 | content = window.get_elements('.content') 9 | print('Heading:\n %s ' % heading[0].node['outerHTML']) 10 | print('Content 1:\n %s ' % content[0].node['outerHTML']) 11 | print('Content 2:\n %s ' % content[1].node['outerHTML']) 12 | 13 | 14 | if __name__ == '__main__': 15 | html = """ 16 | 17 | 18 |

Heading

19 |
Content 1
20 |
Content 2
21 | 22 | 23 | """ 24 | window = webview.create_window('Get elements example', html=html) 25 | webview.start(get_elements, window) 26 | -------------------------------------------------------------------------------- /examples/hide_window.py: -------------------------------------------------------------------------------- 1 | """Programmatically hide and show window.""" 2 | 3 | import time 4 | 5 | import webview 6 | 7 | 8 | def hide_show(window): 9 | print('Window is started hidden') 10 | 11 | time.sleep(5) 12 | print('Showing window') 13 | window.show() 14 | 15 | time.sleep(5) 16 | print('Hiding window') 17 | window.hide() 18 | 19 | time.sleep(5) 20 | print('And showing again') 21 | window.show() 22 | 23 | 24 | if __name__ == '__main__': 25 | window = webview.create_window( 26 | 'Hide / show window', 'https://pywebview.flowrl.com/hello', hidden=True 27 | ) 28 | webview.start(hide_show, window) 29 | -------------------------------------------------------------------------------- /examples/http_server.py: -------------------------------------------------------------------------------- 1 | """A built-in HTTP server example.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | webview.create_window('My first HTML5 application', 'assets/index.html') 7 | # HTTP server is started automatically for local relative paths 8 | webview.start(ssl=True) 9 | -------------------------------------------------------------------------------- /examples/icon.py: -------------------------------------------------------------------------------- 1 | """Set window icon using `webview.start(icon=). This is supported only on GTK and QT. For other 2 | platforms, icon is set during freezing.""" 3 | 4 | import webview 5 | 6 | if __name__ == '__main__': 7 | window = webview.create_window('Set window icon', 'https://pywebview.flowrl.com/hello') 8 | webview.start(icon='../logo/logo.png') 9 | -------------------------------------------------------------------------------- /examples/links.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Demonstrate a difference between different link types 5 | """ 6 | 7 | 8 | html = """ 9 | 10 | 11 | 12 |

Links

13 | 14 |

Regular links are opened in the application window.

15 |

target='_blank' links are opened in an external browser.

16 | 17 | 18 | 19 | """ 20 | 21 | 22 | if __name__ == '__main__': 23 | window = webview.create_window('Link types', html=html) 24 | webview.start() 25 | -------------------------------------------------------------------------------- /examples/load_css.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Loading custom CSS in a webview window 5 | """ 6 | 7 | 8 | def load_css(window): 9 | window.load_css('body { background: red !important; }') 10 | 11 | 12 | if __name__ == '__main__': 13 | window = webview.create_window('Load CSS Example', 'https://pywebview.flowrl.com/hello') 14 | webview.start(load_css, window) 15 | -------------------------------------------------------------------------------- /examples/load_html.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import webview 4 | 5 | """ 6 | Loading new HTML after the window is created 7 | """ 8 | 9 | 10 | def load_html(window): 11 | sleep(5) 12 | window.load_html('

This is dynamically loaded HTML

') 13 | 14 | 15 | if __name__ == '__main__': 16 | window = webview.create_window('Load HTML Example', html='

This is initial HTML

') 17 | webview.start(load_html, window) 18 | -------------------------------------------------------------------------------- /examples/localhost_ssl.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """" 4 | Use SSL with a local HTTP server. 5 | """ 6 | 7 | if __name__ == '__main__': 8 | webview.create_window('Local SSL Test', 'assets/index.html') 9 | webview.start(ssl=True) 10 | -------------------------------------------------------------------------------- /examples/localization.py: -------------------------------------------------------------------------------- 1 | """Localize system text string used by pywebview. For a full list of used string, refer to the `webview/localization.py` file.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | localization = { 7 | 'global.saveFile': 'Сохранить файл', 8 | 'cocoa.menu.about': 'О программе', 9 | 'cocoa.menu.services': 'Cлужбы', 10 | 'cocoa.menu.view': 'Вид', 11 | 'cocoa.menu.hide': 'Скрыть', 12 | 'cocoa.menu.hideOthers': 'Скрыть остальные', 13 | 'cocoa.menu.showAll': 'Показать все', 14 | 'cocoa.menu.quit': 'Завершить', 15 | 'cocoa.menu.fullscreen': 'Перейти ', 16 | 'windows.fileFilter.allFiles': 'Все файлы', 17 | 'windows.fileFilter.otherFiles': 'Остальлные файльы', 18 | 'linux.openFile': 'Открыть файл', 19 | 'linux.openFiles': 'Открыть файлы', 20 | 'linux.openFolder': 'Открыть папку', 21 | } 22 | 23 | window_localization_override = { 24 | 'global.saveFile': 'Save file', 25 | } 26 | 27 | webview.create_window( 28 | 'Localization Example', 29 | 'https://pywebview.flowrl.com/hello', 30 | localization=window_localization_override, 31 | ) 32 | webview.start(localization=localization) 33 | -------------------------------------------------------------------------------- /examples/menu.py: -------------------------------------------------------------------------------- 1 | """Create an application menu.""" 2 | 3 | import webview 4 | import webview.menu as wm 5 | 6 | 7 | def change_active_window_content(): 8 | active_window = webview.active_window() 9 | if active_window: 10 | active_window.load_html('

You changed this window!

') 11 | 12 | 13 | def click_me(): 14 | active_window = webview.active_window() 15 | if active_window: 16 | active_window.load_html('

You clicked me!

') 17 | 18 | 19 | def do_nothing(): 20 | pass 21 | 22 | 23 | def say_this_is_window_2(): 24 | active_window = webview.active_window() 25 | if active_window: 26 | active_window.load_html('

This is window 2

') 27 | 28 | 29 | def open_file_dialog(): 30 | active_window = webview.active_window() 31 | active_window.create_file_dialog(webview.SAVE_DIALOG, directory='/', save_filename='test.file') 32 | 33 | 34 | if __name__ == '__main__': 35 | window_1 = webview.create_window( 36 | 'Application Menu Example', 'https://pywebview.flowrl.com/hello' 37 | ) 38 | window_2 = webview.create_window( 39 | 'Another Window', html='

Another window to test application menu

' 40 | ) 41 | 42 | menu_items = [ 43 | wm.Menu( 44 | 'Test Menu', 45 | [ 46 | wm.MenuAction('Change Active Window Content', change_active_window_content), 47 | wm.MenuSeparator(), 48 | wm.Menu( 49 | 'Random', 50 | [ 51 | wm.MenuAction('Click Me', click_me), 52 | wm.MenuAction('File Dialog', open_file_dialog), 53 | ], 54 | ), 55 | ], 56 | ), 57 | wm.Menu('Nothing Here', [wm.MenuAction('This will do nothing', do_nothing)]), 58 | ] 59 | 60 | webview.start(menu=menu_items) 61 | -------------------------------------------------------------------------------- /examples/min_size.py: -------------------------------------------------------------------------------- 1 | """Set minimum window dimensions.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | # Create a resizable webview window with minimum size constraints 7 | webview.create_window( 8 | 'Minimum window size', 'https://pywebview.flowrl.com/hello', min_size=(400, 200) 9 | ) 10 | webview.start() 11 | -------------------------------------------------------------------------------- /examples/move_window.py: -------------------------------------------------------------------------------- 1 | """Set window coordinates and move window after its creation.""" 2 | 3 | from time import sleep 4 | 5 | import webview 6 | 7 | 8 | def move(window): 9 | print('Window coordinates are ({0}, {1})'.format(window.x, window.y)) 10 | 11 | sleep(2) 12 | window.move(200, 200) 13 | sleep(1) 14 | print('Window coordinates are ({0}, {1})'.format(window.x, window.y)) 15 | 16 | 17 | if __name__ == '__main__': 18 | window = webview.create_window('Move window example', html='

Move window

', x=300, y=300) 19 | webview.start(move, window) 20 | -------------------------------------------------------------------------------- /examples/multiple_windows.py: -------------------------------------------------------------------------------- 1 | """Create multiple windows.""" 2 | 3 | import webview 4 | 5 | 6 | def third_window(): 7 | # Create a new window after the loop started 8 | third_window = webview.create_window('Window #3', html='

Third Window

') 9 | 10 | 11 | if __name__ == '__main__': 12 | # Master window 13 | master_window = webview.create_window('Window #1', html='

First window

') 14 | second_window = webview.create_window('Window #2', html='

Second window

') 15 | webview.start(third_window) 16 | -------------------------------------------------------------------------------- /examples/on_top.py: -------------------------------------------------------------------------------- 1 | """Create a window that stays on top of other windows.""" 2 | 3 | import time 4 | import webview 5 | 6 | 7 | def deactivate(window): 8 | # window starts as on top of and reverts back to normal after 20 seconds 9 | time.sleep(20) 10 | window.on_top = False 11 | window.load_html('

This window is no longer on top of other windows

') 12 | 13 | 14 | if __name__ == '__main__': 15 | # Create webview window that stays on top of, all other windows 16 | window = webview.create_window( 17 | 'Topmost window', html='

This window is on top of other windows

', on_top=True 18 | ) 19 | webview.start(deactivate, window) 20 | -------------------------------------------------------------------------------- /examples/open_file_dialog.py: -------------------------------------------------------------------------------- 1 | """Create an open file dialog after page content is loaded.""" 2 | 3 | import webview 4 | 5 | 6 | def open_file_dialog(window): 7 | file_types = ('Image Files (*.bmp;*.jpg;*.gif)', 'All files (*.*)') 8 | 9 | result = window.create_file_dialog( 10 | webview.OPEN_DIALOG, allow_multiple=True, file_types=file_types 11 | ) 12 | print(result) 13 | 14 | 15 | if __name__ == '__main__': 16 | window = webview.create_window('Open file dialog example', 'https://pywebview.flowrl.com/hello') 17 | webview.start(open_file_dialog, window) 18 | -------------------------------------------------------------------------------- /examples/py2app_setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | An example of py2app setup.py script for freezing your pywebview 3 | application 4 | 5 | Usage: 6 | `python setup.py py2app` 7 | """ 8 | 9 | import os 10 | 11 | from setuptools import setup 12 | 13 | 14 | def tree(src): 15 | return [ 16 | (root, map(lambda f: os.path.join(root, f), files)) 17 | for (root, dirs, files) in os.walk(os.path.normpath(src)) 18 | ] 19 | 20 | 21 | ENTRY_POINT = ['simple_browser.py'] 22 | 23 | DATA_FILES = tree('DATA_FILES_DIR') + tree('DATA_FILE_DIR2') 24 | OPTIONS = { 25 | 'argv_emulation': False, 26 | 'strip': True, 27 | #'iconfile': 'icon.icns', # uncomment to include an icon 28 | 'includes': ['WebKit', 'Foundation', 'webview'], 29 | } 30 | 31 | setup( 32 | app=ENTRY_POINT, 33 | data_files=DATA_FILES, 34 | options={'py2app': OPTIONS}, 35 | setup_requires=['py2app'], 36 | ) 37 | -------------------------------------------------------------------------------- /examples/pystray_icon.py: -------------------------------------------------------------------------------- 1 | """ 2 | Run pywebview alongside with pystray to display a system tray icon. 3 | """ 4 | 5 | import multiprocessing 6 | import sys 7 | 8 | from PIL import Image 9 | from pystray import Icon, Menu, MenuItem 10 | 11 | import webview 12 | 13 | if sys.platform == 'darwin': 14 | ctx = multiprocessing.get_context('spawn') 15 | Process = ctx.Process 16 | Queue = ctx.Queue 17 | else: 18 | Process = multiprocessing.Process 19 | Queue = multiprocessing.Queue 20 | 21 | 22 | webview_process = None 23 | 24 | 25 | def run_webview(): 26 | window = webview.create_window('Webview', 'https://pywebview.flowrl.com/hello') 27 | webview.start() 28 | 29 | 30 | if __name__ == '__main__': 31 | 32 | def start_webview_process(): 33 | global webview_process 34 | webview_process = Process(target=run_webview) 35 | webview_process.start() 36 | 37 | def on_open(icon, item): 38 | global webview_process 39 | if not webview_process.is_alive(): 40 | start_webview_process() 41 | 42 | def on_exit(icon, item): 43 | icon.stop() 44 | 45 | start_webview_process() 46 | 47 | image = Image.open('logo/logo.png') 48 | menu = Menu(MenuItem('Open', on_open), MenuItem('Exit', on_exit)) 49 | icon = Icon('Pystray', image, menu=menu) 50 | icon.run() 51 | 52 | webview_process.terminate() 53 | -------------------------------------------------------------------------------- /examples/qt_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Create a pywebview windows using QT (normally GTK is preferred) 3 | """ 4 | 5 | import webview 6 | 7 | if __name__ == '__main__': 8 | # Create a non-resizable webview window with 800x600 dimensions 9 | webview.create_window('Qt Example', 'http://flowrl.com') 10 | webview.start(gui='qt') 11 | -------------------------------------------------------------------------------- /examples/remote_debugging.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Enable remote debugging when using `edgechromium`. 5 | This can be used to write tests for the application using Playwright. 6 | See [https://playwright.dev/docs/webview2](https://playwright.dev/docs/webview2) for how to configure it. 7 | """ 8 | 9 | if __name__ == '__main__': 10 | webview.settings['REMOTE_DEBUGGING_PORT'] = 9222 11 | 12 | window = webview.create_window('Webview', 'https://pywebview.flowrl.com/hello') 13 | webview.start() 14 | -------------------------------------------------------------------------------- /examples/resize.py: -------------------------------------------------------------------------------- 1 | """Resize window.""" 2 | 3 | from time import sleep 4 | import webview 5 | 6 | 7 | def resize(window): 8 | print('Window size is ({0}, {1})'.format(window.width, window.height)) 9 | sleep(2) 10 | window.resize(420, 420) 11 | print('Window size is ({0}, {1})'.format(window.width, window.height)) 12 | 13 | 14 | if __name__ == '__main__': 15 | window = webview.create_window( 16 | 'Resize window example', html='

Resize window

', width=800, height=600 17 | ) 18 | webview.start(resize, window) 19 | -------------------------------------------------------------------------------- /examples/run_js.py: -------------------------------------------------------------------------------- 1 | """Run Javascript code from Python.""" 2 | 3 | import webview 4 | from webview.errors import JavascriptException 5 | 6 | 7 | def run_js(window): 8 | result = window.run_js( 9 | r""" 10 | var h1 = document.createElement('h1') 11 | var text = document.createTextNode('Hello pywebview') 12 | h1.appendChild(text) 13 | document.body.appendChild(h1) 14 | 15 | function test() { 16 | return 420 17 | } 18 | 19 | test() 20 | """ 21 | ) 22 | 23 | print(result) 24 | 25 | 26 | if __name__ == '__main__': 27 | window = webview.create_window('Run JavaScript', html='') 28 | webview.start(run_js, window) 29 | -------------------------------------------------------------------------------- /examples/save_file_dialog.py: -------------------------------------------------------------------------------- 1 | """Create a save file dialog after a delay.""" 2 | 3 | import webview 4 | 5 | 6 | def save_file_dialog(window): 7 | import time 8 | 9 | time.sleep(5) 10 | result = window.create_file_dialog( 11 | webview.SAVE_DIALOG, directory='/', save_filename='test.file' 12 | ) 13 | print(result) 14 | 15 | 16 | if __name__ == '__main__': 17 | window = webview.create_window('Save file dialog', 'https://pywebview.flowrl.com/hello') 18 | webview.start(save_file_dialog, window) 19 | -------------------------------------------------------------------------------- /examples/screens.py: -------------------------------------------------------------------------------- 1 | """Get available display information using `webview.screens`""" 2 | 3 | import webview 4 | 5 | 6 | 7 | if __name__ == '__main__': 8 | screens = webview.screens 9 | print('Available screens are: ' + str(screens)) 10 | 11 | for i, screen in enumerate(screens): 12 | webview.create_window('', html=f'placed on the monitor {i+1}', screen=screen) 13 | 14 | webview.start() 15 | 16 | -------------------------------------------------------------------------------- /examples/settings.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | """ 4 | Use application flags to modify default behaviour of pywebview 5 | """ 6 | 7 | 8 | html = """ 9 | 10 | 11 | 12 |

13 |

target='_blank' link will be opened in the current window.

14 | 15 | 16 | """ 17 | 18 | 19 | if __name__ == '__main__': 20 | print(webview.settings) 21 | webview.settings['OPEN_EXTERNAL_LINKS_IN_BROWSER'] = False 22 | webview.settings['OPEN_DEVTOOLS_IN_DEBUG'] = False 23 | 24 | window = webview.create_window('Application flags', html=html) 25 | webview.start() 26 | -------------------------------------------------------------------------------- /examples/simple_browser.py: -------------------------------------------------------------------------------- 1 | """The most basic example of creating a webview window.""" 2 | 3 | import webview 4 | 5 | if __name__ == '__main__': 6 | # Create a standard webview window 7 | window = webview.create_window('Simple browser', 'https://pywebview.flowrl.com/hello') 8 | webview.start() 9 | -------------------------------------------------------------------------------- /examples/todos/README.md: -------------------------------------------------------------------------------- 1 | 2 | A simple app built with a relative url and JS API. 3 | 4 | 5 | This example has code adapted from [TodoMVC](https://github.com/tastejs/todomvc) released under MIT license. 6 | 7 | LICENSE 8 | 9 | Copyright (c) Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk. 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /examples/todos/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Todos 6 | 7 | 8 | 9 | 12 |
13 |
14 |

todos

15 | 16 |
17 |
18 | 19 | 20 |
    21 |
    22 |
    23 | 24 | 35 | 36 |
    37 |
    38 |
    39 |

    Double-click to edit a todo

    40 |
    41 | 42 |
    43 |
    44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/todos/assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/examples/todos/assets/logo.jpg -------------------------------------------------------------------------------- /examples/todos/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import webview 4 | 5 | """ 6 | An example of serverless app architecture 7 | """ 8 | 9 | 10 | class Api: 11 | def addItem(self, title): 12 | print('Added item %s' % title) 13 | 14 | def removeItem(self, item): 15 | print('Removed item %s' % item) 16 | 17 | def editItem(self, item): 18 | print('Edited item %s' % item) 19 | 20 | def toggleItem(self, item): 21 | print('Toggled item %s' % item) 22 | 23 | def toggleFullscreen(self): 24 | webview.windows[0].toggle_fullscreen() 25 | 26 | 27 | if __name__ == '__main__': 28 | api = Api() 29 | webview.create_window('Todos magnificos', 'assets/index.html', js_api=api, min_size=(600, 450)) 30 | webview.start(ssl=True) 31 | -------------------------------------------------------------------------------- /examples/toggle_fullscreen.py: -------------------------------------------------------------------------------- 1 | """Switch application window to a full-screen mode after five seconds..""" 2 | 3 | import time 4 | import webview 5 | 6 | 7 | def toggle_fullscreen(window): 8 | # wait a few seconds before toggle fullscreen: 9 | time.sleep(5) 10 | 11 | window.toggle_fullscreen() 12 | 13 | 14 | if __name__ == '__main__': 15 | window = webview.create_window('Full-screen window', 'https://pywebview.flowrl.com/hello') 16 | webview.start(toggle_fullscreen, window) 17 | -------------------------------------------------------------------------------- /examples/transparent.py: -------------------------------------------------------------------------------- 1 | """Create a transparent frameless window with custom chrome.""" 2 | 3 | import webview 4 | 5 | html = """ 6 | 7 | 8 | 9 | 10 | Test app 11 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 64 | 65 | 66 |
    53 | 54 | Danger! 55 |
    59 |
    60 | Alert!
    61 | Lorem ipsum dolor sit amet, consectetur adipiscing elit 62 |
    63 |
    67 | 68 | 69 | """ 70 | 71 | 72 | if __name__ == '__main__': 73 | # Create a transparent webview window 74 | webview.create_window('Transparent window', html=html, transparent=True, frameless=True) 75 | webview.start() 76 | -------------------------------------------------------------------------------- /examples/user_agent.py: -------------------------------------------------------------------------------- 1 | """ 2 | Change the user-agent of a window. 3 | """ 4 | 5 | import webview 6 | 7 | if __name__ == '__main__': 8 | webview.create_window('User Agent Test', 'https://pywebview.flowrl.com/hello') 9 | webview.start(user_agent='Custom user agent') 10 | -------------------------------------------------------------------------------- /examples/vibrancy.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example demonstrates how to set vibrancy on macOS. 3 | """ 4 | 5 | import webview 6 | 7 | 8 | def load_css(window): 9 | window.load_css('body { background: transparent !important; }') 10 | 11 | 12 | if __name__ == '__main__': 13 | window = webview.create_window( 14 | 'Vibrancy example', 'https://pywebview.flowrl.com/hello', transparent=True, vibrancy=True 15 | ) 16 | webview.start(load_css, window) 17 | -------------------------------------------------------------------------------- /examples/window_state.py: -------------------------------------------------------------------------------- 1 | """Minimize, restore and maximize window programmatically""" 2 | 3 | from time import sleep 4 | 5 | import webview 6 | 7 | 8 | def minimize(window): 9 | print('Window is started minimized') 10 | 11 | sleep(5) 12 | print('Restoring window') 13 | window.restore() 14 | 15 | sleep(5) 16 | print('Maximizing window') 17 | window.maximize() 18 | 19 | sleep(5) 20 | print('Minimizing window') 21 | window.minimize() 22 | 23 | 24 | if __name__ == '__main__': 25 | window = webview.create_window( 26 | 'Minimize window example', html='

    Minimize window

    ', minimized=True 27 | ) 28 | webview.start(minimize, window) 29 | -------------------------------------------------------------------------------- /examples/window_title_change.py: -------------------------------------------------------------------------------- 1 | """Change window title every three seconds.""" 2 | 3 | import webview 4 | 5 | 6 | def change_title(window): 7 | """changes title every 3 seconds""" 8 | for i in range(1, 100): 9 | # exit loop when window is closed 10 | if window.events.closed.wait(3): 11 | break 12 | 13 | window.title = f'New Title #{i}' 14 | print(window.title) 15 | 16 | 17 | if __name__ == '__main__': 18 | window = webview.create_window('Change title example', 'https://pywebview.flowrl.com/hello') 19 | webview.start(change_title, window) 20 | -------------------------------------------------------------------------------- /interop/android/.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /interop/android/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' version '8.1.2' apply true 3 | } 4 | // apply plugin: 'com.android.library' 5 | 6 | android { 7 | compileSdkVersion 31 8 | 9 | defaultConfig { 10 | minSdkVersion 21 11 | targetSdkVersion 31 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | } 18 | } 19 | 20 | sourceSets { 21 | main.java.srcDirs += 'lib/src/main/java' 22 | } 23 | 24 | namespace "com.pywebview" 25 | } 26 | 27 | task androidSourcesJar(type: Jar) { 28 | getArchiveClassifier().set('sources') 29 | from android.sourceSets.main.java.srcDirs 30 | } 31 | 32 | artifacts { 33 | archives androidSourcesJar 34 | } 35 | -------------------------------------------------------------------------------- /interop/android/lib/src/main/java/com/pywebview/EventCallbackWrapper.java: -------------------------------------------------------------------------------- 1 | package com.pywebview; 2 | 3 | public interface EventCallbackWrapper { 4 | public void callback(String event, String data); 5 | } -------------------------------------------------------------------------------- /interop/android/lib/src/main/java/com/pywebview/JavascriptValueCallback.java: -------------------------------------------------------------------------------- 1 | package com.pywebview; 2 | 3 | import com.pywebview.EventCallbackWrapper; 4 | import android.webkit.ValueCallback; 5 | import android.util.Log; 6 | 7 | public class JavascriptValueCallback implements ValueCallback { 8 | private EventCallbackWrapper callbackWrapper = null; 9 | 10 | public void setCallback(EventCallbackWrapper callback) { 11 | this.callbackWrapper = callback; 12 | } 13 | 14 | @Override 15 | public void onReceiveValue(String value) { 16 | if (this.callbackWrapper != null) { 17 | this.callbackWrapper.callback("onReceiveValue", value); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /interop/android/lib/src/main/java/com/pywebview/JsApiCallbackWrapper.java: -------------------------------------------------------------------------------- 1 | package com.pywebview; 2 | 3 | public interface JsApiCallbackWrapper { 4 | public void callback(String func, String params, String id); 5 | } -------------------------------------------------------------------------------- /interop/android/lib/src/main/java/com/pywebview/PyJavascriptInterface.java: -------------------------------------------------------------------------------- 1 | package com.pywebview; 2 | 3 | import com.pywebview.JsApiCallbackWrapper; 4 | import android.webkit.JavascriptInterface; 5 | import android.util.Log; 6 | 7 | 8 | public class PyJavascriptInterface { 9 | private JsApiCallbackWrapper callbackWrapper = null; 10 | 11 | public void setCallback(JsApiCallbackWrapper callback) { 12 | this.callbackWrapper = callback; 13 | } 14 | 15 | @JavascriptInterface 16 | public void call(String func, String params, String id) { 17 | if (this.callbackWrapper != null) { 18 | this.callbackWrapper.callback(func, params, id); 19 | } else { 20 | Log.e("pywebviewdebug", "No callback"); 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /interop/android/lib/src/main/java/com/pywebview/PyWebChromeClient.java: -------------------------------------------------------------------------------- 1 | package com.pywebview; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Activity; 5 | import android.content.DialogInterface; 6 | import android.util.Log; 7 | import android.view.View.OnClickListener; 8 | import android.webkit.JsResult; 9 | import android.webkit.WebView; 10 | import android.webkit.WebChromeClient; 11 | 12 | 13 | public class PyWebChromeClient extends WebChromeClient { 14 | private EventCallbackWrapper callbackWrapper = null; 15 | 16 | public void setCallback(EventCallbackWrapper callback) { 17 | this.callbackWrapper = callback; 18 | } 19 | 20 | @Override 21 | public boolean onJsAlert(WebView view, String url, String message, JsResult result) { 22 | // TODO: This code crashes the app. 23 | 24 | Activity context = (Activity)view.getContext(); 25 | new AlertDialog.Builder(context) 26 | .setMessage(message) 27 | .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm()) 28 | .setOnCancelListener(dialog -> result.cancel()) 29 | .show(); 30 | 31 | return true; 32 | } 33 | 34 | @Override 35 | public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { 36 | new AlertDialog.Builder(view.getContext()) 37 | .setMessage(message) 38 | .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm()) 39 | .setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel()) 40 | .setOnCancelListener(dialog -> result.cancel()) 41 | .show(); 42 | return true; 43 | } 44 | 45 | // @Override 46 | // public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { 47 | // LayoutInflater inflater = LayoutInflater.from(context); 48 | // View promptView = inflater.inflate(R.layout.dialog_js_prompt, null, false); 49 | // TextView messageView = promptView.findViewById(R.id.message); 50 | // messageView.setText(message); 51 | // final EditText valueView = promptView.findViewById(R.id.value); 52 | // valueView.setText(defaultValue); 53 | 54 | // new AlertDialog.Builder(context) 55 | // .setView(promptView) 56 | // .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm(valueView.getText().toString())) 57 | // .setOnCancelListener(dialog -> result.cancel()) 58 | // .show(); 59 | 60 | // return true; 61 | // } 62 | 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /interop/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | 9 | // plugins { 10 | // // Apply the foojay-resolver plugin to allow automatic download of JDKs 11 | // id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' 12 | // } 13 | 14 | 15 | dependencyResolutionManagement { 16 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 17 | repositories { 18 | google() 19 | mavenCentral() 20 | maven { 21 | url 'https://maven.google.com' 22 | } 23 | } 24 | } 25 | 26 | rootProject.name = 'pywebview-android' 27 | include('src') 28 | -------------------------------------------------------------------------------- /interop/mshtml/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ClassLibrary1")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ClassLibrary1")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4855b53e-6f15-45cc-bbd0-44d58c672eab")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /interop/mshtml/README.txt: -------------------------------------------------------------------------------- 1 | This is a Visual Studio project for generating lib/WebBrowserInterop.dll. The DLL is required to make certain features work on Windows that would not be possible otherwise. 2 | -------------------------------------------------------------------------------- /interop/mshtml/WebBrowserInterop.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebBrowserInterop", "WebBrowserInterop.csproj", "{4855B53E-6F15-45CC-BBD0-44D58C672EAB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Debug|x64.ActiveCfg = Debug|x64 21 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Debug|x64.Build.0 = Debug|x64 22 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Debug|x86.ActiveCfg = Debug|x86 23 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Debug|x86.Build.0 = Debug|x86 24 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Release|x64.ActiveCfg = Release|x64 27 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Release|x64.Build.0 = Release|x64 28 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Release|x86.ActiveCfg = Release|x86 29 | {4855B53E-6F15-45CC-BBD0-44D58C672EAB}.Release|x86.Build.0 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /logo/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/logo/banner.png -------------------------------------------------------------------------------- /logo/logo-no-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/logo/logo-no-text.png -------------------------------------------------------------------------------- /logo/logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/logo/logo.pdf -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/logo/logo.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pythonnet; sys_platform == "win32" 2 | pyobjc-core; sys_platform == "darwin" 3 | pyobjc-framework-Cocoa; sys_platform == "darwin" 4 | pyobjc-framework-Quartz; sys_platform == "darwin" 5 | pyobjc-framework-WebKit; sys_platform == "darwin" 6 | pyobjc-framework-Security; sys_platform == "darwin" 7 | PyQt6; sys_platform == "openbsd6" or sys_platform == "linux" 8 | PyQt6-WebEngine; sys_platform == "openbsd6" or sys_platform == "linux" 9 | QtPy; sys_platform == "openbsd6" or sys_platform == "linux" 10 | importlib_resources; python_version < "3.7" 11 | typing_extensions 12 | proxy_tools 13 | bottle 14 | cryptography 15 | 16 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/tests/__init__.py -------------------------------------------------------------------------------- /tests/assets/script.js: -------------------------------------------------------------------------------- 1 | window.testResult = 80085 2 | document.cookie = 'pywebview=true' -------------------------------------------------------------------------------- /tests/assets/styles.css: -------------------------------------------------------------------------------- 1 | body {background: red} 2 | -------------------------------------------------------------------------------- /tests/assets/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test app 6 | 7 | 8 | 9 | 10 |

    Hello there!

    11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from importlib import reload 3 | 4 | @pytest.fixture(autouse=True) 5 | def reload_webview(): 6 | import webview 7 | 8 | reload(webview) 9 | 10 | @pytest.fixture(autouse=True) 11 | def set_env(): 12 | import os 13 | 14 | os.environ['PYWEBVIEW_TEST'] = 'true' 15 | 16 | 17 | # @pytest.fixture(autouse=True) 18 | # def set_gui(): 19 | # import os 20 | # os.environ['PYWEBVIEW_GUI'] = 'qt' -------------------------------------------------------------------------------- /tests/run.ps1: -------------------------------------------------------------------------------- 1 | $RootPath = Split-Path $PSScriptRoot 2 | ${env:PYTHONPATH='$RootPath'} 3 | ${env:PYWEBVIEW_LOG='debug'} 4 | $tests = Get-ChildItem -Path $PSScriptRoot -Filter 'test_*.py' | ForEach-Object { $_.Name } 5 | 6 | if (Test-Path __pycache__) { 7 | Remove-Item -Recurse -Force __pycache__ 8 | } 9 | 10 | $errors = 0 11 | foreach ($test in $tests) { 12 | pytest $test -q -s --disable-warnings -r w 13 | $errors = $errors + $LASTEXITCODE 14 | } 15 | 16 | exit $errors 17 | -------------------------------------------------------------------------------- /tests/run.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | if __name__ == '__main__': 6 | pytest.main(['-s']) 7 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PYTHONPATH=.. 3 | PYWEBVIEW_LOG='debug' 4 | PYTEST_OPTIONS='-q -s --disable-warnings -r w' 5 | 6 | # cd "${0%/*}" 7 | # rm -r __pycache__ || true 8 | 9 | exitcode=0 10 | pywebviewtest() { 11 | python3 -m pytest ${PYTEST_OPTIONS} "$@" || exitcode=$? 12 | } 13 | 14 | # cd .. 15 | echo Starting tests... 16 | for test in $(ls test_*); do 17 | echo $test 18 | pywebviewtest $test 19 | done 20 | 21 | if [ $exitcode != 0 ]; then 22 | echo -e '\033[0;31mTEST FAILURES HAVE OCCURRED!\033[0m' 23 | exit 1 24 | fi 25 | -------------------------------------------------------------------------------- /tests/run_cef.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | if __name__ == '__main__': 6 | os.environ['PYWEBVIEW_GUI'] = 'cef' 7 | pytest.main() 8 | -------------------------------------------------------------------------------- /tests/run_qt.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | if __name__ == '__main__': 6 | os.environ['PYWEBVIEW_GUI'] = 'qt' 7 | pytest.main() 8 | -------------------------------------------------------------------------------- /tests/test_bg_color.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import webview 4 | 5 | from .util import run_test 6 | 7 | 8 | def test_bg_color(): 9 | window = webview.create_window( 10 | 'Background color test', 'https://www.example.org', background_color='#0000FF' 11 | ) 12 | run_test(webview, window) 13 | 14 | 15 | def test_invalid_bg_color(): 16 | with pytest.raises(ValueError): 17 | webview.create_window( 18 | 'Background color test', 'https://www.example.org', background_color='#dsg0000FF' 19 | ) 20 | 21 | with pytest.raises(ValueError): 22 | webview.create_window( 23 | 'Background color test', 'https://www.example.org', background_color='FF00FF' 24 | ) 25 | 26 | with pytest.raises(ValueError): 27 | webview.create_window( 28 | 'Background color test', 'https://www.example.org', background_color='#ac' 29 | ) 30 | 31 | with pytest.raises(ValueError): 32 | webview.create_window( 33 | 'Background color test', 'https://www.example.org', background_color='#EFEFEH' 34 | ) 35 | 36 | with pytest.raises(ValueError): 37 | webview.create_window( 38 | 'Background color test', 'https://www.example.org', background_color='#0000000' 39 | ) 40 | -------------------------------------------------------------------------------- /tests/test_cookies.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import webview 4 | from time import sleep 5 | from .util import run_test 6 | import os 7 | 8 | 9 | @pytest.fixture 10 | def window(): 11 | return webview.create_window('Cookie test', 'assets/test.html') 12 | 13 | 14 | def test_get_cookies(window): 15 | run_test(webview, window, get_cookies_test) 16 | 17 | 18 | @pytest.mark.skipif(os.environ.get('PYWEBVIEW_GUI') == 'qt', reason='This test crashes QT') 19 | def test_clear_cookies(window): 20 | run_test(webview, window, clear_cookies_test) 21 | 22 | 23 | def get_cookies_test(window): 24 | cookies = window.get_cookies() 25 | assert len(cookies) == 1 26 | assert cookies[0].output().startswith('Set-Cookie: pywebview=true; Domain=127.0.0.1;') 27 | 28 | 29 | def clear_cookies_test(window): 30 | window.clear_cookies() 31 | cookies = window.get_cookies() 32 | assert len(cookies) == 0 -------------------------------------------------------------------------------- /tests/test_download.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import webview 4 | 5 | from .util import run_test, assert_js 6 | 7 | import time 8 | 9 | html = """ 10 | 11 | 12 | 13 | 14 | 33 | 34 | """ 35 | 36 | @pytest.fixture 37 | def window(): 38 | return webview.create_window('Download test', html=html) 39 | 40 | # skip this test by default since it's a manual test that requires user interaction 41 | @pytest.mark.skip 42 | def test_download_attribute(window): 43 | webview.settings['ALLOW_DOWNLOADS'] = True 44 | run_test(webview, window, download_test) 45 | 46 | def get_result(): 47 | res = input("Did the system prompt you for a download with name test_download.txt or download a file by that name? Y/N").upper() 48 | return (res == 'Y') 49 | 50 | def download_test(window): 51 | # this should not cause the browser to navigate away but instead should trigger a download 52 | # since the type is text the browser should support it and if it navigates to it it will display it instead 53 | window.evaluate_js("document.getElementById('start_download').click();") 54 | time.sleep(0.5) # make sure it has executed 55 | 56 | # if it loaded on the page and navigated away then it will fail this 57 | elements = window.dom.get_elements('#start_download') 58 | assert len(elements) == 1 59 | 60 | # wait for download prompt to be dismissed and focus to come back to window 61 | while not window.evaluate_js("checkFocus()"): 62 | time.sleep(0.5) 63 | 64 | # ask user if the test passed 65 | assert window.evaluate_js("verify_result();") 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /tests/test_expose.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import assert_js, run_test 4 | 5 | 6 | def test_expose_single(): 7 | window = webview.create_window('JSBridge test', html='TEST') 8 | window.expose(get_int) 9 | run_test(webview, window, expose_single) 10 | 11 | 12 | def test_expose_multiple(): 13 | window = webview.create_window('JSBridge test', html='TEST') 14 | window.expose(get_int, get_float) 15 | run_test(webview, window, expose_multiple) 16 | 17 | 18 | def test_expose_runtime(): 19 | window = webview.create_window('JSBridge test', html='TEST') 20 | run_test(webview, window, expose_runtime) 21 | 22 | 23 | def test_override(): 24 | api = Api() 25 | window = webview.create_window('JSBridge test', html='TEST', js_api=api) 26 | window.expose(get_int) 27 | run_test(webview, window, expose_override) 28 | 29 | 30 | def get_int(): 31 | return 420 32 | 33 | 34 | def get_float(): 35 | return 420.420 36 | 37 | 38 | class Api: 39 | def get_int(self): 40 | return 421 41 | 42 | 43 | def expose_single(window): 44 | assert_js(window, 'get_int', 420) 45 | 46 | 47 | def expose_multiple(window): 48 | assert_js(window, 'get_int', 420) 49 | assert_js(window, 'get_float', 420.420) 50 | 51 | 52 | def expose_runtime(window): 53 | window.expose(get_int, get_float) 54 | assert_js(window, 'get_int', 420) 55 | 56 | 57 | def expose_override(window): 58 | assert_js(window, 'get_int', 420) 59 | -------------------------------------------------------------------------------- /tests/test_frameless.py: -------------------------------------------------------------------------------- 1 | import os 2 | import webview 3 | import pytest 4 | from .util import run_test 5 | 6 | 7 | @pytest.mark.skipif(os.environ.get('PYWEBVIEW_GUI') == 'qt', reason='This test crashes PyQt5') 8 | def test_frameless(): 9 | window = webview.create_window('Frameless test', 'https://www.example.org', frameless=True) 10 | run_test(webview, window) 11 | -------------------------------------------------------------------------------- /tests/test_fullscreen.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_fullscreen(): 7 | window = webview.create_window('Fullscreen test', 'https://www.example.org', fullscreen=True) 8 | run_test(webview, window) 9 | -------------------------------------------------------------------------------- /tests/test_get_current_url.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_current_url(): 7 | window = webview.create_window('Get Current URL test', 'https://example.org') 8 | run_test(webview, window, current_url_test, destroy_delay=5) 9 | 10 | 11 | def test_no_url(): 12 | window = webview.create_window('Get Current URL test') 13 | run_test(webview, window, no_url_test) 14 | 15 | 16 | def current_url_test(window): 17 | assert window.get_current_url() == 'https://example.org/' 18 | 19 | 20 | def no_url_test(window): 21 | assert window.get_current_url() is None 22 | -------------------------------------------------------------------------------- /tests/test_get_elements.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import webview 4 | 5 | from .util import run_test 6 | 7 | html = """ 8 | 9 | 10 |

    Heading

    11 |
    Content 1
    12 |
    Content 2
    13 | 14 | 15 | """ 16 | 17 | 18 | @pytest.fixture 19 | def window(): 20 | return webview.create_window('Get elements test', html=html) 21 | 22 | 23 | def test_single(window): 24 | run_test(webview, window, single_test) 25 | 26 | 27 | def test_multiple(window): 28 | run_test(webview, window, multiple_test) 29 | 30 | 31 | def test_none(window): 32 | run_test(webview, window, none_test) 33 | 34 | 35 | def single_test(window): 36 | try: 37 | elements = window.dom.get_elements('#heading') 38 | assert len(elements) == 1 39 | assert elements[0].node['innerHTML'] == 'Heading' 40 | except NotImplementedError: 41 | pass 42 | 43 | 44 | def multiple_test(window): 45 | try: 46 | elements = window.dom.get_elements('.content') 47 | assert len(elements) == 2 48 | assert elements[0].node['innerHTML'] == 'Content 1' 49 | assert elements[1].node['innerHTML'] == 'Content 2' 50 | except NotImplementedError: 51 | pass 52 | 53 | 54 | def none_test(window): 55 | try: 56 | elements = window.dom.get_elements('.adgdfg') 57 | assert len(elements) == 0 58 | except NotImplementedError: 59 | pass 60 | -------------------------------------------------------------------------------- /tests/test_hide_window.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_hide_show_window(): 7 | window = webview.create_window('Hide/show window test', 'https://www.example.org', hidden=True) 8 | run_test(webview, window, hide_show_window) 9 | 10 | 11 | def hide_show_window(window): 12 | window.show() 13 | window.hide() 14 | -------------------------------------------------------------------------------- /tests/test_http_server.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import assert_js, run_test 4 | 5 | 6 | class Api: 7 | def test(self): 8 | return 'JS Api is working too' 9 | 10 | 11 | def test_start(): 12 | api = Api() 13 | window = webview.create_window('Relative URL test', 'assets/test.html', js_api=api) 14 | run_test(webview, window, assert_func, start_args={'http_server': True}) 15 | 16 | 17 | def assert_func(window): 18 | html_result = window.evaluate_js('document.getElementById("heading").innerText') 19 | assert html_result == 'Hello there!' 20 | 21 | css_result = window.evaluate_js( 22 | 'window.getComputedStyle(document.body, null).getPropertyValue("background-color")' 23 | ) 24 | assert css_result == 'rgb(255, 0, 0)' 25 | 26 | js_result = window.evaluate_js('window.testResult') 27 | assert js_result == 80085 28 | 29 | assert_js(window, 'test', 'JS Api is working too') 30 | -------------------------------------------------------------------------------- /tests/test_load_html.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_load_html(): 7 | window = webview.create_window('Load HTML test') 8 | run_test(webview, window, load_html) 9 | 10 | 11 | def load_html(window): 12 | window.load_html('

    This is dynamically loaded HTML

    ') 13 | -------------------------------------------------------------------------------- /tests/test_localization.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_localization(): 7 | localization = { 8 | 'cocoa.menu.about': 'О программе', 9 | 'cocoa.menu.services': 'Cлужбы', 10 | 'cocoa.menu.view': 'Вид', 11 | 'cocoa.menu.hide': 'Скрыть', 12 | 'cocoa.menu.hideOthers': 'Скрыть остальные', 13 | 'cocoa.menu.showAll': 'Показать все', 14 | 'cocoa.menu.quit': 'Завершить', 15 | 'cocoa.menu.fullscreen': 'Полнж', 16 | 'windows.fileFilter.allFiles': 'Все файлы', 17 | 'windows.fileFilter.otherFiles': 'Остальлные файльы', 18 | 'linux.openFile': 'Открыть файл', 19 | 'linux.openFiles': 'Открыть файлы', 20 | 'linux.openFolder': 'Открыть папку', 21 | 'linux.saveFile': 'Сохранить файл', 22 | } 23 | 24 | window = webview.create_window('Localization test', 'https://www.example.org') 25 | run_test(webview, window, start_args={'localization': localization}) 26 | -------------------------------------------------------------------------------- /tests/test_min_size.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_min_size(): 7 | window = webview.create_window('Min size test', 'https://www.example.org', min_size=(400, 200)) 8 | run_test(webview, window) 9 | -------------------------------------------------------------------------------- /tests/test_move_window.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | import pytest 3 | import webview 4 | 5 | from .util import run_test 6 | 7 | def test_xy(): 8 | window = webview.create_window('xy test', x=200, y=200, width=100, height=100) 9 | run_test(webview, window, xy) 10 | 11 | 12 | @pytest.mark.skip 13 | def test_move_window(): 14 | window = webview.create_window('Move window test', x=200, y=200, width=100, height=100) 15 | run_test(webview, window, move_window) 16 | 17 | 18 | def xy(window): 19 | # Coordinates are not always exact due to window manager decorations 20 | assert abs(window.x - 200) < 10 21 | assert abs(window.y - 200) < 10 22 | 23 | 24 | def move_window(window): 25 | # Coordinates are not always exact due to window manager decorations 26 | window.move(300, 300) 27 | sleep(1) 28 | 29 | assert abs(window.x - 300) < 10 30 | assert abs(window.y - 300) < 10 31 | -------------------------------------------------------------------------------- /tests/test_noresize.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_noresize(): 7 | window = webview.create_window( 8 | 'Min size test', 9 | 'https://www.example.org', 10 | width=800, 11 | height=600, 12 | resizable=True, 13 | min_size=(400, 200), 14 | ) 15 | run_test(webview, window) 16 | -------------------------------------------------------------------------------- /tests/test_on_top.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_on_top(): 7 | window = webview.create_window('Toggle on_top test', 'https://www.example.org') 8 | run_test(webview, window, on_top) 9 | 10 | 11 | def on_top(window): 12 | try: 13 | window.on_top = True 14 | window.on_top = False 15 | except NotImplementedError: 16 | print('This OS/guilib does not yet have "on_top" feature.') 17 | -------------------------------------------------------------------------------- /tests/test_resize.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import pytest 4 | 5 | import webview 6 | 7 | from .util import run_test 8 | 9 | 10 | @pytest.mark.skip 11 | def test_resize(): 12 | window = webview.create_window( 13 | 'Set Window Size Test', 'https://www.example.org', width=800, height=600 14 | ) 15 | run_test(webview, window, resize) 16 | 17 | 18 | def resize(window): 19 | sleep(3) 20 | window.events.shown.wait() 21 | # assert window.width == 800 22 | # assert window.height == 600 23 | 24 | window.resize(500, 500) 25 | 26 | sleep(0.5) 27 | 28 | assert window.width == 500 29 | assert window.height == 500 30 | -------------------------------------------------------------------------------- /tests/test_run_js.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import webview 4 | 5 | from .util import run_test 6 | 7 | 8 | @pytest.fixture 9 | def window(): 10 | return webview.create_window('Run JS test', html='
    TEST
    ') 11 | 12 | 13 | 14 | def test_string(window): 15 | run_test(webview, window, string_test) 16 | 17 | 18 | def test_int(window): 19 | run_test(webview, window, int_test) 20 | 21 | def test_numeric(window): 22 | run_test(webview, window, numeric_test) 23 | 24 | def test_stringify(window): 25 | run_test(webview, window, stringify_test) 26 | 27 | 28 | def string_test(window): 29 | result = window.run_js( 30 | """ 31 | "this is only a test" 32 | """ 33 | ) 34 | assert result == 'this is only a test' 35 | 36 | 37 | def int_test(window): 38 | result = window.run_js( 39 | """ 40 | 420 41 | """ 42 | ) 43 | assert result == 420 44 | 45 | 46 | def numeric_test(window): 47 | result = window.run_js( 48 | """ 49 | '420' 50 | """ 51 | ) 52 | assert result == '420' 53 | 54 | 55 | def stringify_test(window): 56 | window.events.loaded.wait() 57 | result = window.run_js( 58 | """ 59 | var obj = {a: 420}; 60 | var value = pywebview.stringify(obj); 61 | value; 62 | """ 63 | ) 64 | assert result == '{"a":420}' 65 | -------------------------------------------------------------------------------- /tests/test_screens.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | 4 | def test_screens(): 5 | assert len(webview.screens) > 0 6 | assert webview.screens[0].width > 0 7 | assert webview.screens[0].height > 0 8 | assert type(webview.screens[0].x) == int 9 | assert type(webview.screens[0].y) == int 10 | -------------------------------------------------------------------------------- /tests/test_serialization.py: -------------------------------------------------------------------------------- 1 | 2 | import webview 3 | import json 4 | from .util import run_test 5 | import pytest 6 | 7 | test_values = [ 8 | ('int', 1), 9 | ('string', 'test'), 10 | ('null_value', None), 11 | ('object', {'key1': 'value', 'key2': 420}), 12 | ('array', [1, 2, 3]), 13 | ('mixed', {'key1': 'value', 'key2': [ 1, 2, {'id': 2}], 'nullValue': None}), 14 | ('boolean', True), 15 | ] 16 | 17 | test_value_string = '\n'.join([ f"var {name} = {json.dumps(value)}" for name, value in test_values]) 18 | 19 | HTML = f""" 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 36 |
    1
    37 |
    38 |

    THIS IS ONLY A TEST

    39 |
    40 |
    3
    41 | 42 | 43 | """ 44 | 45 | def test_basic_serialization(): 46 | window = webview.create_window('Basic serialization test', html=HTML) 47 | run_test(webview, window, serialization) 48 | 49 | 50 | def test_circular_serialization(): 51 | window = webview.create_window('Circular reference test', html=HTML) 52 | run_test(webview, window, circular_reference) 53 | 54 | 55 | def test_dom_serialization(): 56 | window = webview.create_window('DOM serialization test', html=HTML) 57 | run_test(webview, window, dom_serialization) 58 | 59 | 60 | def serialization(window): 61 | for name, expected_value in test_values: 62 | result = window.evaluate_js(name) 63 | assert result == expected_value 64 | 65 | def circular_reference(window): 66 | result = window.evaluate_js('circular') 67 | assert result == {'key': 'test', 'circular': '[Circular Reference]'} 68 | 69 | result = window.evaluate_js('nonCircular') 70 | assert result == [{'id': 1}, {'id': 1}] 71 | 72 | 73 | def dom_serialization(window): 74 | result = window.evaluate_js('nodes') 75 | assert len(result) == 3 76 | assert result[0]['innerText'] == '1' 77 | assert result[1]['innerText'].strip() == 'THIS IS ONLY A TEST' 78 | assert result[2]['innerText'] == '3' 79 | -------------------------------------------------------------------------------- /tests/test_set_title.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_set_title(): 7 | window = webview.create_window('Set title test', 'https://www.example.org') 8 | run_test(webview, window, set_title) 9 | 10 | 11 | def set_title(window): 12 | assert window.title == 'Set title test' 13 | window.title = 'New title' 14 | 15 | assert window.title == 'New title' 16 | -------------------------------------------------------------------------------- /tests/test_simple_browser.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_simple_browser(): 7 | window = webview.create_window('Simple browser test', 'https://www.example.org') 8 | run_test(webview, window) 9 | -------------------------------------------------------------------------------- /tests/test_start.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | import webview 4 | 5 | from .util import assert_js, run_test 6 | 7 | 8 | class Api: 9 | def test(self): 10 | return 'JS Api is working too' 11 | 12 | 13 | def test_start(): 14 | api = Api() 15 | window = webview.create_window('Relative URL test', 'assets/test.html', js_api=api) 16 | run_test(webview, window, assert_func) 17 | 18 | 19 | def assert_func(window): 20 | sleep(1) 21 | html_result = window.evaluate_js('document.getElementById("heading").innerText') 22 | assert html_result == 'Hello there!' 23 | 24 | css_result = window.evaluate_js( 25 | 'window.getComputedStyle(document.body, null).getPropertyValue("background-color")' 26 | ) 27 | assert css_result == 'rgb(255, 0, 0)' 28 | 29 | js_result = window.evaluate_js('window.testResult') 30 | assert js_result == 80085 31 | 32 | assert_js(window, 'test', 'JS Api is working too') 33 | -------------------------------------------------------------------------------- /tests/test_toggle_fullscreen.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_toggle_fullscreen(): 7 | window = webview.create_window('Toggle fullscreen test', 'https://www.example.org') 8 | run_test(webview, window, toggle_fullscreen) 9 | 10 | 11 | def toggle_fullscreen(window): 12 | window.toggle_fullscreen() 13 | -------------------------------------------------------------------------------- /tests/test_token.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_token(): 7 | window = webview.create_window('Token test') 8 | run_test(webview, window, token_test) 9 | 10 | 11 | def test_persistance(): 12 | window = webview.create_window('Token persistence test') 13 | run_test(webview, window, persistance_test) 14 | 15 | 16 | def token_test(window): 17 | token = window.evaluate_js('window.pywebview.token') 18 | assert token is not None 19 | assert len(token) > 0 20 | 21 | 22 | def persistance_test(window): 23 | token1 = window.evaluate_js('pywebview.token') 24 | assert token1 is not None 25 | 26 | window.load_html('
    new HTML
    ') 27 | token2 = window.evaluate_js('pywebview.token') 28 | assert token1 == token2 29 | 30 | window.load_url('https://example.org') 31 | token3 = window.evaluate_js('pywebview.token') 32 | assert token1 == token2 == token3 33 | -------------------------------------------------------------------------------- /tests/test_url_load.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_url_load(): 7 | window = webview.create_window('URL change test', 'https://www.example.org') 8 | run_test(webview, window, url_load) 9 | 10 | 11 | def url_load(window): 12 | window.load_url('https://pywebview.flowrl.com') 13 | -------------------------------------------------------------------------------- /tests/test_vibrancy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | import webview 4 | 5 | from .util import run_test 6 | 7 | 8 | def load_css(window): 9 | window.load_css('body { background: transparent !important; }') 10 | 11 | 12 | @pytest.mark.skipif(sys.platform != 'darwin', reason='vibrancy is macOS only') 13 | def test_vibrancy(): 14 | window = webview.create_window( 15 | 'set vibrancy example', 16 | 'https://pywebview.flowrl.com/hello', 17 | transparent=True, 18 | vibrancy=True, 19 | ) 20 | run_test(webview, window, start_args={'func': load_css, 'args': window}) 21 | -------------------------------------------------------------------------------- /tests/test_window.py: -------------------------------------------------------------------------------- 1 | import webview 2 | 3 | from .util import run_test 4 | 5 | 6 | def test_window_count(): 7 | window = webview.create_window('Window object test') 8 | run_test(webview, window, window_count) 9 | 10 | 11 | def window_count(window): 12 | assert len(webview.windows) == 1 13 | -------------------------------------------------------------------------------- /tests/util_cocoa.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cocoa window cannot be destroyed programmatically, until it finishes processing a NSEvent 3 | So we need to simulate a mouse movement in order to generate an event. 4 | """ 5 | 6 | from Quartz.CoreGraphics import (CGEventCreate, CGEventCreateMouseEvent, CGEventGetLocation, 7 | CGEventPost, kCGEventLeftMouseDown, kCGEventLeftMouseUp, 8 | kCGEventMouseMoved, kCGHIDEventTap, kCGMouseButtonLeft) 9 | 10 | 11 | def mousePos(): 12 | event = CGEventCreate(None) 13 | pointer = CGEventGetLocation(event) 14 | # CFRelease(event) 15 | return pointer.x, pointer.y 16 | 17 | 18 | def mouseEvent(type, posx, posy): 19 | theEvent = CGEventCreateMouseEvent(None, type, (posx, posy), kCGMouseButtonLeft) 20 | CGEventPost(kCGHIDEventTap, theEvent) 21 | 22 | 23 | def mouseMove(posx, posy): 24 | mousePos() 25 | mouseEvent(kCGEventMouseMoved, posx, posy) 26 | 27 | 28 | def mouseMoveRelative(dx, dy): 29 | posx, posy = mousePos() 30 | mouseMove(posx + dx, posy + dy) 31 | 32 | 33 | def mouseclick(posx, posy): 34 | mouseEvent(kCGEventLeftMouseDown, posx, posy) 35 | mouseEvent(kCGEventLeftMouseUp, posx, posy) 36 | 37 | 38 | if __name__ == '__main__': 39 | mouseMoveRelative(100, 100) 40 | -------------------------------------------------------------------------------- /webview/__pyinstaller/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def get_hook_dirs(): 4 | return [os.path.dirname(__file__)] -------------------------------------------------------------------------------- /webview/__pyinstaller/hook-webview.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_data_files, collect_dynamic_libs 2 | from PyInstaller.compat import is_win 3 | 4 | datas = [] 5 | if is_win: 6 | datas = collect_data_files('webview', subdir='lib') 7 | binaries = collect_dynamic_libs('webview') 8 | 9 | 10 | datas += collect_data_files('webview', subdir='js') -------------------------------------------------------------------------------- /webview/dom/__init__.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Callable 3 | 4 | 5 | class ManipulationMode(Enum): 6 | LastChild = 'LAST_CHILD' 7 | FirstChild = 'FIRST_CHILD' 8 | Before = 'BEFORE' 9 | After = 'AFTER' 10 | Replace = 'REPLACE' 11 | 12 | 13 | class DOMEventHandler: 14 | def __init__( 15 | self, 16 | callback: Callable, 17 | prevent_default: bool = False, 18 | stop_propagation: bool = False, 19 | stop_immediate_propagation: bool = False, 20 | debounce: int = 0, 21 | ): 22 | self.__callback = callback 23 | self.__prevent_default = prevent_default 24 | self.__stop_propagation = stop_propagation 25 | self.__stop_immediate_propagation = stop_immediate_propagation 26 | self.__debounce = debounce 27 | 28 | @property 29 | def callback(self): 30 | return self.__callback 31 | 32 | @property 33 | def prevent_default(self): 34 | return self.__prevent_default 35 | 36 | @property 37 | def stop_propagation(self): 38 | return self.__stop_propagation 39 | 40 | @property 41 | def stop_immediate_propagation(self): 42 | return self.__stop_immediate_propagation 43 | 44 | @property 45 | def debounce(self): 46 | return self.__debounce 47 | 48 | 49 | _dnd_state = {'num_listeners': 0, 'paths': []} 50 | -------------------------------------------------------------------------------- /webview/dom/classlist.py: -------------------------------------------------------------------------------- 1 | class ClassList: 2 | def __init__(self, element, classes=None): 3 | self.__element = element 4 | 5 | if classes: 6 | classes = ' '.join(classes) 7 | self.__element._window.evaluate_js(f"{self.__element._query_command}; element.className = '{classes}'") 8 | 9 | def append(self, cls): 10 | self.__element._window.run_js(f"{self.__element._query_command}; element.classList.add('{cls}')") 11 | 12 | def remove(self, cls): 13 | self.__element._window.run_js(f"{self.__element._query_command}; element.classList.remove('{cls}')") 14 | 15 | def toggle(self, cls): 16 | self.__element._window.run_js(f"{self.__element._query_command}; element.classList.toggle('{cls}')") 17 | 18 | def __get_classes(self): 19 | classes = self.__element._window.evaluate_js(f"{self.__element._query_command}; element.className").split(' ') 20 | return [c for c in classes if c != ''] 21 | 22 | def __getitem__(self, index): 23 | classes = self.__get_classes() 24 | return classes[index] 25 | 26 | def __len__(self): 27 | classes = self.__get_classes() 28 | return len(classes) 29 | 30 | def __str__(self): 31 | classes = self.__get_classes() 32 | return str(classes) 33 | 34 | def __repr__(self): 35 | classes = self.__get_classes() 36 | return repr(classes) 37 | 38 | def clear(self): 39 | self.__element._window.run_js(f"{self.__element._query_command}; element.className = ''") 40 | -------------------------------------------------------------------------------- /webview/dom/event.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Any, Callable 3 | from typing_extensions import Self 4 | from webview import Window 5 | from webview.dom.element import Element 6 | 7 | logger = logging.getLogger(__file__) 8 | 9 | class DOMEvent: 10 | def __init__(self, event, window: Window, element: Element) -> None: 11 | self.event = event 12 | self.__element = element 13 | self._items: list[Callable[..., Any]] = [] 14 | 15 | def __add__(self, item: Callable[..., Any]) -> Self: 16 | self._items.append(item) 17 | self.__element.on(self.event, item) 18 | return self 19 | def __sub__(self, item: Callable[..., Any]) -> Self: 20 | self._items.remove(item) 21 | self.__element.off(self.event, item) 22 | return self 23 | 24 | def __iadd__(self, item: Callable[..., Any]) -> Self: 25 | self._items.append(item) 26 | self.__element.on(self.event, item) 27 | return self 28 | 29 | def __isub__(self, item: Callable[..., Any]) -> Self: 30 | if item in self._items: 31 | self._items.remove(item) 32 | self.__element.off(self.event, item) 33 | else: 34 | logger.warning(f'Event handler {item} not found') 35 | return self 36 | -------------------------------------------------------------------------------- /webview/errors.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class WebViewException(Exception): 4 | pass 5 | 6 | class JavascriptException(Exception): 7 | pass -------------------------------------------------------------------------------- /webview/js/finish.js: -------------------------------------------------------------------------------- 1 | window.pywebview._createApi(JSON.parse('%(functions)s')); 2 | 3 | if (window.pywebview.platform == 'qtwebengine') { 4 | new QWebChannel(qt.webChannelTransport, function(channel) { 5 | window.pywebview._QWebChannel = channel; 6 | window.dispatchEvent(new CustomEvent('pywebviewready')); 7 | }); 8 | } else { 9 | window.dispatchEvent(new CustomEvent('pywebviewready')); 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /webview/lib/Microsoft.Web.WebView2.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/Microsoft.Web.WebView2.Core.dll -------------------------------------------------------------------------------- /webview/lib/Microsoft.Web.WebView2.LICENSE.md: -------------------------------------------------------------------------------- 1 | ## Microsoft.Web.WebView2 2 | 3 | * Microsoft.Web.WebView2.Core.dll 4 | * Microsoft.Web.WebView2.WinForms.dll 5 | * WebView2Loader.dll 6 | 7 | Copyright (C) Microsoft Corporation. All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are 11 | met: 12 | 13 | * Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above 16 | copyright notice, this list of conditions and the following disclaimer 17 | in the documentation and/or other materials provided with the 18 | distribution. 19 | * The name of Microsoft Corporation, or the names of its contributors 20 | may not be used to endorse or promote products derived from this 21 | software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /webview/lib/Microsoft.Web.WebView2.WinForms.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/Microsoft.Web.WebView2.WinForms.dll -------------------------------------------------------------------------------- /webview/lib/WebBrowserInterop.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/WebBrowserInterop.x64.dll -------------------------------------------------------------------------------- /webview/lib/WebBrowserInterop.x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/WebBrowserInterop.x86.dll -------------------------------------------------------------------------------- /webview/lib/pywebview-android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/pywebview-android.jar -------------------------------------------------------------------------------- /webview/lib/runtimes/win-arm64/native/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/runtimes/win-arm64/native/WebView2Loader.dll -------------------------------------------------------------------------------- /webview/lib/runtimes/win-x64/native/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/runtimes/win-x64/native/WebView2Loader.dll -------------------------------------------------------------------------------- /webview/lib/runtimes/win-x86/native/WebView2Loader.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/lib/runtimes/win-x86/native/WebView2Loader.dll -------------------------------------------------------------------------------- /webview/localization.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | original_localization: Dict[str, str] = { 4 | 'global.quitConfirmation': 'Do you really want to quit?', 5 | 'global.ok': 'OK', 6 | 'global.quit': 'Quit', 7 | 'global.cancel': 'Cancel', 8 | 'global.saveFile': 'Save file', 9 | 'cocoa.menu.about': 'About', 10 | 'cocoa.menu.services': 'Services', 11 | 'cocoa.menu.view': 'View', 12 | 'cocoa.menu.edit': 'Edit', 13 | 'cocoa.menu.hide': 'Hide', 14 | 'cocoa.menu.hideOthers': 'Hide Others', 15 | 'cocoa.menu.showAll': 'Show All', 16 | 'cocoa.menu.quit': 'Quit', 17 | 'cocoa.menu.fullscreen': 'Enter Fullscreen', 18 | 'cocoa.menu.cut': 'Cut', 19 | 'cocoa.menu.copy': 'Copy', 20 | 'cocoa.menu.paste': 'Paste', 21 | 'cocoa.menu.selectAll': 'Select All', 22 | 'windows.fileFilter.allFiles': 'All files', 23 | 'windows.fileFilter.otherFiles': 'Other file types', 24 | 'linux.openFile': 'Open file', 25 | 'linux.openFiles': 'Open files', 26 | 'linux.openFolder': 'Open folder', 27 | } 28 | -------------------------------------------------------------------------------- /webview/menu.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from collections.abc import Callable 4 | from typing import Any, Union 5 | 6 | 7 | class Menu: 8 | def __init__(self, title: str, items: list[Union[Menu, MenuAction, MenuSeparator]] = []) -> None: 9 | """ 10 | Args: 11 | title: the menu or submenu title 12 | items: the contents of the menu (can consist of Menu, MenuAction, or MenuSeparator instances) 13 | """ 14 | self.title = title 15 | self.items = items 16 | 17 | 18 | class MenuAction: 19 | def __init__(self, title: str, function: Callable[[], Any]) -> None: 20 | self.title = title 21 | self.function = function 22 | # TODO: support platform-agnostic shortcut 23 | # self.shortcut = shortcut 24 | 25 | 26 | class MenuSeparator: 27 | pass 28 | -------------------------------------------------------------------------------- /webview/platforms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/platforms/__init__.py -------------------------------------------------------------------------------- /webview/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r0x0r/pywebview/eccca484120ad004907f541c5b40e9f5903d2071/webview/py.typed -------------------------------------------------------------------------------- /webview/screen.py: -------------------------------------------------------------------------------- 1 | class Screen: 2 | def __init__(self, x: int, y: int, width: int, height: int, frame: object = None) -> None: 3 | self.x = int(x) 4 | self.y = int(y) 5 | self.width = int(width) 6 | self.height = int(height) 7 | self.frame = frame 8 | 9 | def __str__(self) -> str: 10 | return repr(self) 11 | 12 | def __repr__(self) -> str: 13 | return f'{self.width}x{self.height} at {self.x},{self.y}' 14 | --------------------------------------------------------------------------------