fit-webview-bridge 0.1.1a5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fit-webview-bridge might be problematic. Click here for more details.
- fit_webview_bridge-0.1.1a5/.github/workflows/wheels-macos.yml +82 -0
- fit_webview_bridge-0.1.1a5/.gitignore +184 -0
- fit_webview_bridge-0.1.1a5/.vscode/settings.json +46 -0
- fit_webview_bridge-0.1.1a5/CMakeLists.txt +29 -0
- fit_webview_bridge-0.1.1a5/PKG-INFO +151 -0
- fit_webview_bridge-0.1.1a5/README.it.md +139 -0
- fit_webview_bridge-0.1.1a5/README.md +138 -0
- fit_webview_bridge-0.1.1a5/bindings/pyside6/macos/CMakeLists.txt +249 -0
- fit_webview_bridge-0.1.1a5/bindings/pyside6/macos/typesystem_wkwebview.xml +12 -0
- fit_webview_bridge-0.1.1a5/examples/macos/wkwebview_demo.py +37 -0
- fit_webview_bridge-0.1.1a5/fit_webview_bridge/__init__.py +1 -0
- fit_webview_bridge-0.1.1a5/pyproject.toml +33 -0
- fit_webview_bridge-0.1.1a5/src/macos/CMakeLists.txt +49 -0
- fit_webview_bridge-0.1.1a5/src/macos/WKWebViewWidget.h +34 -0
- fit_webview_bridge-0.1.1a5/src/macos/WKWebViewWidget.mm +96 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: wheels-macos
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build-macos:
|
|
11
|
+
runs-on: macos-14
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Extract version from tag
|
|
16
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
17
|
+
id: v
|
|
18
|
+
run: |
|
|
19
|
+
REF="${GITHUB_REF##*/}"
|
|
20
|
+
echo "version=${REF#v}" >> $GITHUB_OUTPUT
|
|
21
|
+
|
|
22
|
+
- name: Sync pyproject version to tag (workspace only)
|
|
23
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
24
|
+
env:
|
|
25
|
+
VERSION: ${{ steps.v.outputs.version }}
|
|
26
|
+
run: |
|
|
27
|
+
python - <<'PY'
|
|
28
|
+
import os, re, pathlib
|
|
29
|
+
ver = os.environ["VERSION"]
|
|
30
|
+
p = pathlib.Path("pyproject.toml")
|
|
31
|
+
s = p.read_text(encoding="utf-8")
|
|
32
|
+
s = re.sub(r'(?m)^version\s*=\s*".*"', f'version = "{ver}"', s)
|
|
33
|
+
p.write_text(s, encoding="utf-8")
|
|
34
|
+
print("pyproject.toml version ->", ver)
|
|
35
|
+
PY
|
|
36
|
+
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: '3.13'
|
|
40
|
+
|
|
41
|
+
- name: Install build tools
|
|
42
|
+
run: |
|
|
43
|
+
python -m pip install -U cibuildwheel aqtinstall build
|
|
44
|
+
|
|
45
|
+
- name: Install Qt SDK via aqt
|
|
46
|
+
run: |
|
|
47
|
+
aqt install-qt mac desktop 6.9.0 clang_64 -O "$RUNNER_TEMP/Qt"
|
|
48
|
+
echo "QT6_DIR=$RUNNER_TEMP/Qt/6.9.0/macos/lib/cmake/Qt6" >> $GITHUB_ENV
|
|
49
|
+
|
|
50
|
+
- name: Build wheels (macOS arm64, cp311/cp312/cp313)
|
|
51
|
+
env:
|
|
52
|
+
SKBUILD_CMAKE_ARGS: "-DQt6_DIR=${{ env.QT6_DIR }}"
|
|
53
|
+
CIBW_REPAIR_WHEEL_COMMAND: ""
|
|
54
|
+
FITWVB_VENDORIZE: "1"
|
|
55
|
+
CIBW_BUILD_VERBOSITY: "1"
|
|
56
|
+
SKBUILD_CONFIGURE_LOG: "1"
|
|
57
|
+
SKBUILD_BUILD_LOG: "1"
|
|
58
|
+
run: python -m cibuildwheel --output-dir dist
|
|
59
|
+
|
|
60
|
+
- name: Build sdist
|
|
61
|
+
run: python -m build --sdist
|
|
62
|
+
|
|
63
|
+
- name: Upload dist artifact
|
|
64
|
+
uses: actions/upload-artifact@v4
|
|
65
|
+
with:
|
|
66
|
+
name: dist
|
|
67
|
+
path: dist/*
|
|
68
|
+
|
|
69
|
+
publish:
|
|
70
|
+
needs: build-macos
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/download-artifact@v4
|
|
75
|
+
with:
|
|
76
|
+
name: dist
|
|
77
|
+
path: dist
|
|
78
|
+
|
|
79
|
+
- name: Publish to PyPI
|
|
80
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
81
|
+
with:
|
|
82
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python
|
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
|
3
|
+
|
|
4
|
+
### Python ###
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
|
|
10
|
+
# C extensions
|
|
11
|
+
*.so
|
|
12
|
+
|
|
13
|
+
# Distribution / packaging
|
|
14
|
+
.Python
|
|
15
|
+
build/
|
|
16
|
+
develop-eggs/
|
|
17
|
+
dist/
|
|
18
|
+
downloads/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# PyInstaller
|
|
34
|
+
# Usually these files are written by a python script from a template
|
|
35
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
36
|
+
*.manifest
|
|
37
|
+
#*.spec
|
|
38
|
+
|
|
39
|
+
# Installer logs
|
|
40
|
+
pip-log.txt
|
|
41
|
+
pip-delete-this-directory.txt
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
cover/
|
|
57
|
+
|
|
58
|
+
# Translations
|
|
59
|
+
*.mo
|
|
60
|
+
*.pot
|
|
61
|
+
|
|
62
|
+
# Django stuff:
|
|
63
|
+
*.log
|
|
64
|
+
local_settings.py
|
|
65
|
+
db.sqlite3
|
|
66
|
+
db.sqlite3-journal
|
|
67
|
+
|
|
68
|
+
# Flask stuff:
|
|
69
|
+
instance/
|
|
70
|
+
.webassets-cache
|
|
71
|
+
|
|
72
|
+
# Scrapy stuff:
|
|
73
|
+
.scrapy
|
|
74
|
+
|
|
75
|
+
# Sphinx documentation
|
|
76
|
+
docs/_build/
|
|
77
|
+
|
|
78
|
+
# PyBuilder
|
|
79
|
+
.pybuilder/
|
|
80
|
+
target/
|
|
81
|
+
|
|
82
|
+
# Jupyter Notebook
|
|
83
|
+
.ipynb_checkpoints
|
|
84
|
+
|
|
85
|
+
# IPython
|
|
86
|
+
profile_default/
|
|
87
|
+
ipython_config.py
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
91
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
92
|
+
# .python-version
|
|
93
|
+
|
|
94
|
+
# pipenv
|
|
95
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
96
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
97
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
98
|
+
# install all needed dependencies.
|
|
99
|
+
#Pipfile.lock
|
|
100
|
+
|
|
101
|
+
# poetry
|
|
102
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
103
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
104
|
+
# commonly ignored for libraries.
|
|
105
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
106
|
+
#poetry.lock
|
|
107
|
+
|
|
108
|
+
# pdm
|
|
109
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
110
|
+
#pdm.lock
|
|
111
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
112
|
+
# in version control.
|
|
113
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
114
|
+
.pdm.toml
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.venv*
|
|
129
|
+
env/
|
|
130
|
+
venv/
|
|
131
|
+
ENV/
|
|
132
|
+
env.bak/
|
|
133
|
+
venv.bak/
|
|
134
|
+
|
|
135
|
+
# Spyder project settings
|
|
136
|
+
.spyderproject
|
|
137
|
+
.spyproject
|
|
138
|
+
|
|
139
|
+
# Rope project settings
|
|
140
|
+
.ropeproject
|
|
141
|
+
|
|
142
|
+
# mkdocs documentation
|
|
143
|
+
/site
|
|
144
|
+
|
|
145
|
+
# mypy
|
|
146
|
+
.mypy_cache/
|
|
147
|
+
.dmypy.json
|
|
148
|
+
dmypy.json
|
|
149
|
+
|
|
150
|
+
# Pyre type checker
|
|
151
|
+
.pyre/
|
|
152
|
+
|
|
153
|
+
# pytype static type analyzer
|
|
154
|
+
.pytype/
|
|
155
|
+
|
|
156
|
+
# Cython debug symbols
|
|
157
|
+
cython_debug/
|
|
158
|
+
|
|
159
|
+
# PyCharm
|
|
160
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
161
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
162
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
163
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
164
|
+
#.idea/
|
|
165
|
+
|
|
166
|
+
### Python Patch ###
|
|
167
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
168
|
+
poetry.toml
|
|
169
|
+
|
|
170
|
+
# ruff
|
|
171
|
+
.ruff_cache/
|
|
172
|
+
|
|
173
|
+
# LSP config files
|
|
174
|
+
pyrightconfig.json
|
|
175
|
+
|
|
176
|
+
# End of https://www.toptal.com/developers/gitignore/api/python
|
|
177
|
+
|
|
178
|
+
# Fit archive
|
|
179
|
+
*.db
|
|
180
|
+
.DS_Store
|
|
181
|
+
FIT/
|
|
182
|
+
COMPILAZIONE.md
|
|
183
|
+
TEST.md
|
|
184
|
+
Qt/
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"psi-header.config": {
|
|
3
|
+
"forceToTop": true,
|
|
4
|
+
"blankLinesAfter": 1,
|
|
5
|
+
"license": "Custom"
|
|
6
|
+
},
|
|
7
|
+
"psi-header.license-text": [
|
|
8
|
+
"Copyright (c) <<yeartoyear(fc, now)>> <<company>>",
|
|
9
|
+
"SPDX-License-Identifier: LGPL-3.0-or-later"
|
|
10
|
+
],
|
|
11
|
+
"psi-header.variables": [
|
|
12
|
+
["company", "FIT-Project"]
|
|
13
|
+
],
|
|
14
|
+
"psi-header.lang-config": [
|
|
15
|
+
{
|
|
16
|
+
"language": "python",
|
|
17
|
+
"begin": "#####",
|
|
18
|
+
"end": "#####",
|
|
19
|
+
"prefix": "# ",
|
|
20
|
+
"blankLinesAfter": 1,
|
|
21
|
+
"beforeHeader": [
|
|
22
|
+
"#!/usr/bin/env python3",
|
|
23
|
+
"# -*- coding:utf-8 -*-"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"psi-header.templates": [
|
|
28
|
+
{
|
|
29
|
+
"language": "python",
|
|
30
|
+
"template": [
|
|
31
|
+
"-----",
|
|
32
|
+
"<<licensetext>>",
|
|
33
|
+
"-----"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"[python]": {
|
|
38
|
+
"editor.formatOnSave": true,
|
|
39
|
+
"editor.defaultFormatter": "ms-python.black-formatter",
|
|
40
|
+
"editor.codeActionsOnSave": {
|
|
41
|
+
"source.organizeImports": "explicit",
|
|
42
|
+
"source.fixAll": "explicit"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"ruff.enable": true
|
|
46
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.24)
|
|
2
|
+
project(FITWebViewBridge LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
# Standard C++
|
|
5
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
6
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
7
|
+
|
|
8
|
+
# Opzione unica rimasta
|
|
9
|
+
option(BUILD_BINDINGS "Build Python bindings" ON)
|
|
10
|
+
|
|
11
|
+
# Dispatch per-OS: tutta la logica sta nelle sottodirectory
|
|
12
|
+
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
|
13
|
+
add_subdirectory(src/macos)
|
|
14
|
+
if(BUILD_BINDINGS)
|
|
15
|
+
add_subdirectory(bindings/pyside6/macos)
|
|
16
|
+
endif()
|
|
17
|
+
elseif(WIN32)
|
|
18
|
+
add_subdirectory(src/win)
|
|
19
|
+
if(BUILD_BINDINGS)
|
|
20
|
+
add_subdirectory(bindings/pyside6/win)
|
|
21
|
+
endif()
|
|
22
|
+
elseif(UNIX) # Linux (non macOS)
|
|
23
|
+
add_subdirectory(src/linux)
|
|
24
|
+
if(BUILD_BINDINGS)
|
|
25
|
+
add_subdirectory(bindings/pyside6/linux)
|
|
26
|
+
endif()
|
|
27
|
+
else()
|
|
28
|
+
message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}")
|
|
29
|
+
endif()
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: fit-webview-bridge
|
|
3
|
+
Version: 0.1.1a5
|
|
4
|
+
Summary: Qt native WebView bridge with PySide6 bindings
|
|
5
|
+
Author: FIT Project
|
|
6
|
+
License: LGPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/fit-project/fit-webview-bridge
|
|
8
|
+
Requires-Python: <3.14,>=3.11
|
|
9
|
+
Requires-Dist: PySide6==6.9.0
|
|
10
|
+
Requires-Dist: shiboken6==6.9.0
|
|
11
|
+
Requires-Dist: shiboken6-generator==6.9.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# FIT WebView Bridge
|
|
15
|
+
|
|
16
|
+
[🇬🇧 English](README.md) | [🇮🇹 Italiano](README.it.md)
|
|
17
|
+
|
|
18
|
+
### Description
|
|
19
|
+
|
|
20
|
+
**FIT WebView Bridge** is a cross‑platform Qt widget (C++/Objective‑C++) with **PySide6** bindings that wraps the OS‑native web engines:
|
|
21
|
+
- **Windows →** Edge WebView2
|
|
22
|
+
- **macOS →** WKWebView
|
|
23
|
+
- **Linux →** WebKitGTK (with **GStreamer** for codecs)
|
|
24
|
+
|
|
25
|
+
It exposes a **unified Python API** for browser control and enables **forensic viewing/capture** of content, including media requiring proprietary codecs (e.g., **H.264/AAC**), **without** custom QtWebEngine builds or codec redistribution burdens (system codecs are used). All **controls** (UI and app logic) are **delegated to the PySide window**.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Why this project
|
|
29
|
+
QtWebEngine (Chromium) **does not enable proprietary codecs** by default, and redistributing them requires **licensing**. Alternatives (building QtWebEngine with codecs or using QtWebView/QML) have portability/control limitations. The chosen path is to **leverage native engines**, achieving codec compatibility and **full control** via a Python API.
|
|
30
|
+
|
|
31
|
+
### Repository layout
|
|
32
|
+
```
|
|
33
|
+
fit-webview-bridge/
|
|
34
|
+
├─ CMakeLists.txt
|
|
35
|
+
├─ cmake/ # Find*.cmake, toolchains, helpers
|
|
36
|
+
├─ include/fitwvb/ # Public headers (API)
|
|
37
|
+
├─ src/
|
|
38
|
+
│ ├─ core/ # Facade / common interfaces
|
|
39
|
+
│ ├─ win/ # Edge WebView2 backend (C++)
|
|
40
|
+
│ ├─ macos/ # WKWebView backend (Obj-C++)
|
|
41
|
+
│ └─ linux/ # WebKitGTK backend (C++)
|
|
42
|
+
├─ bindings/pyside6/ # Shiboken6: typesystem & config
|
|
43
|
+
├─ tests/ # Unit / integration
|
|
44
|
+
└─ examples/ # Minimal PySide6 demo app
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Interface (methods/slots + signals)
|
|
48
|
+
**Methods / slots**
|
|
49
|
+
- `load(url)`
|
|
50
|
+
- `back()`
|
|
51
|
+
- `forward()`
|
|
52
|
+
- `reload()`
|
|
53
|
+
- `stop()`
|
|
54
|
+
- `setHtml(html, baseUrl)`
|
|
55
|
+
- `evalJs(script, callback)`
|
|
56
|
+
|
|
57
|
+
**Signals**
|
|
58
|
+
- `urlChanged(QUrl)`
|
|
59
|
+
- `titleChanged(QString)`
|
|
60
|
+
- `loadProgress(int)`
|
|
61
|
+
- `loadFinished(bool)`
|
|
62
|
+
- `consoleMessage(QString)`
|
|
63
|
+
|
|
64
|
+
> Note: the API is **uniform** across OSes; implementations delegate to the native engine.
|
|
65
|
+
|
|
66
|
+
### Prerequisites
|
|
67
|
+
**Common**
|
|
68
|
+
- **CMake** (>= 3.24 recommended)
|
|
69
|
+
- **Ninja** (generator)
|
|
70
|
+
- **Python** 3.9+
|
|
71
|
+
- **PySide6** and **Shiboken6** (for Python bindings)
|
|
72
|
+
- Platform build toolchain
|
|
73
|
+
|
|
74
|
+
**Windows**
|
|
75
|
+
- **MSVC** (Visual Studio 2022 or Build Tools) and Windows SDK
|
|
76
|
+
- **Microsoft Edge WebView2 Runtime**
|
|
77
|
+
- **WebView2 SDK** (NuGet/vcpkg)
|
|
78
|
+
|
|
79
|
+
**macOS**
|
|
80
|
+
- **Xcode** + Command Line Tools
|
|
81
|
+
- **Objective‑C++** enabled (.mm)
|
|
82
|
+
- Frameworks: `WebKit`, `Cocoa`
|
|
83
|
+
|
|
84
|
+
**Linux**
|
|
85
|
+
- **GCC/Clang**, `pkg-config`
|
|
86
|
+
- **WebKitGTK** dev packages (e.g., `webkit2gtk-4.1` or distro equivalent)
|
|
87
|
+
- **GStreamer** (base + required plugins for codecs)
|
|
88
|
+
|
|
89
|
+
### Build (indicative)
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/fit-project/fit-webview-bridge.git
|
|
92
|
+
cd fit-webview-bridge
|
|
93
|
+
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_PYSIDE6_BINDINGS=ON
|
|
94
|
+
cmake --build build
|
|
95
|
+
# (optional) ctest --test-dir build
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Examples
|
|
99
|
+
PySide6 samples in `examples/` demonstrate URL loading, JS injection, and signal handling.
|
|
100
|
+
|
|
101
|
+
### Codec & licensing notes
|
|
102
|
+
The project **does not** redistribute proprietary codecs: it leverages codecs **already provided by the OS**. End‑user usage must comply with the relevant licenses/formats.
|
|
103
|
+
|
|
104
|
+
### Project status
|
|
105
|
+
Initial/alpha (API subject to change).
|
|
106
|
+
|
|
107
|
+
# Fit Web — Project rationale and options for proprietary codecs
|
|
108
|
+
|
|
109
|
+
**Fit Web** is the FIT project's *scraper* module designed to **forensically acquire and preserve web content**: <https://github.com/fit-project/fit-web>.
|
|
110
|
+
|
|
111
|
+
Like the other modules, **Fit Web** is based on **PySide** (Qt for Python). It currently uses **QtWebEngine**, which is a **Chromium** wrapper.
|
|
112
|
+
|
|
113
|
+
## The problem
|
|
114
|
+
By default, Chromium **does not enable proprietary audio/video codecs**, notably **H.264** and **AAC**.
|
|
115
|
+
|
|
116
|
+
## Options considered
|
|
117
|
+
|
|
118
|
+
### 1) Build QtWebEngine with proprietary codecs
|
|
119
|
+
Enable the `-webengine-proprietary-codecs` option.
|
|
120
|
+
Documentation: <https://doc.qt.io/qt-6/qtwebengine-overview.html>
|
|
121
|
+
|
|
122
|
+
**Drawbacks**
|
|
123
|
+
- Must be done for **all supported operating systems**.
|
|
124
|
+
- The build requires **very powerful machines** (e.g., difficulties on a MacBook Air M2 with 16 GB RAM).
|
|
125
|
+
- **Licensing**: distributing H.264 and AAC **requires a license**.
|
|
126
|
+
|
|
127
|
+
### 2) Use QtWebView
|
|
128
|
+
QtWebView relies on **the OS’s native web APIs**; for proprietary‑codec content it uses **the system’s codecs**.
|
|
129
|
+
**Pros**: no custom builds, no direct license handling.
|
|
130
|
+
**Cons**: the UI layer is **QML**, geared toward lightweight (often mobile) UIs, so it **doesn’t provide full browser control** compared to QtWebEngine.
|
|
131
|
+
|
|
132
|
+
Documentation: <https://doc.qt.io/qt-6/qtwebview-index.html>
|
|
133
|
+
|
|
134
|
+
### 3) Implement a native Qt widget (C/C++) per OS
|
|
135
|
+
Develop a Qt widget (usable from **PySide6**) that embeds the system’s web engine:
|
|
136
|
+
|
|
137
|
+
- **Windows →** Edge WebView2
|
|
138
|
+
- **macOS →** WKWebView
|
|
139
|
+
- **Linux →** WebKitGTK (with **GStreamer** for codecs)
|
|
140
|
+
|
|
141
|
+
**Advantages**
|
|
142
|
+
- **No redistribution licensing**: leverage the codecs already provided by the OS.
|
|
143
|
+
- A **common API** can be exposed to PySide6.
|
|
144
|
+
- **More control** than QtWebView, without QML’s limitations.
|
|
145
|
+
|
|
146
|
+
**Disadvantages**
|
|
147
|
+
- **Medium‑to‑high complexity** to implement.
|
|
148
|
+
- Requires **C++** and, on macOS, **Objective‑C++**.
|
|
149
|
+
- Requires **custom CMake** to include libraries and linking.
|
|
150
|
+
|
|
151
|
+
---
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# FIT WebView Bridge
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English](README.md) | [🇮🇹 Italiano](README.it.md)
|
|
4
|
+
|
|
5
|
+
### Descrizione
|
|
6
|
+
|
|
7
|
+
**FIT WebView Bridge** è un widget Qt multipiattaforma (C++/Objective-C++) con binding **PySide6** che incapsula i motori web nativi del sistema:
|
|
8
|
+
- **Windows →** Edge WebView2
|
|
9
|
+
- **macOS →** WKWebView
|
|
10
|
+
- **Linux →** WebKitGTK (con **GStreamer** per i codec)
|
|
11
|
+
|
|
12
|
+
Espone una **API Python unificata** per controllare il browser ed abilita la **visualizzazione/acquisizione forense** di contenuti, inclusi quelli che richiedono codec proprietari (es. **H.264/AAC**), **senza** build personalizzate di QtWebEngine né oneri di redistribuzione dei codec (si usano quelli del sistema operativo). I **controlli** (UI e logica applicativa) sono **demandati alla finestra PySide**.
|
|
13
|
+
|
|
14
|
+
### Perché questo progetto
|
|
15
|
+
QtWebEngine (Chromium) di default **non abilita i codec proprietari** e la loro distribuzione richiede **licenza**. Le alternative considerate (compilare QtWebEngine con codec o usare QtWebView/QML) presentano limiti di portabilità/controllo. La via scelta è **incapsulare i motori di sistema**, ottenendo compatibilità con i codec e **massimo controllo** via API Python.
|
|
16
|
+
|
|
17
|
+
### Struttura del repository
|
|
18
|
+
```
|
|
19
|
+
fit-webview-bridge/
|
|
20
|
+
├─ CMakeLists.txt
|
|
21
|
+
├─ cmake/ # Find*.cmake, toolchain, helper
|
|
22
|
+
├─ include/fitwvb/ # Header pubblici (API)
|
|
23
|
+
├─ src/
|
|
24
|
+
│ ├─ core/ # Facade / interfacce comuni
|
|
25
|
+
│ ├─ win/ # Backend Edge WebView2 (C++)
|
|
26
|
+
│ ├─ macos/ # Backend WKWebView (Obj-C++)
|
|
27
|
+
│ └─ linux/ # Backend WebKitGTK (C++)
|
|
28
|
+
├─ bindings/pyside6/ # Shiboken6: typesystem e config
|
|
29
|
+
├─ tests/ # Unit / integration
|
|
30
|
+
└─ examples/ # Mini app PySide6 dimostrativa
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Interfaccia (metodi/slot + segnali)
|
|
34
|
+
**Metodi / slot**
|
|
35
|
+
- `load(url)`
|
|
36
|
+
- `back()`
|
|
37
|
+
- `forward()`
|
|
38
|
+
- `reload()`
|
|
39
|
+
- `stop()`
|
|
40
|
+
- `setHtml(html, baseUrl)`
|
|
41
|
+
- `evalJs(script, callback)`
|
|
42
|
+
|
|
43
|
+
**Segnali**
|
|
44
|
+
- `urlChanged(QUrl)`
|
|
45
|
+
- `titleChanged(QString)`
|
|
46
|
+
- `loadProgress(int)`
|
|
47
|
+
- `loadFinished(bool)`
|
|
48
|
+
- `consoleMessage(QString)`
|
|
49
|
+
|
|
50
|
+
> Nota: l’API è esposta in modo **uniforme** su tutti gli OS; l’implementazione delega al motore nativo.
|
|
51
|
+
|
|
52
|
+
### Prerequisiti
|
|
53
|
+
**Comuni**
|
|
54
|
+
- **CMake** (>= 3.24 consigliato)
|
|
55
|
+
- **Ninja** (generator)
|
|
56
|
+
- **Python** 3.9+
|
|
57
|
+
- **PySide6** e **Shiboken6** (per i binding Python)
|
|
58
|
+
- Strumenti di build della piattaforma
|
|
59
|
+
|
|
60
|
+
**Windows**
|
|
61
|
+
- **MSVC** (Visual Studio 2022 o Build Tools) e Windows SDK
|
|
62
|
+
- **Microsoft Edge WebView2 Runtime**
|
|
63
|
+
- **WebView2 SDK** (NuGet/vcpkg)
|
|
64
|
+
|
|
65
|
+
**macOS**
|
|
66
|
+
- **Xcode** + Command Line Tools
|
|
67
|
+
- Linguaggio **Objective-C++** abilitato (.mm)
|
|
68
|
+
- Framework: `WebKit`, `Cocoa`
|
|
69
|
+
|
|
70
|
+
**Linux**
|
|
71
|
+
- **GCC/Clang**, `pkg-config`
|
|
72
|
+
- **WebKitGTK** dev (es. `webkit2gtk-4.1` o equivalente della distro)
|
|
73
|
+
- **GStreamer** (base + plugin necessari per i codec)
|
|
74
|
+
|
|
75
|
+
### Compilazione (indicativa)
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/fit-project/fit-webview-bridge.git
|
|
78
|
+
cd fit-webview-bridge
|
|
79
|
+
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_PYSIDE6_BINDINGS=ON
|
|
80
|
+
cmake --build build
|
|
81
|
+
# (opzionale) ctest --test-dir build
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Esempi
|
|
85
|
+
Gli esempi PySide6 si trovano in `examples/` e mostrano caricamento URL, iniezione JS e ascolto dei segnali.
|
|
86
|
+
|
|
87
|
+
### Considerazioni su codec e licenze
|
|
88
|
+
Il progetto **non** ridistribuisce codec proprietari: sfrutta i codec **già presenti nel sistema operativo**. L’uso finale dei contenuti è responsabilità dell’utente in base alle rispettive licenze/formati.
|
|
89
|
+
|
|
90
|
+
### Stato del progetto
|
|
91
|
+
Iniziale/alpha (API soggetta a modifiche).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
Fit Web — Motivazioni del progetto e opzioni per i codec proprietari
|
|
96
|
+
|
|
97
|
+
**Fit Web** è il modulo *scraper* del progetto FIT pensato per **acquisire e cristallizzare, con modalità forensi, contenuti web**: <https://github.com/fit-project/fit-web>.
|
|
98
|
+
|
|
99
|
+
Come gli altri moduli, **Fit Web** si basa su **PySide** (Qt per Python). Attualmente utilizza **QtWebEngine**, che è un *wrapper* di **Chromium**.
|
|
100
|
+
|
|
101
|
+
## Il problema
|
|
102
|
+
Chromium, di default, **non abilita i codec audio/video proprietari**, in particolare **H.264** e **AAC**.
|
|
103
|
+
|
|
104
|
+
## Soluzioni considerate
|
|
105
|
+
|
|
106
|
+
### 1) Compilare QtWebEngine con codec proprietari
|
|
107
|
+
Abilitare l’opzione `-webengine-proprietary-codecs`.
|
|
108
|
+
Documentazione: <https://doc.qt.io/qt-6/qtwebengine-overview.html>
|
|
109
|
+
|
|
110
|
+
**Criticità**
|
|
111
|
+
- Va eseguito per **tutti i sistemi operativi** supportati.
|
|
112
|
+
- La compilazione richiede **macchine molto performanti** (es.: difficoltà su MacBook Air M2 con 16 GB di RAM).
|
|
113
|
+
- **Licenze**: la **distribuzione** di H.264 e AAC **richiede una licenza**.
|
|
114
|
+
|
|
115
|
+
### 2) Usare QtWebView
|
|
116
|
+
QtWebView utilizza **le API web native del sistema operativo**; per i contenuti con codec proprietari sfrutta **i codec del sistema**.
|
|
117
|
+
**Vantaggi**: niente compilazioni personalizzate, niente gestione diretta delle licenze.
|
|
118
|
+
**Limiti**: l’interfaccia è in **QML**, pensata per UI leggere (spesso mobile), e **non offre il controllo completo** sul browser rispetto a QtWebEngine.
|
|
119
|
+
|
|
120
|
+
Documentazione: <https://doc.qt.io/qt-6/qtwebview-index.html>
|
|
121
|
+
|
|
122
|
+
### 3) Scrivere un widget Qt nativo (C/C++) per ogni OS
|
|
123
|
+
Creare un widget Qt (usabile da **PySide6**) che *embedda* il motore web di sistema:
|
|
124
|
+
|
|
125
|
+
- **Windows →** Edge WebView2
|
|
126
|
+
- **macOS →** WKWebView
|
|
127
|
+
- **Linux →** WebKitGTK (con **GStreamer** per i codec)
|
|
128
|
+
|
|
129
|
+
**Vantaggi**
|
|
130
|
+
- **Nessuna licenza da redistribuire**: si usano i codec già forniti dal sistema.
|
|
131
|
+
- Possibilità di **un’API comune** da esporre a PySide6.
|
|
132
|
+
- **Maggiore controllo** rispetto a QtWebView, senza i limiti imposti da QML.
|
|
133
|
+
|
|
134
|
+
**Svantaggi**
|
|
135
|
+
- **Complessità medio‑alta** di implementazione.
|
|
136
|
+
- Richiede **C++** e, su macOS, anche **Objective‑C++**.
|
|
137
|
+
- Necessità di **CMake** ad hoc per includere librerie e collegamenti.
|
|
138
|
+
|
|
139
|
+
---
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# FIT WebView Bridge
|
|
2
|
+
|
|
3
|
+
[🇬🇧 English](README.md) | [🇮🇹 Italiano](README.it.md)
|
|
4
|
+
|
|
5
|
+
### Description
|
|
6
|
+
|
|
7
|
+
**FIT WebView Bridge** is a cross‑platform Qt widget (C++/Objective‑C++) with **PySide6** bindings that wraps the OS‑native web engines:
|
|
8
|
+
- **Windows →** Edge WebView2
|
|
9
|
+
- **macOS →** WKWebView
|
|
10
|
+
- **Linux →** WebKitGTK (with **GStreamer** for codecs)
|
|
11
|
+
|
|
12
|
+
It exposes a **unified Python API** for browser control and enables **forensic viewing/capture** of content, including media requiring proprietary codecs (e.g., **H.264/AAC**), **without** custom QtWebEngine builds or codec redistribution burdens (system codecs are used). All **controls** (UI and app logic) are **delegated to the PySide window**.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Why this project
|
|
16
|
+
QtWebEngine (Chromium) **does not enable proprietary codecs** by default, and redistributing them requires **licensing**. Alternatives (building QtWebEngine with codecs or using QtWebView/QML) have portability/control limitations. The chosen path is to **leverage native engines**, achieving codec compatibility and **full control** via a Python API.
|
|
17
|
+
|
|
18
|
+
### Repository layout
|
|
19
|
+
```
|
|
20
|
+
fit-webview-bridge/
|
|
21
|
+
├─ CMakeLists.txt
|
|
22
|
+
├─ cmake/ # Find*.cmake, toolchains, helpers
|
|
23
|
+
├─ include/fitwvb/ # Public headers (API)
|
|
24
|
+
├─ src/
|
|
25
|
+
│ ├─ core/ # Facade / common interfaces
|
|
26
|
+
│ ├─ win/ # Edge WebView2 backend (C++)
|
|
27
|
+
│ ├─ macos/ # WKWebView backend (Obj-C++)
|
|
28
|
+
│ └─ linux/ # WebKitGTK backend (C++)
|
|
29
|
+
├─ bindings/pyside6/ # Shiboken6: typesystem & config
|
|
30
|
+
├─ tests/ # Unit / integration
|
|
31
|
+
└─ examples/ # Minimal PySide6 demo app
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Interface (methods/slots + signals)
|
|
35
|
+
**Methods / slots**
|
|
36
|
+
- `load(url)`
|
|
37
|
+
- `back()`
|
|
38
|
+
- `forward()`
|
|
39
|
+
- `reload()`
|
|
40
|
+
- `stop()`
|
|
41
|
+
- `setHtml(html, baseUrl)`
|
|
42
|
+
- `evalJs(script, callback)`
|
|
43
|
+
|
|
44
|
+
**Signals**
|
|
45
|
+
- `urlChanged(QUrl)`
|
|
46
|
+
- `titleChanged(QString)`
|
|
47
|
+
- `loadProgress(int)`
|
|
48
|
+
- `loadFinished(bool)`
|
|
49
|
+
- `consoleMessage(QString)`
|
|
50
|
+
|
|
51
|
+
> Note: the API is **uniform** across OSes; implementations delegate to the native engine.
|
|
52
|
+
|
|
53
|
+
### Prerequisites
|
|
54
|
+
**Common**
|
|
55
|
+
- **CMake** (>= 3.24 recommended)
|
|
56
|
+
- **Ninja** (generator)
|
|
57
|
+
- **Python** 3.9+
|
|
58
|
+
- **PySide6** and **Shiboken6** (for Python bindings)
|
|
59
|
+
- Platform build toolchain
|
|
60
|
+
|
|
61
|
+
**Windows**
|
|
62
|
+
- **MSVC** (Visual Studio 2022 or Build Tools) and Windows SDK
|
|
63
|
+
- **Microsoft Edge WebView2 Runtime**
|
|
64
|
+
- **WebView2 SDK** (NuGet/vcpkg)
|
|
65
|
+
|
|
66
|
+
**macOS**
|
|
67
|
+
- **Xcode** + Command Line Tools
|
|
68
|
+
- **Objective‑C++** enabled (.mm)
|
|
69
|
+
- Frameworks: `WebKit`, `Cocoa`
|
|
70
|
+
|
|
71
|
+
**Linux**
|
|
72
|
+
- **GCC/Clang**, `pkg-config`
|
|
73
|
+
- **WebKitGTK** dev packages (e.g., `webkit2gtk-4.1` or distro equivalent)
|
|
74
|
+
- **GStreamer** (base + required plugins for codecs)
|
|
75
|
+
|
|
76
|
+
### Build (indicative)
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/fit-project/fit-webview-bridge.git
|
|
79
|
+
cd fit-webview-bridge
|
|
80
|
+
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_PYSIDE6_BINDINGS=ON
|
|
81
|
+
cmake --build build
|
|
82
|
+
# (optional) ctest --test-dir build
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Examples
|
|
86
|
+
PySide6 samples in `examples/` demonstrate URL loading, JS injection, and signal handling.
|
|
87
|
+
|
|
88
|
+
### Codec & licensing notes
|
|
89
|
+
The project **does not** redistribute proprietary codecs: it leverages codecs **already provided by the OS**. End‑user usage must comply with the relevant licenses/formats.
|
|
90
|
+
|
|
91
|
+
### Project status
|
|
92
|
+
Initial/alpha (API subject to change).
|
|
93
|
+
|
|
94
|
+
# Fit Web — Project rationale and options for proprietary codecs
|
|
95
|
+
|
|
96
|
+
**Fit Web** is the FIT project's *scraper* module designed to **forensically acquire and preserve web content**: <https://github.com/fit-project/fit-web>.
|
|
97
|
+
|
|
98
|
+
Like the other modules, **Fit Web** is based on **PySide** (Qt for Python). It currently uses **QtWebEngine**, which is a **Chromium** wrapper.
|
|
99
|
+
|
|
100
|
+
## The problem
|
|
101
|
+
By default, Chromium **does not enable proprietary audio/video codecs**, notably **H.264** and **AAC**.
|
|
102
|
+
|
|
103
|
+
## Options considered
|
|
104
|
+
|
|
105
|
+
### 1) Build QtWebEngine with proprietary codecs
|
|
106
|
+
Enable the `-webengine-proprietary-codecs` option.
|
|
107
|
+
Documentation: <https://doc.qt.io/qt-6/qtwebengine-overview.html>
|
|
108
|
+
|
|
109
|
+
**Drawbacks**
|
|
110
|
+
- Must be done for **all supported operating systems**.
|
|
111
|
+
- The build requires **very powerful machines** (e.g., difficulties on a MacBook Air M2 with 16 GB RAM).
|
|
112
|
+
- **Licensing**: distributing H.264 and AAC **requires a license**.
|
|
113
|
+
|
|
114
|
+
### 2) Use QtWebView
|
|
115
|
+
QtWebView relies on **the OS’s native web APIs**; for proprietary‑codec content it uses **the system’s codecs**.
|
|
116
|
+
**Pros**: no custom builds, no direct license handling.
|
|
117
|
+
**Cons**: the UI layer is **QML**, geared toward lightweight (often mobile) UIs, so it **doesn’t provide full browser control** compared to QtWebEngine.
|
|
118
|
+
|
|
119
|
+
Documentation: <https://doc.qt.io/qt-6/qtwebview-index.html>
|
|
120
|
+
|
|
121
|
+
### 3) Implement a native Qt widget (C/C++) per OS
|
|
122
|
+
Develop a Qt widget (usable from **PySide6**) that embeds the system’s web engine:
|
|
123
|
+
|
|
124
|
+
- **Windows →** Edge WebView2
|
|
125
|
+
- **macOS →** WKWebView
|
|
126
|
+
- **Linux →** WebKitGTK (with **GStreamer** for codecs)
|
|
127
|
+
|
|
128
|
+
**Advantages**
|
|
129
|
+
- **No redistribution licensing**: leverage the codecs already provided by the OS.
|
|
130
|
+
- A **common API** can be exposed to PySide6.
|
|
131
|
+
- **More control** than QtWebView, without QML’s limitations.
|
|
132
|
+
|
|
133
|
+
**Disadvantages**
|
|
134
|
+
- **Medium‑to‑high complexity** to implement.
|
|
135
|
+
- Requires **C++** and, on macOS, **Objective‑C++**.
|
|
136
|
+
- Requires **custom CMake** to include libraries and linking.
|
|
137
|
+
|
|
138
|
+
---
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.21)
|
|
2
|
+
|
|
3
|
+
# Python + Shiboken6 + PySide6
|
|
4
|
+
find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
|
|
5
|
+
find_program(SHIBOKEN6_GEN shiboken6 REQUIRED)
|
|
6
|
+
|
|
7
|
+
# =============== Qt headers (solo SDK, via aqt) ==================
|
|
8
|
+
# Passa -DQt6_DIR=/path/to/Qt/6.9.0/macos/lib/cmake/Qt6
|
|
9
|
+
find_package(Qt6 6.7 REQUIRED COMPONENTS Core Gui Widgets)
|
|
10
|
+
|
|
11
|
+
# Deriva la cartella frameworks (…/macos/lib) a partire da Qt6_DIR
|
|
12
|
+
get_filename_component(_qt_cmake_dir "${Qt6_DIR}" DIRECTORY) # …/macos/lib/cmake
|
|
13
|
+
get_filename_component(QT_FRAMEWORKS_DIR "${_qt_cmake_dir}" DIRECTORY) # …/macos/lib
|
|
14
|
+
|
|
15
|
+
# Verifica headers Qt (SDK)
|
|
16
|
+
foreach(_mod QtCore QtGui QtWidgets)
|
|
17
|
+
if(NOT EXISTS "${QT_FRAMEWORKS_DIR}/${_mod}.framework/Headers")
|
|
18
|
+
message(FATAL_ERROR "Missing ${_mod}.framework/Headers under: ${QT_FRAMEWORKS_DIR}")
|
|
19
|
+
endif()
|
|
20
|
+
endforeach()
|
|
21
|
+
|
|
22
|
+
# Shim uniforme per includere QtCore/QtGui/QtWidgets
|
|
23
|
+
set(QT_HDRSHIM "${CMAKE_BINARY_DIR}/qt_hdrshim")
|
|
24
|
+
file(MAKE_DIRECTORY "${QT_HDRSHIM}")
|
|
25
|
+
file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers" "${QT_HDRSHIM}/QtCore" SYMBOLIC)
|
|
26
|
+
file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers" "${QT_HDRSHIM}/QtGui" SYMBOLIC)
|
|
27
|
+
file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers" "${QT_HDRSHIM}/QtWidgets" SYMBOLIC)
|
|
28
|
+
|
|
29
|
+
# =============== PySide6 runtime (da site-packages) ===============
|
|
30
|
+
# Dir dei frameworks Qt nel venv (PySide6/Qt/lib)
|
|
31
|
+
if(NOT DEFINED PYSIDE_QT_LIBDIR)
|
|
32
|
+
execute_process(
|
|
33
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os, PySide6; print(os.path.join(os.path.dirname(PySide6.__file__), 'Qt', 'lib'))"
|
|
34
|
+
OUTPUT_VARIABLE PYSIDE_QT_LIBDIR
|
|
35
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
36
|
+
)
|
|
37
|
+
endif()
|
|
38
|
+
if(NOT EXISTS "${PYSIDE_QT_LIBDIR}")
|
|
39
|
+
message(FATAL_ERROR "PySide6 Qt frameworks not found: ${PYSIDE_QT_LIBDIR}")
|
|
40
|
+
endif()
|
|
41
|
+
|
|
42
|
+
# Binari framework da PySide6 (linker)
|
|
43
|
+
set(QTCORE_BIN "${PYSIDE_QT_LIBDIR}/QtCore.framework/Versions/A/QtCore")
|
|
44
|
+
set(QTGUI_BIN "${PYSIDE_QT_LIBDIR}/QtGui.framework/Versions/A/QtGui")
|
|
45
|
+
set(QTWIDGETS_BIN "${PYSIDE_QT_LIBDIR}/QtWidgets.framework/Versions/A/QtWidgets")
|
|
46
|
+
foreach(_f ${QTCORE_BIN} ${QTGUI_BIN} ${QTWIDGETS_BIN})
|
|
47
|
+
if(NOT EXISTS "${_f}")
|
|
48
|
+
message(FATAL_ERROR "Framework binary not found: ${_f}")
|
|
49
|
+
endif()
|
|
50
|
+
endforeach()
|
|
51
|
+
|
|
52
|
+
# Trova anche i frameworks per completezza (stessa cartella)
|
|
53
|
+
find_library(QTCORE_FW NAMES QtCore PATHS "${PYSIDE_QT_LIBDIR}" NO_DEFAULT_PATH REQUIRED)
|
|
54
|
+
find_library(QTGUI_FW NAMES QtGui PATHS "${PYSIDE_QT_LIBDIR}" NO_DEFAULT_PATH REQUIRED)
|
|
55
|
+
find_library(QTWIDGETS_FW NAMES QtWidgets PATHS "${PYSIDE_QT_LIBDIR}" NO_DEFAULT_PATH REQUIRED)
|
|
56
|
+
|
|
57
|
+
# =============== Typesystem & includes Shiboken/PySide6 ==========
|
|
58
|
+
execute_process(
|
|
59
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os,shiboken6;print(os.path.join(os.path.dirname(shiboken6.__file__),'typesystems'))"
|
|
60
|
+
OUTPUT_VARIABLE SHIBOKEN_TYPESYS
|
|
61
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
62
|
+
)
|
|
63
|
+
execute_process(
|
|
64
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os,PySide6;print(os.path.join(os.path.dirname(PySide6.__file__),'typesystems'))"
|
|
65
|
+
OUTPUT_VARIABLE PYSIDE_TYPESYS
|
|
66
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Shiboken include (runtime, poi fallback al generator)
|
|
70
|
+
execute_process(
|
|
71
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os,importlib; m=importlib.import_module('shiboken6'); print(os.path.join(os.path.dirname(m.__file__),'include'))"
|
|
72
|
+
OUTPUT_VARIABLE SHIBOKEN_INCLUDE_DIR
|
|
73
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
74
|
+
)
|
|
75
|
+
if(NOT EXISTS "${SHIBOKEN_INCLUDE_DIR}/shiboken.h")
|
|
76
|
+
execute_process(
|
|
77
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os,importlib; m=importlib.import_module('shiboken6_generator'); print(os.path.join(os.path.dirname(m.__file__),'include'))"
|
|
78
|
+
OUTPUT_VARIABLE SHIBOKEN_INCLUDE_DIR
|
|
79
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
80
|
+
)
|
|
81
|
+
endif()
|
|
82
|
+
if(NOT EXISTS "${SHIBOKEN_INCLUDE_DIR}/shiboken.h")
|
|
83
|
+
message(FATAL_ERROR "Cannot find shiboken.h in shiboken6/include nor shiboken6_generator/include.")
|
|
84
|
+
endif()
|
|
85
|
+
message(STATUS "Shiboken include dir: ${SHIBOKEN_INCLUDE_DIR}")
|
|
86
|
+
|
|
87
|
+
# Shiboken runtime library
|
|
88
|
+
execute_process(
|
|
89
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os,glob,importlib; d=os.path.dirname(importlib.import_module('shiboken6').__file__); m=glob.glob(os.path.join(d,'libshiboken*.dylib')) or glob.glob(os.path.join(d,'libshiboken*.so*')); print(m[0] if m else '')"
|
|
90
|
+
OUTPUT_VARIABLE SHIBOKEN_LIBRARY
|
|
91
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
92
|
+
)
|
|
93
|
+
if(NOT SHIBOKEN_LIBRARY)
|
|
94
|
+
execute_process(
|
|
95
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os,glob,importlib; d=os.path.dirname(importlib.import_module('PySide6').__file__); m=glob.glob(os.path.join(d,'libshiboken*.dylib')) or glob.glob(os.path.join(d,'libshiboken*.so*')); print(m[0] if m else '')"
|
|
96
|
+
OUTPUT_VARIABLE SHIBOKEN_LIBRARY
|
|
97
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
98
|
+
)
|
|
99
|
+
endif()
|
|
100
|
+
if(NOT SHIBOKEN_LIBRARY)
|
|
101
|
+
message(FATAL_ERROR "libshiboken*.dylib/.so not found in the venv.")
|
|
102
|
+
endif()
|
|
103
|
+
message(STATUS "Shiboken library: ${SHIBOKEN_LIBRARY}")
|
|
104
|
+
|
|
105
|
+
# PySide6 include & lib
|
|
106
|
+
execute_process(
|
|
107
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os, PySide6; print(os.path.join(os.path.dirname(PySide6.__file__), 'include'))"
|
|
108
|
+
OUTPUT_VARIABLE PYSIDE_INCLUDE_DIR
|
|
109
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
110
|
+
)
|
|
111
|
+
execute_process(
|
|
112
|
+
COMMAND ${Python3_EXECUTABLE} -c "import os, glob, PySide6 as P; d=os.path.dirname(P.__file__); m=sorted(glob.glob(os.path.join(d,'libpyside6.abi3*.dylib'))+glob.glob(os.path.join(d,'libpyside6.abi3*.so*'))); print(m[0] if m else '')"
|
|
113
|
+
OUTPUT_VARIABLE PYSIDE_LIBRARY
|
|
114
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
115
|
+
)
|
|
116
|
+
if(NOT PYSIDE_LIBRARY)
|
|
117
|
+
message(FATAL_ERROR "libpyside6.abi3*.dylib not found in the PySide6 venv.")
|
|
118
|
+
endif()
|
|
119
|
+
message(STATUS "PySide library : ${PYSIDE_LIBRARY}")
|
|
120
|
+
get_filename_component(PYSIDE_LIB_DIR "${PYSIDE_LIBRARY}" DIRECTORY)
|
|
121
|
+
|
|
122
|
+
# Include per shiboken (SDK Qt + tuoi header + shiboken + PySide6)
|
|
123
|
+
set(SHIBOKEN_INCLUDE_LIST
|
|
124
|
+
"${QT_HDRSHIM}" # QtCore/QtGui/QtWidgets headers
|
|
125
|
+
"${PROJECT_SOURCE_DIR}/src/macos" # tuoi header nativi
|
|
126
|
+
"${SHIBOKEN_INCLUDE_DIR}" # shiboken.h
|
|
127
|
+
"${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers"
|
|
128
|
+
"${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers"
|
|
129
|
+
"${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers"
|
|
130
|
+
)
|
|
131
|
+
if(EXISTS "${PYSIDE_INCLUDE_DIR}")
|
|
132
|
+
list(APPEND SHIBOKEN_INCLUDE_LIST
|
|
133
|
+
"${PYSIDE_INCLUDE_DIR}"
|
|
134
|
+
"${PYSIDE_INCLUDE_DIR}/QtCore"
|
|
135
|
+
"${PYSIDE_INCLUDE_DIR}/QtGui"
|
|
136
|
+
"${PYSIDE_INCLUDE_DIR}/QtWidgets"
|
|
137
|
+
)
|
|
138
|
+
endif()
|
|
139
|
+
list(JOIN SHIBOKEN_INCLUDE_LIST ":" SHIBOKEN_INCLUDE_ARG)
|
|
140
|
+
message(STATUS "Shiboken include paths:\n ${SHIBOKEN_INCLUDE_ARG}")
|
|
141
|
+
|
|
142
|
+
# =============== Shiboken: codegen ================================
|
|
143
|
+
set(GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/shiboken_out)
|
|
144
|
+
file(MAKE_DIRECTORY ${GEN_DIR})
|
|
145
|
+
|
|
146
|
+
set(HEADERS "${PROJECT_SOURCE_DIR}/src/macos/WKWebViewWidget.h")
|
|
147
|
+
set(TYPESYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/typesystem_wkwebview.xml")
|
|
148
|
+
|
|
149
|
+
message(STATUS "Running shiboken6: output -> ${GEN_DIR}")
|
|
150
|
+
execute_process(
|
|
151
|
+
COMMAND "${SHIBOKEN6_GEN}"
|
|
152
|
+
--generator-set=shiboken
|
|
153
|
+
--enable-pyside-extensions
|
|
154
|
+
--include-paths=${SHIBOKEN_INCLUDE_ARG}
|
|
155
|
+
--typesystem-paths=${SHIBOKEN_TYPESYS}
|
|
156
|
+
--typesystem-paths=${PYSIDE_TYPESYS}
|
|
157
|
+
--output-directory=${GEN_DIR}
|
|
158
|
+
${HEADERS}
|
|
159
|
+
${TYPESYSTEM}
|
|
160
|
+
RESULT_VARIABLE SHIBOKEN_RV
|
|
161
|
+
OUTPUT_VARIABLE SHIBOKEN_STDOUT
|
|
162
|
+
ERROR_VARIABLE SHIBOKEN_STDERR
|
|
163
|
+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
|
164
|
+
)
|
|
165
|
+
if (NOT SHIBOKEN_RV EQUAL 0)
|
|
166
|
+
message(STATUS "Shiboken stdout:\n${SHIBOKEN_STDOUT}")
|
|
167
|
+
message(STATUS "Shiboken stderr:\n${SHIBOKEN_STDERR}")
|
|
168
|
+
message(FATAL_ERROR "Shiboken generation failed with code ${SHIBOKEN_RV}")
|
|
169
|
+
endif()
|
|
170
|
+
|
|
171
|
+
file(GLOB_RECURSE SHIBOKEN_SRCS ${GEN_DIR}/*.cpp)
|
|
172
|
+
if (SHIBOKEN_SRCS STREQUAL "")
|
|
173
|
+
message(FATAL_ERROR "No sources generated in ${GEN_DIR}")
|
|
174
|
+
endif()
|
|
175
|
+
|
|
176
|
+
# =============== Estensione Python ================================
|
|
177
|
+
add_library(_wkwebview MODULE ${SHIBOKEN_SRCS})
|
|
178
|
+
target_compile_features(_wkwebview PRIVATE cxx_std_17)
|
|
179
|
+
|
|
180
|
+
# Installa l’estensione nel pacchetto Python
|
|
181
|
+
install(TARGETS _wkwebview
|
|
182
|
+
LIBRARY DESTINATION fit_webview_bridge
|
|
183
|
+
RUNTIME DESTINATION fit_webview_bridge
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Suffisso Python (es. .cpython-311-darwin.so)
|
|
187
|
+
execute_process(
|
|
188
|
+
COMMAND ${Python3_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX') or '.so')"
|
|
189
|
+
OUTPUT_VARIABLE PY_EXT_SUFFIX
|
|
190
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Include & link
|
|
194
|
+
target_include_directories(_wkwebview PRIVATE
|
|
195
|
+
${GEN_DIR}
|
|
196
|
+
"${PROJECT_SOURCE_DIR}/src/macos"
|
|
197
|
+
${Python3_INCLUDE_DIRS}
|
|
198
|
+
${SHIBOKEN_INCLUDE_DIR}
|
|
199
|
+
${QT_HDRSHIM}
|
|
200
|
+
)
|
|
201
|
+
if(EXISTS "${PYSIDE_INCLUDE_DIR}")
|
|
202
|
+
target_include_directories(_wkwebview PRIVATE
|
|
203
|
+
"${PYSIDE_INCLUDE_DIR}"
|
|
204
|
+
"${PYSIDE_INCLUDE_DIR}/QtCore"
|
|
205
|
+
"${PYSIDE_INCLUDE_DIR}/QtGui"
|
|
206
|
+
"${PYSIDE_INCLUDE_DIR}/QtWidgets"
|
|
207
|
+
)
|
|
208
|
+
endif()
|
|
209
|
+
|
|
210
|
+
# CMake deve cercare nelle dir dei framework di PySide6
|
|
211
|
+
target_link_directories(_wkwebview PRIVATE "${PYSIDE_QT_LIBDIR}")
|
|
212
|
+
|
|
213
|
+
# Linka CONTRO i binari di PySide6 + shiboken + Python + lib nativa
|
|
214
|
+
target_link_libraries(_wkwebview PRIVATE
|
|
215
|
+
"${QTCORE_BIN}"
|
|
216
|
+
"${QTGUI_BIN}"
|
|
217
|
+
"${QTWIDGETS_BIN}"
|
|
218
|
+
"${SHIBOKEN_LIBRARY}"
|
|
219
|
+
"${PYSIDE_LIBRARY}"
|
|
220
|
+
Python3::Module
|
|
221
|
+
wkwebview
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# =============== RPATH per runtime su macOS =======================
|
|
225
|
+
get_filename_component(SHIBOKEN_LIB_DIR "${SHIBOKEN_LIBRARY}" DIRECTORY)
|
|
226
|
+
option(FITWVB_VENDORIZE "Vendorize PySide/Qt lookup via @loader_path" ON)
|
|
227
|
+
|
|
228
|
+
set_target_properties(_wkwebview PROPERTIES
|
|
229
|
+
PREFIX "" OUTPUT_NAME "wkwebview" SUFFIX "${PY_EXT_SUFFIX}"
|
|
230
|
+
MACOSX_RPATH ON BUILD_WITH_INSTALL_RPATH ON
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if(FITWVB_VENDORIZE)
|
|
234
|
+
set_target_properties(_wkwebview PROPERTIES
|
|
235
|
+
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/fit_webview_bridge"
|
|
236
|
+
)
|
|
237
|
+
set_property(TARGET _wkwebview APPEND PROPERTY BUILD_RPATH
|
|
238
|
+
"@loader_path" "@loader_path/../PySide6" "@loader_path/../PySide6/Qt/lib" "@loader_path/../shiboken6")
|
|
239
|
+
set_property(TARGET _wkwebview APPEND PROPERTY INSTALL_RPATH
|
|
240
|
+
"@loader_path" "@loader_path/../PySide6" "@loader_path/../PySide6/Qt/lib" "@loader_path/../shiboken6")
|
|
241
|
+
else()
|
|
242
|
+
set_target_properties(_wkwebview PROPERTIES
|
|
243
|
+
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
244
|
+
)
|
|
245
|
+
set_property(TARGET _wkwebview APPEND PROPERTY BUILD_RPATH
|
|
246
|
+
"@loader_path" "${PYSIDE_QT_LIBDIR}" "${SHIBOKEN_LIB_DIR}" "${PYSIDE_LIB_DIR}")
|
|
247
|
+
set_property(TARGET _wkwebview APPEND PROPERTY INSTALL_RPATH
|
|
248
|
+
"@loader_path" "${PYSIDE_QT_LIBDIR}" "${SHIBOKEN_LIB_DIR}" "${PYSIDE_LIB_DIR}")
|
|
249
|
+
endif()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<typesystem package="wkwebview">
|
|
3
|
+
<load-typesystem name="typesystem_core.xml" generate="no"/>
|
|
4
|
+
<load-typesystem name="typesystem_gui.xml" generate="no"/>
|
|
5
|
+
<load-typesystem name="typesystem_widgets.xml" generate="no"/>
|
|
6
|
+
|
|
7
|
+
<object-type name="WKWebViewWidget">
|
|
8
|
+
<include file-name="WKWebViewWidget.h" location="global"/>
|
|
9
|
+
</object-type>
|
|
10
|
+
</typesystem>
|
|
11
|
+
|
|
12
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
3
|
+
sys.path[:0] = [os.path.join(ROOT, "build"),
|
|
4
|
+
os.path.join(ROOT, "build", "bindings", "shiboken_out")]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
|
|
8
|
+
from fit_webview_bridge import SystemWebView
|
|
9
|
+
# tentativo 1: pacchetto generato da shiboken (wkwebview)
|
|
10
|
+
try:
|
|
11
|
+
import wkwebview
|
|
12
|
+
WKWebViewWidget = wkwebview.WKWebViewWidget # accesso via attributo
|
|
13
|
+
except Exception:
|
|
14
|
+
# tentativo 2: modulo nativo diretto
|
|
15
|
+
from _wkwebview import WKWebViewWidget
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Main(QMainWindow):
|
|
19
|
+
def __init__(self):
|
|
20
|
+
super().__init__()
|
|
21
|
+
central = QWidget(self)
|
|
22
|
+
lay = QVBoxLayout(central)
|
|
23
|
+
self.view = WKWebViewWidget()
|
|
24
|
+
lay.addWidget(self.view)
|
|
25
|
+
self.setCentralWidget(central)
|
|
26
|
+
|
|
27
|
+
# segnali utili
|
|
28
|
+
self.view.titleChanged.connect(self.setWindowTitle)
|
|
29
|
+
self.view.loadProgress.connect(lambda p: print("progress:", p))
|
|
30
|
+
|
|
31
|
+
self.view.load("https://www.facebook.com/reel/574839388710756/")
|
|
32
|
+
|
|
33
|
+
app = QApplication([])
|
|
34
|
+
m = Main()
|
|
35
|
+
m.resize(1200, 800)
|
|
36
|
+
m.show()
|
|
37
|
+
app.exec()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .wkwebview import WKWebViewWidget as SystemWebView
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fit-webview-bridge"
|
|
3
|
+
version = "0.1.1a5"
|
|
4
|
+
description = "Qt native WebView bridge with PySide6 bindings"
|
|
5
|
+
requires-python = ">=3.11,<3.14"
|
|
6
|
+
dependencies = ["PySide6==6.9.0", "shiboken6==6.9.0", "shiboken6-generator==6.9.0"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
license = {text = "LGPL-3.0-or-later"}
|
|
9
|
+
authors = [{ name = "FIT Project" }]
|
|
10
|
+
urls = { Homepage = "https://github.com/fit-project/fit-webview-bridge" }
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = [
|
|
14
|
+
"scikit-build-core>=0.9",
|
|
15
|
+
"cmake>=3.26",
|
|
16
|
+
"ninja",
|
|
17
|
+
"PySide6==6.9.0",
|
|
18
|
+
"shiboken6==6.9.0",
|
|
19
|
+
"shiboken6-generator==6.9.0"
|
|
20
|
+
]
|
|
21
|
+
build-backend = "scikit_build_core.build"
|
|
22
|
+
|
|
23
|
+
[tool.scikit-build]
|
|
24
|
+
wheel.packages = ["fit_webview_bridge"]
|
|
25
|
+
cmake.build-type = "Release"
|
|
26
|
+
cmake.define = { FITWVB_VENDORIZE = "ON" }
|
|
27
|
+
|
|
28
|
+
[tool.cibuildwheel]
|
|
29
|
+
build = ["cp311-*", "cp312-*", "cp313-*"]
|
|
30
|
+
|
|
31
|
+
[tool.cibuildwheel.macos]
|
|
32
|
+
archs = ["arm64"]
|
|
33
|
+
repair-wheel-command = ""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.21)
|
|
2
|
+
enable_language(OBJCXX)
|
|
3
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
4
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
5
|
+
set(CMAKE_OBJCXX_STANDARD 17)
|
|
6
|
+
set(CMAKE_OBJCXX_STANDARD_REQUIRED ON)
|
|
7
|
+
set(CMAKE_AUTOMOC ON)
|
|
8
|
+
|
|
9
|
+
# Qt da aqt: serve solo per moc + headers
|
|
10
|
+
# Passa -DQt6_DIR=/path/to/Qt/6.9.0/macos/lib/cmake/Qt6
|
|
11
|
+
find_package(Qt6 6.7 REQUIRED COMPONENTS Core Gui Widgets)
|
|
12
|
+
|
|
13
|
+
# Deriva la cartella dei frameworks (…/macos/lib) da Qt6_DIR
|
|
14
|
+
get_filename_component(_qt_cmake_dir "${Qt6_DIR}" DIRECTORY) # …/macos/lib/cmake
|
|
15
|
+
get_filename_component(QT_FRAMEWORKS_DIR "${_qt_cmake_dir}" DIRECTORY) # …/macos/lib
|
|
16
|
+
|
|
17
|
+
# Verifica headers
|
|
18
|
+
foreach(_m QtCore QtGui QtWidgets)
|
|
19
|
+
if(NOT EXISTS "${QT_FRAMEWORKS_DIR}/${_m}.framework/Headers")
|
|
20
|
+
message(FATAL_ERROR "Missing ${_m}.framework/Headers in: ${QT_FRAMEWORKS_DIR}")
|
|
21
|
+
endif()
|
|
22
|
+
endforeach()
|
|
23
|
+
|
|
24
|
+
# Shim include uniforme
|
|
25
|
+
set(QT_HDRSHIM "${CMAKE_BINARY_DIR}/qt_hdrshim")
|
|
26
|
+
file(MAKE_DIRECTORY "${QT_HDRSHIM}")
|
|
27
|
+
file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers" "${QT_HDRSHIM}/QtCore" SYMBOLIC)
|
|
28
|
+
file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers" "${QT_HDRSHIM}/QtGui" SYMBOLIC)
|
|
29
|
+
file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers" "${QT_HDRSHIM}/QtWidgets" SYMBOLIC)
|
|
30
|
+
|
|
31
|
+
add_library(wkwebview STATIC
|
|
32
|
+
WKWebViewWidget.mm
|
|
33
|
+
WKWebViewWidget.h
|
|
34
|
+
)
|
|
35
|
+
target_compile_features(wkwebview PRIVATE cxx_std_17)
|
|
36
|
+
|
|
37
|
+
target_include_directories(wkwebview PUBLIC
|
|
38
|
+
${CMAKE_CURRENT_SOURCE_DIR}
|
|
39
|
+
"${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers"
|
|
40
|
+
"${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers"
|
|
41
|
+
"${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers"
|
|
42
|
+
${QT_HDRSHIM}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Link SOLO framework di sistema (Cocoa/WebKit). Niente Qt qui.
|
|
46
|
+
target_link_libraries(wkwebview PUBLIC
|
|
47
|
+
"-framework Cocoa"
|
|
48
|
+
"-framework WebKit"
|
|
49
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <QWidget>
|
|
3
|
+
#include <QObject>
|
|
4
|
+
|
|
5
|
+
class QString; class QUrl; class QShowEvent; class QResizeEvent;
|
|
6
|
+
|
|
7
|
+
class WKWebViewWidget : public QWidget {
|
|
8
|
+
Q_OBJECT
|
|
9
|
+
public:
|
|
10
|
+
explicit WKWebViewWidget(QWidget* parent = nullptr);
|
|
11
|
+
~WKWebViewWidget() override;
|
|
12
|
+
|
|
13
|
+
Q_INVOKABLE void load(const QString& url);
|
|
14
|
+
Q_INVOKABLE void back();
|
|
15
|
+
Q_INVOKABLE void forward();
|
|
16
|
+
Q_INVOKABLE void stop();
|
|
17
|
+
Q_INVOKABLE void reload();
|
|
18
|
+
Q_INVOKABLE void evaluateJavaScript(const QString& script);
|
|
19
|
+
|
|
20
|
+
signals:
|
|
21
|
+
void loadFinished(bool ok);
|
|
22
|
+
void urlChanged(const QUrl& url);
|
|
23
|
+
void titleChanged(const QString& title);
|
|
24
|
+
void loadProgress(int percent);
|
|
25
|
+
void canGoBackChanged(bool);
|
|
26
|
+
void canGoForwardChanged(bool);
|
|
27
|
+
|
|
28
|
+
protected:
|
|
29
|
+
void showEvent(QShowEvent*) override;
|
|
30
|
+
void resizeEvent(QResizeEvent*) override;
|
|
31
|
+
|
|
32
|
+
private:
|
|
33
|
+
struct Impl; Impl* d = nullptr;
|
|
34
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#import <Cocoa/Cocoa.h>
|
|
2
|
+
#import <WebKit/WebKit.h>
|
|
3
|
+
|
|
4
|
+
#include "WKWebViewWidget.h"
|
|
5
|
+
|
|
6
|
+
#include <QtWidgets>
|
|
7
|
+
#include <QString>
|
|
8
|
+
#include <QUrl>
|
|
9
|
+
|
|
10
|
+
@class WKNavDelegate;
|
|
11
|
+
|
|
12
|
+
struct WKWebViewWidget::Impl {
|
|
13
|
+
WKWebView* wk = nil;
|
|
14
|
+
WKNavDelegate* delegate = nil;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
@interface WKNavDelegate : NSObject <WKNavigationDelegate>
|
|
18
|
+
@property(nonatomic, assign) WKWebViewWidget* owner;
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
@implementation WKNavDelegate
|
|
22
|
+
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
|
23
|
+
if (!self.owner) return;
|
|
24
|
+
emit self.owner->loadFinished(true);
|
|
25
|
+
if (webView.URL) emit self.owner->urlChanged(QUrl(QString::fromUtf8(webView.URL.absoluteString.UTF8String)));
|
|
26
|
+
if (webView.title) emit self.owner->titleChanged(QString::fromUtf8(webView.title.UTF8String));
|
|
27
|
+
emit self.owner->loadProgress(100);
|
|
28
|
+
emit self.owner->canGoBackChanged(webView.canGoBack);
|
|
29
|
+
emit self.owner->canGoForwardChanged(webView.canGoForward);
|
|
30
|
+
}
|
|
31
|
+
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
|
|
32
|
+
if (!self.owner) return;
|
|
33
|
+
emit self.owner->loadFinished(false);
|
|
34
|
+
emit self.owner->loadProgress(0);
|
|
35
|
+
emit self.owner->canGoBackChanged(webView.canGoBack);
|
|
36
|
+
emit self.owner->canGoForwardChanged(webView.canGoForward);
|
|
37
|
+
}
|
|
38
|
+
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
|
|
39
|
+
if (self.owner) emit self.owner->loadProgress(5);
|
|
40
|
+
}
|
|
41
|
+
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
|
|
42
|
+
if (self.owner) emit self.owner->loadProgress(50);
|
|
43
|
+
}
|
|
44
|
+
@end
|
|
45
|
+
|
|
46
|
+
static NSURL* toNSURL(QString u) {
|
|
47
|
+
u = u.trimmed();
|
|
48
|
+
if (!u.startsWith("http://") && !u.startsWith("https://")) u = "https://" + u;
|
|
49
|
+
return [NSURL URLWithString:[NSString stringWithUTF8String:u.toUtf8().constData()]];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
WKWebViewWidget::WKWebViewWidget(QWidget* parent)
|
|
53
|
+
: QWidget(parent), d(new Impl) {
|
|
54
|
+
setAttribute(Qt::WA_NativeWindow, true);
|
|
55
|
+
(void)winId();
|
|
56
|
+
|
|
57
|
+
NSView* nsParent = (__bridge NSView*)reinterpret_cast<void*>(winId());
|
|
58
|
+
WKWebViewConfiguration* cfg = [[WKWebViewConfiguration alloc] init];
|
|
59
|
+
if ([cfg respondsToSelector:@selector(defaultWebpagePreferences)]) {
|
|
60
|
+
cfg.defaultWebpagePreferences.allowsContentJavaScript = YES;
|
|
61
|
+
}
|
|
62
|
+
d->wk = [[WKWebView alloc] initWithFrame:nsParent.bounds configuration:cfg];
|
|
63
|
+
d->wk.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
64
|
+
[nsParent addSubview:d->wk];
|
|
65
|
+
|
|
66
|
+
d->delegate = [WKNavDelegate new];
|
|
67
|
+
d->delegate.owner = this;
|
|
68
|
+
[d->wk setNavigationDelegate:d->delegate];
|
|
69
|
+
|
|
70
|
+
load(QStringLiteral("https://example.org"));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
WKWebViewWidget::~WKWebViewWidget() {
|
|
74
|
+
if (!d) return;
|
|
75
|
+
if (d->wk) { [d->wk removeFromSuperview]; d->wk = nil; }
|
|
76
|
+
d->delegate = nil;
|
|
77
|
+
delete d; d = nullptr;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
void WKWebViewWidget::showEvent(QShowEvent* e) { QWidget::showEvent(e); /* opzionale */ }
|
|
81
|
+
void WKWebViewWidget::resizeEvent(QResizeEvent* e) { QWidget::resizeEvent(e); /* opzionale */ }
|
|
82
|
+
|
|
83
|
+
void WKWebViewWidget::load(const QString& url) {
|
|
84
|
+
if (d && d->wk) [d->wk loadRequest:[NSURLRequest requestWithURL:toNSURL(url)]];
|
|
85
|
+
}
|
|
86
|
+
void WKWebViewWidget::back() { if (d && d->wk && d->wk.canGoBack) [d->wk goBack]; }
|
|
87
|
+
void WKWebViewWidget::forward() { if (d && d->wk && d->wk.canGoForward) [d->wk goForward]; }
|
|
88
|
+
void WKWebViewWidget::stop() { if (d && d->wk) [d->wk stopLoading:nil]; }
|
|
89
|
+
void WKWebViewWidget::reload() { if (d && d->wk) [d->wk reload]; }
|
|
90
|
+
void WKWebViewWidget::evaluateJavaScript(const QString& script) {
|
|
91
|
+
if (!d || !d->wk) return;
|
|
92
|
+
NSString* s = [NSString stringWithUTF8String:script.toUtf8().constData()];
|
|
93
|
+
[d->wk evaluateJavaScript:s completionHandler:^(id result, NSError* error){
|
|
94
|
+
Q_UNUSED(result); Q_UNUSED(error);
|
|
95
|
+
}];
|
|
96
|
+
}
|