File: state.py

package info (click to toggle)
python-pywebview 6.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,436 kB
  • sloc: python: 10,230; javascript: 3,185; java: 522; cs: 130; sh: 16; makefile: 3
file content (105 lines) | stat: -rw-r--r-- 3,161 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import json
from typing import Any, Callable
from typing_extensions import Self
from threading import Thread

try:
    from enum import StrEnum # Python 3.11 and above
except ImportError:
    from enum import Enum

    class StrEnum(str, Enum):
        pass


class StateEventType(StrEnum):
    CHANGE = 'change'
    DELETE = 'delete'


class State(dict):

    _serializable = False

    def __init__(self, window):
        self.__event_handlers = []
        self.__window = window

    def __update_js(self, key, value):
        special_key = '__pywebviewHaltUpdate__' + key
        script = f"window.pywebview.state.{special_key} = JSON.parse('{json.dumps(value)}')"
        self.__window.run_js(script)

    def __notify_handlers(self, type, key, value=None):
        def notify_handlers():
            for handler in self.__event_handlers:
                if callable(handler):
                    if type == StateEventType.CHANGE:
                        handler(type, key, value)
                    elif type == StateEventType.DELETE:
                        handler(type, key, value)

        t = Thread(target=notify_handlers)
        t.start()

    def __setattr__(self, key, value, should_update_js=True):
        def update_and_notify():
            self.__update_js(key, value)
            self.__notify_handlers(StateEventType.CHANGE, key, value)

        if key.startswith('_State__'):
            super().__setattr__(key, value)
            return

        if key not in self or self[key] != value:
            self[key] = value

            if not should_update_js:
                self.__notify_handlers(key, value)
                return

            if self.__window.events.loaded.is_set():
                update_and_notify()
            else:
                self.__window.events.loaded += update_and_notify

    def __getattr__(self, key):
        if key in self:
            return self[key]
        raise AttributeError(f"'{type(self).__key__}' object has no attribute '{key}'")

    def __delattr__(self, key):
        if key.startswith('__pywebviewHaltUpdate__'):
            key = key.replace('__pywebviewHaltUpdate__', '')
            halt_update = True
        else:
            halt_update = False

        if key in self:
            old_value = self[key]
            del self[key]

            if not halt_update:
                special_key = '__pywebviewHaltUpdate__' + key
                self.__window.run_js(f"delete window.pywebview.state.{special_key}")

            self.__notify_handlers(StateEventType.DELETE, key, old_value)

        else:
            raise AttributeError(f"'{type(self).__key__}' object has no attribute '{key}'")

    def __add__(self, item: Callable[..., Any]) -> Self:
        self.__event_handlers.append(item)
        return self

    def __sub__(self, item: Callable[..., Any]) -> Self:
        self.__event_handlers.remove(item)
        return self

    def __iadd__(self, item: Callable[..., Any]) -> Self:
        self.__event_handlers.append(item)
        return self

    def __isub__(self, item: Callable[..., Any]) -> Self:
        self.__event_handlers.remove(item)
        return self