404
How did we get here?Take me home.
diff --git a/.travis.yml b/.travis.yml index 2c7f6abb..22ab428c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,23 +26,37 @@ matrix: dist: xenial language: python python: "3.5_with_system_site_packages" - env: + env: - PACKAGES="xvfb python3-pyqt5 python3-pyqt5.qtwebkit libqt5webkit5-dev python3-pep8 pyflakes python3-pytest python3-six" PYTHON="python3" + - PYTHON_VERSION="3" - PYWEBVIEW_GUI=qt - os: linux sudo: required dist: xenial language: python python: "2.7_with_system_site_packages" - env: + env: - PACKAGES="xvfb python-pyqt5 python-pyqt5.qtwebkit libqt5webkit5-dev pep8 pyflakes python-pytest" PYTHON="python" + - PYTHON_VERSION="2" - PYWEBVIEW_GUI=qt + # - os: linux + # sudo: required + # dist: xenial + # language: python + # python: "3.5_with_system_site_packages" + # env: + # - PACKAGES="xvfb python3-pep8 pyflakes python3-pytest python3-six" PYTHON="python3" + # - QT_PLUGIN_PATH="/home/travis/.local/lib/python3.6/site-packages/PyQt5/Qt/plugins/" + # - PIP_PACKAGES="pyqt5 pyqtwebengine" + # - PYTHON_VERSION="3" + # - PYWEBVIEW_GUI=qt install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$PYTHON_VERSION" == "3" ]]; then pip3 install pyobjc pytest six; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]] && [[ "$PYTHON_VERSION" == "2" ]]; then sudo pip2 install pyobjc pytest six; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -q; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y $(echo $PACKAGES); fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]] && [[ "$PYTHON_VERSION" == "3" ]] && [[ "$PIP_PACKAGES" == "pyqt5 pyqtwebengine" ]]; then pip3 install $(echo $PIP_PACKAGES); fi before_script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=:99.0; fi @@ -50,8 +64,8 @@ before_script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sleep 3; fi script: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then if [[ "$PYTHON_VERSION" == "3" ]]; then python3 -m pytest tests; else python2 -m pytest tests; fi; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then python -m pytest tests -s; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then if [[ "$PYTHON_VERSION" == "3" ]]; then python3 -m pytest tests -s; else python2 -m pytest tests -s; fi; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cd tests; python -m site; ./run.sh; fi after_script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then killall Xvfb; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index e4431334..4004584e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ # Changelog +## 3.0 + +_Released xx/xx/2019_ + +- `New` [All] New API. The API is not compatible with older versions of _pywebview_. See https://pywebview.flowrl.com for usage details. #272 +- `New` [All] Built-in HTTP server. #260 +- `New` [All] Autogenerated CSRF token exposed as `window.pywebview.token`. #316 +- `New` [All] `get_elements` function to retrieve DOM nodes. #292 +- `New` [All] New events system that lets you to subscribe to events. `loaded` and `shown` events are implemented. #201 +- `New` [Windows] EdgeHTML support. Thanks @heavenvolkoff. #243 +- `Fix` [Windows] Fullscreen mode. #338 +- `Fix` [GTK] Better Javascript support for recent version of WebKit2 +- `Fix` [CEF] Support for PyInstaller in onefile mode + + ## 2.4 _Released 17/02/2019_ diff --git a/MANIFEST.in b/MANIFEST.in index c8580f42..f1533ff7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,4 @@ include webview/lib/WebBrowserInterop.x64.dll -include webview/lib/WebBrowserInterop.x86.dll \ No newline at end of file +include webview/lib/WebBrowserInterop.x86.dll +include webview/lib/Microsoft.Toolkit.Forms.UI.Controls.WebView.dll +include webview/lib/Microsoft.Toolkit.Forms.UI.Controls.WebView.LICENSE.md diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index f8c847a4..39f49caf 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -1,6 +1,7 @@ module.exports = { title: 'pywebview', description: 'Build GUI for your Python program with JavaScript, HTML, and CSS', + ga: 'UA-12494025-18', themeConfig: { repo: 'r0x0r/pywebview', docsDir: 'docs', @@ -27,8 +28,9 @@ module.exports = { { title: 'Development', collapsable: false, - children: [ + children: [ '/guide/api', + '/guide/architecture', '/guide/debugging', '/guide/freezing', '/guide/security', @@ -38,11 +40,16 @@ module.exports = { } ], '/examples/': [ + 'cef', 'change_url', 'css_load', + 'close_confirm', 'debug', 'destroy_window', + 'events', + 'frameless', 'fullscreen', + 'get_elements', 'get_current_url', 'html_load', 'js_evaluate', @@ -53,7 +60,6 @@ module.exports = { 'multiple_windows', 'open_file_dialog', 'open_url', - 'quit_confirm', 'save_file_dialog', 'toggle_fullscreen', 'window_title_change' diff --git a/docs/.vuepress/public/2.4/404.html b/docs/.vuepress/public/2.4/404.html new file mode 100644 index 00000000..5edfa43c --- /dev/null +++ b/docs/.vuepress/public/2.4/404.html @@ -0,0 +1,17 @@ + + +
+ + +Before you get busy coding a new feature, create an issue and discuss the details in the issue tracker.
This guide assumes you have a GitHub account, as well as Python 3, virtualenv and Git installed. The guide is written for Bash, for Windows you can use for example Bash bundled with Git.
git clone https://github.com/<username>/pywebview
+cd pywebview
+
virtualenv -p python3 venv
+source venv/bin/activate
+pip intall -e .
+pip install pytest
+
python examples/simple_browser.py
+
git checkout -b new-branch master
+
Make your changes
Run tests
pytest tests
+
git add .
+git commit -m "Your commit message goes here"
+git push -u origin new-branch
+
pywebview uses pytest for testing.
To run all the tests in the project root directory
pytest tests
+
To run a specific test
pytest tests/test_simple_browser.py
+
Tests cover only trivial mistakes, syntax errors, exceptions and such. In other words there is no functional testing. Each test verifies that a pywebview window can be opened and exited without errors when run under different scenarios. Sometimes test fail / stuck randomly. The cause of the issue is not known, any help on resolving random fails is greatly appreciated.
+ Bug reporting + + → +
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.
+ ← + + Donating +
Recurring pledges come perks, like getting email support or featuring your name or logo in the project repository
+ ← + + Bug reporting + + Documentation + + → +
Thanks for considering contributing to pywebview.
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.
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.
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.
For other ways to donate refer to the donation page.
To use Chrome Embedded Framework on Windows.
import webview
+
+
+if __name__ == '__main__':
+ webview.config.gui = 'cef'
+ webview.create_window('CEF Example', 'https://pywebview.flowrl.com/hello')
+
Change URL ten seconds after the first URL is loaded.
import webview
+import threading
+import time
+
+
+def change_url():
+ # wait a few seconds before changing url:
+ time.sleep(10)
+
+ # change url:
+ webview.load_url("https://pywebview.flowrl.com/hello")
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=change_url)
+ t.start()
+
+ webview.create_window("URL Change Example", "http://www.google.com")
+
+ CSS load + + → +
Change window background color by loading CSS
+import webview
+import threading
+
+
+def load_css():
+ webview.load_css('body { background: red !important; }')
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=load_css)
+ t.start()
+
+ webview.create_window('Load CSS Example', 'https://pywebview.flowrl.com/hello')
+
+
+ ← + + Change URL + + Debugging + + → +
To open up debugging console, right click on an element and select Inspect.
import webview
+
+if __name__ == '__main__':
+ webview.create_window('Debug window', 'https://pywebview.flowrl.com/hello', debug=True)
+
+ ← + + CSS load + + Destroy window + + → +
Programmatically destroy created window after five seconds.
import webview
+import threading
+import time
+
+def destroy():
+ # show the window for a few seconds before destroying it:
+ time.sleep(5)
+
+ print("Destroying window..")
+ webview.destroy_window()
+ print("Destroyed!")
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=destroy)
+ t.start()
+ webview.create_window("Destroy Window Example", "https://pywebview.flowrl.com/hello")
+ print("Window is destroyed")
+
+ ← + + Debugging + + Fullscreen window + + → +
Create a fullscreen window.
import webview
+
+
+if __name__ == '__main__':
+ # Create a non-resizable webview window with 800x600 dimensions
+ webview.create_window("Full-screen browser",
+ "https://pywebview.flowrl.com/hello",
+ fullscreen=True)
+
+ ← + + Destroy window + + Get current URL + + → +
Print current URL after page is loaded.
import webview
+import threading
+
+
+def get_current_url():
+ print(webview.get_current_url())
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=get_current_url)
+ t.start()
+
+ webview.create_window("Get current URL", "https://pywebview.flowrl.com/hello")
+
+ ← + + Fullscreen window + + HTML load + + → +
Display content by loading HTML on the fly.
import webview
+import threading
+
+def load_html():
+ webview.load_html('<h1>This is dynamically loaded HTML</h1>')
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=load_html)
+ t.start()
+
+ webview.create_window('Load HTML Example')
+
+ ← + + Get current URL + + Javascript evaluation + + → +
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.
Create an application without a HTTP server. The application uses Javascript API object to communicate between Python and Javascript.
import webview
+import threading
+import time
+import sys
+import random
+
+
+html = """
+<!DOCTYPE html>
+<html>
+<head lang="en">
+<meta charset="UTF-8">
+
+<style>
+ #response-container {
+ display: none;
+ padding: 3rem;
+ margin: 3rem 5rem;
+ font-size: 120%;
+ border: 5px dashed #ccc;
+ }
+
+ label {
+ margin-left: 0.3rem;
+ margin-right: 0.3rem;
+ }
+
+ button {
+ font-size: 100%;
+ padding: 0.5rem;
+ margin: 0.3rem;
+ text-transform: uppercase;
+ }
+
+</style>
+</head>
+<body>
+
+
+<h1>JS API Example</h1>
+<button onClick="initialize()">Hello Python</button><br/>
+<button id="heavy-stuff-btn" onClick="doHeavyStuff()">Perform a heavy operation</button><br/>
+<button onClick="getRandomNumber()">Get a random number</button><br/>
+<label for="name_input">Say hello to:</label><input id="name_input" placeholder="put a name here">
+<button onClick="greet()">Greet</button><br/>
+
+<div id="response-container"></div>
+<script>
+function showResponse(response) {
+ var container = document.getElementById('response-container')
+
+ container.innerText = response.message
+ container.style.display = 'block'
+}
+
+function initialize() {
+ pywebview.api.init().then(showResponse)
+}
+
+function doHeavyStuff() {
+ var btn = document.getElementById('heavy-stuff-btn')
+
+ pywebview.api.doHeavyStuff().then(function(response) {
+ showResponse(response)
+ btn.onclick = doHeavyStuff
+ btn.innerText = 'Perform a heavy operation'
+ })
+
+ showResponse({message: 'Working...'})
+ btn.innerText = 'Cancel the heavy operation'
+ btn.onclick = cancelHeavyStuff
+}
+
+function cancelHeavyStuff() {
+ pywebview.api.cancelHeavyStuff()
+}
+
+function getRandomNumber() {
+ pywebview.api.getRandomNumber().then(showResponse)
+}
+
+function greet() {
+ var name_input = document.getElementById('name_input').value;
+ pywebview.api.sayHelloTo(name_input).then(showResponse)
+}
+
+</script>
+</body>
+</html>
+"""
+
+
+class Api:
+ def __init__(self):
+ self.cancel_heavy_stuff_flag = False
+
+ def init(self, params):
+ response = {
+ 'message': 'Hello from Python {0}'.format(sys.version)
+ }
+ return response
+
+ def getRandomNumber(self, params):
+ response = {
+ 'message': 'Here is a random number courtesy of randint: {0}'.format(random.randint(0, 100000000))
+ }
+ return response
+
+ def doHeavyStuff(self, params):
+ time.sleep(0.1) # sleep to prevent from the ui thread from freezing for a moment
+ now = time.time()
+ self.cancel_heavy_stuff_flag = False
+ for i in range(0, 1000000):
+ _ = i * random.randint(0, 1000)
+ if self.cancel_heavy_stuff_flag:
+ response = {'message': 'Operation cancelled'}
+ break
+ else:
+ then = time.time()
+ response = {
+ 'message': 'Operation took {0:.1f} seconds on the thread {1}'.format((then - now), threading.current_thread())
+ }
+ return response
+
+ def cancelHeavyStuff(self, params):
+ time.sleep(0.1)
+ self.cancel_heavy_stuff_flag = True
+
+ def sayHelloTo(self, params):
+ response = {
+ 'message': 'Hello {0}!'.format(params)
+ }
+ return response
+
+
+def create_app():
+ webview.load_html(html)
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=create_app)
+ t.start()
+
+ api = Api()
+ webview.create_window('API example', js_api=api)
+
+ ← + + Javascript evaluation + + Loading animation + + → +
Evaluate Javascript from Python code.
import threading
+
+
+def evaluate_js():
+ result = webview.evaluate_js(
+ r"""
+ var h1 = document.createElement('h1')
+ var text = document.createTextNode('Hello pywebview')
+ h1.appendChild(text)
+ document.body.appendChild(h1)
+
+ document.body.style.backgroundColor = '#212121'
+ document.body.style.color = '#f2f2f2'
+
+ // Return user agent
+ 'User agent:\n' + navigator.userAgent;
+ """
+ )
+
+ print(result)
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=evaluate_js)
+ t.start()
+
+ webview.create_window('Run custom JavaScript')
+
+ ← + + HTML load + + Javascript API + + → +
Create a loading animation that is displayed before application is loaded.
import webview
+import threading
+
+
+def load_html():
+ webview.load_html("""
+ <style>
+ body {
+ background-color: #333;
+ color: white;
+ font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
+ }
+
+ .main-container {
+ width: 100%;
+ height: 90vh;
+ display: flex;
+ display: -webkit-flex;
+ align-items: center;
+ -webkit-align-items: center;
+ justify-content: center;
+ -webkit-justify-content: center;
+ overflow: hidden;
+ }
+
+ .loading-container {
+ }
+
+ .loader {
+ font-size: 10px;
+ margin: 50px auto;
+ text-indent: -9999em;
+ width: 3rem;
+ height: 3rem;
+ border-radius: 50%;
+ background: #ffffff;
+ background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ position: relative;
+ -webkit-animation: load3 1.4s infinite linear;
+ animation: load3 1.4s infinite linear;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ }
+ .loader:before {
+ width: 50%;
+ height: 50%;
+ background: #ffffff;
+ border-radius: 100% 0 0 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ content: '';
+ }
+ .loader:after {
+ background: #333;
+ width: 75%;
+ height: 75%;
+ border-radius: 50%;
+ content: '';
+ margin: auto;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ }
+ @-webkit-keyframes load3 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+ @keyframes load3 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+
+ .loaded-container {
+ display: none;
+ }
+
+
+ </style>
+ <body>
+ <div class="main-container">
+ <div id="loader" class="loading-container">
+ <div class="loader">Loading...</div>
+ </div>
+
+ <div id="main" class="loaded-container">
+ <h1>Content is loaded!</h1>
+ </div>
+ </div>
+
+ <script>
+ setTimeout(function() {
+ document.getElementById('loader').style.display = 'none'
+ document.getElementById('main').style.display = 'block'
+ }, 5000)
+ </script>
+ </body>
+
+ """)
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=load_html)
+ t.start()
+
+ webview.create_window('Loading Animation', background_color='#333333')
+
+ ← + + Javascript API + + Localization + + → +
Localize system text string used by pywebview. For a full list of used string, refer to the webview/localization.py
file.
# -*- coding: utf-8 -*-
+
+import webview
+
+
+if __name__ == "__main__":
+ strings = {
+ "global.saveFile": u"Сохранить файл",
+ "cocoa.menu.about": u"О программе",
+ "cocoa.menu.services": u"Cлужбы",
+ "cocoa.menu.view": u"Вид",
+ "cocoa.menu.hide": u"Скрыть",
+ "cocoa.menu.hideOthers": u"Скрыть остальные",
+ "cocoa.menu.showAll": u"Показать все",
+ "cocoa.menu.quit": u"Завершить",
+ "cocoa.menu.fullscreen": u"Перейти ",
+ "windows.fileFilter.allFiles": u"Все файлы",
+ "windows.fileFilter.otherFiles": u"Остальлные файльы",
+ "linux.openFile": u"Открыть файл",
+ "linux.openFiles": u"Открыть файлы",
+ "linux.openFolder": u"Открыть папку",
+ }
+
+ webview.create_window("Localization Example",
+ "https://pywebview.flowrl.com/hello",
+ strings=strings)
+
+ ← + + Loading animation + + Minimum window size + + → +
Set minimum window dimensions.
import webview
+
+if __name__ == '__main__':
+ # Create a resizable webview window with minimum size constraints
+ webview.create_window('Minimum window size',
+ 'https://pywebview.flowrl.com/hello',
+ width=800, height=600,
+ resizable=True,
+ min_size=(400, 200))
+
+ ← + + Localization + + Multi-window + + → +
Create multiple windows.
import webview
+import threading
+
+
+def create_new_window():
+ # Create new window and store its uid
+ child_window = webview.create_window('Window #2', width=800, height=400)
+
+ # Load content into both windows
+ webview.load_html('<h1>Master Window</h1>')
+ webview.load_html('<h1>Child Window</h1>', uid=child_window)
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=create_new_window)
+ t.start()
+
+ # Master window
+ webview.create_window('Window #1', width=800, height=600)
+
+ ← + + Minimum window size + + Open file dialog + + → +
Create an open file dialog after page content is loaded.
import webview
+import threading
+
+
+def open_file_dialog():
+ file_types = ('Image Files (*.bmp;*.jpg;*.gif)', 'All files (*.*)')
+
+ print(webview.create_file_dialog(webview.OPEN_DIALOG,
+ allow_multiple=True,
+ file_types=file_types))
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=open_file_dialog)
+ t.start()
+
+ webview.create_window("Open file dialog example", "https://pywebview.flowrl.com/hello")
+
+ ← + + Multi-window + + Open URL + + → +
import webview
+
+if __name__ == '__main__':
+ webview.create_window('Simple browser', 'https://pywebview.flowrl.com/hello')
+
+ ← + + Open file dialog + + Quit confirmation dialog + + → +
import webview
+
+if __name__ == '__main__':
+ # Create a standard webview window
+ webview.create_window('Confirm Quit Example',
+ 'https://pywebview.flowrl.com/hello',
+ confirm_quit=True)
+
+ ← + + Open URL + + Save file dialog + + → +
Create a save file dialog after page content is loaded.
import webview
+import threading
+
+
+def save_file_dialog():
+ import time
+ time.sleep(5)
+ print(webview.create_file_dialog(webview.SAVE_DIALOG,
+ directory="/",
+ save_filename='test.file'))
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=save_file_dialog)
+ t.start()
+
+ webview.create_window("Save file dialog", "https://pywebview.flowrl.com/hello")
+
+ ← + + Quit confirmation dialog + + Toggle full-screen + + → +
Switch application window to a full-screen mode after five seconds.
import webview
+import threading
+import time
+
+
+def toggle_fullscreen():
+ # wait a few seconds before toggle fullscreen:
+ time.sleep(5)
+
+ webview.toggle_fullscreen()
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=toggle_fullscreen)
+ t.start()
+
+ webview.create_window("Full-screen window", "https://pywebview.flowrl.com/hello")
+
+ ← + + Save file dialog + + Window title change + + → +
Change window title every three seconds.
import webview
+import threading
+import time
+
+def change_title():
+ """changes title every 3 seconds"""
+ for i in range(1, 100):
+ time.sleep(3)
+ webview.set_title("New Title #{}".format(i))
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=change_title)
+ t.start()
+
+ webview.create_window('Change window title', 'https://pywebview.flowrl.com/hello')
+
+ ← + + Toggle full-screen +
create_window(title, url='', js_api=None, width=800, height=600, resizable=True,\
+ fullscreen=False, min_size=(200, 100), strings={}, confirm_quit=False, \
+ background_color='#FFF', debug=False, text_select=False)
+
Create a new pywebview window. Calling this function for the first time will start the application and block program execution. You have to execute your program logic in a separate thread. Subsequent calls to create_window
will return a unique window uid
, which can be used to refer to the specific window in the API functions. Single-window applications need not bother about the uid
and can simply omit it from function calls.
title
- Window titleurl
- URL to load. If the URL does not have a protocol prefix, it is resolved as a path relative to the application entry point.js_api
- Expose a js_api
class object to the DOM of the current pywebview
window. Callable functions of js_api
can be executed using Javascript page via window.pywebview.api
object. Custom functions accept a single parameter, either a primitive type or an object. Object types are converted between Javascript and Python. Functions are executed in separate
+threads and are not thread-safe. window.pywebview
is not guaranteed to be available on window.onload
and its access must be deferred.width
- Window width. Default is 800px.height
- Window height. Default is 600px.resizable
- Whether window can be resized. Default is Truefullscreen
- Whether to start in fullscreen mode. Default is Falsemin_size
- a (width, height) tuple that specifies a minimum window size. Default is 200x100strings
- a dictionary with localized strings. Default strings and their keys are defined in localization.pyconfirm_quit
- Whether to display a quit confirmation dialog. Default is Falsebackground_color
- Background color of the window displayed before WebView is loaded. Specified as a hex color. Default is white.debug
- Enabled debug mode. See debugging for detailstext_select
- Enables document text selection. Default is False. To control text selection on per element basis, use user-select CSS property.The functions below must be invoked after a pywebview window is created, otherwise an exception is thrown.
+In all cases, uid
is the uid of the target window returned by create_window()
; if no window exists with the given uid
, an exception is thrown. Default is 'master'
, which is the special uid given to the first window.
create_file_dialog(dialog_type=OPEN_DIALOG, directory='', allow_multiple=False, save_filename='', file_types=())`
+
Create an open file (webview.OPEN_DIALOG
), open folder (webview.FOLDER_DIALOG
) or save file (webview.SAVE_DIALOG
) dialog.
Return a tuple of selected files, None if cancelled.
allow_multiple=True
enables multiple selection.directory
Initial directory.save_filename
Default filename for save file dialog.file_types
A tuple of supported file type strings in the open file dialog. A file type string must follow this format "Description (*.ext1;*.ext2...)"
.If the argument is not specified, then the "All files (*.*)"
mask is used by default. The 'All files' string can be changed in the localization dictionary.
destroy_window(uid='master')
+
Destroy the specified WebView window.
evaluate_js(script, uid='master')
+
Execute Javascript code in the specified window. The last evaluated expression is returned. Javascript types are converted to Python types, eg. JS objects to dicts, arrays to lists, undefined to None. Note that due implementation limitations the string 'null' will be evaluated to None. +You must escape \n and \r among other escape sequences if they present in Javascript code. Otherwise they get parsed by Python. r'strings' is a recommended way to load Javascript.
get_current_url(uid='master')
+
Return the current URL in the specified window. None if no url is loaded.
load_css(css, uid='master')
+
Load CSS as string into the specified window.
load_html(content, base_uri=base_uri(), uid='master')
+
Load HTML code into the specified window. Base URL for resolving relative URLs is set to the directory the program is launched from. Note that you cannot use hashbang anchors when HTML is loaded this way.
load_url(url, uid='master')
+
Load a new URL into the specified pywebview window.
set_title(title, uid='master')
+
Change the title of the window
togle_fullscree(uid='master')
+
Toggle fullscreen mode of a window on the active monitor.
window_exists(uid='master')
+
Return True if a pywebview window with the given uid is up and running, False otherwise.
config.gui = 'qt' | 'gtk'
+
Force GUI library to either GTK or QT. The same setting can be controlled via PYWEBVIEW_GUI
environmental variable
+ ← + + Usage + + Application architecture + + → +
There are two ways to build your application using pywebview:
Running a local web server is a traditional way to build your local application. This way everything is served from a local web server and pywebview points to the URL provided by the server. In this model the server is responsible for both serving static contents and handling API calls. +When building an application this way, you should protect your API calls against CSRF attacks. See security for more information.
Pros:
Cons
Another way to build an application is to use pywebview's provided JS API and serve static files locally. When built this way, the application does not rely on any external libraries.
Pros:
Cons
+ ← + + API + + Debugging + + → +
To debug Javascript, set the debug
parameter to True
import webview
+
+webview.create_window('https://pywebview.flowrl.com', debug=True)
+
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 debug
flag enables Javascript error reporting and right-click context menu on Windows.
You can debug Python code normally using your favorite debugger.
+ ← + + Application architecture + + Freezing + + → +
To freeze your application use py2app on macOS ans pyinstaller on Windows. For a reference setup.py for py2app, look here
For Pyinstaller you need to include either WebBrowserInterop.x86.dll or WebBrowserInterop.x64.dll depending on whether you build against 32-bit or 64-bit Python. The DLLs bundled with pywebview and are located in the site-packages/webview/lib
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.
TODO: add Linux freezing guide
+ ← + + Debugging + + Security + + → +
pywebview is a lightweight cross-platform wrapper around a webview component 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. You can use pywebview either with a lightweight web framework like Flask or Bottle or on its own with a two way bridge between Python and DOM.
pywebview 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. pywebview is compatible with both Python 2 and 3.
pywebview is a BSD licensed open source project. It is an independent project with no corporate backing. If you find pywebview useful, consider supporting it. More donation options are outlined on the Donating page.
pywebview is created by Roman Sirokov. Maintained by Roman and Shiva Prasad.
pip install pywebview
+
This will install pywebview with default dependencies. To install pywebview with PyQt5 (available on Linux and macOS) use
pip install pywebview[qt]
+
To install pywebview with CEF (available on Windows) use
pip install pywebview[cef]
+
pythonnet
requires to have .NET 4.0 installed
pip install cefpython3
+
PyObjC
comes presintalled with the Python bundled in macOS. For a stand-alone Python installation you have to install it separately.
+You can also use QT5 in macOS
You have to install Linux dependencies manually. You can choose between GTK and QT.
PyGObject is used with GTK. To install dependencies on Ubuntu for both Python 3 and 2
sudo apt install python-gi python-gi-cairo python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.0
+
For other distributions, consult the PyGObject documentation
PyQt5 is used with QT. pywebview
supports both QtWebChannel (newer and preferred) and QtWebKit implementations. Use QtWebChannel, unless it is not available on your system.
To install QtWebChannel on Debian-based systems.
sudo apt install python3-pyqt5 python3-pyqt5.qtwebengine python3-pyqt5.qtwebchannel python-pyqt5 python-pyqt5.qtwebengine python-pyqt5.qtwebchannel libqt5webkit5-dev
+
To install QtWebKit.
sudo apt install python3-pyqt5 python3-pyqt5.qtwebkit python-pyqt5 python-pyqt5.qtwebkit libqt5webkit5-dev
+
+ Usage + + → +
The following renderers are used on each platform
Platform | Renderer | Provider | Browser compatibility |
---|---|---|---|
GTK | WebKit | WebKit2 | |
macOS | WebKit | WebKit.WKWebView (bundled with OS) | |
QT | WebKit | QtWebKit | |
Windows | Trident | MSHTML via .NET / System.Windows.Forms.WebBrowser | IE11 (Windows 10/8/7) |
Windows | CEF | CEF Python | Chrome 66 |
To change a default renderer set either PYWEBVIEW_GUI
environment variable or webview.gui
value in the code. Available values are cef
(Windows), qt
(Linux, macOS) and gtk
(Linux).
For example to use CEF on Windows
PYWEBVIEW_GUI=cef
+
or
import webview
+webview.config.gui = 'cef'
+
To force QT on Linux systems
PYWEBVIEW_GUI=qt
+
or
import webview
+webview.config.gui = 'qt'
+
+ ← + + Virtual environment +
When using a local web server, you must protect your API from unauthorized access. CSRF attacks can be a major problem if API is not protected in an adequate matter. Refer to this document for API securing approaches. A library like flask-seasurf alongside Flask can be used too.
+ ← + + Freezing + + Virtual environment + + → +
The bare minimum to get pywebview started is
import webview
+webview.create_window("It works, Jim!", "https://pywebview.flowrl.com")
+
The second argument url
can point to either to a remote a local url, a local path or be left empty. If empty, you can load HTML using a load_html
function. E.g.
def load_html():
+ webview.load_html('<h1>This is dynamically loaded HTML</h1>')
+
+
+if __name__ == '__main__':
+ t = threading.Thread(target=load_html)
+ t.start()
+
+ webview.create_window('Load HTML example')
+
To change a web renderer, set webview.gui
to the desired value (e.g cef
). See Renderer for details.
Note that webview.create_window
blocks the main thread execution, so other code has to be run on a separate thread.
+ ← + + Installation + + API + + → +
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 here. For example, Python 3 installed via Homebrew](https://brew.sh) does not
brew install python3
+virtualenv pywebview_env -p python3
+
+ ← + + Security + + Web engine + + → +
+ You are running a pywebview application. Your user-agent is:
+