SwiftGUI 0.11.20__tar.gz → 0.12.1__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.
Files changed (95) hide show
  1. {swiftgui-0.11.20 → swiftgui-0.12.1}/PKG-INFO +35 -34
  2. swiftgui-0.12.1/README.md +100 -0
  3. {swiftgui-0.11.20 → swiftgui-0.12.1}/pyproject.toml +1 -1
  4. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Base.py +22 -0
  5. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/GlobalOptions.py +3 -2
  6. swiftgui-0.12.1/src/SwiftGUI/Validate/Base.py +119 -0
  7. swiftgui-0.12.1/src/SwiftGUI/Validate/Common.py +82 -0
  8. swiftgui-0.12.1/src/SwiftGUI/Validate/Regex.py +20 -0
  9. swiftgui-0.12.1/src/SwiftGUI/Validate/__init__.py +4 -0
  10. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Combobox.py +7 -3
  11. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Input.py +2 -2
  12. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Spinbox.py +20 -15
  13. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/TextField.py +7 -0
  14. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/__init__.py +2 -0
  15. swiftgui-0.11.20/README.md +0 -99
  16. {swiftgui-0.11.20 → swiftgui-0.12.1}/LICENSE +0 -0
  17. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/BasePopup.py +0 -0
  18. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Arc.py +0 -0
  19. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Bitmap.py +0 -0
  20. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Element.py +0 -0
  21. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Image.py +0 -0
  22. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Line.py +0 -0
  23. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Oval.py +0 -0
  24. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Polygon.py +0 -0
  25. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Rectangle.py +0 -0
  26. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/Text.py +0 -0
  27. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/_Base.py +0 -0
  28. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Canvas_Elements/__init__.py +0 -0
  29. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Colors.py +0 -0
  30. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Combined_Elements/BaseCombinedElement.py +0 -0
  31. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Combined_Elements/Calendar.py +0 -0
  32. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Combined_Elements/Console.py +0 -0
  33. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Combined_Elements/Form.py +0 -0
  34. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Combined_Elements/MultistateButton.py +0 -0
  35. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Compat.py +0 -0
  36. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Debug.py +0 -0
  37. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/DecoratorKeys.py +0 -0
  38. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/ElementFlags.py +0 -0
  39. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Events.py +0 -0
  40. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Examples/AllElements.py +0 -0
  41. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Examples/AllThemes.py +0 -0
  42. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Examples/GlobalOptionBrowser.py +0 -0
  43. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Examples/Previews.py +0 -0
  44. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Examples/__init__.py +0 -0
  45. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Exceptions.py +0 -0
  46. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/ColorChooserButton.py +0 -0
  47. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/ComboboxMapping.py +0 -0
  48. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/FileBrowseButton.py +0 -0
  49. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/Image.py +0 -0
  50. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/ImageButton.py +0 -0
  51. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/PythonConsole.py +0 -0
  52. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/Separator.py +0 -0
  53. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/Spacer.py +0 -0
  54. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/TabFrame.py +0 -0
  55. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extended_Elements/Table.py +0 -0
  56. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extras/SwiftGUI.py +0 -0
  57. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Extras/__init__.py +0 -0
  58. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Files/ConfigFile.py +0 -0
  59. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Files/ConfigurationEditor.py +0 -0
  60. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Files/DictFile.py +0 -0
  61. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Files/ProgramDirectory.py +0 -0
  62. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Files/__init__.py +0 -0
  63. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Fonts.py +0 -0
  64. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/KeyFunctions.py +0 -0
  65. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Literals.py +0 -0
  66. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Popups/ElementPopup.py +0 -0
  67. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Popups/ListPicker.py +0 -0
  68. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Popups/SmallPopups.py +0 -0
  69. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Popups/TooltipPopup.py +0 -0
  70. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Popups/VirtualKeyboard.py +0 -0
  71. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Popups/__init__.py +0 -0
  72. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Themes/FourColors.py +0 -0
  73. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Themes/Thematic.py +0 -0
  74. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Themes/_BaseTheme.py +0 -0
  75. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Themes/__init__.py +0 -0
  76. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Tools.py +0 -0
  77. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Tooltips.py +0 -0
  78. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Utilities/Images.py +0 -0
  79. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Utilities/Threads.py +0 -0
  80. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Button.py +0 -0
  81. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Canvas.py +0 -0
  82. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Checkbox.py +0 -0
  83. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Frame.py +0 -0
  84. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/GridFrame.py +0 -0
  85. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/LabelFrame.py +0 -0
  86. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Listbox.py +0 -0
  87. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Notebook.py +0 -0
  88. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Progressbar.py +0 -0
  89. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Radiobutton.py +0 -0
  90. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Scale.py +0 -0
  91. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Scrollbar.py +0 -0
  92. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/TKContainer.py +0 -0
  93. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Text.py +0 -0
  94. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Widget_Elements/Treeview.py +0 -0
  95. {swiftgui-0.11.20 → swiftgui-0.12.1}/src/SwiftGUI/Windows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SwiftGUI
3
- Version: 0.11.20
3
+ Version: 0.12.1
4
4
  Summary: An easy-to-use package for quickly creating smaller and bigger GUIs
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -21,7 +21,6 @@ Description-Content-Type: text/markdown
21
21
 
22
22
 
23
23
  # SwiftGUI
24
-
25
24
  A python-package to easily create user-interfaces (GUIs).
26
25
 
27
26
  While most packages focus either on small, or big applications, SwiftGUI does both.
@@ -32,20 +31,20 @@ Has some minor optimizations when running in Python 3.12+.
32
31
  # Getting started / documentation
33
32
  [Start your journey here](https://github.com/CheesecakeTV/SwiftGUI-Docs/blob/main/01%20Basic%20tutorials/01%20Getting-started.md)
34
33
 
35
- The documentation now has [its own repository](https://github.com/CheesecakeTV/SwiftGUI-Docs).
34
+ [Documentation](https://github.com/CheesecakeTV/SwiftGUI-Docs).
36
35
 
37
- # Completely vibe-coding free
36
+ # Vibe-coding free
38
37
  No AI was used in writing this code.
39
38
 
40
39
  The only way I used it was to research really specific topics with tkinter.
41
40
 
42
- I did not use AI to write, check, correct, or refractor code.
43
-
44
41
  Even the documentation was written completely AI-free.
45
42
  Takes me around 2 hours per document, but that's a good price for keeping everything slop-free.
46
43
 
47
- # 35 different elements, 9 different canvas-elements
48
- (Version 0.11.0)
44
+ Yes, this does slow down development quite a bit, but I trust myself to be more reliable than an AI agent.
45
+
46
+ # 38 different elements, 9 different canvas-elements
47
+ (Version 0.12.0)
49
48
 
50
49
  `import SwiftGUI as sg`
51
50
 
@@ -60,18 +59,14 @@ This applies the `Emerald`-theme, my personal favorite.
60
59
 
61
60
  See which themes are available by calling `sg.Examples.preview_all_themes()`.
62
61
 
63
- # Alpha-phase!
64
- I am already using SwiftGUI for smaller projects and personally, like it a lot so far.
62
+ # Final minor version before Beta starts!
63
+ You might have noticed that updates for SwiftGUI are rarer than they used to be.
64
+ That's actually a good sign, it means I find less things to add/correct.
65
65
 
66
- However, until version 1.0.0, downward-compatability is not a priority.
67
- Names and functions/methods might change, which could potentially mess up your code.
66
+ There will not be version 0.13.0, but 1.0.0 instead.
68
67
 
69
- For version 1.0.0, I'll sort and standardize names, so they are easier to remember.
70
- After version 1.0.0, there will be little to no changes that might break existing code.
71
- If changes can't be avoided, there will be a separate changelog which shows everything you need to
72
- adjust so you may update without issues.
73
-
74
- Don't worry too much though, the package is pretty much ready for beta-phase.
68
+ I do have a couple of major tasks on my list before rolling out 1.0 though.
69
+ Fixing the treeview for example.
75
70
 
76
71
  # Installation
77
72
  Install using pip:
@@ -79,27 +74,28 @@ Install using pip:
79
74
  pip install SwiftGUI
80
75
  ```
81
76
 
82
- Update SwiftGUI for the newest features and elements:
77
+ Regularely update SwiftGUI for the newest features and bugfixes:
83
78
  ```bash
84
79
  pip install SwiftGUI -U
85
80
  ```
86
81
 
87
82
  # NOT a clone of PySimpleGUI
88
- SwiftGUI is it's own package with its own functionality.
83
+ SwiftGUI is it's own package, with its own functionality.
89
84
 
90
85
  I really liked PySimpleGUI (until they went "premium"),
91
- but once you work a lot with it, you'll notice the downsides of it more and more.
86
+ but once you work a lot with it, you'll notice its downsides more and more.
92
87
 
93
- **SwiftGUI can be used very simmilar to PySimpleGUI**, but has a lot of additional features.\
94
- So if you know PySimpleGUI, you won't need to start from scratch.
88
+ The basics of SwiftGUI are almost the same as PySimpleGUI.
89
+ So if you know PySimpleGUI, you already know many concepts of SwiftGUI.
95
90
 
96
- Also, there will be a lot of learning-material, including
97
- - Written tutorials (see "getting started" below)
98
- - Video tutorials (Planned for version 1.0.0)
99
- - Application notes, which are short descriptions of actual applications
91
+ There is already a collection of helpful learning material, including:
92
+ - Written tutorials (see "getting started" above)
93
+ - Application notes, which summarize different tutorials regarding specific applications
100
94
  - Examples, which show an actual application of SwiftGUI
101
95
  - The GitHub forum ([discussions](https://github.com/CheesecakeTV/SwiftGUI/discussions)) for questions
102
96
 
97
+ Video tutorials are planned for version 1.0.0.
98
+
103
99
  ## Why SwiftGUI instead of PySimpleGUI?
104
100
  I have a lot of experience with `PySimpleGUI`, used it for years.\
105
101
  It is very useful for smaller applications.
@@ -107,15 +103,20 @@ It is very useful for smaller applications.
107
103
  Unfortunately, at a certain level of complexity, you'll hit a wall.\
108
104
  All those simple features that make PySimpleGUI great, are suddenly very annoying.
109
105
 
110
- What annoys me the most is that PySimpleGUI forces you to use only the event-loop for functionality.
111
- That means, every tiny tiniest functionality needs a key and code in the event-loop.
106
+ In the earlier versions of SwiftGUI, it was ment to "fix" annoying things in PySimpleGUI.
107
+ For example:
108
+ - You are forced to use the event-loop for every tiny functionality
109
+ - You can pretty much forget about editing elements, since they are implemented so convoluded
110
+ - Some elements throw events wrong, including sg.Table and sg.Input
112
111
 
113
- SwiftGUI on the other hand offers many different ways to connect layout with functionality.
114
- That makes it easy to "copy and paste" parts of the layout.
112
+ Nowadays, SwiftGUI's functionality goes far beyond what PySimpleGUI has to offer.
115
113
 
116
- SwiftGUI also supports multiple windows much better, with every window having its own event-loop.
114
+ The reason is simple:
115
+ SwiftGUI is built for modularity and expandability.
116
+ You can easily create templates, themes, custom popups, modify elements, etc.
117
117
 
118
- On top of that, SwiftGUI enables expandability everywhere.
119
- You can easily create your own elements, themes, popups and templates.
118
+ Also, SwiftGUI works a lot better with multiple windows.
119
+ Creating a second window is almost as simple as creating a single one.
120
120
 
121
+ A lot more features are described/mentioned in the documentation.
121
122
 
@@ -0,0 +1,100 @@
1
+
2
+ # SwiftGUI
3
+ A python-package to easily create user-interfaces (GUIs).
4
+
5
+ While most packages focus either on small, or big applications, SwiftGUI does both.
6
+
7
+ ## Compatible with Python 3.10 and above
8
+ Has some minor optimizations when running in Python 3.12+.
9
+
10
+ # Getting started / documentation
11
+ [Start your journey here](https://github.com/CheesecakeTV/SwiftGUI-Docs/blob/main/01%20Basic%20tutorials/01%20Getting-started.md)
12
+
13
+ [Documentation](https://github.com/CheesecakeTV/SwiftGUI-Docs).
14
+
15
+ # Vibe-coding free
16
+ No AI was used in writing this code.
17
+
18
+ The only way I used it was to research really specific topics with tkinter.
19
+
20
+ Even the documentation was written completely AI-free.
21
+ Takes me around 2 hours per document, but that's a good price for keeping everything slop-free.
22
+
23
+ Yes, this does slow down development quite a bit, but I trust myself to be more reliable than an AI agent.
24
+
25
+ # 38 different elements, 9 different canvas-elements
26
+ (Version 0.12.0)
27
+
28
+ `import SwiftGUI as sg`
29
+
30
+ Call `sg.Examples.preview_all_elements()` for an overview of all currently available elements.
31
+
32
+ # Does your GUI look bad?
33
+ `import SwiftGUI as sg`
34
+
35
+ Just call `sg.Themes.FourColors.Emerald()` before creating the layout.
36
+
37
+ This applies the `Emerald`-theme, my personal favorite.
38
+
39
+ See which themes are available by calling `sg.Examples.preview_all_themes()`.
40
+
41
+ # Final minor version before Beta starts!
42
+ You might have noticed that updates for SwiftGUI are rarer than they used to be.
43
+ That's actually a good sign, it means I find less things to add/correct.
44
+
45
+ There will not be version 0.13.0, but 1.0.0 instead.
46
+
47
+ I do have a couple of major tasks on my list before rolling out 1.0 though.
48
+ Fixing the treeview for example.
49
+
50
+ # Installation
51
+ Install using pip:
52
+ ```bash
53
+ pip install SwiftGUI
54
+ ```
55
+
56
+ Regularely update SwiftGUI for the newest features and bugfixes:
57
+ ```bash
58
+ pip install SwiftGUI -U
59
+ ```
60
+
61
+ # NOT a clone of PySimpleGUI
62
+ SwiftGUI is it's own package, with its own functionality.
63
+
64
+ I really liked PySimpleGUI (until they went "premium"),
65
+ but once you work a lot with it, you'll notice its downsides more and more.
66
+
67
+ The basics of SwiftGUI are almost the same as PySimpleGUI.
68
+ So if you know PySimpleGUI, you already know many concepts of SwiftGUI.
69
+
70
+ There is already a collection of helpful learning material, including:
71
+ - Written tutorials (see "getting started" above)
72
+ - Application notes, which summarize different tutorials regarding specific applications
73
+ - Examples, which show an actual application of SwiftGUI
74
+ - The GitHub forum ([discussions](https://github.com/CheesecakeTV/SwiftGUI/discussions)) for questions
75
+
76
+ Video tutorials are planned for version 1.0.0.
77
+
78
+ ## Why SwiftGUI instead of PySimpleGUI?
79
+ I have a lot of experience with `PySimpleGUI`, used it for years.\
80
+ It is very useful for smaller applications.
81
+
82
+ Unfortunately, at a certain level of complexity, you'll hit a wall.\
83
+ All those simple features that make PySimpleGUI great, are suddenly very annoying.
84
+
85
+ In the earlier versions of SwiftGUI, it was ment to "fix" annoying things in PySimpleGUI.
86
+ For example:
87
+ - You are forced to use the event-loop for every tiny functionality
88
+ - You can pretty much forget about editing elements, since they are implemented so convoluded
89
+ - Some elements throw events wrong, including sg.Table and sg.Input
90
+
91
+ Nowadays, SwiftGUI's functionality goes far beyond what PySimpleGUI has to offer.
92
+
93
+ The reason is simple:
94
+ SwiftGUI is built for modularity and expandability.
95
+ You can easily create templates, themes, custom popups, modify elements, etc.
96
+
97
+ Also, SwiftGUI works a lot better with multiple windows.
98
+ Creating a second window is almost as simple as creating a single one.
99
+
100
+ A lot more features are described/mentioned in the documentation.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "SwiftGUI"
3
- version = "0.11.20"
3
+ version = "0.12.1"
4
4
  packages = [
5
5
  { include = "SwiftGUI", from = "src" }
6
6
  ]
@@ -766,6 +766,28 @@ class BaseWidget(BaseElement):
766
766
  def __repr__(self) -> str:
767
767
  return f"<sg.{self.__class__.__name__} element with key={self.key} at {id(self)}>"
768
768
 
769
+ def _update_special_key(self,key:str,new_val:Any) -> bool|None:
770
+
771
+ match key:
772
+ case "validatecommand":
773
+ if new_val is None:
774
+ return True
775
+
776
+ if not self.has_flag(ElementFlag.IS_CREATED):
777
+ self.update_after_window_creation(validatecommand=new_val)
778
+ return True
779
+
780
+ as_tuple = tuple(new_val)
781
+ self._tk_kwargs["validatecommand"] = (
782
+ self.tk_widget.register(as_tuple[0]),
783
+ *as_tuple[1:],
784
+ )
785
+
786
+ case _:
787
+ return super()._update_special_key(key, new_val)
788
+
789
+ return True
790
+
769
791
  class MixinScrollbar:
770
792
  """
771
793
  Base for every widget that has a scrollbar.
@@ -5,7 +5,7 @@ from collections.abc import Iterable
5
5
  from os import PathLike
6
6
  from typing import Literal, Union, Any, Callable
7
7
 
8
- from SwiftGUI import Literals, Color, font_windows, Font, Extras
8
+ from SwiftGUI import Literals, Color, font_windows, Font, Extras, Validate
9
9
  from SwiftGUI.Utilities.Images import file_from_b64
10
10
  import SwiftGUI as sg
11
11
 
@@ -306,7 +306,7 @@ class Input(Common,Common_Textual,Common_Field_Background):
306
306
  relief: Literals.relief = None
307
307
  exportselection: bool = None
308
308
  validate: Literals.validate = None
309
- validatecommand: Callable = None
309
+ validatecommand: Validate.BaseValidate = None
310
310
  insertbackground_color: str | Color = None
311
311
  #
312
312
  # Mixed options
@@ -544,6 +544,7 @@ class TabFrame(Frame):
544
544
  class Spinbox(Button, Input):
545
545
  default_value: float = None
546
546
  value_type: type = float
547
+ allow_empty: bool = True
547
548
  cursor_button: Literals.cursor = None
548
549
  background_color_button: Color | str = None
549
550
  relief_button_down: Literals.relief = None
@@ -0,0 +1,119 @@
1
+
2
+ class Substitutions:
3
+ action_code = "%d" # 0 for deletion, 1 for insertion, -1 for focus or backend-change
4
+ index = "%i" # Index where a deletion/addition was attempted. -1 if none was attempted
5
+ new_value = "%P" # Value after the change, if allowed
6
+ old_value = "%s" # Value before the change
7
+ changed_text = "%S" # The text that was inserted/deleted
8
+ validate = "%v" # This element's validate option
9
+ callback_reason = "%V" # The reason for this callback: 'focusin', 'focusout', 'key', or 'forced' if the text was changed from the backend
10
+ widget_name = "%W" # The name of the TKINTER-widget. This is not the element-key!!!
11
+
12
+ class BaseValidate:
13
+ substitutions: tuple[str, ...] = tuple() # https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/entry-validation.html
14
+
15
+ def __iter__(self): # Generate the tuple that is passed to the tk.widget (after registering the function) later
16
+ return iter((
17
+ self._callback,
18
+ *self.substitutions,
19
+ ))
20
+
21
+ def _callback(self, *args) -> bool:
22
+ """
23
+ Overwrite this to specify the actual validation
24
+ :return:
25
+ """
26
+ return True
27
+
28
+ class BaseValidateValue(BaseValidate):
29
+ substitutions = (
30
+ Substitutions.new_value,
31
+ Substitutions.callback_reason,
32
+ Substitutions.old_value,
33
+ Substitutions.changed_text,
34
+ Substitutions.action_code,
35
+ )
36
+
37
+ def __init__(
38
+ self,
39
+ #validate_backend_change: bool = False,
40
+ ):
41
+ self._validate_backend_change = False # This doesn't work...
42
+
43
+ def _callback(self, val: str, reason: str, val_before: str, changed_val: str, action_code: str) -> bool:
44
+ """
45
+ Gets called by tkinter.
46
+ All checks must pass to allow the action.
47
+ """
48
+ if (not self._validate_backend_change) and reason == "forced":
49
+ return True
50
+
51
+ if action_code == "0": # Deletion
52
+ if not self.is_valid_deletion(val, val_before, changed_val):
53
+ return False
54
+ elif action_code == "1": # Insertion
55
+ if not self.is_valid_insertion(val, val_before, changed_val):
56
+ return False
57
+
58
+ return self.is_valid_full_value(val)
59
+
60
+ def is_valid_full_value(self, val: str) -> bool:
61
+ """Called on every check with the full value after the change"""
62
+ return True
63
+
64
+ def is_valid_insertion(self, val: str, val_before: str, inserted_val: str) -> bool:
65
+ """Called when text is inserted by the user"""
66
+ return True
67
+
68
+ def is_valid_deletion(self, val: str, val_before: str, deleted_val: str) -> bool:
69
+ """Called when text is deleted by the user"""
70
+ return True
71
+
72
+ def __or__(self, other: "BaseValidateValue") -> "Union":
73
+ return Union(self, other)
74
+
75
+ def __and__(self, other: "BaseValidateValue") -> "Chain":
76
+ return Chain(self, other)
77
+
78
+ def __neg__(self) -> "Negate":
79
+ return Negate(self)
80
+
81
+ class Chain(BaseValidateValue):
82
+
83
+ def __init__(self, *validators: BaseValidateValue):
84
+ """
85
+ Is valid, if ALL passed value-validators passes the value-test
86
+ :param validators:
87
+ """
88
+ self._validators = validators
89
+ super().__init__()
90
+
91
+ def _callback(self, *args) -> bool:
92
+ return all(map(lambda a: a._callback(*args), self._validators))
93
+
94
+
95
+ class Union(BaseValidateValue):
96
+
97
+ def __init__(self, *validators: BaseValidateValue):
98
+ """
99
+ Is valid, if ANY passed value-validator passes the value-test
100
+ :param validators:
101
+ """
102
+ self._validators = validators
103
+ super().__init__()
104
+
105
+ def _callback(self, *args) -> bool:
106
+ return any(map(lambda a: a._callback(*args), self._validators))
107
+
108
+ class Negate(BaseValidateValue):
109
+ def __init__(self, validator: BaseValidateValue):
110
+ """
111
+ Is valid, if the passed validator is not valid
112
+ :param validator:
113
+ """
114
+ self._validator = validator
115
+ super().__init__()
116
+
117
+ def _callback(self, *args) -> bool:
118
+ return not self._validator._callback(*args)
119
+
@@ -0,0 +1,82 @@
1
+ from typing import Iterable, Callable
2
+
3
+ from SwiftGUI.Validate import BaseValidateValue
4
+
5
+ class CanConvertTo(BaseValidateValue):
6
+
7
+ def __init__(
8
+ self,
9
+ *types: type,
10
+ allow_empty: bool = True,
11
+ exception: type[BaseException] = ValueError,
12
+ ):
13
+ """
14
+ Valid, if the value can be converted to ANY passed type
15
+ :param types: Types to convert into
16
+ :param allow_empty: True, if the value is allowed to be an empty string
17
+ :param exception: This exception will be checked. You can almost always leave it ValueError. If not, change it to Exception.
18
+ :param validate_backend_change:
19
+ """
20
+ self._types = types
21
+ self._allow_empty = allow_empty
22
+ self._exception = exception
23
+
24
+ super().__init__()
25
+
26
+ def is_valid_full_value(self, val: str) -> bool:
27
+ if self._allow_empty and not val:
28
+ return True
29
+
30
+ return any(map(lambda a:self._can_convert_to(val, a), self._types))
31
+
32
+ def _can_convert_to(self, val: str, this_type: type) -> bool:
33
+ try:
34
+ this_type(val)
35
+ except self._exception:
36
+ return False
37
+
38
+ return True
39
+
40
+ class MaxLength(BaseValidateValue):
41
+
42
+ def __init__(self, max_len: int):
43
+ """
44
+ The value can not be longer than max_len characters
45
+ :param max_len:
46
+ """
47
+ self._max_len = max_len
48
+ super().__init__()
49
+
50
+ def is_valid_full_value(self, val: str) -> bool:
51
+ return len(val) <= self._max_len
52
+
53
+ class ForbidDeletions(BaseValidateValue):
54
+ def is_valid_deletion(self, val: str, val_before: str, deleted_val: str) -> bool:
55
+ return False
56
+
57
+ class ForbidInsertions(BaseValidateValue):
58
+ def is_valid_insertion(self, val: str, val_before: str, inserted_val: str) -> bool:
59
+ return False
60
+
61
+ class WhitelistCharacters(BaseValidateValue):
62
+ def __init__(self, characters: Iterable[str]):
63
+ """
64
+ Forbid every character not passed.
65
+ Case sensitive
66
+ :param characters:
67
+ """
68
+ self._chars = set(characters)
69
+ super().__init__()
70
+
71
+ def is_valid_insertion(self, val: str, val_before: str, inserted_val: str) -> bool:
72
+ return all(map(lambda a:a in self._chars, inserted_val))
73
+
74
+ class FullValueFunction(BaseValidateValue):
75
+ def __init__(self, function: Callable[[str], bool]):
76
+ self._fct = function
77
+ super().__init__()
78
+
79
+ def is_valid_full_value(self, val: str) -> bool:
80
+ return self._fct(val)
81
+
82
+
@@ -0,0 +1,20 @@
1
+ import re
2
+ from SwiftGUI.Validate import BaseValidateValue
3
+
4
+ class ReFullMatch(BaseValidateValue):
5
+
6
+ def __init__(
7
+ self,
8
+ pattern: str,
9
+ ):
10
+ """
11
+ The full value must match the passed regex-pattern
12
+ :param pattern:
13
+ """
14
+ self._compiled = re.compile(pattern)
15
+ super().__init__()
16
+
17
+ def is_valid_full_value(self, val: str) -> bool:
18
+ return bool(self._compiled.fullmatch(val))
19
+
20
+
@@ -0,0 +1,4 @@
1
+
2
+ from .Base import *
3
+ from .Common import *
4
+ from .Regex import ReFullMatch
@@ -4,7 +4,8 @@ from typing import Any, Iterable, Callable, Hashable
4
4
  from SwiftGUI.Base import run_after_window_creation
5
5
  from SwiftGUI.Compat import Self
6
6
 
7
- from SwiftGUI import GlobalOptions, BaseWidgetTTK, Literals, Color, ElementFlag, Scrollbar, MixinElementWithValue
7
+ from SwiftGUI import GlobalOptions, BaseWidgetTTK, Literals, Color, ElementFlag, Scrollbar, MixinElementWithValue, \
8
+ Validate
8
9
 
9
10
 
10
11
  class Combobox(MixinElementWithValue, BaseWidgetTTK):
@@ -57,8 +58,8 @@ class Combobox(MixinElementWithValue, BaseWidgetTTK):
57
58
 
58
59
  exportselection: bool = None,
59
60
 
60
- # Todo: validate,
61
- # validatecommand,
61
+ validate: Literals.validate = None,
62
+ validatecommand: Validate.BaseValidate = None,
62
63
 
63
64
  height: int = None,
64
65
  width: int = None,
@@ -155,6 +156,9 @@ class Combobox(MixinElementWithValue, BaseWidgetTTK):
155
156
  font_overstrike=font_overstrike,
156
157
 
157
158
  insertbackground = insertbackground,
159
+
160
+ validate=validate,
161
+ validatecommand=validatecommand,
158
162
  )
159
163
 
160
164
  @BaseWidgetTTK._run_after_window_creation
@@ -3,7 +3,7 @@ import tkinter.font as font
3
3
  from collections.abc import Iterable, Callable
4
4
  from typing import Literal, Any, Hashable
5
5
 
6
- from SwiftGUI import ElementFlag, BaseWidget, GlobalOptions, Literals, Color, MixinElementWithValue
6
+ from SwiftGUI import ElementFlag, BaseWidget, GlobalOptions, Literals, Color, MixinElementWithValue, Validate
7
7
  from SwiftGUI.Compat import Self
8
8
 
9
9
 
@@ -61,7 +61,7 @@ class Input(MixinElementWithValue, BaseWidget):
61
61
  relief: Literals.relief = None,
62
62
  exportselection: bool = None,
63
63
  validate: Literals.validate = None,
64
- validatecommand: Callable = None,
64
+ validatecommand: Validate.BaseValidate = None,
65
65
  #
66
66
  fonttype: str = None,
67
67
  fontsize: int = None,
@@ -3,7 +3,7 @@ import tkinter.font as font
3
3
  from collections.abc import Iterable, Callable
4
4
  from typing import Literal, Any, Hashable
5
5
 
6
- from SwiftGUI import ElementFlag, BaseWidget, GlobalOptions, Literals, Color, MixinElementWithValue
6
+ from SwiftGUI import ElementFlag, BaseWidget, GlobalOptions, Literals, Color, MixinElementWithValue, Validate
7
7
  from SwiftGUI.Compat import Self
8
8
 
9
9
 
@@ -11,6 +11,7 @@ class Spinbox(MixinElementWithValue, BaseWidget):
11
11
  _tk_widget_class: type = tk.Spinbox # Class of the connected widget
12
12
  tk_widget: tk.Spinbox
13
13
  defaults = GlobalOptions.Spinbox # Default values (Will be applied to kw_args-dict and passed onto the tk_widget
14
+ _value_type: type = str # Set in update_initial
14
15
 
15
16
  _transfer_keys = {
16
17
  "background_color_disabled": "disabledbackground",
@@ -48,6 +49,7 @@ class Spinbox(MixinElementWithValue, BaseWidget):
48
49
 
49
50
  default_value: Any = None,
50
51
  value_type: type = None,
52
+ allow_empty: bool = None,
51
53
 
52
54
  cursor: Literals.cursor = None,
53
55
  cursor_button: Literals.cursor = None,
@@ -159,7 +161,7 @@ class Spinbox(MixinElementWithValue, BaseWidget):
159
161
  self._key_function = key_function
160
162
 
161
163
  self._default_val = default_value
162
- self._value_type = self.defaults.single("value_type", value_type, default= float)
164
+ self._allow_empty = self.defaults.single("allow_empty", allow_empty, default=True)
163
165
 
164
166
  self._update_initial(cursor=cursor, cursor_button=cursor_button,
165
167
  takefocus=takefocus, justify=justify, background_color=background_color,
@@ -178,6 +180,7 @@ class Spinbox(MixinElementWithValue, BaseWidget):
178
180
  number_min=number_min, number_max=number_max, increment=increment, width=width,
179
181
  repeatdelay=repeatdelay, repeatinterval=repeatinterval, state=state,
180
182
  insertbackground_color=insertbackground_color,
183
+ value_type=value_type,
181
184
  # validate = "key", # This restricts the user to one type of value
182
185
  # validatecommand = (self.parent.register(self._validate), "%P"), # This too
183
186
  **tk_kwargs)
@@ -217,8 +220,15 @@ class Spinbox(MixinElementWithValue, BaseWidget):
217
220
  case "font_overstrike":
218
221
  self._overstrike = self.defaults.single(key,new_val)
219
222
  self.add_flags(ElementFlag.UPDATE_FONT)
223
+ case "value_type":
224
+ self._value_type = new_val
225
+ self._update_initial(
226
+ validate="key",
227
+ validatecommand=Validate.CanConvertTo(new_val, allow_empty=self._allow_empty),
228
+ )
229
+
220
230
  case _: # Not a match
221
- return super()._update_special_key(key, new_val)
231
+ return super()._update_special_key(key, new_val)
222
232
 
223
233
  return True
224
234
 
@@ -242,16 +252,16 @@ class Spinbox(MixinElementWithValue, BaseWidget):
242
252
  except tk.TclError: # _tk_target_value isn't used
243
253
  pass
244
254
 
245
- return self._value_type(self._last_viable_value)
255
+ if not self._last_viable_value:
256
+ return self._value_type()
246
257
 
247
- def _validate(self, val: Any) -> bool:
248
- try:
249
- self._value_type(val)
250
- return True
251
- except (ValueError, TypeError):
252
- return False
258
+ return self._value_type(self._last_viable_value)
253
259
 
254
260
  def set_value(self,val:Any) -> Self:
261
+ if self._allow_empty and not val:
262
+ self._prev_value = self._value_type()
263
+ return super().set_value(self._prev_value)
264
+
255
265
  try:
256
266
  # Try to convert and manually apply the new value
257
267
  self._prev_value = self._value_type(val)
@@ -263,10 +273,5 @@ class Spinbox(MixinElementWithValue, BaseWidget):
263
273
  def init_window_creation_done(self):
264
274
  super().init_window_creation_done()
265
275
 
266
- self._update_initial(
267
- validate= "key",
268
- validatecommand = (self.tk_widget.register(self._validate), "%P")
269
- )
270
-
271
276
  if self._default_val is not None:
272
277
  self._tk_target_value.set(self._default_val)
@@ -274,6 +274,13 @@ class TextField(MixinElementWithValue, BaseWidget, MixinScrollbar):
274
274
 
275
275
  @_forced_value_change
276
276
  def set_value(self, val:Any):
277
+ # Todo: For some reason, this function is called twice on updates.
278
+ # This should be investigated further.
279
+ # The second call can be None, which is why the None-check below is necessary atm.
280
+
281
+ if val is None:
282
+ return
283
+
277
284
  self._apply_value(val)
278
285
  self.tk_widget.delete("1.0","end")
279
286
  self.tk_widget.insert("1.0",val)
@@ -6,6 +6,8 @@ logger.debug("Beginning to load SwiftGUI")
6
6
  loaded_addons: set[str] = set() # Contains all included SwiftGUI-addons.
7
7
  # If you'd like your own addon officially included, create an issue on GitHub.
8
8
 
9
+ from . import Validate
10
+
9
11
  from .Exceptions import *
10
12
 
11
13
  from . import Extras
@@ -1,99 +0,0 @@
1
-
2
- # SwiftGUI
3
-
4
- A python-package to easily create user-interfaces (GUIs).
5
-
6
- While most packages focus either on small, or big applications, SwiftGUI does both.
7
-
8
- ## Compatible with Python 3.10 and above
9
- Has some minor optimizations when running in Python 3.12+.
10
-
11
- # Getting started / documentation
12
- [Start your journey here](https://github.com/CheesecakeTV/SwiftGUI-Docs/blob/main/01%20Basic%20tutorials/01%20Getting-started.md)
13
-
14
- The documentation now has [its own repository](https://github.com/CheesecakeTV/SwiftGUI-Docs).
15
-
16
- # Completely vibe-coding free
17
- No AI was used in writing this code.
18
-
19
- The only way I used it was to research really specific topics with tkinter.
20
-
21
- I did not use AI to write, check, correct, or refractor code.
22
-
23
- Even the documentation was written completely AI-free.
24
- Takes me around 2 hours per document, but that's a good price for keeping everything slop-free.
25
-
26
- # 35 different elements, 9 different canvas-elements
27
- (Version 0.11.0)
28
-
29
- `import SwiftGUI as sg`
30
-
31
- Call `sg.Examples.preview_all_elements()` for an overview of all currently available elements.
32
-
33
- # Does your GUI look bad?
34
- `import SwiftGUI as sg`
35
-
36
- Just call `sg.Themes.FourColors.Emerald()` before creating the layout.
37
-
38
- This applies the `Emerald`-theme, my personal favorite.
39
-
40
- See which themes are available by calling `sg.Examples.preview_all_themes()`.
41
-
42
- # Alpha-phase!
43
- I am already using SwiftGUI for smaller projects and personally, like it a lot so far.
44
-
45
- However, until version 1.0.0, downward-compatability is not a priority.
46
- Names and functions/methods might change, which could potentially mess up your code.
47
-
48
- For version 1.0.0, I'll sort and standardize names, so they are easier to remember.
49
- After version 1.0.0, there will be little to no changes that might break existing code.
50
- If changes can't be avoided, there will be a separate changelog which shows everything you need to
51
- adjust so you may update without issues.
52
-
53
- Don't worry too much though, the package is pretty much ready for beta-phase.
54
-
55
- # Installation
56
- Install using pip:
57
- ```bash
58
- pip install SwiftGUI
59
- ```
60
-
61
- Update SwiftGUI for the newest features and elements:
62
- ```bash
63
- pip install SwiftGUI -U
64
- ```
65
-
66
- # NOT a clone of PySimpleGUI
67
- SwiftGUI is it's own package with its own functionality.
68
-
69
- I really liked PySimpleGUI (until they went "premium"),
70
- but once you work a lot with it, you'll notice the downsides of it more and more.
71
-
72
- **SwiftGUI can be used very simmilar to PySimpleGUI**, but has a lot of additional features.\
73
- So if you know PySimpleGUI, you won't need to start from scratch.
74
-
75
- Also, there will be a lot of learning-material, including
76
- - Written tutorials (see "getting started" below)
77
- - Video tutorials (Planned for version 1.0.0)
78
- - Application notes, which are short descriptions of actual applications
79
- - Examples, which show an actual application of SwiftGUI
80
- - The GitHub forum ([discussions](https://github.com/CheesecakeTV/SwiftGUI/discussions)) for questions
81
-
82
- ## Why SwiftGUI instead of PySimpleGUI?
83
- I have a lot of experience with `PySimpleGUI`, used it for years.\
84
- It is very useful for smaller applications.
85
-
86
- Unfortunately, at a certain level of complexity, you'll hit a wall.\
87
- All those simple features that make PySimpleGUI great, are suddenly very annoying.
88
-
89
- What annoys me the most is that PySimpleGUI forces you to use only the event-loop for functionality.
90
- That means, every tiny tiniest functionality needs a key and code in the event-loop.
91
-
92
- SwiftGUI on the other hand offers many different ways to connect layout with functionality.
93
- That makes it easy to "copy and paste" parts of the layout.
94
-
95
- SwiftGUI also supports multiple windows much better, with every window having its own event-loop.
96
-
97
- On top of that, SwiftGUI enables expandability everywhere.
98
- You can easily create your own elements, themes, popups and templates.
99
-
File without changes