Skip to content

Commit 19f3d4c

Browse files
webui_set_file_handler implemented
webui_set_file_handler implemented
2 parents c4e5d39 + 1b8a4dd commit 19f3d4c

29 files changed

+18256
-111
lines changed

PyPI/Package/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Python WebUI v2.5.5
1+
# Python WebUI v2.5.6
22

33
> Use any web browser as GUI, with Python in the backend and HTML5 in the frontend, all in a lightweight Python package.
44

PyPI/Package/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "webui2"
7-
version = "2.5.5"
7+
version = "2.5.6"
88
authors = [
99
{ name="Hassan Draga" },
1010
]

PyPI/Package/src/webui/webui.py

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Python WebUI v2.5.5
1+
# Python WebUI v2.5.6
22
#
33
# http://webui.me
44
# https://github.com/webui-dev/python-webui
@@ -20,10 +20,6 @@
2020
from . import webui_bindings as _raw
2121

2222

23-
# C function type for the file handler window
24-
filehandler_window_callback = CFUNCTYPE(c_void_p, c_size_t, c_char_p, POINTER(c_int))
25-
26-
2723
# == Enums ====================================================================
2824

2925

@@ -635,8 +631,8 @@ def __init__(self, window_id: Optional[int] = None):
635631
self._cb_func_list: dict = {}
636632

637633
# gets used for both filehandler and filehandler_window, should wipe out the other just how it does in C
638-
self._file_handler_cb: Any = None
639-
self._buffers: list = []
634+
self._file_handler_cb = None
635+
self._buffers = []
640636

641637
# -- dispatcher for function bindings -----------
642638
def _make_dispatcher(self):
@@ -895,58 +891,64 @@ def set_root_folder(self, path: str) -> bool:
895891
"""
896892
return bool(_raw.webui_set_root_folder(c_size_t(self._window), path.encode("utf-8")))
897893

898-
# -- set_file_handler --------------------------- # TODO: still errors on call to c bind
899-
# def set_file_handler(self, handler: Callable[[str], Optional[str]]) -> None:
900-
# """Set a custom file handler for serving files.
901-
#
902-
# This function registers a custom file handler that processes file requests
903-
# and serves HTTP responses. The handler must return a full HTTP response
904-
# (headers and body) as a UTF-8 encoded string. Setting a new handler overrides
905-
# any previously registered file handler.
906-
#
907-
# Args:
908-
# handler (Callable[[str], str]): A function that takes a filename as input
909-
# and returns a complete HTTP response as a string.
910-
#
911-
# Returns:
912-
# None
913-
#
914-
# Example:
915-
# def my_handler(filename: str) -> str:
916-
# response_body = "Hello, World!"
917-
# response_headers = (
918-
# "HTTP/1.1 200 OK\r\n"
919-
# "Content-Type: text/plain\r\n"
920-
# f"Content-Length: {len(response_body)}\r\n"
921-
# "\r\n"
922-
# )
923-
# return response_headers + response_body
924-
#
925-
# my_window.set_file_handler(my_handler)
926-
# """
927-
# def _internal_file_handler(filename_ptr: c_char_p, length_ptr: POINTER(c_int)) -> c_void_p:
928-
# """
929-
# Internal C callback that matches the signature required by webui_set_file_handler_window.
930-
# """
931-
# # Decode the incoming filename from C
932-
# filename = filename_ptr.decode('utf-8') if filename_ptr else ""
933-
#
934-
# # Call the Python-level handler to get the HTTP response
935-
# response_bytes = handler(filename).encode("utf-8")
936-
#
937-
# # Create a ctypes buffer from the Python bytes; this buffer must remain alive
938-
# # at least until WebUI is done with it.
939-
# buf = create_string_buffer(response_bytes)
940-
#
941-
# # Set the length (the int* that C expects)
942-
# length_ptr[0] = len(response_bytes)
943-
#
944-
# # Return a pointer (void*) to the buffer
945-
# return cast(buf, c_void_p)
946-
#
947-
# # Keep a reference so it doesn't get garbage collected
948-
# self._file_handler_cb = filehandler_window_callback(_internal_file_handler)
949-
# _raw.webui_set_file_handler_window(c_size_t(self._window), _raw.FILE_HANDLER_CB(self._file_handler_cb))
894+
# -- set_file_handler ---------------------------
895+
def set_file_handler(self, handler: Callable[[str], Optional[str]]) -> None:
896+
"""Set a custom file handler for serving files.
897+
898+
This function registers a custom file handler that processes file requests
899+
and serves HTTP responses. The handler must return a full HTTP response
900+
(headers and body) as a UTF-8 encoded string. Setting a new handler overrides
901+
any previously registered file handler.
902+
903+
Args:
904+
handler (Callable[[str], str]): A function that takes a filename as input
905+
and returns a complete HTTP response as a string.
906+
907+
Returns:
908+
None
909+
910+
Example:
911+
def my_handler(filename: str) -> Optional[str]:
912+
response_body = "Hello, World!"
913+
response_headers = (
914+
"HTTP/1.1 200 OK\r\n"
915+
"Content-Type: text/plain\r\n"
916+
f"Content-Length: {len(response_body)}\r\n"
917+
"\r\n"
918+
)
919+
return response_headers + response_body
920+
921+
my_window.set_file_handler(my_handler)
922+
"""
923+
# _raw bindings moved here due to CFUNCTYPE factory conflicts
924+
callback_handler_type = CFUNCTYPE(c_void_p, c_char_p, POINTER(c_int))
925+
_raw.webui_set_file_handler.argtypes = [c_size_t, callback_handler_type]
926+
_raw.webui_set_file_handler.restype = None
927+
928+
self._buffers.clear()
929+
930+
def _c_handler(filename_ptr, length_ptr):
931+
path = filename_ptr.decode("utf-8")
932+
response_str = handler(path)
933+
934+
# None, tells WebUI to look for files elsewhere
935+
if response_str is None:
936+
length_ptr[0] = 0
937+
return 0
938+
939+
data = response_str.encode("latin-1")
940+
length_ptr[0] = len(data)
941+
942+
# allocate and pin
943+
buf = create_string_buffer(data)
944+
self._buffers.append(buf)
945+
946+
# return the raw address as an integer
947+
return addressof(buf)
948+
949+
# Keep a reference so it doesn't get garbage collected
950+
self._file_handler_cb = callback_handler_type(_c_handler)
951+
_raw.webui_set_file_handler(self._window, self._file_handler_cb)
950952

951953

952954
# -- set_file_handler_window -------------------- # TODO: still errors on call to c bind

PyPI/Package/src/webui/webui_bindings.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class WebuiConfig(enum.IntEnum):
7474

7575
show_wait_connection = 0
7676
"""
77-
Control if 'webui_show()', 'webui_show_browser()' and
77+
Control if 'webui_show()', ' # TODO: still errors on call to c bind # TODO: still errors on call to c bindrowser()' and
7878
'webui_show_wv()' should wait for the window to connect
7979
before returns or not.
8080
@@ -643,7 +643,7 @@ class WebuiEventT(Structure):
643643
webui_set_default_root_folder.restype = c_bool
644644

645645

646-
# -- set_file_handler---------------------------- # TODO: testing required
646+
# -- set_file_handler----------------------------
647647
webui_set_file_handler = webui_lib.webui_set_file_handler
648648
"""
649649
brief:
@@ -660,12 +660,9 @@ class WebuiEventT(Structure):
660660
C Signature:
661661
WEBUI_EXPORT void webui_set_file_handler(size_t window, const void* (*handler)(const char* filename, int* length));
662662
"""
663-
FILE_HANDLER_CB = CFUNCTYPE(c_void_p, c_char_p, POINTER(c_int))
664-
webui_set_file_handler.argtypes = [
665-
c_size_t, # size_t window
666-
FILE_HANDLER_CB
667-
]
668-
webui_set_file_handler.restype = None
663+
# Due to wierd CFUNCTYPE instancing, the argtypes and restype
664+
# are in the wrapper function to keep a single instance of the
665+
# CFUNCTYPE factory.
669666

670667

671668
# -- set_file_handler_window -------------------- # TODO: python wrapper
@@ -686,7 +683,7 @@ class WebuiEventT(Structure):
686683
WEBUI_EXPORT void webui_set_file_handler_window(size_t window, const void* (*handler)(size_t window, const char* filename, int* length));
687684
"""
688685
webui_set_file_handler.argtypes = [
689-
c_size_t, # size_t window
686+
c_size_t, # size_t window
690687
CFUNCTYPE(c_void_p,c_size_t, c_char_p, POINTER(c_int)) # const void* (*handler)(size_t window, const char* filename, int* length)
691688
]
692689
webui_set_file_handler.restype = None

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![Logo](https://raw.githubusercontent.com/webui-dev/webui-logo/main/webui_python.png)
44

5-
# Python-WebUI v2.5.5
5+
# Python-WebUI v2.5.6
66

77
[last-commit]: https://img.shields.io/github/last-commit/webui-dev/webui?style=for-the-badge&logo=github&logoColor=C0CAF5&labelColor=414868
88
[release-version]: https://img.shields.io/github/v/tag/webui-dev/webui?style=for-the-badge&logo=webtrees&logoColor=C0CAF5&labelColor=414868&color=7664C6

examples/react/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## React WebUI Example
2+
3+
This is a basic example of how to use WebUI with React to generate a portable single executable program. WebUI will run the internal web server and use any installed web browser as GUI to show the React UI.
4+
5+
__NOTE: To make an executable for python, look into `pyinstaller` or similar type libraries (pathing may have to be automated in some places to differentiate runtime and built file locations)__
6+
7+
![Screenshot](webui_react.png)
8+
9+
### How to use it?
10+
11+
1. Run script `build_react` to re-build the React project and run the pyhton file
12+
13+
### How to create a React WebUI project from scratch?
14+
15+
1. Run `npx create-react-app my-react-app` to create a React app using NPM
16+
2. Add `<script src="webui.js"></script>` into `public/index.html` to connect UI with the backend
17+
3. Copy or make your own vfs functions similar to how it's done in main.py
18+
4. Build the react-app portion with `npm run build`; This step must be done for every change you make to the react portion of the app
19+
5. Now, run `python main.py` or whatever your main entry script is.
20+
21+
### Other backend languages examples:
22+
23+
- Coming soon...

examples/react/build_react.bat

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@echo off
2+
3+
echo.
4+
echo * Build React project...
5+
6+
cd webui-react-example
7+
call npm install
8+
call npm run build
9+
cd ..
10+
11+
echo.
12+
echo * Running main.py
13+
14+
python main.py

examples/react/build_react.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
echo
2+
echo "* Build React project..."
3+
4+
cd webui-react-example
5+
npm install || exit
6+
npm run build || exit
7+
cd ..
8+
9+
echo
10+
echo "* Running main.py"
11+
12+
python3 main.py

0 commit comments

Comments
 (0)