Python Language Bindings
BlinkGTK supports GObject Introspection, making it accessible from Python through PyGObject.
# Fedora/RHEL
sudo dnf install python3-gobject gtk4 gobject-introspection-devel
# Ubuntu/Debian
sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-4.0
# Arch Linux
sudo pacman -S python-gobject gtk4# Build BlinkGTK (requires C++/Chromium build)
cd $BLINKGTK_ROOT
# See BUILD.md for build instructions
# Generate GIR/typelib files
./scripts/generate_gir.sh
# Set GI_TYPELIB_PATH when using from Python
export GI_TYPELIB_PATH=$BLINKGTK_ROOT/gir:$GI_TYPELIB_PATH# In the future, it will be auto-detected via pkg-config
sudo cp gir/BlinkGTK-0.1.typelib /usr/lib64/girepository-1.0/import gi
gi.require_version('Gtk', '4.0')
gi.require_version('BlinkGTK', '0.1')
from gi.repository import Gtk, BlinkGTK
print(f"GTK version: {Gtk.get_major_version()}.{Gtk.get_minor_version()}")
print(f"BlinkGTK loaded successfully!")#!/usr/bin/env python3
import gi
import sys
import os
# For local build typelib
typelib_dir = os.path.join(os.path.dirname(__file__), '../../gir')
if os.path.exists(typelib_dir):
gi.require_foreign('cairo')
from gi.repository import GLib
GLib.setenv('GI_TYPELIB_PATH', typelib_dir, True)
gi.require_version('Gtk', '4.0')
gi.require_version('BlinkGTK', '0.1')
from gi.repository import Gtk, BlinkGTK
class MinimalBrowser(Gtk.ApplicationWindow):
"""Minimal BlinkGTK browser"""
def __init__(self, app):
super().__init__(application=app, title="BlinkGTK Minimal Browser")
self.set_default_size(1024, 768)
# Create BlinkWebView
self.webview = BlinkGTK.WebView.new()
self.set_child(self.webview)
# Load URL
self.webview.load_uri("https://www.example.com")
def on_activate(app):
window = MinimalBrowser(app)
window.present()
def main():
app = Gtk.Application(application_id='org.example.MinimalBrowser')
app.connect('activate', on_activate)
return app.run(sys.argv)
if __name__ == '__main__':
sys.exit(main())typelib_dir = os.path.join(os.path.dirname(__file__), '../../gir')
if os.path.exists(typelib_dir):
gi.require_foreign('cairo')
from gi.repository import GLib
GLib.setenv('GI_TYPELIB_PATH', typelib_dir, True).typelib filesgi.require_version('Gtk', '4.0')
gi.require_version('BlinkGTK', '0.1')self.webview = BlinkGTK.WebView.new()BlinkGTK.WebView.new()BlinkWebView emits the following signals:
| Signal Name | Arguments | Description |
|---|---|---|
load-changed |
BlinkLoadEvent |
Page load state changes |
load-failed |
BlinkLoadEvent, uri,
error |
Load failure |
title-changed |
None | Page title changes |
class BrowserWindow(Gtk.ApplicationWindow):
def __init__(self, app):
super().__init__(application=app)
self.webview = BlinkGTK.WebView.new()
self.set_child(self.webview)
# Connect signals
self.webview.connect("load-changed", self.on_load_changed)
self.webview.connect("load-failed", self.on_load_failed)
self.webview.connect("title-changed", self.on_title_changed)
self.webview.load_uri("https://www.example.com")
def on_load_changed(self, webview, load_event):
"""Load state change handler"""
# BlinkLoadEvent: 0=STARTED, 1=REDIRECTED, 2=COMMITTED, 3=FINISHED
event_names = ["STARTED", "REDIRECTED", "COMMITTED", "FINISHED"]
print(f"Load state: {event_names[load_event]}")
if load_event == 3: # FINISHED
uri = self.webview.get_uri()
title = self.webview.get_title()
print(f"Page loaded: {title} ({uri})")
def on_load_failed(self, webview, load_event, failing_uri, error):
"""Load failure handler"""
print(f"Load failed: {failing_uri}")
print(f"Error: {error.message if error else 'Unknown'}")
return True # Indicate signal has been handled
def on_title_changed(self, webview):
"""Title change handler"""
title = self.webview.get_title()
self.set_title(f"{title} - Browser")# BlinkLoadEvent enumeration
BLINK_LOAD_STARTED = 0 # Load started
BLINK_LOAD_REDIRECTED = 1 # Redirect
BLINK_LOAD_COMMITTED = 2 # Navigation committed
BLINK_LOAD_FINISHED = 3 # Load finished
def on_load_changed(self, webview, load_event):
if load_event == 0:
print("Loading started")
elif load_event == 1:
print("Redirecting")
elif load_event == 2:
print("Navigation committed")
elif load_event == 3:
print("Loading finished")GObject property changes can be monitored via
notify::property-name signals.
class ProgressMonitor(Gtk.ApplicationWindow):
def __init__(self, app):
super().__init__(application=app)
self.webview = BlinkGTK.WebView.new()
self.set_child(self.webview)
# Connect property change notifications
self.webview.connect("notify::estimated-load-progress",
self.on_progress_changed)
self.webview.connect("notify::uri", self.on_uri_changed)
self.webview.connect("notify::title", self.on_title_changed)
def on_progress_changed(self, webview, pspec):
"""Load progress change"""
progress = self.webview.get_estimated_load_progress()
print(f"Progress: {progress:.0%}")
def on_uri_changed(self, webview, pspec):
"""URI change"""
uri = self.webview.get_uri()
print(f"URI changed: {uri}")
def on_title_changed(self, webview, pspec):
"""Title change (via property)"""
title = self.webview.get_title()
print(f"Title: {title}")| Property Name | Type | Description |
|---|---|---|
uri |
str |
Current URI |
title |
str |
Page title |
estimated-load-progress |
float |
Load progress (0.0~1.0) |
is-loading |
bool |
Whether loading |
class BrowserWithNavigation(Gtk.ApplicationWindow):
def __init__(self, app):
super().__init__(application=app)
# Toolbar
toolbar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
# Back button
back_button = Gtk.Button(label="◀")
back_button.connect("clicked", lambda btn: self.webview.go_back())
toolbar.append(back_button)
# Forward button
forward_button = Gtk.Button(label="▶")
forward_button.connect("clicked", lambda btn: self.webview.go_forward())
toolbar.append(forward_button)
# Reload button
reload_button = Gtk.Button(label="↻")
reload_button.connect("clicked", lambda btn: self.webview.reload())
toolbar.append(reload_button)
# URL Entry
self.url_entry = Gtk.Entry()
self.url_entry.set_hexpand(True)
self.url_entry.connect("activate", self.on_url_activate)
toolbar.append(self.url_entry)
# Main box
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
main_box.append(toolbar)
self.webview = BlinkGTK.WebView.new()
main_box.append(self.webview)
self.set_child(main_box)
def on_url_activate(self, entry):
"""URL Enter key handler"""
url = entry.get_text()
self.webview.load_uri(url)# Update back/forward button enabled/disabled state
def update_navigation_buttons(self):
can_go_back = self.webview.can_go_back()
can_go_forward = self.webview.can_go_forward()
self.back_button.set_sensitive(can_go_back)
self.forward_button.set_sensitive(can_go_forward)class ModernBrowser(Gtk.ApplicationWindow):
def __init__(self, app):
super().__init__(application=app, title="Modern Browser")
self.set_default_size(1200, 900)
# Header bar
header = Gtk.HeaderBar()
self.set_titlebar(header)
# Progress bar
self.progress_bar = Gtk.ProgressBar()
# WebView
self.webview = BlinkGTK.WebView.new()
self.webview.connect("notify::estimated-load-progress",
self.on_progress_changed)
# Layout
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
vbox.append(self.progress_bar)
vbox.append(self.webview)
self.set_child(vbox)
def on_progress_changed(self, webview, pspec):
progress = self.webview.get_estimated_load_progress()
self.progress_bar.set_fraction(progress)
self.progress_bar.set_visible(progress < 1.0)# Run JavaScript and receive the result as a JSON string
def on_result(result, user_data):
# result is a JSON-encoded string, or None on error
print("Result:", result)
self.webview.execute_javascript("document.title", on_result, None)Pass None as the callback if you do not need the
result.
self.webview.execute_javascript("document.body.style.zoom = '1.5'", None, None)# Custom protocol handlers (future feature)
# Example: handling myapp:// schemeCause: PyGObject is not installed
Solution:
# Fedora
sudo dnf install python3-gobject
# Ubuntu
sudo apt install python3-giCause: BlinkGTK typelib not found
Solution:
# Set GI_TYPELIB_PATH
import os
from gi.repository import GLib
typelib_dir = "/path/to/BlinkGTK/gir"
GLib.setenv('GI_TYPELIB_PATH', typelib_dir, True)Or install system-wide:
sudo cp gir/BlinkGTK-0.1.typelib /usr/lib64/girepository-1.0/Cause: Old BlinkGTK version or GIR not generated correctly
Solution:
# Regenerate GIR
cd $BLINKGTK_ROOT
./scripts/generate_gir.sh
# Clear Python cache
rm -rf __pycache__import logging
# GObject debug logs
logging.basicConfig(level=logging.DEBUG)
# GI_TYPELIB debug via environment variable
import os
os.environ['G_MESSAGES_DEBUG'] = 'all'Complete sample code is available at:
examples/blinkgtk_browser.c - Minimal browser in C
(official sample)How to run (C version):
cd $BLINKGTK_ROOT
gcc -o blinkgtk_browser examples/blinkgtk_browser.c $(pkg-config --cflags --libs blinkgtk-0.1)
./blinkgtk_browserUsing BlinkGTK from Python offers the following benefits:
Next Steps:
BlinkGTK Project | Copyright 2025 | BSD-3-Clause License