SwiftGUI 0.11.18__tar.gz → 0.11.20__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.
- {swiftgui-0.11.18 → swiftgui-0.11.20}/PKG-INFO +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/pyproject.toml +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Base.py +74 -8
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/BasePopup.py +23 -13
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Text.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/_Base.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Combined_Elements/BaseCombinedElement.py +6 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Combined_Elements/Console.py +19 -6
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Combined_Elements/Form.py +104 -43
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Compat.py +0 -2
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Examples/AllElements.py +22 -6
- swiftgui-0.11.20/src/SwiftGUI/Exceptions.py +13 -0
- swiftgui-0.11.20/src/SwiftGUI/Extended_Elements/PythonConsole.py +299 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/TabFrame.py +2 -2
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/Table.py +3 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/GlobalOptions.py +16 -3
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Popups/SmallPopups.py +36 -29
- swiftgui-0.11.20/src/SwiftGUI/Popups/TooltipPopup.py +41 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Popups/__init__.py +1 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Themes/FourColors.py +3 -0
- swiftgui-0.11.20/src/SwiftGUI/Tooltips.py +112 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Button.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Checkbox.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Combobox.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Frame.py +10 -5
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/GridFrame.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Input.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Listbox.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Radiobutton.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Scale.py +2 -4
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Spinbox.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Text.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/TextField.py +4 -3
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Treeview.py +1 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Windows.py +34 -2
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/__init__.py +5 -1
- {swiftgui-0.11.18 → swiftgui-0.11.20}/LICENSE +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/README.md +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Arc.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Bitmap.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Element.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Image.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Line.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Oval.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Polygon.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/Rectangle.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Canvas_Elements/__init__.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Colors.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Combined_Elements/Calendar.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Combined_Elements/MultistateButton.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Debug.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/DecoratorKeys.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/ElementFlags.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Events.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Examples/AllThemes.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Examples/GlobalOptionBrowser.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Examples/Previews.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Examples/__init__.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/ColorChooserButton.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/ComboboxMapping.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/FileBrowseButton.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/Image.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/ImageButton.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/Separator.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extended_Elements/Spacer.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extras/SwiftGUI.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Extras/__init__.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Files/ConfigFile.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Files/ConfigurationEditor.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Files/DictFile.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Files/ProgramDirectory.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Files/__init__.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Fonts.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/KeyFunctions.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Literals.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Popups/ElementPopup.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Popups/ListPicker.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Popups/VirtualKeyboard.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Themes/Thematic.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Themes/_BaseTheme.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Themes/__init__.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Tools.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Utilities/Images.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Utilities/Threads.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Canvas.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/LabelFrame.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Notebook.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Progressbar.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/Scrollbar.py +0 -0
- {swiftgui-0.11.18 → swiftgui-0.11.20}/src/SwiftGUI/Widget_Elements/TKContainer.py +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from collections.abc import Iterable, Callable
|
|
2
1
|
from functools import wraps
|
|
3
|
-
from typing import Literal, Union, Any, Hashable, Protocol
|
|
2
|
+
from typing import Literal, Union, Any, Hashable, Protocol, Iterable, Callable, TypeVar
|
|
4
3
|
from SwiftGUI.Compat import Self
|
|
5
4
|
import tkinter as tk
|
|
6
5
|
|
|
7
|
-
from SwiftGUI import Event, GlobalOptions, Color, remove_None_vals, Literals
|
|
6
|
+
from SwiftGUI import Event, GlobalOptions, Color, remove_None_vals, Literals, ReuseError, RowTypeError, Tooltips
|
|
8
7
|
from SwiftGUI.ElementFlags import ElementFlag
|
|
9
8
|
#from SwiftGUI.Widget_Elements.Frame import Frame
|
|
10
9
|
|
|
@@ -52,6 +51,7 @@ class _BaseSharedAttributes:
|
|
|
52
51
|
key: Hashable = None # If given, this will be written to the event-value. Elements without a key can not throw key-events
|
|
53
52
|
_key_function: Callable | Iterable[Callable] = None # Saves the key-function(s), if given
|
|
54
53
|
|
|
54
|
+
same_class = TypeVar("same_class")
|
|
55
55
|
class BaseElement(_BaseSharedAttributes):
|
|
56
56
|
"""
|
|
57
57
|
Base for every element and widget.
|
|
@@ -59,11 +59,11 @@ class BaseElement(_BaseSharedAttributes):
|
|
|
59
59
|
The different to BaseWidget is that BaseWidget is designed for a single tk-Widget.
|
|
60
60
|
BaseElement allows you to do whatever the f you want, it's just a class pattern.
|
|
61
61
|
"""
|
|
62
|
-
parent:Self = None # next higher Element
|
|
63
|
-
_fake_tk_element:tk.Widget = None # This gets returned when parent is None
|
|
64
|
-
_element_flags:set[ElementFlag] # Properties the element has
|
|
62
|
+
parent: Self = None # next higher Element
|
|
63
|
+
_fake_tk_element: tk.Widget = None # This gets returned when parent is None
|
|
64
|
+
_element_flags: set[ElementFlag] # Properties the element has
|
|
65
65
|
|
|
66
|
-
defaults:type[GlobalOptions.DEFAULT_OPTIONS_CLASS] = GlobalOptions.EMPTY # Change this to apply a different default configuration
|
|
66
|
+
defaults: type[GlobalOptions.DEFAULT_OPTIONS_CLASS] = GlobalOptions.EMPTY # Change this to apply a different default configuration
|
|
67
67
|
|
|
68
68
|
# So you can use it on inheriting classes without importing it
|
|
69
69
|
_run_after_window_creation = run_after_window_creation
|
|
@@ -73,6 +73,7 @@ class BaseElement(_BaseSharedAttributes):
|
|
|
73
73
|
self._run_when_window_exists = list()
|
|
74
74
|
|
|
75
75
|
self._update_options = dict() # This saves all valuew passed by .update, so they can be recalled using .get_option()
|
|
76
|
+
self._is_used_up: bool = False # True, if self._init ran at least once, indicating that this element was part of a window once
|
|
76
77
|
|
|
77
78
|
def _init(self,parent:"BaseElement",window):
|
|
78
79
|
"""
|
|
@@ -84,6 +85,10 @@ class BaseElement(_BaseSharedAttributes):
|
|
|
84
85
|
:param window: Main Window
|
|
85
86
|
:return:
|
|
86
87
|
"""
|
|
88
|
+
if self._is_used_up:
|
|
89
|
+
raise ReuseError(f"Element was used in a window multiple times: {repr(self)}")
|
|
90
|
+
self._is_used_up = True
|
|
91
|
+
|
|
87
92
|
self._init_defaults() # Default configuration
|
|
88
93
|
self._flag_init()
|
|
89
94
|
|
|
@@ -91,7 +96,7 @@ class BaseElement(_BaseSharedAttributes):
|
|
|
91
96
|
self._personal_init()
|
|
92
97
|
|
|
93
98
|
self._apply_update()
|
|
94
|
-
self.add_flags(ElementFlag.IS_CREATED)
|
|
99
|
+
self.add_flags(ElementFlag.IS_CREATED)
|
|
95
100
|
#element_logger.debug(f"Initialized {repr(self)} in {window}")
|
|
96
101
|
|
|
97
102
|
def _flag_init(self):
|
|
@@ -378,6 +383,52 @@ class BaseElement(_BaseSharedAttributes):
|
|
|
378
383
|
|
|
379
384
|
return self
|
|
380
385
|
|
|
386
|
+
_tooltip_text: str | None = None
|
|
387
|
+
_bound_tooltip_functions: bool = False # True, if the events for tooltips are already bound
|
|
388
|
+
@run_after_window_creation
|
|
389
|
+
def set_tooltip(self, text: str | None = None) -> Self:
|
|
390
|
+
"""
|
|
391
|
+
Add/Change/Remove a tooltip from this element.
|
|
392
|
+
|
|
393
|
+
Set it to None to deactivate tooltips for this element
|
|
394
|
+
|
|
395
|
+
:param text: Text for the tooltip to show
|
|
396
|
+
:return:
|
|
397
|
+
"""
|
|
398
|
+
self._tooltip_text = text
|
|
399
|
+
|
|
400
|
+
if not self._bound_tooltip_functions:
|
|
401
|
+
self._bound_tooltip_functions = True
|
|
402
|
+
aw = self.window.actual_window
|
|
403
|
+
aw.activate_tooltips()
|
|
404
|
+
self.bind_event(
|
|
405
|
+
"Enter",
|
|
406
|
+
key_function=Tooltips.mouse_entered_elem_callback,
|
|
407
|
+
).bind_event(
|
|
408
|
+
"Leave",
|
|
409
|
+
key_function=Tooltips.mouse_exited_elem_callback,
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
@property
|
|
413
|
+
def tooltip_text(self) -> str | None:
|
|
414
|
+
return self._tooltip_text
|
|
415
|
+
|
|
416
|
+
@tooltip_text.setter
|
|
417
|
+
def tooltip_text(self, text: str | None):
|
|
418
|
+
self.set_tooltip(text)
|
|
419
|
+
|
|
420
|
+
@classmethod
|
|
421
|
+
def with_go(cls: type[same_class], go_class: type[GlobalOptions.DEFAULT_OPTIONS_CLASS]) -> type[same_class]:
|
|
422
|
+
"""
|
|
423
|
+
Create this element with a different global-option-class
|
|
424
|
+
:param go_class: The global-option-class this object should apply
|
|
425
|
+
:return:
|
|
426
|
+
"""
|
|
427
|
+
class Modified(cls):
|
|
428
|
+
defaults = go_class
|
|
429
|
+
|
|
430
|
+
return Modified
|
|
431
|
+
|
|
381
432
|
class BaseWidget(BaseElement):
|
|
382
433
|
"""
|
|
383
434
|
Base for every Widget
|
|
@@ -766,6 +817,21 @@ class BaseWidgetContainer(BaseWidget):
|
|
|
766
817
|
self._line_insert_kwargs = dict()
|
|
767
818
|
self.add_flags(ElementFlag.IS_CONTAINER)
|
|
768
819
|
|
|
820
|
+
def _row_iter_to_list(self, row: Iterable[BaseElement | None]) -> list[BaseElement | None]:
|
|
821
|
+
"""
|
|
822
|
+
Convert a row to a list.
|
|
823
|
+
If the row can't be converted, a RowTypeError is raised
|
|
824
|
+
:param row:
|
|
825
|
+
:return:
|
|
826
|
+
"""
|
|
827
|
+
try:
|
|
828
|
+
return list(row)
|
|
829
|
+
except TypeError:
|
|
830
|
+
raise RowTypeError("\n\nA row in one of your layouts is not a row.\n"
|
|
831
|
+
"\n"
|
|
832
|
+
f"This... {repr(self)}\n\n"
|
|
833
|
+
f"Contains this (should be a row)... {repr(row)}")
|
|
834
|
+
|
|
769
835
|
def update_all_containing(self, **kwargs) -> Self:
|
|
770
836
|
"""
|
|
771
837
|
Update all elements inside this frame
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import io
|
|
2
|
-
from collections.abc import Iterable
|
|
3
2
|
from os import PathLike
|
|
4
|
-
from typing import Hashable, Any, Literal
|
|
3
|
+
from typing import Hashable, Any, Literal, Generic, Iterable
|
|
4
|
+
from typing_extensions import TypeVar
|
|
5
5
|
from PIL import Image
|
|
6
6
|
|
|
7
7
|
import SwiftGUI as sg
|
|
8
|
-
from SwiftGUI import Color, ValueDict
|
|
8
|
+
from SwiftGUI import Color, ValueDict, ReuseError
|
|
9
9
|
from SwiftGUI.Compat import Self
|
|
10
10
|
|
|
11
11
|
class BasePopupNonblocking:
|
|
@@ -67,12 +67,14 @@ class BasePopupNonblocking:
|
|
|
67
67
|
"""
|
|
68
68
|
...
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
return_type = TypeVar("return_type", default=Any)
|
|
71
|
+
|
|
72
|
+
class BasePopupTyped(BasePopupNonblocking, Generic[return_type]):
|
|
71
73
|
def __init__(
|
|
72
74
|
self,
|
|
73
75
|
layout: Iterable[Iterable[sg.BaseElement]],
|
|
74
76
|
*,
|
|
75
|
-
default:
|
|
77
|
+
default: return_type = None, # Returned instead of None
|
|
76
78
|
keep_on_top: bool = True,
|
|
77
79
|
title: str = None,
|
|
78
80
|
titlebar: bool = None,
|
|
@@ -86,6 +88,7 @@ class BasePopup(BasePopupNonblocking):
|
|
|
86
88
|
|
|
87
89
|
self._return = None
|
|
88
90
|
self._default = default
|
|
91
|
+
self._used_up = False
|
|
89
92
|
|
|
90
93
|
super().__init__(
|
|
91
94
|
layout,
|
|
@@ -100,13 +103,7 @@ class BasePopup(BasePopupNonblocking):
|
|
|
100
103
|
**kwargs,
|
|
101
104
|
)
|
|
102
105
|
|
|
103
|
-
def
|
|
104
|
-
me = super().__new__(cls)
|
|
105
|
-
me.__init__(*args, **kwargs)
|
|
106
|
-
|
|
107
|
-
return me() # Run the popup and return the result
|
|
108
|
-
|
|
109
|
-
def done(self, val: Any = None):
|
|
106
|
+
def done(self, val: Any = None) -> return_type:
|
|
110
107
|
"""
|
|
111
108
|
Call this instead of return.
|
|
112
109
|
The popup will close and return_value is returned.
|
|
@@ -116,7 +113,7 @@ class BasePopup(BasePopupNonblocking):
|
|
|
116
113
|
self._return = val
|
|
117
114
|
self.w.close()
|
|
118
115
|
|
|
119
|
-
def __call__(self, *args, **kwargs):
|
|
116
|
+
def __call__(self, *args, **kwargs) -> return_type:
|
|
120
117
|
"""
|
|
121
118
|
Execute the popup-functionality.
|
|
122
119
|
YOU DON'T NEED TO CALL THIS!
|
|
@@ -125,9 +122,22 @@ class BasePopup(BasePopupNonblocking):
|
|
|
125
122
|
:param kwargs:
|
|
126
123
|
:return:
|
|
127
124
|
"""
|
|
125
|
+
if self._used_up:
|
|
126
|
+
raise ReuseError("You cannot re-open popup-instances. Create a new instance to re-use the popup.")
|
|
127
|
+
self._used_up = True
|
|
128
|
+
|
|
128
129
|
self.w.block_others_until_close()
|
|
129
130
|
|
|
130
131
|
if self._return is None:
|
|
131
132
|
return self._default
|
|
132
133
|
|
|
133
134
|
return self._return
|
|
135
|
+
|
|
136
|
+
class BasePopup(BasePopupTyped, Generic[return_type]):
|
|
137
|
+
|
|
138
|
+
def __new__(cls, *args, **kwargs) -> return_type:
|
|
139
|
+
me = super().__new__(cls)
|
|
140
|
+
me.__init__(*args, **kwargs)
|
|
141
|
+
|
|
142
|
+
return me() # Run the popup and return the result
|
|
143
|
+
|
|
@@ -89,7 +89,7 @@ class Text(Canvas_Elements.BaseCanvasElement):
|
|
|
89
89
|
new_font = font.Font(
|
|
90
90
|
self.canvas.tk_widget,
|
|
91
91
|
family=self._fonttype,
|
|
92
|
-
size=self._fontsize,
|
|
92
|
+
size=self._fontsize if self._fontsize else 8,
|
|
93
93
|
weight="bold" if self._bold else "normal",
|
|
94
94
|
slant="italic" if self._italic else "roman",
|
|
95
95
|
underline=bool(self._underline),
|
|
@@ -200,4 +200,4 @@ class BaseCanvasElement(sg.BaseWidget): # Inheritance mainly for the update-rout
|
|
|
200
200
|
return self._unflatten(self.canvas.tk_widget.bbox(self.canvas_id))
|
|
201
201
|
|
|
202
202
|
def __repr__(self):
|
|
203
|
-
return f"<sgc.{self.__class__.__name__} element at {id(self)}>"
|
|
203
|
+
return f"<sgc.{self.__class__.__name__} canvas-element with key={self.key} at {id(self)}>"
|
|
@@ -43,6 +43,7 @@ class BaseCombinedElement(MixinElementWithDefaultEvent, BaseElement):
|
|
|
43
43
|
frame = layout
|
|
44
44
|
else:
|
|
45
45
|
frame = Frame(layout)
|
|
46
|
+
self.frame = frame
|
|
46
47
|
|
|
47
48
|
if internal_key_system:
|
|
48
49
|
self._has_sublayout = True
|
|
@@ -233,3 +234,8 @@ class BaseCombinedElement(MixinElementWithDefaultEvent, BaseElement):
|
|
|
233
234
|
self.set_value(val)
|
|
234
235
|
|
|
235
236
|
return self
|
|
237
|
+
|
|
238
|
+
def _bind_event_to_widget(self, tk_event: str, event_function: Callable) -> Self:
|
|
239
|
+
self.frame.tk_widget.bind(tk_event, event_function)
|
|
240
|
+
|
|
241
|
+
|
|
@@ -14,6 +14,7 @@ class Console(sg.BaseCombinedElement):
|
|
|
14
14
|
*,
|
|
15
15
|
key: Hashable = None,
|
|
16
16
|
key_function: Callable | Iterable[Callable] = None,
|
|
17
|
+
input_text_color: str | sg.Color = None,
|
|
17
18
|
default_event: bool = False,
|
|
18
19
|
input_prefix: str = None,
|
|
19
20
|
print_prefix: str = None,
|
|
@@ -54,6 +55,7 @@ class Console(sg.BaseCombinedElement):
|
|
|
54
55
|
add_timestamp = add_timestamp,
|
|
55
56
|
width = width,
|
|
56
57
|
height = height,
|
|
58
|
+
input_text_color=input_text_color,
|
|
57
59
|
)
|
|
58
60
|
|
|
59
61
|
def _get_value(self) -> Any:
|
|
@@ -86,6 +88,16 @@ class Console(sg.BaseCombinedElement):
|
|
|
86
88
|
self._print_prefix = new_val
|
|
87
89
|
case "add_timestamp":
|
|
88
90
|
self._add_timestamp = new_val
|
|
91
|
+
case "input_text_color":
|
|
92
|
+
if new_val is None:
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
if self.window is None:
|
|
96
|
+
self.update_after_window_creation(input_text_color = new_val)
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
self.textField.tk_widget.tag_config("input", foreground=new_val)
|
|
100
|
+
|
|
89
101
|
case _: # No other case covered this key, so let's let's the parent-class handle the rest
|
|
90
102
|
return super()._update_special_key(key, new_val)
|
|
91
103
|
|
|
@@ -138,23 +150,24 @@ class Console(sg.BaseCombinedElement):
|
|
|
138
150
|
"""
|
|
139
151
|
self._prev_value = text
|
|
140
152
|
|
|
141
|
-
text = self._input_prefix + text
|
|
142
|
-
|
|
143
153
|
if self._add_timestamp:
|
|
144
|
-
|
|
154
|
+
self.append(self.get_time() + " ", add_newline=False)
|
|
155
|
+
|
|
156
|
+
self.append(self._input_prefix, add_newline=False)
|
|
145
157
|
|
|
146
|
-
self.append(text + "\n", add_newline= False)
|
|
158
|
+
self.append(text + "\n", add_newline= False, tag="input")
|
|
147
159
|
|
|
148
160
|
return self
|
|
149
161
|
|
|
150
|
-
def append(self, text: str, add_newline: bool = True) -> Self:
|
|
162
|
+
def append(self, text: str, add_newline: bool = True, tag: str = None) -> Self:
|
|
151
163
|
"""
|
|
152
164
|
Just append to the text-field, nothing more, nothing less
|
|
153
165
|
:param add_newline: True, if \n should be appended too
|
|
154
166
|
:param text:
|
|
167
|
+
:param tag: tkinter-tag
|
|
155
168
|
:return:
|
|
156
169
|
"""
|
|
157
|
-
self.textField.append(text, add_newline=add_newline)
|
|
170
|
+
self.textField.append(text, add_newline=add_newline, tags=tag)
|
|
158
171
|
self.textField.see_end()
|
|
159
172
|
return self
|
|
160
173
|
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
from collections.abc import Iterable, Callable
|
|
3
|
-
from functools import partial
|
|
4
|
-
from typing import Any, Hashable, Mapping
|
|
1
|
+
from typing import Any, Hashable, Mapping, Iterable, Callable
|
|
5
2
|
from SwiftGUI.Compat import Self
|
|
6
|
-
import json
|
|
7
3
|
|
|
8
|
-
from SwiftGUI import BaseElement,
|
|
9
|
-
#from SwiftGUI.Widget_Elements.Separator import HorizontalSeparator
|
|
10
|
-
from SwiftGUI.Extended_Elements.Spacer import Spacer
|
|
4
|
+
from SwiftGUI import BaseElement, Text, Input, BaseCombinedElement, Button, Event, GlobalOptions, ValueDict, Spacer
|
|
11
5
|
|
|
12
6
|
|
|
13
7
|
# Advanced / Combined elements
|
|
@@ -18,6 +12,8 @@ class Form(BaseCombinedElement):
|
|
|
18
12
|
It is WIP, but I'm adding more functionality regularely
|
|
19
13
|
I'll probably throw this whole class out of the window and redo it, since it is so unfathomly bad atm.
|
|
20
14
|
"""
|
|
15
|
+
defaults = GlobalOptions.Form
|
|
16
|
+
value: ValueDict | tuple
|
|
21
17
|
|
|
22
18
|
def __init__(
|
|
23
19
|
self,
|
|
@@ -27,11 +23,15 @@ class Form(BaseCombinedElement):
|
|
|
27
23
|
key: Hashable = None,
|
|
28
24
|
key_function: Callable | Iterable[Callable] = None,
|
|
29
25
|
default_event: bool = None,
|
|
26
|
+
return_submits: bool = None,
|
|
30
27
|
small_clear_buttons: bool = None,
|
|
31
28
|
big_clear_button: bool = None,
|
|
29
|
+
clear_button_text: str = None,
|
|
32
30
|
submit_button: bool = None,
|
|
31
|
+
submit_button_text: str = None,
|
|
32
|
+
space_over_buttons: int = None,
|
|
33
|
+
space_between_rows: int = None,
|
|
33
34
|
#submit_key: Any = None,
|
|
34
|
-
return_submits: bool = None,
|
|
35
35
|
):
|
|
36
36
|
"""
|
|
37
37
|
|
|
@@ -44,10 +44,17 @@ class Form(BaseCombinedElement):
|
|
|
44
44
|
:param big_clear_button: One big clear-button under the inputs to clear all inputs at once
|
|
45
45
|
:param submit_button: True, if there should be a submit-button that throws an event when clicked. Ignores the default event
|
|
46
46
|
:param return_submits: True, if pressing enter should be equal to pressing submit. Ignores the default event
|
|
47
|
+
:param clear_button_text: Text on the clear-button
|
|
48
|
+
:param submit_button_text: Text on the submit-button
|
|
49
|
+
:param space_over_buttons: Height of the separator in the row before the buttons
|
|
50
|
+
:param space_between_rows: Height of the separators between the rows
|
|
47
51
|
"""
|
|
48
|
-
|
|
52
|
+
space_between_rows = self.defaults.single("space_between_rows", space_between_rows)
|
|
53
|
+
space_over_buttons = self.defaults.single("space_over_buttons", space_over_buttons)
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
self._mapping_mode = isinstance(texts, Mapping)
|
|
56
|
+
|
|
57
|
+
if self._mapping_mode:
|
|
51
58
|
values = list(texts.values())
|
|
52
59
|
else:
|
|
53
60
|
values = list(texts)
|
|
@@ -56,41 +63,51 @@ class Form(BaseCombinedElement):
|
|
|
56
63
|
|
|
57
64
|
self._input_elements: list[Input] = list()
|
|
58
65
|
self._text_elements: list[Text] = list()
|
|
66
|
+
self._small_clear_button_elements: list[Button] = list()
|
|
59
67
|
|
|
60
68
|
self._small_clear_buttons = small_clear_buttons
|
|
61
69
|
self._return_submits = return_submits
|
|
62
70
|
|
|
63
|
-
if self.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
self._make_row(k,t, text_len=_max_len) for t,k in texts.items()
|
|
67
|
-
]
|
|
71
|
+
if self._mapping_mode:
|
|
72
|
+
rows = [
|
|
73
|
+
self._make_row(k,t, text_len=_max_len) for t,k in texts.items()
|
|
68
74
|
]
|
|
69
75
|
else:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
self._make_row(t, text_len=_max_len) for t in values
|
|
73
|
-
]
|
|
76
|
+
rows = [
|
|
77
|
+
self._make_row(t, text_len=_max_len) for t in values
|
|
74
78
|
]
|
|
75
79
|
|
|
80
|
+
layout = []
|
|
81
|
+
if space_between_rows:
|
|
82
|
+
for row in rows[:-1]:
|
|
83
|
+
layout.append(row)
|
|
84
|
+
layout.append([Spacer(height=space_between_rows)])
|
|
85
|
+
|
|
86
|
+
if len(rows):
|
|
87
|
+
layout.append(rows[-1])
|
|
88
|
+
else:
|
|
89
|
+
layout = rows
|
|
90
|
+
|
|
76
91
|
button_row: list[BaseElement] = list()
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
self.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
button_row.append(self.clear_button)
|
|
93
|
+
self.clear_button = Button(
|
|
94
|
+
self.defaults.single("clear_button_text", clear_button_text),
|
|
95
|
+
key_function= lambda: self.clear_all_values(throw_default_event=True),
|
|
96
|
+
).set_visible(big_clear_button)
|
|
97
|
+
button_row.append(self.clear_button)
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
self.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
button_row.append(self.submit_button)
|
|
99
|
+
self.submit_button = Button(
|
|
100
|
+
self.defaults.single("submit_button_text", submit_button_text),
|
|
101
|
+
key_function=self.throw_event,
|
|
102
|
+
).set_visible(submit_button)
|
|
103
|
+
button_row.append(self.submit_button)
|
|
91
104
|
|
|
92
|
-
if
|
|
93
|
-
layout.append(
|
|
105
|
+
if space_over_buttons:
|
|
106
|
+
layout.append([Spacer(height=space_over_buttons)])
|
|
107
|
+
elif space_between_rows:
|
|
108
|
+
layout.append([Spacer(height=space_between_rows)])
|
|
109
|
+
|
|
110
|
+
layout.append(button_row)
|
|
94
111
|
|
|
95
112
|
super().__init__(
|
|
96
113
|
layout,
|
|
@@ -102,6 +119,18 @@ class Form(BaseCombinedElement):
|
|
|
102
119
|
if default_values is not None:
|
|
103
120
|
self.set_value(default_values)
|
|
104
121
|
|
|
122
|
+
def _update_special_key(self,key:str,new_val:Any) -> bool|None:
|
|
123
|
+
match key:
|
|
124
|
+
case "clear_button_text":
|
|
125
|
+
self.clear_button.set_value(new_val)
|
|
126
|
+
case "submit_button_text":
|
|
127
|
+
self.submit_button.set_value(new_val)
|
|
128
|
+
|
|
129
|
+
case _:
|
|
130
|
+
return super()._update_special_key(key, new_val)
|
|
131
|
+
|
|
132
|
+
return True
|
|
133
|
+
|
|
105
134
|
def _make_row(self, text: str, key: Hashable = None, text_len: int = 20) -> list[BaseElement]:
|
|
106
135
|
"""
|
|
107
136
|
Create a single row of elements
|
|
@@ -128,15 +157,32 @@ class Form(BaseCombinedElement):
|
|
|
128
157
|
]
|
|
129
158
|
|
|
130
159
|
if self._small_clear_buttons:
|
|
131
|
-
|
|
160
|
+
button = Button(
|
|
132
161
|
text= "x",
|
|
133
162
|
width=2,
|
|
134
163
|
key_function= lambda: input_elem.set_value("", throw_event=True),
|
|
135
164
|
takefocus= False,
|
|
136
|
-
)
|
|
165
|
+
)
|
|
166
|
+
row.append(button)
|
|
167
|
+
self._small_clear_button_elements.append(button)
|
|
137
168
|
|
|
138
169
|
return row
|
|
139
170
|
|
|
171
|
+
def __getitem__(self, item: Hashable | int) -> str:
|
|
172
|
+
assert self._mapping_mode or isinstance(item, int), f"{repr(self)} is created in list-mode, so only integer indexes can be used as key!"
|
|
173
|
+
|
|
174
|
+
return self.value[item]
|
|
175
|
+
|
|
176
|
+
def __setitem__(self, key: Hashable | int, value):
|
|
177
|
+
assert self._mapping_mode or isinstance(key, int), f"{repr(self)} is created in list-mode, so only integer indexes can be used as key!"
|
|
178
|
+
|
|
179
|
+
if self._mapping_mode:
|
|
180
|
+
if not key in self.v:
|
|
181
|
+
raise KeyError(f"{repr(self)} was given a key that it doesn't contain")
|
|
182
|
+
self.v[key] = value
|
|
183
|
+
else:
|
|
184
|
+
self._input_elements[key].value = value
|
|
185
|
+
|
|
140
186
|
@BaseCombinedElement._run_after_window_creation
|
|
141
187
|
def set_value(self, val: Iterable | Mapping) -> Self:
|
|
142
188
|
"""
|
|
@@ -150,7 +196,9 @@ class Form(BaseCombinedElement):
|
|
|
150
196
|
for value, elem in zip(val, self._input_elements):
|
|
151
197
|
elem.value = value
|
|
152
198
|
|
|
153
|
-
|
|
199
|
+
return self
|
|
200
|
+
|
|
201
|
+
def as_tuple(self) -> tuple:
|
|
154
202
|
"""
|
|
155
203
|
Return a tuple with all form-values one after the other
|
|
156
204
|
:return:
|
|
@@ -158,10 +206,10 @@ class Form(BaseCombinedElement):
|
|
|
158
206
|
return tuple(map(lambda a:a.value, self._input_elements))
|
|
159
207
|
|
|
160
208
|
def _get_value(self):
|
|
161
|
-
if self.
|
|
209
|
+
if self._mapping_mode:
|
|
162
210
|
return super()._get_value()
|
|
163
211
|
|
|
164
|
-
return self.
|
|
212
|
+
return self.as_tuple()
|
|
165
213
|
|
|
166
214
|
@BaseElement._run_after_window_creation
|
|
167
215
|
def update_texts(self,**kwargs) -> Self:
|
|
@@ -177,7 +225,7 @@ class Form(BaseCombinedElement):
|
|
|
177
225
|
@BaseElement._run_after_window_creation
|
|
178
226
|
def update_inputs(self,**kwargs) -> Self:
|
|
179
227
|
"""
|
|
180
|
-
Evoke .update on every
|
|
228
|
+
Evoke .update on every input-element
|
|
181
229
|
:param kwargs:
|
|
182
230
|
:return:
|
|
183
231
|
"""
|
|
@@ -185,7 +233,18 @@ class Form(BaseCombinedElement):
|
|
|
185
233
|
elem._update_initial(**kwargs)
|
|
186
234
|
return self
|
|
187
235
|
|
|
188
|
-
|
|
236
|
+
@BaseElement._run_after_window_creation
|
|
237
|
+
def update_small_clear_buttons(self,**kwargs) -> Self:
|
|
238
|
+
"""
|
|
239
|
+
Evoke .update on every input-element
|
|
240
|
+
:param kwargs:
|
|
241
|
+
:return:
|
|
242
|
+
"""
|
|
243
|
+
for elem in self._small_clear_button_elements:
|
|
244
|
+
elem._update_initial(**kwargs)
|
|
245
|
+
return self
|
|
246
|
+
|
|
247
|
+
def clear_all_values(self, throw_default_event: bool = False) -> Self:
|
|
189
248
|
"""
|
|
190
249
|
Does what it says
|
|
191
250
|
:return:
|
|
@@ -196,13 +255,15 @@ class Form(BaseCombinedElement):
|
|
|
196
255
|
if throw_default_event:
|
|
197
256
|
self.throw_default_event()
|
|
198
257
|
|
|
199
|
-
|
|
258
|
+
return self
|
|
259
|
+
|
|
260
|
+
def set_focus(self) -> Self:
|
|
200
261
|
"""
|
|
201
262
|
Focus the first row of this element
|
|
202
263
|
:return:
|
|
203
264
|
"""
|
|
204
265
|
self._input_elements[0].set_focus()
|
|
205
|
-
|
|
266
|
+
return self
|
|
206
267
|
|
|
207
268
|
|
|
208
269
|
|
|
@@ -97,6 +97,19 @@ def preview_all_elements(
|
|
|
97
97
|
sg.FileBrowseButton("save_single", file_browse_type="save_single", disabled= flip_disabled),
|
|
98
98
|
sg.FileBrowseButton("open_multiple", file_browse_type="open_multiple", disabled= flip_disabled),
|
|
99
99
|
sg.FileBrowseButton("open_directory", file_browse_type="open_directory", disabled= flip_disabled),
|
|
100
|
+
],[
|
|
101
|
+
sg.HSep(),
|
|
102
|
+
sg.Spacer(height=10),
|
|
103
|
+
], [
|
|
104
|
+
sg.T("sg.PythonConsole, works with the normal print and input functions:"),
|
|
105
|
+
],[
|
|
106
|
+
sg.PythonConsole(width=50, height=10),
|
|
107
|
+
],[
|
|
108
|
+
sg.Spacer(height=5),
|
|
109
|
+
], [
|
|
110
|
+
sg.T("See for yourself: "),
|
|
111
|
+
sg.Button("print('Hello World')", key_function=lambda: print("Hello World")),
|
|
112
|
+
sg.Button("input('Your input: ')", key_function=lambda: input("Your input: ")),
|
|
100
113
|
]
|
|
101
114
|
], fake_key= "Extended elements")
|
|
102
115
|
|
|
@@ -152,7 +165,6 @@ def preview_all_elements(
|
|
|
152
165
|
sg.Notebook(
|
|
153
166
|
sg.TabFrame([[sg.T("sg.Notebook")]], fake_key="Tab1"),
|
|
154
167
|
sg.TabFrame([[sg.T("still sg.Notebook")]], fake_key="Tab2"),
|
|
155
|
-
|
|
156
168
|
)
|
|
157
169
|
],[
|
|
158
170
|
sg.Spacer(height=20)
|
|
@@ -161,9 +173,9 @@ def preview_all_elements(
|
|
|
161
173
|
[
|
|
162
174
|
sg.T("sg.SubLayout"),
|
|
163
175
|
],[
|
|
164
|
-
sg.T("A
|
|
176
|
+
sg.T("A separate layout inside your layout.")
|
|
165
177
|
],[
|
|
166
|
-
sg.T("It has
|
|
178
|
+
sg.T("It has separate keys and its own segregated event-loop.")
|
|
167
179
|
]
|
|
168
180
|
], alignment="left"))
|
|
169
181
|
]
|
|
@@ -212,6 +224,8 @@ def preview_all_elements(
|
|
|
212
224
|
big_clear_button= True,
|
|
213
225
|
submit_button= True,
|
|
214
226
|
default_values= ("", "", "Click on Clear"),
|
|
227
|
+
).update_inputs(
|
|
228
|
+
readonly=flip_readonly,
|
|
215
229
|
)
|
|
216
230
|
],[
|
|
217
231
|
sg.HSep()
|
|
@@ -245,7 +259,9 @@ def preview_all_elements(
|
|
|
245
259
|
sg.T("Lets you select a day"),
|
|
246
260
|
], [
|
|
247
261
|
sg.T(expand=True),
|
|
248
|
-
sg.Calendar(
|
|
262
|
+
sg.Calendar(
|
|
263
|
+
disabled=flip_disabled,
|
|
264
|
+
),
|
|
249
265
|
sg.T(expand=True),
|
|
250
266
|
], [
|
|
251
267
|
sg.HSep()
|
|
@@ -305,7 +321,7 @@ def preview_all_elements(
|
|
|
305
321
|
[
|
|
306
322
|
sg.T("sgc.Element")
|
|
307
323
|
],[
|
|
308
|
-
sg.Button("Put any sg-element onto your canvas!!")
|
|
324
|
+
sg.Button("Put any sg-element onto your canvas!!", disabled=flip_disabled)
|
|
309
325
|
]
|
|
310
326
|
]),
|
|
311
327
|
anchor= "nw",
|
|
@@ -388,7 +404,7 @@ def preview_all_elements(
|
|
|
388
404
|
]
|
|
389
405
|
]
|
|
390
406
|
|
|
391
|
-
w = sg.SubWindow(layout, title="Preview of all currently available elements")
|
|
407
|
+
w = sg.SubWindow(layout, title="Preview of all currently available elements", grab_anywhere=True)
|
|
392
408
|
w["List"].index = 2
|
|
393
409
|
console.print("Hi, I am sg.Console!")
|
|
394
410
|
console.print("I have a print-function that works just like the normal print(...)")
|