scustomtkinter 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scustomtkinter/__init__.py +81 -0
- scustomtkinter/assets/images/error.png +0 -0
- scustomtkinter/assets/images/info.png +0 -0
- scustomtkinter/assets/images/warning.png +0 -0
- scustomtkinter/assets/sCTkThemes.json +1963 -0
- scustomtkinter/license_morecustomtkinterwidgets.txt +22 -0
- scustomtkinter/sctk_button_primary.py +366 -0
- scustomtkinter/sctk_button_secondary.py +354 -0
- scustomtkinter/sctk_button_tertiary.py +375 -0
- scustomtkinter/sctk_checkbox.py +270 -0
- scustomtkinter/sctk_combobox.py +155 -0
- scustomtkinter/sctk_core.py +15 -0
- scustomtkinter/sctk_dial.py +1472 -0
- scustomtkinter/sctk_dialog.py +701 -0
- scustomtkinter/sctk_dialog_toplevel.py +268 -0
- scustomtkinter/sctk_entry_primary.py +400 -0
- scustomtkinter/sctk_entry_secondary.py +342 -0
- scustomtkinter/sctk_file_explorer.py +879 -0
- scustomtkinter/sctk_frame.py +210 -0
- scustomtkinter/sctk_frame_labeled_primary.py +667 -0
- scustomtkinter/sctk_frame_labeled_secondary.py +598 -0
- scustomtkinter/sctk_frequency_display.py +636 -0
- scustomtkinter/sctk_label_primary.py +283 -0
- scustomtkinter/sctk_label_secondary.py +275 -0
- scustomtkinter/sctk_label_tertiary.py +278 -0
- scustomtkinter/sctk_messagebox.py +275 -0
- scustomtkinter/sctk_notebook.py +1103 -0
- scustomtkinter/sctk_optionmenu_border_mixin.py +203 -0
- scustomtkinter/sctk_optionmenu_primary.py +378 -0
- scustomtkinter/sctk_optionmenu_secondary.py +346 -0
- scustomtkinter/sctk_path_chooser.py +516 -0
- scustomtkinter/sctk_progress_bar.py +274 -0
- scustomtkinter/sctk_radiobutton.py +360 -0
- scustomtkinter/sctk_scroll_mixin.py +546 -0
- scustomtkinter/sctk_scrollable_frame.py +795 -0
- scustomtkinter/sctk_scrollbar.py +313 -0
- scustomtkinter/sctk_segmentedbutton.py +424 -0
- scustomtkinter/sctk_selector.py +581 -0
- scustomtkinter/sctk_separator.py +308 -0
- scustomtkinter/sctk_slider.py +264 -0
- scustomtkinter/sctk_smeter.py +405 -0
- scustomtkinter/sctk_smeter_bar.py +557 -0
- scustomtkinter/sctk_spinbox.py +664 -0
- scustomtkinter/sctk_switch.py +463 -0
- scustomtkinter/sctk_tableview.py +869 -0
- scustomtkinter/sctk_tabview.py +409 -0
- scustomtkinter/sctk_textbox_primary.py +291 -0
- scustomtkinter/sctk_textbox_secondary.py +225 -0
- scustomtkinter/sctk_toplevel.py +153 -0
- scustomtkinter/themeable_widget.py +938 -0
- scustomtkinter-0.2.1.dist-info/METADATA +336 -0
- scustomtkinter-0.2.1.dist-info/RECORD +101 -0
- scustomtkinter-0.2.1.dist-info/WHEEL +5 -0
- scustomtkinter-0.2.1.dist-info/entry_points.txt +2 -0
- scustomtkinter-0.2.1.dist-info/licenses/LICENSE +21 -0
- scustomtkinter-0.2.1.dist-info/licenses/scustomtkinter/license_morecustomtkinterwidgets.txt +22 -0
- scustomtkinter-0.2.1.dist-info/top_level.txt +2 -0
- scustomtkinter_pygubu/__init__.py +1 -0
- scustomtkinter_pygubu/designer/__init__.py +0 -0
- scustomtkinter_pygubu/designer/plugin.py +1854 -0
- scustomtkinter_pygubu/designer/properties.py +247 -0
- scustomtkinter_pygubu/sCTkButtonPrimarybo.py +46 -0
- scustomtkinter_pygubu/sCTkButtonSecondarybo.py +45 -0
- scustomtkinter_pygubu/sCTkButtonTertiarybo.py +44 -0
- scustomtkinter_pygubu/sCTkCheckBoxbo.py +45 -0
- scustomtkinter_pygubu/sCTkComboBoxbo.py +45 -0
- scustomtkinter_pygubu/sCTkCorebo.py +120 -0
- scustomtkinter_pygubu/sCTkDialbo.py +371 -0
- scustomtkinter_pygubu/sCTkDialogbo.py +397 -0
- scustomtkinter_pygubu/sCTkEntryPrimarybo.py +100 -0
- scustomtkinter_pygubu/sCTkEntrySecondarybo.py +98 -0
- scustomtkinter_pygubu/sCTkFileExplorerbo.py +189 -0
- scustomtkinter_pygubu/sCTkFrameLabeledPrimarybo.py +69 -0
- scustomtkinter_pygubu/sCTkFrameLabeledSecondarybo.py +71 -0
- scustomtkinter_pygubu/sCTkFramebo.py +46 -0
- scustomtkinter_pygubu/sCTkFrequencyDisplaybo.py +175 -0
- scustomtkinter_pygubu/sCTkLabelPrimarybo.py +45 -0
- scustomtkinter_pygubu/sCTkLabelSecondarybo.py +44 -0
- scustomtkinter_pygubu/sCTkLabelTertiarybo.py +45 -0
- scustomtkinter_pygubu/sCTkNotebookbo.py +392 -0
- scustomtkinter_pygubu/sCTkOptionMenuPrimarybo.py +45 -0
- scustomtkinter_pygubu/sCTkOptionMenuSecondarybo.py +39 -0
- scustomtkinter_pygubu/sCTkPathChooserbo.py +204 -0
- scustomtkinter_pygubu/sCTkProgressBarbo.py +45 -0
- scustomtkinter_pygubu/sCTkRadioButtonbo.py +45 -0
- scustomtkinter_pygubu/sCTkSMeterBarbo.py +153 -0
- scustomtkinter_pygubu/sCTkSMeterbo.py +101 -0
- scustomtkinter_pygubu/sCTkScrollableFramebo.py +66 -0
- scustomtkinter_pygubu/sCTkScrollbarbo.py +48 -0
- scustomtkinter_pygubu/sCTkSegmentedButtonbo.py +47 -0
- scustomtkinter_pygubu/sCTkSelectorbo.py +167 -0
- scustomtkinter_pygubu/sCTkSeparatorbo.py +141 -0
- scustomtkinter_pygubu/sCTkSliderbo.py +45 -0
- scustomtkinter_pygubu/sCTkSpinboxbo.py +200 -0
- scustomtkinter_pygubu/sCTkSwitchbo.py +45 -0
- scustomtkinter_pygubu/sCTkTableviewbo.py +188 -0
- scustomtkinter_pygubu/sCTkTabviewbo.py +226 -0
- scustomtkinter_pygubu/sCTkTextboxPrimarybo.py +45 -0
- scustomtkinter_pygubu/sCTkTextboxSecondarybo.py +45 -0
- scustomtkinter_pygubu/sCTkToplevelbo.py +55 -0
- scustomtkinter_pygubu/sCTkWidgetSetForPygubuDesigner.py +141 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""
|
|
3
|
+
sCTkDialog
|
|
4
|
+
|
|
5
|
+
A consistent popup dialog: heading, content area, action button row.
|
|
6
|
+
|
|
7
|
+
NOTE there is deliberately no builder object for sCTkDialogToplevel. That
|
|
8
|
+
class is created BY sCTkDialog and is never placed by a user, so
|
|
9
|
+
registering it would only offer something that cannot sensibly be dropped
|
|
10
|
+
anywhere. The class remains; its Designer registration does not.
|
|
11
|
+
"""
|
|
12
|
+
import tkinter as tk
|
|
13
|
+
import tkinter.ttk as ttk
|
|
14
|
+
|
|
15
|
+
from pygubu.api.v1 import (
|
|
16
|
+
BuilderObject,
|
|
17
|
+
register_widget,
|
|
18
|
+
register_custom_property,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from scustomtkinter.themeable_widget import parse_font_property
|
|
22
|
+
from scustomtkinter.sctk_dialog import sCTkDialog
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# Builder definition section
|
|
27
|
+
#
|
|
28
|
+
widget_namespace = "scustomtkinter.sctk_dialog"
|
|
29
|
+
widget_classname = "sCTkDialog"
|
|
30
|
+
builder_namespace = "scustomtkinter"
|
|
31
|
+
section_name = "sCustomTkinter"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class sCTkDialogBO(BuilderObject):
|
|
35
|
+
class_ = sCTkDialog
|
|
36
|
+
container = True
|
|
37
|
+
|
|
38
|
+
# Window properties. All are keyword-only constructor arguments on the
|
|
39
|
+
# widget, not configure() options, so realize() and code_realize() below
|
|
40
|
+
# pass them at construction rather than leaving BuilderObject to apply
|
|
41
|
+
# them afterwards.
|
|
42
|
+
OPTIONS_CUSTOM = ("title", "width", "height", "modal", "transient",
|
|
43
|
+
"offset_x", "offset_y", "heading", "heading_anchor",
|
|
44
|
+
"heading_font", "heading_color", "buttons",
|
|
45
|
+
"apply_text", "cancel_text", "reset_text")
|
|
46
|
+
properties = OPTIONS_CUSTOM + ("apply_command", "cancel_command",
|
|
47
|
+
"reset_command")
|
|
48
|
+
|
|
49
|
+
# Declared so pygubu routes these through its callback handling rather
|
|
50
|
+
# than stringifying the property metadata into the generated code.
|
|
51
|
+
command_properties = ("apply_command", "cancel_command", "reset_command")
|
|
52
|
+
|
|
53
|
+
def get_child_master(self):
|
|
54
|
+
"""
|
|
55
|
+
Children go into the content area, not into the dialog frame itself.
|
|
56
|
+
|
|
57
|
+
This is what makes the dialog pleasant to lay out: drop widgets onto
|
|
58
|
+
it in the Designer and they land between the heading and the button
|
|
59
|
+
row, where they belong.
|
|
60
|
+
"""
|
|
61
|
+
return self.widget.contentFrame
|
|
62
|
+
|
|
63
|
+
def code_child_master(self):
|
|
64
|
+
return f"{self.code_identifier()}.contentFrame"
|
|
65
|
+
|
|
66
|
+
def _dialog_init_args(self):
|
|
67
|
+
"""
|
|
68
|
+
Collects the window properties from the widget metadata, converted to
|
|
69
|
+
the types the constructor expects.
|
|
70
|
+
|
|
71
|
+
Omitted properties are left out entirely rather than passed as None,
|
|
72
|
+
so the widget's own defaults apply.
|
|
73
|
+
"""
|
|
74
|
+
props = getattr(getattr(self, "wmeta", None), "properties", {}) or {}
|
|
75
|
+
args = {}
|
|
76
|
+
|
|
77
|
+
title = props.get("title")
|
|
78
|
+
if title:
|
|
79
|
+
args["title"] = str(title)
|
|
80
|
+
|
|
81
|
+
for name in ("width", "height", "offset_x", "offset_y"):
|
|
82
|
+
raw = props.get(name)
|
|
83
|
+
if raw not in (None, ""):
|
|
84
|
+
try:
|
|
85
|
+
args[name] = int(raw)
|
|
86
|
+
except (TypeError, ValueError):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
for flag in ("modal", "transient"):
|
|
90
|
+
raw = props.get(flag)
|
|
91
|
+
if raw not in (None, ""):
|
|
92
|
+
args[flag] = str(raw).lower() in ("true", "1", "yes")
|
|
93
|
+
|
|
94
|
+
for prop in ("heading", "heading_anchor"):
|
|
95
|
+
value = props.get(prop)
|
|
96
|
+
if value:
|
|
97
|
+
args[prop] = str(value)
|
|
98
|
+
|
|
99
|
+
font = parse_font_property(props.get("heading_font"))
|
|
100
|
+
if font is not None:
|
|
101
|
+
args["heading_font"] = font
|
|
102
|
+
|
|
103
|
+
colour = props.get("heading_color")
|
|
104
|
+
if colour:
|
|
105
|
+
args["heading_color"] = str(colour)
|
|
106
|
+
|
|
107
|
+
buttons = props.get("buttons")
|
|
108
|
+
if buttons not in (None, ""):
|
|
109
|
+
try:
|
|
110
|
+
args["buttons"] = int(buttons)
|
|
111
|
+
except (TypeError, ValueError):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
# Only labels for buttons this dialog actually has. With buttons=2 a
|
|
115
|
+
# reset_text argument is inert -- it lands in _button_text where
|
|
116
|
+
# nothing reads it -- but it is noise in the generated file, and noise
|
|
117
|
+
# that reads like a bug: a label for a button that does not exist.
|
|
118
|
+
#
|
|
119
|
+
# Reads args["buttons"], so this must stay AFTER the buttons parsing
|
|
120
|
+
# above.
|
|
121
|
+
#
|
|
122
|
+
# The COMMANDS are deliberately not filtered the same way. Pygubu emits
|
|
123
|
+
# those through its own path, and each one generates a callback stub in
|
|
124
|
+
# the user's file; dropping a command because the button count was
|
|
125
|
+
# temporarily reduced would take that stub with it.
|
|
126
|
+
count = args.get("buttons", 3)
|
|
127
|
+
for prop in ("apply_text", "cancel_text", "reset_text")[:count]:
|
|
128
|
+
value = props.get(prop)
|
|
129
|
+
if value:
|
|
130
|
+
args[prop] = str(value)
|
|
131
|
+
|
|
132
|
+
return args
|
|
133
|
+
|
|
134
|
+
def realize(self, parent, extra_init_args: dict = None):
|
|
135
|
+
"""Builds the dialog, passing the window properties at construction."""
|
|
136
|
+
init_args = self._dialog_init_args()
|
|
137
|
+
if extra_init_args:
|
|
138
|
+
init_args.update(extra_init_args)
|
|
139
|
+
master = parent.get_child_master() if hasattr(parent, "get_child_master") else parent
|
|
140
|
+
self.widget = self.class_(master, **init_args)
|
|
141
|
+
return self.widget
|
|
142
|
+
|
|
143
|
+
def code_realize(self, boparent, code_identifier=None):
|
|
144
|
+
"""Emits the construction line with the window properties inline."""
|
|
145
|
+
if code_identifier is not None:
|
|
146
|
+
self._code_identifier = code_identifier
|
|
147
|
+
master = boparent.code_child_master()
|
|
148
|
+
args = self._dialog_init_args()
|
|
149
|
+
|
|
150
|
+
bag = [f"{master}"]
|
|
151
|
+
for name, value in args.items():
|
|
152
|
+
bag.append(f"{name}={value!r}")
|
|
153
|
+
|
|
154
|
+
return [f"{self.code_identifier()} = {self._code_class_name()}({', '.join(bag)})"]
|
|
155
|
+
|
|
156
|
+
# Property name -> the widget setter that applies a button command.
|
|
157
|
+
_COMMAND_TARGETS = {
|
|
158
|
+
"apply_command": "set_apply_button",
|
|
159
|
+
"cancel_command": "set_cancel_button",
|
|
160
|
+
"reset_command": "set_reset_button",
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Properties that can be applied to a LIVE widget. Everything else in
|
|
164
|
+
# OPTIONS_CUSTOM affects the window or the set of buttons, and can only be
|
|
165
|
+
# honoured by rebuilding -- the Designer does that on its own when the
|
|
166
|
+
# design is redrawn.
|
|
167
|
+
_LIVE_TEXT = {"apply_text": "applyText_VAR",
|
|
168
|
+
"cancel_text": "cancelText_VAR",
|
|
169
|
+
"reset_text": "resetText_VAR"}
|
|
170
|
+
|
|
171
|
+
# The widget's own defaults. Clearing a field in the inspector must restore
|
|
172
|
+
# these, not blank the button: an empty label looks like a broken widget,
|
|
173
|
+
# and the generated code correctly falls back to them because the property
|
|
174
|
+
# is simply omitted from the constructor call.
|
|
175
|
+
_TEXT_DEFAULTS = {"apply_text": "Apply", "cancel_text": "Cancel",
|
|
176
|
+
"reset_text": "Reset", "heading": "Heading Title"}
|
|
177
|
+
|
|
178
|
+
def _set_property(self, target_widget, pname, value):
|
|
179
|
+
"""
|
|
180
|
+
Applies a property to the live preview.
|
|
181
|
+
|
|
182
|
+
None of this widget's own properties are configure() options --
|
|
183
|
+
sCTkDialog takes them as constructor arguments -- so letting any of
|
|
184
|
+
them reach sCTkFrame.configure() would raise. They are handled here or
|
|
185
|
+
deliberately ignored.
|
|
186
|
+
|
|
187
|
+
Button labels ARE applied live, by setting the StringVar the button
|
|
188
|
+
reads. Without this, editing a label in the inspector changed nothing
|
|
189
|
+
on the canvas until the design was redrawn for some other reason.
|
|
190
|
+
"""
|
|
191
|
+
if pname in self._LIVE_TEXT:
|
|
192
|
+
# FIX: an empty value used to blank the button. Clearing a field
|
|
193
|
+
# means "use the default", which is what the generated code does --
|
|
194
|
+
# the property is omitted and the constructor default applies. The
|
|
195
|
+
# design view now agrees with it.
|
|
196
|
+
# The widget restores its own default when given an empty value,
|
|
197
|
+
# and knows what that default is -- see sCTkDialog.configure() and
|
|
198
|
+
# _PROPERTY_DEFAULTS. Keeping a second copy of the same defaults
|
|
199
|
+
# here would be a place for them to drift apart.
|
|
200
|
+
target_widget.configure(**{pname: value})
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
if pname == "heading_font":
|
|
204
|
+
target_widget.set_heading_font(
|
|
205
|
+
parse_font_property(value)
|
|
206
|
+
or target_widget.final_kw.get("heading_font"))
|
|
207
|
+
return None
|
|
208
|
+
|
|
209
|
+
if pname == "heading_color":
|
|
210
|
+
# Empty restores the theme's text_color, matching what the
|
|
211
|
+
# generated code does: the property is omitted and the theme
|
|
212
|
+
# applies.
|
|
213
|
+
target_widget.set_heading_color(value or None)
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
if pname == "buttons":
|
|
217
|
+
# FIX: this used to rely on builder.recreate_widget(), which did
|
|
218
|
+
# not reach us -- the canvas kept the old button count while the
|
|
219
|
+
# preview and generated code showed the new one. The widget rebuilds
|
|
220
|
+
# its own button row instead, which depends on nothing outside it.
|
|
221
|
+
target_widget.set_buttons(value)
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
if pname == "heading":
|
|
225
|
+
target_widget.configure(heading=value)
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
if pname == "heading_anchor":
|
|
229
|
+
target_widget.set_heading(anchor=value or "center")
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
if pname == "title":
|
|
233
|
+
target_widget.set_title(value or "")
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
if pname in self._COMMAND_TARGETS:
|
|
237
|
+
# A command aimed at a button this dialog does not have is ignored
|
|
238
|
+
# rather than raising: reducing `buttons` while a command is still
|
|
239
|
+
# set on a removed button is an ordinary inspector edit.
|
|
240
|
+
if value:
|
|
241
|
+
getattr(target_widget, self._COMMAND_TARGETS[pname])(
|
|
242
|
+
button_command=value)
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
if pname in ("width", "height"):
|
|
246
|
+
# Applied live: these size the WINDOW, and the widget routes them
|
|
247
|
+
# there. See sCTkDialog._OWN_PROPERTIES for why they are not the
|
|
248
|
+
# frame's own width and height.
|
|
249
|
+
target_widget.configure(**{pname: value})
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
if pname in self.OPTIONS_CUSTOM:
|
|
253
|
+
# Placement, modality and transient are construction-time.
|
|
254
|
+
# realize() passes them; nothing to do here.
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
return super()._set_property(target_widget, pname, value)
|
|
258
|
+
|
|
259
|
+
def set_property(self, name, value):
|
|
260
|
+
"""
|
|
261
|
+
Records a property change in the widget metadata, then applies it live.
|
|
262
|
+
|
|
263
|
+
FIX: without the metadata write, a change made in the inspector did not
|
|
264
|
+
reach code generation until the project was saved and reopened --
|
|
265
|
+
editing `buttons` from 3 to 2 updated the canvas but still generated
|
|
266
|
+
buttons=3. The live widget and the metadata had diverged.
|
|
267
|
+
|
|
268
|
+
An earlier version of this method also called
|
|
269
|
+
builder.recreate_widget(); that has been dropped. It never reached us,
|
|
270
|
+
and the widget rebuilds its own button row now -- see
|
|
271
|
+
sCTkDialog.set_buttons().
|
|
272
|
+
"""
|
|
273
|
+
if hasattr(self, "wmeta") and hasattr(self.wmeta, "properties"):
|
|
274
|
+
self.wmeta.properties[name] = value
|
|
275
|
+
|
|
276
|
+
if getattr(self, "widget", None) is not None:
|
|
277
|
+
self._set_property(self.widget, name, value)
|
|
278
|
+
|
|
279
|
+
def _code_set_property(self, targetid, pname, value, code_bag):
|
|
280
|
+
"""
|
|
281
|
+
Keeps this widget's own properties OUT of the generated configure()
|
|
282
|
+
call.
|
|
283
|
+
|
|
284
|
+
FIX: without this they were emitted as configure() arguments --
|
|
285
|
+
|
|
286
|
+
sctkdialog1.configure(apply_text=""Apply it"", buttons=3, ...)
|
|
287
|
+
|
|
288
|
+
which is wrong twice over. The doubled quotes are a syntax error, and
|
|
289
|
+
even correctly quoted the call would raise at runtime, because
|
|
290
|
+
sCTkFrame.configure() does not accept these names. They belong in the
|
|
291
|
+
constructor, and code_realize() already puts them there.
|
|
292
|
+
|
|
293
|
+
The button commands are emitted as their own statements instead, since
|
|
294
|
+
they are applied through setter methods rather than configure().
|
|
295
|
+
"""
|
|
296
|
+
# The button commands are deliberately NOT intercepted here. Pygubu
|
|
297
|
+
# emits command properties through its own code path, which this
|
|
298
|
+
# override is not consulted for -- the earlier attempt to redirect them
|
|
299
|
+
# produced generated code that still called
|
|
300
|
+
# configure(apply_command=...) and raised ValueError at the widget.
|
|
301
|
+
#
|
|
302
|
+
# sCTkDialog.configure() now accepts them instead, so the ordinary
|
|
303
|
+
# emission works and there is nothing to redirect.
|
|
304
|
+
if pname in self.OPTIONS_CUSTOM:
|
|
305
|
+
# Passed at construction by code_realize(); keeping them out of the
|
|
306
|
+
# configure() call avoids setting the same thing twice.
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
return super()._code_set_property(targetid, pname, value, code_bag)
|
|
310
|
+
|
|
311
|
+
def code_imports(self):
|
|
312
|
+
# should return an iterable of (module, classname/function) to import
|
|
313
|
+
# or None
|
|
314
|
+
return [(widget_namespace, widget_classname)]
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
builder_id = f"{builder_namespace}.{widget_classname}"
|
|
318
|
+
register_widget(
|
|
319
|
+
builder_id, sCTkDialogBO, widget_classname, ("ttk", section_name)
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
register_custom_property(
|
|
323
|
+
builder_id, "title", "entry",
|
|
324
|
+
help="Window title bar text."
|
|
325
|
+
)
|
|
326
|
+
register_custom_property(
|
|
327
|
+
builder_id, "width", "naturalnumber",
|
|
328
|
+
help="Window width in pixels. Leave blank to size to content."
|
|
329
|
+
)
|
|
330
|
+
register_custom_property(
|
|
331
|
+
builder_id, "height", "naturalnumber",
|
|
332
|
+
help="Window height in pixels. Leave blank to size to content."
|
|
333
|
+
)
|
|
334
|
+
register_custom_property(
|
|
335
|
+
builder_id, "modal", "choice", values=("False", "True"), state="readonly",
|
|
336
|
+
help="True blocks interaction with the rest of the application while the "
|
|
337
|
+
"dialog is open. Call run_and_wait() instead to also block the "
|
|
338
|
+
"calling code until it closes."
|
|
339
|
+
)
|
|
340
|
+
register_custom_property(
|
|
341
|
+
builder_id, "offset_x", "integernumber",
|
|
342
|
+
help="Pixels right of the parent window's top-left corner. Default 40."
|
|
343
|
+
)
|
|
344
|
+
register_custom_property(
|
|
345
|
+
builder_id, "offset_y", "integernumber",
|
|
346
|
+
help="Pixels below the parent window's top-left corner. Default 40."
|
|
347
|
+
)
|
|
348
|
+
register_custom_property(
|
|
349
|
+
builder_id, "transient", "choice", values=("True", "False"),
|
|
350
|
+
state="readonly",
|
|
351
|
+
help="True ties the window to its parent -- above it, minimises with it, "
|
|
352
|
+
"usually not in the taskbar. False gives an independent window."
|
|
353
|
+
)
|
|
354
|
+
register_custom_property(
|
|
355
|
+
builder_id, "heading", "entry",
|
|
356
|
+
help="Text shown above the content area. Clear to restore the default."
|
|
357
|
+
)
|
|
358
|
+
register_custom_property(
|
|
359
|
+
builder_id, "heading_anchor", "choice",
|
|
360
|
+
values=("center", "w", "e"), state="readonly",
|
|
361
|
+
help="Heading alignment."
|
|
362
|
+
)
|
|
363
|
+
register_custom_property(
|
|
364
|
+
builder_id, "heading_font", "fontentry",
|
|
365
|
+
help="Heading font. Leave blank to use the theme's heading_font."
|
|
366
|
+
)
|
|
367
|
+
register_custom_property(
|
|
368
|
+
builder_id, "heading_color", "colorentry",
|
|
369
|
+
help="Heading colour. Leave blank to use the theme's text_color."
|
|
370
|
+
)
|
|
371
|
+
register_custom_property(
|
|
372
|
+
builder_id, "buttons", "choice", values=("3", "2", "1"), state="readonly",
|
|
373
|
+
help="3 = Apply, Cancel, Reset. 2 = Apply, Cancel. 1 = Apply only. "
|
|
374
|
+
"Apply is always present."
|
|
375
|
+
)
|
|
376
|
+
register_custom_property(
|
|
377
|
+
builder_id, "apply_text", "entry", help="Apply button label. Default 'Apply'."
|
|
378
|
+
)
|
|
379
|
+
register_custom_property(
|
|
380
|
+
builder_id, "cancel_text", "entry",
|
|
381
|
+
help="Cancel button label. Default 'Cancel'. Ignored when buttons is 1."
|
|
382
|
+
)
|
|
383
|
+
register_custom_property(
|
|
384
|
+
builder_id, "reset_text", "entry",
|
|
385
|
+
help="Reset button label. Default 'Reset'. Ignored unless buttons is 3."
|
|
386
|
+
)
|
|
387
|
+
register_custom_property(
|
|
388
|
+
builder_id, "apply_command", "commandentry", help="Called when Apply is clicked."
|
|
389
|
+
)
|
|
390
|
+
register_custom_property(
|
|
391
|
+
builder_id, "cancel_command", "commandentry",
|
|
392
|
+
help="Called when Cancel is clicked. Ignored when buttons is 1."
|
|
393
|
+
)
|
|
394
|
+
register_custom_property(
|
|
395
|
+
builder_id, "reset_command", "commandentry",
|
|
396
|
+
help="Called when Reset is clicked. Ignored unless buttons is 3."
|
|
397
|
+
)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""
|
|
3
|
+
sCTkEntryPrimary
|
|
4
|
+
|
|
5
|
+
Customized ctk Entry field. - Primary version
|
|
6
|
+
|
|
7
|
+
UI source file: sCTkEntryPrimary.ui
|
|
8
|
+
"""
|
|
9
|
+
import tkinter as tk
|
|
10
|
+
import tkinter.ttk as ttk
|
|
11
|
+
from customtkinter import CTkEntry
|
|
12
|
+
from pygubu.api.v1 import (
|
|
13
|
+
BuilderObject,
|
|
14
|
+
register_widget,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from pygubu.plugins.customtkinter import nsctk
|
|
18
|
+
from pygubu.plugins.customtkinter.widgets import CTkEntryBO
|
|
19
|
+
|
|
20
|
+
from scustomtkinter.sctk_entry_primary import sCTkEntryPrimary
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Builder definition section
|
|
25
|
+
#
|
|
26
|
+
widget_namespace = "scustomtkinter.sctk_entry_primary"
|
|
27
|
+
widget_classname = "sCTkEntryPrimary"
|
|
28
|
+
builder_namespace = "scustomtkinter"
|
|
29
|
+
section_name = "sCustomTkinter"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class sCTkEntryPrimaryBO(CTkEntryBO):
|
|
33
|
+
class_ = sCTkEntryPrimary
|
|
34
|
+
|
|
35
|
+
# --- Workarounds for CTkEntryBO, both reported upstream -----------------
|
|
36
|
+
#
|
|
37
|
+
# 1. invalidcommand is REMOVED from the inspector. CTkEntry does not accept
|
|
38
|
+
# it -- the name is absent from the widget's own attribute whitelist, so
|
|
39
|
+
# configure() raises:
|
|
40
|
+
#
|
|
41
|
+
# ['invalidcommand'] are not supported arguments.
|
|
42
|
+
#
|
|
43
|
+
# CTkEntryBO offers it anyway. Since sCTkEntry{Primary,Secondary} pass
|
|
44
|
+
# keywords straight through to native CTkEntry, the property could never
|
|
45
|
+
# work at any layer. Better not to offer it than to offer a field that
|
|
46
|
+
# errors when used.
|
|
47
|
+
#
|
|
48
|
+
# Subtracted from the inherited tuple rather than restated, so any
|
|
49
|
+
# property CustomTkinter adds later is picked up automatically.
|
|
50
|
+
properties = tuple(p for p in CTkEntryBO.properties if p != "invalidcommand")
|
|
51
|
+
|
|
52
|
+
# 2. validatecommand and xscrollcommand ARE accepted by CTkEntry, but
|
|
53
|
+
# CTkEntryBO never declares them as command properties -- so pygubu does
|
|
54
|
+
# not route them through its callback handling and instead stringifies
|
|
55
|
+
# the raw property metadata into the generated code:
|
|
56
|
+
#
|
|
57
|
+
# validatecommand="{"name": "validatecommand", "type": "command", ...}"
|
|
58
|
+
#
|
|
59
|
+
# Note the unescaped inner quotes: that is not merely wrong, it is a
|
|
60
|
+
# syntax error, and the whole generated module fails to import.
|
|
61
|
+
#
|
|
62
|
+
# invalidcommand is deliberately NOT listed here even though it is a
|
|
63
|
+
# command property, because the widget rejects it -- see above.
|
|
64
|
+
command_properties = tuple(getattr(CTkEntryBO, "command_properties", ())) + (
|
|
65
|
+
"validatecommand",
|
|
66
|
+
"xscrollcommand",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def _code_define_callback_args(self, cmd_pname, cmd):
|
|
70
|
+
"""
|
|
71
|
+
Declares what each command callback receives.
|
|
72
|
+
|
|
73
|
+
`xscrollcommand` is called by Tk with TWO arguments -- the first and
|
|
74
|
+
last visible fractions, the same pair a scrollbar's set() takes. A stub
|
|
75
|
+
generated with no parameters raises the moment the entry scrolls:
|
|
76
|
+
|
|
77
|
+
TypeError: on_scroll() takes 1 positional argument but 3 were given
|
|
78
|
+
|
|
79
|
+
`validatecommand` is called with NO arguments when it is a plain
|
|
80
|
+
callable, because Tk's %-substitutions are only passed when the command
|
|
81
|
+
is registered with them and pygubu does not do that. The base class
|
|
82
|
+
already returns nothing, so it is left alone rather than restated.
|
|
83
|
+
"""
|
|
84
|
+
if cmd_pname == "xscrollcommand":
|
|
85
|
+
return ("first", "last")
|
|
86
|
+
return super()._code_define_callback_args(cmd_pname, cmd)
|
|
87
|
+
|
|
88
|
+
def code_imports(self):
|
|
89
|
+
# should return an iterable of (module, classname/function) to import
|
|
90
|
+
# or None
|
|
91
|
+
imports = [(widget_namespace, widget_classname)]
|
|
92
|
+
imports.extend(self.code_extra_imports())
|
|
93
|
+
return imports
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
builder_id = f"{builder_namespace}.{widget_classname}"
|
|
97
|
+
register_widget(
|
|
98
|
+
builder_id, sCTkEntryPrimaryBO, widget_classname, ("ttk", section_name)
|
|
99
|
+
)
|
|
100
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""
|
|
3
|
+
sCTkEntrySecondary
|
|
4
|
+
|
|
5
|
+
Customized ctk Entry field. - Secondary version
|
|
6
|
+
|
|
7
|
+
UI source file: sCTkEntrySecondary.ui
|
|
8
|
+
"""
|
|
9
|
+
import tkinter as tk
|
|
10
|
+
import tkinter.ttk as ttk
|
|
11
|
+
from customtkinter import CTkEntry
|
|
12
|
+
from pygubu.api.v1 import (
|
|
13
|
+
BuilderObject,
|
|
14
|
+
register_widget,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from pygubu.plugins.customtkinter.widgets import CTkEntryBO
|
|
18
|
+
from scustomtkinter.sctk_entry_secondary import sCTkEntrySecondary
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Builder definition section
|
|
23
|
+
#
|
|
24
|
+
widget_namespace = "scustomtkinter.sctk_entry_secondary"
|
|
25
|
+
widget_classname = "sCTkEntrySecondary"
|
|
26
|
+
builder_namespace = "scustomtkinter"
|
|
27
|
+
section_name = "sCustomTkinter"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class sCTkEntrySecondaryBO(CTkEntryBO):
|
|
31
|
+
class_ = sCTkEntrySecondary
|
|
32
|
+
|
|
33
|
+
# --- Workarounds for CTkEntryBO, both reported upstream -----------------
|
|
34
|
+
#
|
|
35
|
+
# 1. invalidcommand is REMOVED from the inspector. CTkEntry does not accept
|
|
36
|
+
# it -- the name is absent from the widget's own attribute whitelist, so
|
|
37
|
+
# configure() raises:
|
|
38
|
+
#
|
|
39
|
+
# ['invalidcommand'] are not supported arguments.
|
|
40
|
+
#
|
|
41
|
+
# CTkEntryBO offers it anyway. Since sCTkEntry{Primary,Secondary} pass
|
|
42
|
+
# keywords straight through to native CTkEntry, the property could never
|
|
43
|
+
# work at any layer. Better not to offer it than to offer a field that
|
|
44
|
+
# errors when used.
|
|
45
|
+
#
|
|
46
|
+
# Subtracted from the inherited tuple rather than restated, so any
|
|
47
|
+
# property CustomTkinter adds later is picked up automatically.
|
|
48
|
+
properties = tuple(p for p in CTkEntryBO.properties if p != "invalidcommand")
|
|
49
|
+
|
|
50
|
+
# 2. validatecommand and xscrollcommand ARE accepted by CTkEntry, but
|
|
51
|
+
# CTkEntryBO never declares them as command properties -- so pygubu does
|
|
52
|
+
# not route them through its callback handling and instead stringifies
|
|
53
|
+
# the raw property metadata into the generated code:
|
|
54
|
+
#
|
|
55
|
+
# validatecommand="{"name": "validatecommand", "type": "command", ...}"
|
|
56
|
+
#
|
|
57
|
+
# Note the unescaped inner quotes: that is not merely wrong, it is a
|
|
58
|
+
# syntax error, and the whole generated module fails to import.
|
|
59
|
+
#
|
|
60
|
+
# invalidcommand is deliberately NOT listed here even though it is a
|
|
61
|
+
# command property, because the widget rejects it -- see above.
|
|
62
|
+
command_properties = tuple(getattr(CTkEntryBO, "command_properties", ())) + (
|
|
63
|
+
"validatecommand",
|
|
64
|
+
"xscrollcommand",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def _code_define_callback_args(self, cmd_pname, cmd):
|
|
68
|
+
"""
|
|
69
|
+
Declares what each command callback receives.
|
|
70
|
+
|
|
71
|
+
`xscrollcommand` is called by Tk with TWO arguments -- the first and
|
|
72
|
+
last visible fractions, the same pair a scrollbar's set() takes. A stub
|
|
73
|
+
generated with no parameters raises the moment the entry scrolls:
|
|
74
|
+
|
|
75
|
+
TypeError: on_scroll() takes 1 positional argument but 3 were given
|
|
76
|
+
|
|
77
|
+
`validatecommand` is called with NO arguments when it is a plain
|
|
78
|
+
callable, because Tk's %-substitutions are only passed when the command
|
|
79
|
+
is registered with them and pygubu does not do that. The base class
|
|
80
|
+
already returns nothing, so it is left alone rather than restated.
|
|
81
|
+
"""
|
|
82
|
+
if cmd_pname == "xscrollcommand":
|
|
83
|
+
return ("first", "last")
|
|
84
|
+
return super()._code_define_callback_args(cmd_pname, cmd)
|
|
85
|
+
|
|
86
|
+
def code_imports(self):
|
|
87
|
+
# should return an iterable of (module, classname/function) to import
|
|
88
|
+
# or None
|
|
89
|
+
imports = [(widget_namespace, widget_classname)]
|
|
90
|
+
imports.extend(self.code_extra_imports())
|
|
91
|
+
return imports
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
builder_id = f"{builder_namespace}.{widget_classname}"
|
|
95
|
+
register_widget(
|
|
96
|
+
builder_id, sCTkEntrySecondaryBO, widget_classname, ("ttk", section_name)
|
|
97
|
+
)
|
|
98
|
+
|