tkinter-web-backend 0.1.0__py3-none-any.whl

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.
Files changed (84) hide show
  1. tkinter_web/__init__.py +89 -0
  2. tkinter_web/__main__.py +69 -0
  3. tkinter_web/_tkinter.py +321 -0
  4. tkinter_web/_vendor/LICENSE-Python.txt +277 -0
  5. tkinter_web/_vendor/README.md +9 -0
  6. tkinter_web/_vendor/tkinter/__init__.py +4988 -0
  7. tkinter_web/_vendor/tkinter/__main__.py +7 -0
  8. tkinter_web/_vendor/tkinter/colorchooser.py +86 -0
  9. tkinter_web/_vendor/tkinter/commondialog.py +53 -0
  10. tkinter_web/_vendor/tkinter/constants.py +110 -0
  11. tkinter_web/_vendor/tkinter/dialog.py +49 -0
  12. tkinter_web/_vendor/tkinter/dnd.py +324 -0
  13. tkinter_web/_vendor/tkinter/filedialog.py +492 -0
  14. tkinter_web/_vendor/tkinter/font.py +239 -0
  15. tkinter_web/_vendor/tkinter/messagebox.py +146 -0
  16. tkinter_web/_vendor/tkinter/scrolledtext.py +56 -0
  17. tkinter_web/_vendor/tkinter/simpledialog.py +441 -0
  18. tkinter_web/_vendor/tkinter/ttk.py +1647 -0
  19. tkinter_web/app.py +358 -0
  20. tkinter_web/client/fonts/LICENSE-DejaVu.txt +47 -0
  21. tkinter_web/client/fonts/mono-bold-italic.woff2 +0 -0
  22. tkinter_web/client/fonts/mono-bold.woff2 +0 -0
  23. tkinter_web/client/fonts/mono-italic.woff2 +0 -0
  24. tkinter_web/client/fonts/mono.woff2 +0 -0
  25. tkinter_web/client/fonts/sans-bold-italic.woff2 +0 -0
  26. tkinter_web/client/fonts/sans-bold.woff2 +0 -0
  27. tkinter_web/client/fonts/sans-italic.woff2 +0 -0
  28. tkinter_web/client/fonts/sans.woff2 +0 -0
  29. tkinter_web/client/fonts/serif-bold-italic.woff2 +0 -0
  30. tkinter_web/client/fonts/serif-bold.woff2 +0 -0
  31. tkinter_web/client/fonts/serif-italic.woff2 +0 -0
  32. tkinter_web/client/fonts/serif.woff2 +0 -0
  33. tkinter_web/client/index.html +17 -0
  34. tkinter_web/client/js/bitmaps.js +82 -0
  35. tkinter_web/client/js/main.js +1427 -0
  36. tkinter_web/client/js/paint.js +694 -0
  37. tkinter_web/client/tkweb.css +342 -0
  38. tkinter_web/embed.py +204 -0
  39. tkinter_web/page.py +49 -0
  40. tkinter_web/tk/LICENSE-Tk.txt +40 -0
  41. tkinter_web/tk/__init__.py +11 -0
  42. tkinter_web/tk/_keysyms.py +547 -0
  43. tkinter_web/tk/_specs.py +1137 -0
  44. tkinter_web/tk/colors.json +1 -0
  45. tkinter_web/tk/dialogs.py +554 -0
  46. tkinter_web/tk/events.py +458 -0
  47. tkinter_web/tk/fontmetrics.json +1 -0
  48. tkinter_web/tk/fonts.py +467 -0
  49. tkinter_web/tk/geometry.py +1170 -0
  50. tkinter_web/tk/images.py +875 -0
  51. tkinter_web/tk/input.py +361 -0
  52. tkinter_web/tk/interp.py +1287 -0
  53. tkinter_web/tk/layout.py +192 -0
  54. tkinter_web/tk/options.py +368 -0
  55. tkinter_web/tk/render.py +258 -0
  56. tkinter_web/tk/tcl.py +262 -0
  57. tkinter_web/tk/ttk/__init__.py +16 -0
  58. tkinter_web/tk/ttk/style.py +357 -0
  59. tkinter_web/tk/ttk/treeview.py +817 -0
  60. tkinter_web/tk/ttk/widgets.py +1818 -0
  61. tkinter_web/tk/variables.py +152 -0
  62. tkinter_web/tk/widgets/__init__.py +89 -0
  63. tkinter_web/tk/widgets/base.py +255 -0
  64. tkinter_web/tk/widgets/buttons.py +452 -0
  65. tkinter_web/tk/widgets/canvas.py +1643 -0
  66. tkinter_web/tk/widgets/entry.py +752 -0
  67. tkinter_web/tk/widgets/frame.py +189 -0
  68. tkinter_web/tk/widgets/listbox.py +450 -0
  69. tkinter_web/tk/widgets/menu.py +723 -0
  70. tkinter_web/tk/widgets/message.py +85 -0
  71. tkinter_web/tk/widgets/panedwindow.py +249 -0
  72. tkinter_web/tk/widgets/scale.py +423 -0
  73. tkinter_web/tk/widgets/scrollbar.py +224 -0
  74. tkinter_web/tk/widgets/text.py +1546 -0
  75. tkinter_web/tk/winfo.py +147 -0
  76. tkinter_web/tk/wm.py +236 -0
  77. tkinter_web/transport/__init__.py +1 -0
  78. tkinter_web/transport/base.py +34 -0
  79. tkinter_web/transport/port.py +88 -0
  80. tkinter_web/transport/websocket.py +142 -0
  81. tkinter_web_backend-0.1.0.dist-info/METADATA +247 -0
  82. tkinter_web_backend-0.1.0.dist-info/RECORD +84 -0
  83. tkinter_web_backend-0.1.0.dist-info/WHEEL +4 -0
  84. tkinter_web_backend-0.1.0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,89 @@
1
+ """
2
+ tkinter-web-backend: a web backend for tkinter.
3
+
4
+ tkinter programs run unchanged; their windows are displayed in a web
5
+ browser. A Tk engine written in Python replaces Tcl/Tk (the _tkinter
6
+ module): it keeps the state of the windows, computes their geometry and
7
+ sends display operations to the browser, which sends back user input.
8
+
9
+ Use it with ``python -m tkinter_web script.py``, or connect it from the
10
+ program (commenting the second line gives back the native Tk)::
11
+
12
+ import tkinter_web
13
+ tkinter_web.install()
14
+ """
15
+
16
+ import sys
17
+
18
+ __version__ = "0.1.0"
19
+ __all__ = ["install", "uninstall", "is_installed"]
20
+
21
+
22
+ def install():
23
+ """Make tkinter use tkinter-web-backend.
24
+
25
+ It can be called before or after importing tkinter, but before creating
26
+ any window.
27
+ """
28
+ from . import _tkinter
29
+
30
+ current = sys.modules.get("_tkinter")
31
+ if current is _tkinter:
32
+ return
33
+ tkinter = sys.modules.get("tkinter")
34
+ if tkinter is not None and current is not None:
35
+ if getattr(tkinter, "_default_root", None) is not None:
36
+ raise RuntimeError("a tkinter window already exists with the native Tk: "
37
+ "call tkinter_web.install() before creating it")
38
+ _rebind(current, _tkinter)
39
+ sys.modules["_tkinter"] = _tkinter
40
+ _ensure_tkinter_package()
41
+
42
+
43
+ def _rebind(old, new):
44
+ """Point the modules already imported (tkinter's, the program's after
45
+ ``from tkinter import *``...) at the new _tkinter."""
46
+ replace = {id(old): new}
47
+ for name, value in vars(old).items():
48
+ if not name.startswith("__") and hasattr(new, name) and \
49
+ not isinstance(value, (int, float, str)):
50
+ replace[id(value)] = getattr(new, name)
51
+ for name, module in list(sys.modules.items()):
52
+ namespace = getattr(module, "__dict__", None)
53
+ if namespace is None or module is old:
54
+ continue
55
+ for key, value in list(namespace.items()):
56
+ if id(value) in replace:
57
+ namespace[key] = replace[id(value)]
58
+ tkinter = sys.modules["tkinter"]
59
+ tkinter.TkVersion = float(new.TK_VERSION)
60
+ tkinter.TclVersion = float(new.TCL_VERSION)
61
+
62
+
63
+ def uninstall():
64
+ from . import _tkinter
65
+
66
+ if sys.modules.get("_tkinter") is _tkinter:
67
+ del sys.modules["_tkinter"]
68
+ for name in [n for n in sys.modules if n == "tkinter" or n.startswith("tkinter.")]:
69
+ del sys.modules[name]
70
+
71
+
72
+ def is_installed():
73
+ from . import _tkinter
74
+
75
+ return sys.modules.get("_tkinter") is _tkinter
76
+
77
+
78
+ def _ensure_tkinter_package():
79
+ """Use the bundled copy of tkinter if the Python installation has none."""
80
+ import importlib.util
81
+ import os
82
+ from pathlib import Path
83
+
84
+ forced = os.environ.get("TKINTER_WEB_VENDORED", "") not in ("", "0")
85
+ if not forced and importlib.util.find_spec("tkinter") is not None:
86
+ return
87
+ vendor = str(Path(__file__).parent / "_vendor")
88
+ if vendor not in sys.path:
89
+ sys.path.insert(0, vendor)
@@ -0,0 +1,69 @@
1
+ """
2
+ Run a tkinter program with tkinter-web-backend:
3
+
4
+ python -m tkinter_web [options] script.py [args...]
5
+ python -m tkinter_web [options] -m module [args...]
6
+ python -m tkinter_web [options] (interactive console)
7
+ """
8
+
9
+ import argparse
10
+ import code
11
+ import os
12
+ import runpy
13
+ import sys
14
+
15
+ from . import __version__, install
16
+
17
+
18
+ def main(argv=None):
19
+ argv = list(sys.argv[1:] if argv is None else argv)
20
+ parser = argparse.ArgumentParser(
21
+ prog="python -m tkinter_web",
22
+ description="Run a tkinter program in a web browser.",
23
+ usage="%(prog)s [options] [-m module | script] [args ...]")
24
+ parser.add_argument("-m", dest="module", help="run a library module as a script")
25
+ parser.add_argument("--port", type=int, help="port of the local server (default: any)")
26
+ parser.add_argument("--no-browser", action="store_true",
27
+ help="do not open a web browser")
28
+ parser.add_argument("--version", action="version", version=f"tkinter-web-backend {__version__}")
29
+ # options end at the script or at the module name: what follows is theirs
30
+ options, rest = [], []
31
+ i = 0
32
+ while i < len(argv):
33
+ a = argv[i]
34
+ if a == "-m" and i + 1 < len(argv):
35
+ options += argv[i:i + 2]
36
+ rest = argv[i + 2:]
37
+ break
38
+ if a == "--port" and i + 1 < len(argv):
39
+ options += argv[i:i + 2]
40
+ i += 2
41
+ continue
42
+ if a.startswith("-") and a != "-":
43
+ options.append(a)
44
+ i += 1
45
+ continue
46
+ rest = argv[i:]
47
+ break
48
+ ns = parser.parse_args(options)
49
+ if ns.port is not None:
50
+ os.environ["TKINTER_WEB_PORT"] = str(ns.port)
51
+ if ns.no_browser:
52
+ os.environ["TKINTER_WEB_BROWSER"] = "0"
53
+ install()
54
+ if ns.module:
55
+ sys.argv = [ns.module] + rest
56
+ runpy.run_module(ns.module, run_name="__main__", alter_sys=True)
57
+ elif rest:
58
+ script = rest[0]
59
+ sys.argv = rest
60
+ sys.path.insert(0, os.path.dirname(os.path.abspath(script)))
61
+ runpy.run_path(script, run_name="__main__")
62
+ else:
63
+ sys.ps1 = ">>> "
64
+ code.interact(banner=f"tkinter-web-backend {__version__} (Python {sys.version.split()[0]})",
65
+ local={"__name__": "__main__"})
66
+
67
+
68
+ if __name__ == "__main__":
69
+ main()
@@ -0,0 +1,321 @@
1
+ """
2
+ A replacement of the _tkinter C extension module.
3
+
4
+ tkinter (the pure Python part of the standard library) talks to Tcl/Tk
5
+ through this module only: here, ``create()`` returns an application object
6
+ whose commands are executed by the tkinter_web engine, rendered in a web
7
+ browser instead of a native window.
8
+ """
9
+
10
+ import sys
11
+ import threading
12
+
13
+ from .tk.tcl import TclError, get_boolean, get_double, get_int, split_list, to_str
14
+ from .tk.interp import TK_VERSION
15
+
16
+ __all__ = ["create", "TclError", "TkappType", "Tcl_Obj"]
17
+
18
+ TK_VERSION = TK_VERSION
19
+ TCL_VERSION = TK_VERSION
20
+
21
+ READABLE = 2
22
+ WRITABLE = 4
23
+ EXCEPTION = 8
24
+
25
+ WINDOW_EVENTS = 4
26
+ FILE_EVENTS = 8
27
+ TIMER_EVENTS = 16
28
+ IDLE_EVENTS = 32
29
+ ALL_EVENTS = -3
30
+ DONT_WAIT = 2
31
+
32
+ _busywaitinterval = 20
33
+
34
+
35
+ def setbusywaitinterval(ms):
36
+ global _busywaitinterval
37
+ _busywaitinterval = int(ms)
38
+
39
+
40
+ def getbusywaitinterval():
41
+ return _busywaitinterval
42
+
43
+
44
+ class Tcl_Obj:
45
+ """Tcl objects returned without conversion (rare with tkinter_web)."""
46
+
47
+ def __init__(self, value, typename="string"):
48
+ self._value = value
49
+ self.typename = typename
50
+
51
+ @property
52
+ def string(self):
53
+ return to_str(self._value)
54
+
55
+ def __str__(self):
56
+ return to_str(self._value)
57
+
58
+ def __repr__(self):
59
+ return f"<{self.typename} object: {self.string!r}>"
60
+
61
+ def __eq__(self, other):
62
+ if isinstance(other, Tcl_Obj):
63
+ return self.string == other.string
64
+ return NotImplemented
65
+
66
+ def __hash__(self):
67
+ return hash(self.string)
68
+
69
+
70
+ class TkttObject:
71
+ def __init__(self, interp, ident):
72
+ self._interp = interp
73
+ self._id = ident
74
+
75
+ def deletetimerhandler(self):
76
+ self._interp.call("after", "cancel", self._id)
77
+
78
+
79
+ def _value(v):
80
+ """Values returned to tkinter (Python objects, as with wantobjects)."""
81
+ if isinstance(v, list):
82
+ return tuple(_value(x) for x in v)
83
+ if isinstance(v, tuple):
84
+ return tuple(_value(x) for x in v)
85
+ if v is None:
86
+ return ""
87
+ return v
88
+
89
+
90
+ _NATIVE = (str, int, float, bytes, bytearray, Tcl_Obj)
91
+
92
+
93
+ def _arg(a):
94
+ """Arguments of calls as _tkinter converts them (objects become strings)."""
95
+ if isinstance(a, _NATIVE):
96
+ return a
97
+ if isinstance(a, (tuple, list)):
98
+ return tuple(_arg(x) for x in a)
99
+ if a is None:
100
+ return ""
101
+ return str(a)
102
+
103
+
104
+ class TkappType:
105
+ """The Tcl/Tk application of a Tk() window."""
106
+
107
+ def __init__(self, screenName=None, baseName=None, className="Tk", interactive=False,
108
+ wantobjects=1, useTk=True, sync=False, use=None):
109
+ from .app import get_app
110
+ self._app = get_app()
111
+ self._interp = self._app.new_interp(className or "Tk", baseName or "tk")
112
+ if not useTk:
113
+ # Tcl(): an interpreter without a main window
114
+ self._interp.root.wm["state"] = "withdrawn"
115
+ self._wantobjects = wantobjects
116
+ self._trace = None
117
+ self._thread = threading.get_ident()
118
+
119
+ # -- evaluation
120
+
121
+ def call(self, *args):
122
+ if len(args) == 1 and isinstance(args[0], tuple):
123
+ args = args[0]
124
+ if None in args:
125
+ # as _tkinter: None ends the argument list
126
+ args = args[: args.index(None)]
127
+ args = tuple(_arg(a) for a in args)
128
+ if self._trace is not None:
129
+ self._trace(args)
130
+ with self._app.lock:
131
+ return _value(self._interp.call(*args))
132
+
133
+ def eval(self, script):
134
+ if self._trace is not None:
135
+ self._trace((script,))
136
+ with self._app.lock:
137
+ return _value(self._interp.eval(script))
138
+
139
+ def evalfile(self, filename):
140
+ return ""
141
+
142
+ def record(self, script):
143
+ return self.eval(script)
144
+
145
+ def adderrorinfo(self, msg):
146
+ pass
147
+
148
+ # -- variables
149
+
150
+ def _varname(self, args):
151
+ if len(args) >= 2 and isinstance(args[1], str) and args[1] and len(args) == 3:
152
+ return f"{args[0]}({args[1]})"
153
+ return to_str(args[0])
154
+
155
+ def setvar(self, *args):
156
+ if len(args) == 3:
157
+ name, value = f"{args[0]}({args[1]})", args[2]
158
+ elif len(args) == 2:
159
+ name, value = to_str(args[0]), args[1]
160
+ else:
161
+ raise TypeError("setvar requires 2 or 3 arguments")
162
+ with self._app.lock:
163
+ self._interp.variables.set(name, value)
164
+
165
+ globalsetvar = setvar
166
+
167
+ def getvar(self, *args):
168
+ name = f"{args[0]}({args[1]})" if len(args) == 2 and args[1] else to_str(args[0])
169
+ with self._app.lock:
170
+ return _value(self._interp.variables.get(name))
171
+
172
+ globalgetvar = getvar
173
+
174
+ def unsetvar(self, *args):
175
+ name = f"{args[0]}({args[1]})" if len(args) == 2 and args[1] else to_str(args[0])
176
+ with self._app.lock:
177
+ self._interp.variables.unset(name)
178
+
179
+ globalunsetvar = unsetvar
180
+
181
+ # -- conversions
182
+
183
+ def getint(self, arg):
184
+ if isinstance(arg, Tcl_Obj):
185
+ arg = arg.string
186
+ if isinstance(arg, float):
187
+ raise TypeError("getint() argument must be str or int")
188
+ return get_int(arg)
189
+
190
+ def getdouble(self, arg):
191
+ if isinstance(arg, Tcl_Obj):
192
+ arg = arg.string
193
+ return get_double(arg)
194
+
195
+ def getboolean(self, arg):
196
+ if isinstance(arg, Tcl_Obj):
197
+ arg = arg.string
198
+ return bool(get_boolean(arg))
199
+
200
+ def exprstring(self, s):
201
+ return to_str(s)
202
+
203
+ def exprlong(self, s):
204
+ return get_int(s)
205
+
206
+ def exprdouble(self, s):
207
+ return get_double(s)
208
+
209
+ def exprboolean(self, s):
210
+ return bool(get_boolean(s))
211
+
212
+ def splitlist(self, arg):
213
+ if isinstance(arg, Tcl_Obj):
214
+ arg = arg.string
215
+ if isinstance(arg, (tuple, list)):
216
+ return tuple(arg)
217
+ if isinstance(arg, str):
218
+ return split_list(arg)
219
+ if isinstance(arg, (bytes, bytearray)):
220
+ return split_list(arg.decode())
221
+ return (arg,)
222
+
223
+ def split(self, arg):
224
+ if isinstance(arg, str):
225
+ items = split_list(arg)
226
+ if len(items) == 1 and items[0] == arg:
227
+ return arg
228
+ return tuple(self.split(i) if " " in i else i for i in items)
229
+ return arg
230
+
231
+ # -- commands
232
+
233
+ def createcommand(self, name, func):
234
+ if not callable(func):
235
+ raise TypeError("command not callable")
236
+ with self._app.lock:
237
+ self._interp.commands[to_str(name)] = func
238
+
239
+ def deletecommand(self, name):
240
+ with self._app.lock:
241
+ if self._interp.commands.pop(to_str(name), None) is None:
242
+ raise TclError(f"can't delete Tcl command")
243
+
244
+ def createfilehandler(self, file, mask, func):
245
+ raise NotImplementedError("createfilehandler is not supported by tkinter-web-backend")
246
+
247
+ def deletefilehandler(self, file):
248
+ pass
249
+
250
+ def createtimerhandler(self, milliseconds, func):
251
+ name = f"::tkinter_web_timer{id(func)}"
252
+ interp = self._interp
253
+
254
+ def run():
255
+ interp.commands.pop(name, None)
256
+ func()
257
+
258
+ with self._app.lock:
259
+ interp.commands[name] = run
260
+ ident = interp.call("after", milliseconds, name)
261
+ return TkttObject(interp, ident)
262
+
263
+ # -- event loop
264
+
265
+ def mainloop(self, threshold=0):
266
+ if threading.get_ident() != self._thread:
267
+ raise RuntimeError("Calling Tcl from different apartment")
268
+ self._app.mainloop(self._interp, threshold)
269
+
270
+ def dooneevent(self, flags=0):
271
+ self._app.step(block=not flags & DONT_WAIT)
272
+ return 1
273
+
274
+ def quit(self):
275
+ self._app.quit()
276
+
277
+ def interpaddr(self):
278
+ return id(self._interp)
279
+
280
+ def loadtk(self):
281
+ pass
282
+
283
+ def willdispatch(self):
284
+ pass
285
+
286
+ def wantobjects(self, *args):
287
+ if args:
288
+ self._wantobjects = args[0]
289
+ return None
290
+ return self._wantobjects
291
+
292
+ def settrace(self, func):
293
+ self._trace = func
294
+
295
+ def gettrace(self):
296
+ return self._trace
297
+
298
+ # access to the engine (tests, debugging)
299
+ @property
300
+ def tkinter_web_interp(self):
301
+ return self._interp
302
+
303
+
304
+ def create(screenName=None, baseName=None, className="Tk", interactive=False,
305
+ wantobjects=1, useTk=True, sync=False, use=None):
306
+ return TkappType(screenName, baseName, className, interactive, wantobjects,
307
+ useTk, sync, use)
308
+
309
+
310
+ def _flatten(item):
311
+ res = []
312
+
313
+ def walk(x):
314
+ if isinstance(x, (tuple, list)):
315
+ for y in x:
316
+ walk(y)
317
+ elif x is not None:
318
+ res.append(x)
319
+
320
+ walk(item)
321
+ return tuple(res)