Skip to content

Commit

Permalink
python: initial bindings (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
zserge authored Mar 7, 2018
0 parents commit 78e0850
Show file tree
Hide file tree
Showing 4 changed files with 379 additions and 0 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
graft webview
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# webview

Python extension that provides API for the [webview] library.

## Getting started

Install the bindings:

```bash
pip install webview
```

Try the following example:

```python
import webview

w = webview.WebView(width=320, height=240, title="Hello", url="https://google.com", resizable=True, debug=False)
w.run()
```

You may use most of the webview APIs:

```python
# Change window title
w.set_title("New title")
# Make window fullscreen
w.set_fullscreen(True)
# Change initial window background color
w.set_color(255, 0, 0)
# Inject some JS
w.eval("alert('hello')")
# Inject some CSS
w.inject_css('* {background-color: yellow; }')
# Show native OS dialog
file_path = w.dialog(0, 0, "open file", "")
# Post funciton to the UI thread
w.dispatch(some_func)
w.dispatch(lambda: some_func())
# Control run loop
while w.loop(True):
pass
```

Dispatch is currently only a stub and is implemented as direct function call.
Also, proper Python-to-JS object mapping is not implemented yet, but is highly

## Development

To build and install the library locally:

```bash
python setup.py sync install
```

To upload a new version:

```bash
python setup.py sync sdist
twine upload dist/webview-*.tar.gz
```

To build and install it locally:

```bash
python setup.py sync install
```

Please, ensure that all sources are formatted using `yapf`.


[webview]: https://github.com/zserge/webview
72 changes: 72 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/env python

import os
import commands
import shutil

from distutils.core import setup
from distutils.extension import Extension
from distutils.cmd import Command


class sync_command(Command):
"""A custom command to synchronize top-level webview.h implementation
with the one included in the Python bindings source package."""
description = 'synchronize webview.h'
user_options = []

def initialize_options(self):
pass

def finalize_options(self):
pass

def run(self):
shutil.copyfile('../../webview.h', 'webview/webview.h')
pass


if hasattr(os, 'uname'):
OSNAME = os.uname()[0]
else:
OSNAME = 'Windows'

if OSNAME == 'Linux':

def pkgconfig(flags):
return commands.getoutput(
"pkg-config %s gtk+-3.0 webkit2gtk-4.0" % flags)

define_macros = [("WEBVIEW_GTK", '1')]
extra_cflags = pkgconfig("--cflags").split()
extra_ldflags = pkgconfig("--libs").split()
elif OSNAME == 'Darwin':
define_macros = [('WEBVIEW_COCOA', '1')]
extra_cflags = ""
extra_ldflags = ['-framework', 'CoreAudio']
elif OSNAME == 'Windows':
define_macros = [('WEBVIEW_WINAPI', '1')]
extra_cflags = ""
extra_ldflags = ['-framework', 'CoreAudio']

webview = Extension(
'webview',
sources=['webview/webview.c'],
define_macros=define_macros,
extra_compile_args=extra_cflags,
extra_link_args=extra_ldflags,
)

setup(
name='webview',
version='0.1.4',
description='Python WebView bindings',
author='Serge Zaitsev',
author_email='[email protected]',
url='https://github.com/zserge/webview',
keywords=[],
license='MIT',
classifiers=[],
ext_modules=[webview],
cmdclass={'sync': sync_command},
)
234 changes: 234 additions & 0 deletions webview/webview.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#include <Python.h>

#include "structmember.h"

#define WEBVIEW_IMPLEMENTATION
#include "webview.h"

typedef struct { PyObject_HEAD struct webview w; } WebView;

static void WebView_dealloc(WebView *self) {
webview_exit(&self->w);
Py_TYPE(self)->tp_free((PyObject *)self);
}

static PyObject *WebView_new(PyTypeObject *type, PyObject *args,
PyObject *kwds) {
WebView *self = (WebView *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
memset(&self->w, 0, sizeof(self->w));
return (PyObject *)self;
}

static int WebView_init(WebView *self, PyObject *args, PyObject *kwds) {
const char *url = NULL;
const char *title = NULL;
static char *kwlist[] = {"width", "height", "resizable", "debug",
"url", "title", NULL};

if (!PyArg_ParseTupleAndKeywords(
args, kwds, "ii|iiss", kwlist, &self->w.width, &self->w.height,
&self->w.resizable, &self->w.debug, &url, &title)) {
return -1;
}

self->w.url = url;
self->w.title = title;

return webview_init(&self->w);
}

static PyObject *WebView_run(WebView *self) {
while (webview_loop(&self->w, 1) == 0)
;
Py_RETURN_NONE;
}

static PyObject *WebView_loop(WebView *self, PyObject *args, PyObject *kwds) {
int blocking = 1;
static char *kwlist[] = {"blocking", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &blocking)) {
return NULL;
}
if (webview_loop(&self->w, blocking) == 0) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}

static PyObject *WebView_terminate(WebView *self) {
webview_terminate(&self->w);
Py_RETURN_NONE;
}

static PyObject *WebView_set_title(WebView *self, PyObject *args) {
const char *title = "";
if (!PyArg_ParseTuple(args, "s", &title)) {
return NULL;
}
webview_set_title(&self->w, title);
Py_RETURN_NONE;
}

static PyObject *WebView_set_fullscreen(WebView *self, PyObject *args) {
int fullscreen = 0;
if (!PyArg_ParseTuple(args, "i", &fullscreen)) {
return NULL;
}
webview_set_fullscreen(&self->w, fullscreen);
Py_RETURN_NONE;
}

static PyObject *WebView_set_color(WebView *self, PyObject *args) {
int r, g, b, a = 255;
if (!PyArg_ParseTuple(args, "iii|i", &r, &g, &b, &a)) {
return NULL;
}
webview_set_color(&self->w, r, g, b, a);
Py_RETURN_NONE;
}

static PyObject *WebView_eval(WebView *self, PyObject *args) {
const char *js = NULL;
if (!PyArg_ParseTuple(args, "s", &js)) {
return NULL;
}
webview_eval(&self->w, js);
Py_RETURN_NONE;
}

static PyObject *WebView_inject_css(WebView *self, PyObject *args) {
const char *css = NULL;
if (!PyArg_ParseTuple(args, "s", &css)) {
return NULL;
}
webview_inject_css(&self->w, css);
Py_RETURN_NONE;
}

static PyObject *WebView_dialog(WebView *self, PyObject *args, PyObject *kwds) {
int type = 0;
int flags = 0;
const char *title = NULL;
const char *arg = NULL;
char result[PATH_MAX];
static char *kwlist[] = {"type", "flags", "title", "arg", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiss", kwlist, &type, &flags,
&title, &arg)) {
return NULL;
}
webview_dialog(&self->w, type, flags, title, arg, result, sizeof(result));
return PyUnicode_FromString(result);
}

static void webview_dispatch_cb(struct webview *w, void *arg) {
PyObject *cb = (PyObject *)arg;
/* TODO */
PyObject_CallObject(cb, NULL);
Py_XINCREF(cb);
}

static PyObject *WebView_dispatch(WebView *self, PyObject *args) {
PyObject *tmp;
if (!PyArg_ParseTuple(args, "O:set_callback", &tmp)) {
return NULL;
}
if (!PyCallable_Check(tmp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(tmp);
webview_dispatch(&self->w, webview_dispatch_cb, tmp);
Py_RETURN_NONE;
}

static PyObject *WebView_bind(WebView *self) {
/* TODO, very complex implementation */
Py_RETURN_NONE;
}

static PyMemberDef WebView_members[] = {
{NULL} /* Sentinel */
};
static PyMethodDef WebView_methods[] = {
{"run", (PyCFunction)WebView_run, METH_NOARGS, "..."},
{"loop", (PyCFunction)WebView_loop, METH_KEYWORDS, "..."},
{"terminate", (PyCFunction)WebView_terminate, METH_NOARGS, "..."},
{"dispatch", (PyCFunction)WebView_dispatch, METH_VARARGS, "..."},
{"eval", (PyCFunction)WebView_eval, METH_VARARGS, "..."},
{"inject_css", (PyCFunction)WebView_inject_css, METH_VARARGS, "..."},
{"dialog", (PyCFunction)WebView_dialog, METH_KEYWORDS, "..."},
{"set_title", (PyCFunction)WebView_set_title, METH_VARARGS, "..."},
{"set_fullscreen", (PyCFunction)WebView_set_fullscreen, METH_VARARGS,
"..."},
{"set_color", (PyCFunction)WebView_set_color, METH_VARARGS, "..."},
{"bind", (PyCFunction)WebView_bind, METH_VARARGS, "..."},
{NULL} /* Sentinel */
};

static PyTypeObject WebViewType = {
PyVarObject_HEAD_INIT(NULL, 0) "webview.WebView", /* tp_name */
sizeof(WebView), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)WebView_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"WebView objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
WebView_methods, /* tp_methods */
WebView_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)WebView_init, /* tp_init */
0, /* tp_alloc */
WebView_new, /* tp_new */
};

static PyMethodDef module_methods[] = {
{NULL} /* Sentinel */
};

#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC initwebview(void) {
PyObject *m;

if (PyType_Ready(&WebViewType) < 0) {
return;
}

m = Py_InitModule3("webview", module_methods,
"Example module that creates an extension type.");
if (m == NULL) {
return;
}

Py_INCREF(&WebViewType);
PyModule_AddObject(m, "WebView", (PyObject *)&WebViewType);
}

0 comments on commit 78e0850

Please sign in to comment.