SwiftGUI 0.11.19__tar.gz → 0.12.0__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.19 → swiftgui-0.12.0}/PKG-INFO +35 -34
- swiftgui-0.12.0/README.md +100 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/pyproject.toml +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Base.py +71 -3
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Text.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Combined_Elements/BaseCombinedElement.py +6 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Combined_Elements/Form.py +104 -43
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Examples/AllElements.py +9 -6
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/Table.py +3 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/GlobalOptions.py +14 -3
- swiftgui-0.12.0/src/SwiftGUI/Popups/TooltipPopup.py +41 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Popups/__init__.py +1 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Themes/FourColors.py +3 -0
- swiftgui-0.12.0/src/SwiftGUI/Tooltips.py +112 -0
- swiftgui-0.12.0/src/SwiftGUI/Validate/Base.py +119 -0
- swiftgui-0.12.0/src/SwiftGUI/Validate/Common.py +82 -0
- swiftgui-0.12.0/src/SwiftGUI/Validate/Regex.py +20 -0
- swiftgui-0.12.0/src/SwiftGUI/Validate/__init__.py +4 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Button.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Checkbox.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Combobox.py +8 -4
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Frame.py +3 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Input.py +3 -3
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Listbox.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Radiobutton.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Scale.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Spinbox.py +21 -16
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Text.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/TextField.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Treeview.py +1 -1
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Windows.py +34 -2
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/__init__.py +3 -0
- swiftgui-0.11.19/README.md +0 -99
- {swiftgui-0.11.19 → swiftgui-0.12.0}/LICENSE +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/BasePopup.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Arc.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Bitmap.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Element.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Image.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Line.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Oval.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Polygon.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/Rectangle.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/_Base.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Canvas_Elements/__init__.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Colors.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Combined_Elements/Calendar.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Combined_Elements/Console.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Combined_Elements/MultistateButton.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Compat.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Debug.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/DecoratorKeys.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/ElementFlags.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Events.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Examples/AllThemes.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Examples/GlobalOptionBrowser.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Examples/Previews.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Examples/__init__.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Exceptions.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/ColorChooserButton.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/ComboboxMapping.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/FileBrowseButton.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/Image.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/ImageButton.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/PythonConsole.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/Separator.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/Spacer.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extended_Elements/TabFrame.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extras/SwiftGUI.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Extras/__init__.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Files/ConfigFile.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Files/ConfigurationEditor.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Files/DictFile.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Files/ProgramDirectory.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Files/__init__.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Fonts.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/KeyFunctions.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Literals.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Popups/ElementPopup.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Popups/ListPicker.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Popups/SmallPopups.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Popups/VirtualKeyboard.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Themes/Thematic.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Themes/_BaseTheme.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Themes/__init__.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Tools.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Utilities/Images.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Utilities/Threads.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Canvas.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/GridFrame.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/LabelFrame.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Notebook.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Progressbar.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/Scrollbar.py +0 -0
- {swiftgui-0.11.19 → swiftgui-0.12.0}/src/SwiftGUI/Widget_Elements/TKContainer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SwiftGUI
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.0
|
|
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
|
-
|
|
34
|
+
[Documentation](https://github.com/CheesecakeTV/SwiftGUI-Docs).
|
|
36
35
|
|
|
37
|
-
#
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
#
|
|
64
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
86
|
+
but once you work a lot with it, you'll notice its downsides more and more.
|
|
92
87
|
|
|
93
|
-
|
|
94
|
-
So if you know PySimpleGUI, you
|
|
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
|
-
|
|
97
|
-
- Written tutorials (see "getting started"
|
|
98
|
-
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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,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, ReuseError, RowTypeError
|
|
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.
|
|
@@ -383,6 +383,52 @@ class BaseElement(_BaseSharedAttributes):
|
|
|
383
383
|
|
|
384
384
|
return self
|
|
385
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
|
+
|
|
386
432
|
class BaseWidget(BaseElement):
|
|
387
433
|
"""
|
|
388
434
|
Base for every Widget
|
|
@@ -720,6 +766,28 @@ class BaseWidget(BaseElement):
|
|
|
720
766
|
def __repr__(self) -> str:
|
|
721
767
|
return f"<sg.{self.__class__.__name__} element with key={self.key} at {id(self)}>"
|
|
722
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
|
+
|
|
723
791
|
class MixinScrollbar:
|
|
724
792
|
"""
|
|
725
793
|
Base for every widget that has a scrollbar.
|
|
@@ -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),
|
|
@@ -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
|
+
|
|
@@ -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
|
|