Merge pull request #1467 from PySimpleGUI/Dev-latest
Release 3.30. New PySimpleGUIdebugger release to PyPI
This commit is contained in:
commit
c291806a48
|
@ -1,14 +1,14 @@
|
|||
import PySimpleGUI as sg
|
||||
import PSGdebugger
|
||||
import PySimpleGUIdebugger
|
||||
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
There are TWO steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import PSGdebugger
|
||||
import PySimpleGUIdebugger
|
||||
2. At the end of your app's event loop add
|
||||
PSGdebugger.refresh(locals(), globals())
|
||||
PySimpleGUIdebugger.refresh(locals(), globals())
|
||||
"""
|
||||
|
||||
|
||||
|
@ -30,9 +30,9 @@ counter = 0
|
|||
timeout = 100
|
||||
|
||||
while True: # Event Loop
|
||||
PySimpleGUIdebugger.refresh(locals(), globals())
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
counter += 1
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
||||
PSGdebugger.refresh(locals(), globals())
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#!/usr/bin/python3
|
||||
#!/usr/bin/python3
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
import tkinter as tk
|
||||
|
@ -817,7 +816,7 @@ class Listbox(Element):
|
|||
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
|
||||
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
|
||||
self.RightClickMenu = right_click_menu
|
||||
|
||||
self.vsb = None # type: tk.Scrollbar
|
||||
super().__init__(ELEM_TYPE_INPUT_LISTBOX, size=size, auto_size_text=auto_size_text, font=font,
|
||||
background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip, visible=visible)
|
||||
|
||||
|
@ -840,9 +839,10 @@ class Listbox(Element):
|
|||
pass
|
||||
if visible is False:
|
||||
self.TKListbox.pack_forget()
|
||||
self.vsb.pack_forget()
|
||||
elif visible is True:
|
||||
self.TKListbox.pack()
|
||||
|
||||
self.vsb.pack()
|
||||
|
||||
def SetValue(self, values):
|
||||
for index, item in enumerate(self.Values):
|
||||
|
@ -3949,7 +3949,7 @@ class Window:
|
|||
return element
|
||||
|
||||
Element = FindElement # Shortcut function
|
||||
|
||||
Find = FindElement
|
||||
|
||||
def FindElementWithFocus(self):
|
||||
element = _FindElementWithFocusInSubForm(self)
|
||||
|
@ -4341,36 +4341,6 @@ def Exit(button_text='Exit', size=(None, None), auto_size_button=None, button_co
|
|||
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
|
||||
|
||||
|
||||
# ------------------------- Up arrow BUTTON Element lazy function ------------------------- #
|
||||
def Up(button_text='▲', size=(None, None), auto_size_button=None, button_color=None, disabled=False, tooltip=None,
|
||||
font=None, bind_return_key=True, focus=False, pad=None, key=None):
|
||||
return Button(button_text=button_text, button_type=BUTTON_TYPE_READ_FORM, tooltip=tooltip, size=size,
|
||||
auto_size_button=auto_size_button, button_color=button_color, font=font, disabled=disabled,
|
||||
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
|
||||
|
||||
# ------------------------- Down arrow BUTTON Element lazy function ------------------------- #
|
||||
def Down(button_text='▼', size=(None, None), auto_size_button=None, button_color=None, disabled=False, tooltip=None,
|
||||
font=None, bind_return_key=True, focus=False, pad=None, key=None):
|
||||
return Button(button_text=button_text, button_type=BUTTON_TYPE_READ_FORM, tooltip=tooltip, size=size,
|
||||
auto_size_button=auto_size_button, button_color=button_color, font=font, disabled=disabled,
|
||||
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
|
||||
|
||||
# ------------------------- Left arrow BUTTON Element lazy function ------------------------- #
|
||||
def Left(button_text='◄', size=(None, None), auto_size_button=None, button_color=None, disabled=False, tooltip=None,
|
||||
font=None, bind_return_key=True, focus=False, pad=None, key=None):
|
||||
return Button(button_text=button_text, button_type=BUTTON_TYPE_READ_FORM, tooltip=tooltip, size=size,
|
||||
auto_size_button=auto_size_button, button_color=button_color, font=font, disabled=disabled,
|
||||
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
|
||||
|
||||
|
||||
# ------------------------- Right arrow BUTTON Element lazy function ------------------------- #
|
||||
def Right(button_text='►', size=(None, None), auto_size_button=None, button_color=None, disabled=False, tooltip=None,
|
||||
font=None, bind_return_key=True, focus=False, pad=None, key=None):
|
||||
return Button(button_text=button_text, button_type=BUTTON_TYPE_READ_FORM, tooltip=tooltip, size=size,
|
||||
auto_size_button=auto_size_button, button_color=button_color, font=font, disabled=disabled,
|
||||
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
|
||||
|
||||
|
||||
# ------------------------- YES BUTTON Element lazy function ------------------------- #
|
||||
def Yes(button_text='Yes', size=(None, None), auto_size_button=None, button_color=None, disabled=False, tooltip=None,
|
||||
font=None, bind_return_key=True, focus=False, pad=None, key=None):
|
||||
|
@ -4481,12 +4451,12 @@ def ColorChooserButton(button_text, target=(None, None), image_filename=None, im
|
|||
def AddToReturnDictionary(form, element, value):
|
||||
form.ReturnValuesDictionary[element.Key] = value
|
||||
return
|
||||
if element.Key is None:
|
||||
form.ReturnValuesDictionary[form.DictionaryKeyCounter] = value
|
||||
element.Key = form.DictionaryKeyCounter
|
||||
form.DictionaryKeyCounter += 1
|
||||
else:
|
||||
form.ReturnValuesDictionary[element.Key] = value
|
||||
# if element.Key is None:
|
||||
# form.ReturnValuesDictionary[form.DictionaryKeyCounter] = value
|
||||
# element.Key = form.DictionaryKeyCounter
|
||||
# form.DictionaryKeyCounter += 1
|
||||
# else:
|
||||
# form.ReturnValuesDictionary[element.Key] = value
|
||||
|
||||
|
||||
def AddToReturnList(form, value):
|
||||
|
@ -5098,7 +5068,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
elif toplevel_form.TextJustification is not None:
|
||||
justification = toplevel_form.TextJustification
|
||||
else:
|
||||
justification = DEFAULT_TEXT_JUSTIFICATION
|
||||
justification = DEFAULT_TEXT_JUSTIFICAION
|
||||
justify = tk.LEFT if justification == 'left' else tk.CENTER if justification == 'center' else tk.RIGHT
|
||||
anchor = tk.NW if justification == 'left' else tk.N if justification == 'center' else tk.NE
|
||||
# tktext_label = tk.Label(tk_row_frame, textvariable=stringvar, width=width, height=height,
|
||||
|
@ -5423,6 +5393,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
timeout=DEFAULT_TOOLTIP_TIME)
|
||||
# ------------------------- LISTBOX element ------------------------- #
|
||||
elif element_type == ELEM_TYPE_INPUT_LISTBOX:
|
||||
element = element # type: Listbox
|
||||
max_line_len = max([len(str(l)) for l in element.Values]) if len(element.Values) else 0
|
||||
if auto_size_text is False:
|
||||
width = element_size[0]
|
||||
|
@ -5442,13 +5413,14 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
element.TKListbox.configure(fg=text_color)
|
||||
if element.ChangeSubmits:
|
||||
element.TKListbox.bind('<<ListboxSelect>>', element.ListboxSelectHandler)
|
||||
vsb = tk.Scrollbar(listbox_frame, orient="vertical", command=element.TKListbox.yview)
|
||||
element.TKListbox.configure(yscrollcommand=vsb.set)
|
||||
element.vsb = tk.Scrollbar(listbox_frame, orient="vertical", command=element.TKListbox.yview)
|
||||
element.TKListbox.configure(yscrollcommand=element.vsb.set)
|
||||
element.TKListbox.pack(side=tk.LEFT)
|
||||
vsb.pack(side=tk.LEFT, fill='y')
|
||||
element.vsb.pack(side=tk.LEFT, fill='y')
|
||||
listbox_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1])
|
||||
if element.Visible is False:
|
||||
listbox_frame.pack_forget()
|
||||
element.vsb.pack_forget()
|
||||
if element.BindReturnKey:
|
||||
element.TKListbox.bind('<Return>', element.ListboxSelectHandler)
|
||||
element.TKListbox.bind('<Double-Button-1>', element.ListboxSelectHandler)
|
||||
|
@ -6212,7 +6184,6 @@ def StartupTK(my_flex_form:Window):
|
|||
else:
|
||||
root = tk.Toplevel()
|
||||
|
||||
|
||||
try:
|
||||
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
|
||||
except:
|
||||
|
@ -7595,8 +7566,7 @@ def PopupGetFolder(message, title=None, default_path='', no_window=False, size=(
|
|||
if button != 'Ok':
|
||||
return None
|
||||
else:
|
||||
path = values['_INPUT_']
|
||||
return path
|
||||
return values['_INPUT_']
|
||||
|
||||
|
||||
|
||||
|
@ -7735,6 +7705,8 @@ def PopupAnimated(image_source, message=None, background_color=None, text_color=
|
|||
|
||||
window.Refresh() # call refresh instead of Read to save significant CPU time
|
||||
|
||||
|
||||
|
||||
"""
|
||||
d8b
|
||||
Y8P
|
||||
|
@ -7862,7 +7834,7 @@ def main():
|
|||
i += 1
|
||||
if event == 'Button':
|
||||
window.Element('_TEXT1_').SetTooltip('NEW TEXT')
|
||||
# window.SetTransparentColor( '#9FB8AD')
|
||||
window.SetTransparentColor( '#9FB8AD')
|
||||
window.Maximize()
|
||||
# TimerStop()
|
||||
window.Close()
|
||||
|
|
|
@ -74,6 +74,14 @@ def TimerStop():
|
|||
are not a mess. The Elements and the methods for them are well-designed.
|
||||
PEP8 - this code is far far from PEP8 compliant.
|
||||
It was written PRIOR to learning that PEP8 existed.
|
||||
|
||||
I'll be honest.... started learning Python in Nov 2017, started writing PySimpleGUI in Feb 2018.
|
||||
Released PySimpleGUI in July 2018. I knew so little about Python that my parameters were all named
|
||||
using CamelCase. DOH! Someone on Reddit set me straight on that. So overnight I renamed all of the
|
||||
parameters to lower case. Unfortunately, the internal naming conventions have been set. Mixing them
|
||||
with PEP8 at this moment would be even MORE confusing.
|
||||
|
||||
Code I write now, outside PySimpleGUI, IS PEP8 compliant.
|
||||
|
||||
The variable and function naming in particular are not compliant. There is
|
||||
liberal use of CamelVariableAndFunctionNames. If you've got a serious enough problem with this
|
||||
|
@ -821,7 +829,7 @@ class Listbox(Element):
|
|||
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
|
||||
fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR
|
||||
self.RightClickMenu = right_click_menu
|
||||
|
||||
self.vsb = None # type: tk.Scrollbar
|
||||
super().__init__(ELEM_TYPE_INPUT_LISTBOX, size=size, auto_size_text=auto_size_text, font=font,
|
||||
background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip, visible=visible)
|
||||
|
||||
|
@ -844,9 +852,10 @@ class Listbox(Element):
|
|||
pass
|
||||
if visible is False:
|
||||
self.TKListbox.pack_forget()
|
||||
self.vsb.pack_forget()
|
||||
elif visible is True:
|
||||
self.TKListbox.pack()
|
||||
|
||||
self.vsb.pack()
|
||||
|
||||
def SetValue(self, values):
|
||||
for index, item in enumerate(self.Values):
|
||||
|
@ -1092,8 +1101,7 @@ class Spin(Element):
|
|||
# Multiline #
|
||||
# ---------------------------------------------------------------------- #
|
||||
class Multiline(Element):
|
||||
def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, size=(None, None),
|
||||
auto_size_text=None, background_color=None, text_color=None, change_submits=False, enable_events=False,do_not_clear=True, key=None, focus=False, font=None, pad=None, tooltip=None, right_click_menu=None, visible=True):
|
||||
def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, border_width=None, size=(None, None), auto_size_text=None, background_color=None, text_color=None, change_submits=False, enable_events=False,do_not_clear=True, key=None, focus=False, font=None, pad=None, tooltip=None, right_click_menu=None, visible=True):
|
||||
'''
|
||||
Multiline
|
||||
:param default_text:
|
||||
|
@ -1126,6 +1134,7 @@ class Multiline(Element):
|
|||
self.Disabled = disabled
|
||||
self.ChangeSubmits = change_submits or enable_events
|
||||
self.RightClickMenu = right_click_menu
|
||||
self.BorderWidth = border_width if border_width is not None else DEFAULT_BORDER_WIDTH
|
||||
|
||||
super().__init__(ELEM_TYPE_INPUT_MULTILINE, size=size, auto_size_text=auto_size_text, background_color=bg,
|
||||
text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT, visible=visible)
|
||||
|
@ -1621,7 +1630,6 @@ class Button(Element):
|
|||
self.ParentForm.TKroot.quit()
|
||||
if self.ParentForm.NonBlocking:
|
||||
self.ParentForm.TKroot.destroy()
|
||||
# _my_windows.Decrement()
|
||||
Window.DecrementOpenCount()
|
||||
elif self.BType == BUTTON_TYPE_READ_FORM: # LEAVE THE WINDOW OPEN!! DO NOT CLOSE
|
||||
# first, get the results table built
|
||||
|
@ -1638,7 +1646,6 @@ class Button(Element):
|
|||
if self.ParentForm.NonBlocking:
|
||||
self.ParentForm.TKroot.destroy()
|
||||
Window.DecrementOpenCount()
|
||||
# _my_windows.Decrement()
|
||||
elif self.BType == BUTTON_TYPE_CALENDAR_CHOOSER: # this is a return type button so GET RESULTS and destroy window
|
||||
should_submit_window = False
|
||||
root = tk.Toplevel()
|
||||
|
@ -3543,7 +3550,6 @@ class Window(object):
|
|||
hidden_master_root = None
|
||||
animated_popup_dict = {}
|
||||
container_element_counter = 0 # used to get a number of Container Elements (Frame, Column, Tab)
|
||||
|
||||
def __init__(self, title, layout=None, default_element_size=DEFAULT_ELEMENT_SIZE, default_button_element_size=(None, None),
|
||||
auto_size_text=None, auto_size_buttons=None, location=(None, None), size=(None, None), element_padding=None, margins=(None, None), button_color=None, font=None,
|
||||
progress_bar_color=(None, None), background_color=None, border_depth=None, auto_close=False,
|
||||
|
@ -3638,7 +3644,7 @@ class Window(object):
|
|||
self.ContainerElemementNumber = Window.GetAContainerNumber()
|
||||
self.AllKeysDict = {}
|
||||
self.TransparentColor = transparent_color
|
||||
|
||||
self.UniqueKeyCounter = 0
|
||||
if layout is not None:
|
||||
self.Layout(layout)
|
||||
|
||||
|
@ -3936,6 +3942,8 @@ class Window(object):
|
|||
return self
|
||||
|
||||
def FindElement(self, key, silent_on_error=False):
|
||||
# print(f'In find elem key={key}', self.AllKeysDict)
|
||||
|
||||
try:
|
||||
element = self.AllKeysDict[key]
|
||||
except KeyError:
|
||||
|
@ -3950,11 +3958,11 @@ class Window(object):
|
|||
'window.FindElement("{}")'.format(key))
|
||||
return ErrorElement(key=key)
|
||||
else:
|
||||
return False
|
||||
return None
|
||||
return element
|
||||
|
||||
Element = FindElement # Shortcut function
|
||||
|
||||
Find = FindElement
|
||||
|
||||
def FindElementWithFocus(self):
|
||||
element = _FindElementWithFocusInSubForm(self)
|
||||
|
@ -3990,7 +3998,10 @@ class Window(object):
|
|||
top_window.DictionaryKeyCounter += 1
|
||||
if element.Key is not None:
|
||||
if element.Key in key_dict.keys():
|
||||
print('*** Duplicate key found in your layout {} ***'.format(element.Key))
|
||||
print('*** Duplicate key found in your layout {} ***'.format(element.Key)) if element.Type != ELEM_TYPE_BUTTON else None
|
||||
element.Key = element.Key + str(self.UniqueKeyCounter)
|
||||
self.UniqueKeyCounter += 1
|
||||
print('*** Replaced new key with {} ***'.format(element.Key)) if element.Type != ELEM_TYPE_BUTTON else None
|
||||
key_dict[element.Key] = element
|
||||
return key_dict
|
||||
|
||||
|
@ -4102,6 +4113,13 @@ class Window(object):
|
|||
# _my_windows.Decrement()
|
||||
except:
|
||||
pass
|
||||
# if down to 1 window, try and destroy the hidden window, if there is one
|
||||
if Window.NumOpenWindows == 1:
|
||||
try:
|
||||
Window.hidden_master_root.destroy()
|
||||
Window.NumOpenWindows = 0 # if no hidden window, then this won't execute
|
||||
except:
|
||||
pass
|
||||
|
||||
CloseNonBlockingForm = Close
|
||||
CloseNonBlocking = Close
|
||||
|
@ -4336,7 +4354,6 @@ def Exit(button_text='Exit', size=(None, None), auto_size_button=None, button_co
|
|||
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
|
||||
|
||||
|
||||
|
||||
# ------------------------- YES BUTTON Element lazy function ------------------------- #
|
||||
def Yes(button_text='Yes', size=(None, None), auto_size_button=None, button_color=None, disabled=False, tooltip=None,
|
||||
font=None, bind_return_key=True, focus=False, pad=None, key=None):
|
||||
|
@ -4447,12 +4464,12 @@ def ColorChooserButton(button_text, target=(None, None), image_filename=None, im
|
|||
def AddToReturnDictionary(form, element, value):
|
||||
form.ReturnValuesDictionary[element.Key] = value
|
||||
return
|
||||
if element.Key is None:
|
||||
form.ReturnValuesDictionary[form.DictionaryKeyCounter] = value
|
||||
element.Key = form.DictionaryKeyCounter
|
||||
form.DictionaryKeyCounter += 1
|
||||
else:
|
||||
form.ReturnValuesDictionary[element.Key] = value
|
||||
# if element.Key is None:
|
||||
# form.ReturnValuesDictionary[form.DictionaryKeyCounter] = value
|
||||
# element.Key = form.DictionaryKeyCounter
|
||||
# form.DictionaryKeyCounter += 1
|
||||
# else:
|
||||
# form.ReturnValuesDictionary[element.Key] = value
|
||||
|
||||
|
||||
def AddToReturnList(form, value):
|
||||
|
@ -4479,7 +4496,6 @@ def EncodeRadioRowCol(container, row, col):
|
|||
RadValue = container*100000 + row * 1000 + col
|
||||
return RadValue
|
||||
|
||||
|
||||
# ------- FUNCTION BuildResults. Form exiting so build the results to pass back ------- #
|
||||
# format of return values is
|
||||
# (Button Pressed, input_values)
|
||||
|
@ -5065,7 +5081,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
elif toplevel_form.TextJustification is not None:
|
||||
justification = toplevel_form.TextJustification
|
||||
else:
|
||||
justification = DEFAULT_TEXT_JUSTIFICATION
|
||||
justification = DEFAULT_TEXT_JUSTIFICAION
|
||||
justify = tk.LEFT if justification == 'left' else tk.CENTER if justification == 'center' else tk.RIGHT
|
||||
anchor = tk.NW if justification == 'left' else tk.N if justification == 'center' else tk.NE
|
||||
# tktext_label = tk.Label(tk_row_frame, textvariable=stringvar, width=width, height=height,
|
||||
|
@ -5263,8 +5279,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
justification = DEFAULT_TEXT_JUSTIFICATION
|
||||
justify = tk.LEFT if justification == 'left' else tk.CENTER if justification == 'center' else tk.RIGHT
|
||||
# anchor = tk.NW if justification == 'left' else tk.N if justification == 'center' else tk.NE
|
||||
element.TKEntry = element.Widget = tk.Entry(tk_row_frame, width=element_size[0], textvariable=element.TKStringVar,
|
||||
bd=border_depth, font=font, show=show, justify=justify)
|
||||
element.TKEntry = element.Widget = tk.Entry(tk_row_frame, width=element_size[0], textvariable=element.TKStringVar, bd=border_depth, font=font, show=show, justify=justify)
|
||||
if element.ChangeSubmits:
|
||||
element.TKEntry.bind('<Key>', element.KeyboardHandler)
|
||||
element.TKEntry.bind('<Return>', element.ReturnKeyHandler)
|
||||
|
@ -5391,7 +5406,8 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
timeout=DEFAULT_TOOLTIP_TIME)
|
||||
# ------------------------- LISTBOX element ------------------------- #
|
||||
elif element_type == ELEM_TYPE_INPUT_LISTBOX:
|
||||
max_line_len = max([len(str(l)) for l in element.Values]) if len(element.Values) != 0 else 0
|
||||
element = element # type: Listbox
|
||||
max_line_len = max([len(str(l)) for l in element.Values]) if len(element.Values) else 0
|
||||
if auto_size_text is False:
|
||||
width = element_size[0]
|
||||
else:
|
||||
|
@ -5410,13 +5426,14 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
element.TKListbox.configure(fg=text_color)
|
||||
if element.ChangeSubmits:
|
||||
element.TKListbox.bind('<<ListboxSelect>>', element.ListboxSelectHandler)
|
||||
vsb = tk.Scrollbar(listbox_frame, orient="vertical", command=element.TKListbox.yview)
|
||||
element.TKListbox.configure(yscrollcommand=vsb.set)
|
||||
element.vsb = tk.Scrollbar(listbox_frame, orient="vertical", command=element.TKListbox.yview)
|
||||
element.TKListbox.configure(yscrollcommand=element.vsb.set)
|
||||
element.TKListbox.pack(side=tk.LEFT)
|
||||
vsb.pack(side=tk.LEFT, fill='y')
|
||||
element.vsb.pack(side=tk.LEFT, fill='y')
|
||||
listbox_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1])
|
||||
if element.Visible is False:
|
||||
listbox_frame.pack_forget()
|
||||
element.vsb.pack_forget()
|
||||
if element.BindReturnKey:
|
||||
element.TKListbox.bind('<Return>', element.ListboxSelectHandler)
|
||||
element.TKListbox.bind('<Double-Button-1>', element.ListboxSelectHandler)
|
||||
|
@ -5436,8 +5453,8 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
|
|||
element = element # type: Multiline
|
||||
default_text = element.DefaultText
|
||||
width, height = element_size
|
||||
element.TKText = element.Widget = tk.scrolledtext.ScrolledText(tk_row_frame, width=width, height=height, wrap='word',
|
||||
bd=border_depth, font=font, relief=tk.FLAT)
|
||||
border_depth = element.BorderWidth
|
||||
element.TKText = element.Widget = tk.scrolledtext.ScrolledText(tk_row_frame, width=width, height=height, wrap='word', bd=border_depth, font=font, relief=RELIEF_SUNKEN)
|
||||
element.TKText.insert(1.0, default_text) # set the default text
|
||||
if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT:
|
||||
element.TKText.configure(background=element.BackgroundColor)
|
||||
|
@ -6120,7 +6137,10 @@ def ConvertFlexToTK(MyFlexForm):
|
|||
InitializeResults(MyFlexForm)
|
||||
try:
|
||||
if MyFlexForm.NoTitleBar:
|
||||
MyFlexForm.TKroot.wm_overrideredirect(True)
|
||||
if sys.platform == 'linux':
|
||||
MyFlexForm.TKroot.wm_attributes("-type","splash")
|
||||
else:
|
||||
MyFlexForm.TKroot.wm_overrideredirect(True)
|
||||
except:
|
||||
pass
|
||||
PackFormIntoFrame(MyFlexForm, master, MyFlexForm)
|
||||
|
@ -6177,7 +6197,6 @@ def StartupTK(my_flex_form):
|
|||
else:
|
||||
root = tk.Toplevel()
|
||||
|
||||
|
||||
try:
|
||||
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
|
||||
except:
|
||||
|
@ -6588,7 +6607,7 @@ def PopupScrolled(*args, **_3to2kwargs):
|
|||
window.AddRow(Multiline(complete_output, size=(max_line_width, height_computed)))
|
||||
pad = max_line_total - 15 if max_line_total > 15 else 1
|
||||
# show either an OK or Yes/No depending on paramater
|
||||
button = DummyButton if non_blocking else Button
|
||||
button = DummyButton if non_blocking else CloseButton
|
||||
if yes_no:
|
||||
window.AddRow(Text('', size=(pad, 1), auto_size_text=False), button('Yes'), button('No'))
|
||||
else:
|
||||
|
@ -6598,6 +6617,7 @@ def PopupScrolled(*args, **_3to2kwargs):
|
|||
button, values = window.Read(timeout=0)
|
||||
else:
|
||||
button, values = window.Read()
|
||||
# window.Close()
|
||||
return button
|
||||
|
||||
|
||||
|
@ -7942,8 +7962,7 @@ def PopupGetFolder(message, title=None, default_path='', no_window=False, size=(
|
|||
if button != 'Ok':
|
||||
return None
|
||||
else:
|
||||
path = values['_INPUT_']
|
||||
return path
|
||||
return values['_INPUT_']
|
||||
|
||||
|
||||
|
||||
|
@ -8082,6 +8101,8 @@ def PopupAnimated(image_source, message=None, background_color=None, text_color=
|
|||
|
||||
window.Refresh() # call refresh instead of Read to save significant CPU time
|
||||
|
||||
|
||||
|
||||
"""
|
||||
d8b
|
||||
Y8P
|
||||
|
@ -8187,6 +8208,7 @@ def main():
|
|||
# background_color='black',
|
||||
right_click_menu=['&Right', ['Right', '!&Click', '&Menu', 'E&xit', 'Properties']],
|
||||
# transparent_color= '#9FB8AD',
|
||||
resizable=False,
|
||||
).Finalize()
|
||||
graph_elem.DrawCircle((200, 200), 50, 'blue')
|
||||
i = 0
|
||||
|
@ -8207,12 +8229,9 @@ def main():
|
|||
window.Element('_IMAGE_').UpdateAnimation(DEFAULT_BASE64_LOADING_GIF, time_between_frames=50)
|
||||
i += 1
|
||||
if event == 'Button':
|
||||
print(window.AllKeysDict)
|
||||
window.Element('_TEXT1_').SetTooltip('NEW TEXT')
|
||||
window.SetTransparentColor( '#9FB8AD')
|
||||
# window.TKroot.wm_attributes("-transparent", '#9FB8AD')
|
||||
# window.TKroot.wm_attributes("-transparentcolor", '#9FB8AD')
|
||||
|
||||
window.Maximize()
|
||||
# TimerStop()
|
||||
window.Close()
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import PySimpleGUI as sg
|
||||
import PySimpleGUIdebugger
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import PySimpleGUIdebugger
|
||||
2. Initialize the debugger by calling:
|
||||
PySimpleGUIdebugger.initialize()
|
||||
2. At the top of your app's event loop add
|
||||
PySimpleGUIdebugger.refresh(locals(), globals())
|
||||
"""
|
||||
|
||||
PySimpleGUIdebugger.initialize()
|
||||
|
||||
layout = [
|
||||
[sg.T('A typical PSG application')],
|
||||
[sg.In(key='_IN_')],
|
||||
[sg.T(' ', key='_OUT_')],
|
||||
[sg.Radio('a',1, key='_R1_'), sg.Radio('b',1, key='_R2_'), sg.Radio('c',1, key='_R3_')],
|
||||
[sg.Combo(['c1', 'c2', 'c3'], size=(6,3), key='_COMBO_')],
|
||||
[sg.Output(size=(50,6))],
|
||||
[sg.Ok(), sg.Exit()],
|
||||
]
|
||||
|
||||
|
||||
window = sg.Window('This is your Application Window', layout)
|
||||
window.Element('_OUT_').Update(background_color='red')
|
||||
# Variables that we'll use to demonstrate the debugger's features
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
while True: # Event Loop
|
||||
PySimpleGUIdebugger.refresh(locals(), globals()) # call the debugger to refresh the items being shown
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Ok':
|
||||
print('You clicked Ok.... this is where print output goes')
|
||||
counter += 1
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
|
@ -0,0 +1,121 @@
|
|||
|
||||
![pysimplegui_logo](https://user-images.githubusercontent.com/13696193/43165867-fe02e3b2-8f62-11e8-9fd0-cc7c86b11772.png)
|
||||
|
||||
![Downloads](http://pepy.tech/badge/pysimpleguidebugger)
|
||||
|
||||
|
||||
|
||||
# PySimpleGUIdebugger
|
||||
|
||||
PySimpleGUI now has it's own built-in, sorta, debugger.
|
||||
|
||||
What you can do with this "debugger" is:
|
||||
* Set "watch points" that update in realtime
|
||||
* Write expressions that update in realtime
|
||||
* Use a REPL style prompt to type in "code" / modify variables
|
||||
|
||||
All of this is done using a window secondary and separate from your primary application window.
|
||||
|
||||
Check out this video as a guide. The user's window is the smaller one one top. The PySimpleGUIdebugger is the green window on the buttom. You can watch variables, evaluate expressions, even xecute code.
|
||||
|
||||
![PSG Debugger2](https://user-images.githubusercontent.com/13696193/58362085-3ead8f00-7e61-11e9-9439-e77e9a059dbc.gif)
|
||||
|
||||
## Installation
|
||||
|
||||
Installation is via pip:
|
||||
|
||||
`pip install PySimpleGUIdebugger`
|
||||
|
||||
or if you need to upgrade later:
|
||||
|
||||
`pip install --upgrade --no-cache-dir PySimpleGUIdebugger`
|
||||
|
||||
Should this not work, you can copy and paste the file PySimpleGUIdebugger.py into your application folder.
|
||||
|
||||
## Integrating PySimpleGUIdebugger Into Your Application
|
||||
|
||||
There are 3 lines of code to add to a PySimpleGUI program in order to make it debugger ready - The import, an initialization, once each time through the even loop.
|
||||
|
||||
Here is an entire program including this integration code:
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
import PySimpleGUIdebugger
|
||||
"""
|
||||
Demo program that shows you how to integrate the PySimpleGUI Debugger
|
||||
into your program.
|
||||
There are THREE steps, and they are copy and pastes.
|
||||
1. At the top of your app to debug add
|
||||
import PySimpleGUIdebugger
|
||||
2. Initialize the debugger by calling:
|
||||
PySimpleGUIdebugger.initialize()
|
||||
2. At the top of your app's event loop add
|
||||
PySimpleGUIdebugger.refresh(locals(), globals())
|
||||
"""
|
||||
|
||||
PySimpleGUIdebugger.initialize()
|
||||
|
||||
layout = [
|
||||
[sg.T('A typical PSG application')],
|
||||
[sg.In(key='_IN_')],
|
||||
[sg.T(' ', key='_OUT_')],
|
||||
[sg.Radio('a',1, key='_R1_'), sg.Radio('b',1, key='_R2_'), sg.Radio('c',1, key='_R3_')],
|
||||
[sg.Combo(['c1', 'c2', 'c3'], size=(6,3), key='_COMBO_')],
|
||||
[sg.Output(size=(50,6))],
|
||||
[sg.Ok(), sg.Exit()],
|
||||
]
|
||||
|
||||
|
||||
window = sg.Window('This is your Application Window', layout)
|
||||
window.Element('_OUT_').Update(background_color='red')
|
||||
# Variables that we'll use to demonstrate the debugger's features
|
||||
counter = 0
|
||||
timeout = 100
|
||||
|
||||
while True: # Event Loop
|
||||
PySimpleGUIdebugger.refresh(locals(), globals()) # call the debugger to refresh the items being shown
|
||||
event, values = window.Read(timeout=timeout)
|
||||
if event in (None, 'Exit'):
|
||||
break
|
||||
elif event == 'Ok':
|
||||
print('You clicked Ok.... this is where print output goes')
|
||||
counter += 1
|
||||
window.Element('_OUT_').Update(values['_IN_'])
|
||||
```
|
||||
|
||||
|
||||
## Using PySimpleGUIdebugger
|
||||
|
||||
To use the debugger in your code you will need to add TWO lines of code:
|
||||
The import at the top of your code:
|
||||
`import PySimpleGUIdebugger`
|
||||
|
||||
You need to "initialize" the PySimpleGUIdebugger package by calling near the top of your code. This is what creates the debugger window:
|
||||
`PySimpleGUIdebugger.initialize()`
|
||||
|
||||
This "refresh" call that must be added to your event loop. Your `window.Read` call should have a timeout value so that it does not block. If you do not have a timeout value, the debugger will not update in realtime.
|
||||
|
||||
Add this line to the top of your event loop.
|
||||
`PySimpleGUIdebugger.refresh(locals(), globals())`
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
You'll need to have PySimpleGUI installed.
|
||||
|
||||
The debugger itself is written using PySimpleGUI, the tkinter version. It could be changed to use Qt for example.
|
||||
|
||||
|
||||
## What's it good for, when should it be used??
|
||||
|
||||
Hell if I know. Maybe it's a terrible idea! Or, maybe it'll be really helpful, particularly in situations where you don't have many resources on the target system and perhaps you can't fit a debugger onto that system. PySimpleGUIdebugger provides another tool for your PySimpleGUI GUI Toolbox.
|
||||
|
||||
|
||||
# Design
|
||||
# Author
|
||||
Mike B.
|
||||
|
||||
|
||||
# License
|
||||
GNU Lesser General Public License (LGPL 3) +
|
252
docs/index.md
252
docs/index.md
|
@ -33,9 +33,9 @@
|
|||
|
||||
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.29.0-red.svg?longCache=true&style=for-the-badge)
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.39.0-red.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.29.0-blue.svg?longCache=true&style=for-the-badge)
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.30.0-blue.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUIQt_Version-0.26.0-orange.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
|
@ -324,7 +324,7 @@ An example of many widgets used on a single window. A little further down you'l
|
|||
sg.FolderBrowse()],
|
||||
[sg.Submit(), sg.Cancel(), sg.Button('Customized', button_color=('white', 'green'))]]
|
||||
|
||||
event, values = sg.Window('Everything bagel', layout, auto_size_text=True, default_element_size=(40, 1)).Read()
|
||||
event, values = sg.Window('Everything bagel', auto_size_text=True, default_element_size=(40, 1)).Layout(layout).Read()
|
||||
|
||||
|
||||
|
||||
|
@ -942,7 +942,7 @@ Finally we can put it all together into a program that will display our window.
|
|||
[sg.Input()],
|
||||
[sg.OK()] ]
|
||||
|
||||
event, (number,) = sg.Window('Enter a number example', layout).Read()
|
||||
event, (number,) = sg.Window('Enter a number example').Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, number)
|
||||
|
||||
|
@ -960,7 +960,7 @@ Writing the code for this one is just as straightforward. There is one tricky t
|
|||
[sg.Input(), sg.FileBrowse()],
|
||||
[sg.OK(), sg.Cancel()] ]
|
||||
|
||||
event, (number,) = sg.Window('Get filename example', layout).Read()
|
||||
event, (number,) = sg.Window('Get filename example').Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, number)
|
||||
|
||||
|
@ -987,7 +987,7 @@ window_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
|
|||
[sg.InputText(), sg.FileBrowse()],
|
||||
[sg.Submit(), sg.Cancel()]]
|
||||
|
||||
window = sg.Window('SHA-1 & 256 Hash', window_rows)
|
||||
window = sg.Window('SHA-1 & 256 Hash').Layout(window_rows)
|
||||
|
||||
event, values = window.Read()
|
||||
window.Close()
|
||||
|
@ -1009,7 +1009,7 @@ layout = [[sg.Text('Persistent window')],
|
|||
[sg.Input(do_not_clear=True)],
|
||||
[sg.Button('Read'), sg.Exit()]]
|
||||
|
||||
window = sg.Window('Window that stays open', layout)
|
||||
window = sg.Window('Window that stays open').Layout(layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
|
@ -1039,7 +1039,7 @@ layout = [[sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_')
|
|||
[sg.Input(do_not_clear=True, key='_IN_')],
|
||||
[sg.Button('Show'), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title', layout)
|
||||
window = sg.Window('Window Title').Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
|
@ -1048,7 +1048,7 @@ while True: # Event Loop
|
|||
break
|
||||
if event == 'Show':
|
||||
# change the "output" element to be the value of "input" element
|
||||
window.Element('_OUTPUT_').Update(values['_IN_'])
|
||||
window.FindElement('_OUTPUT_').Update(values['_IN_'])
|
||||
|
||||
window.Close()
|
||||
```
|
||||
|
@ -1070,9 +1070,9 @@ The key to custom windows in PySimpleGUI is to view windows as ROWS of GUI Elem
|
|||
[sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
|
||||
[sg.Submit(), sg.Cancel()]]
|
||||
|
||||
window = sg.Window('Rename Files or Folders', layout)
|
||||
window = sg.Window('Rename Files or Folders')
|
||||
|
||||
event, values = window.Read()
|
||||
event, values = window.Layout(layout).Read()
|
||||
```
|
||||
|
||||
|
||||
|
@ -1233,13 +1233,13 @@ The second parameter from a Read call is either a list or a dictionary of the in
|
|||
|
||||
Each of the Elements that are Input Elements will have a value in the list of return values. You can unpack your GUI directly into the variables you want to use.
|
||||
|
||||
event, (filename, folder1, folder2, should_overwrite) = sg.Window('My title', window_rows).Read()
|
||||
event, (filename, folder1, folder2, should_overwrite) = sg.Window('My title').Layout(window_rows).Read()
|
||||
|
||||
Or, more commonly, you can unpack the return results separately.
|
||||
|
||||
```python
|
||||
event, values = sg.Window('My title', window_rows).Read()
|
||||
event, value_list = window.Read()
|
||||
event, values = sg.Window('My title').Layout(window_rows).Read()
|
||||
event, value_list = window.Layout(window_rows).Read()
|
||||
value1 = value_list[0]
|
||||
value2 = value_list[1]
|
||||
...
|
||||
|
@ -1256,7 +1256,7 @@ For windows longer than 3 or 4 fields you will want to use a dictionary to help
|
|||
|
||||
The most common window read statement you'll encounter looks something like this:
|
||||
|
||||
window = sg.Window("My title", layout).Read()
|
||||
window = sg.Window("My title").Layout(layout).Read()
|
||||
|
||||
|
||||
To use a dictionary, you will need to:
|
||||
|
@ -1267,7 +1267,7 @@ If **any** element in the window has a `key`, then **all** of the return values
|
|||
Let's take a look at your first dictionary-based window.
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
||||
window = sg.Window('Simple data entry window')
|
||||
layout = [
|
||||
[sg.Text('Please enter your Name, Address, Phone')],
|
||||
[sg.Text('Name', size=(15, 1)), sg.InputText('1', key='_name_')],
|
||||
|
@ -1275,9 +1275,8 @@ Let's take a look at your first dictionary-based window.
|
|||
[sg.Text('Phone', size=(15, 1)), sg.InputText('3', key='_phone_')],
|
||||
[sg.Submit(), sg.Cancel()]
|
||||
]
|
||||
|
||||
window = sg.Window('Simple data entry window', layout)
|
||||
event, values = window.Read()
|
||||
|
||||
event, values = window.Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, values, values['_name_'], values['_address_'], values['_phone_'])
|
||||
|
||||
|
@ -1296,6 +1295,8 @@ The reason for this naming convention is that when you are scanning the code, th
|
|||
`key = '_name_'`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## The Event Loop / Callback Functions
|
||||
|
||||
|
@ -1320,7 +1321,7 @@ layout = [[sg.Text('Click read to read the input value')],
|
|||
[sg.Input()],
|
||||
[sg.RButton('Read'), sg.Exit()]]
|
||||
|
||||
window = sg.Window('Persistent GUI Window', layout)
|
||||
window = sg.Window('Persistent GUI Window').Layout(layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
|
@ -1388,7 +1389,7 @@ This code utilizes many of the common Elements. It does not include Tabs/Tab Gr
|
|||
]
|
||||
|
||||
|
||||
window = sg.Window('Everything bagel', layout, default_element_size=(40, 1), grab_anywhere=False)
|
||||
window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
|
@ -1508,7 +1509,7 @@ You can get your window's size by access the `Size`property. The window has to
|
|||
To finalize your window:
|
||||
|
||||
```pytyhon
|
||||
window = Window('My Title', layout).Finalize()
|
||||
window = Window('My Title').Layout(layout).Finalize()
|
||||
```
|
||||
|
||||
|
||||
|
@ -1556,7 +1557,7 @@ PySimpleGUI will set a default focus location for you. This generally means the
|
|||
|
||||
There are a few methods (functions) that you will see in this document that act on Windows. The ones you will primarily be calling are:
|
||||
|
||||
window.Layout(layout) - Recommend moving away from this nethod and using layout parameter instead. Turns your definition of the Window into Window
|
||||
window.Layout(layout) - Turns your definition of the Window into Window
|
||||
window.Finalize() - creates the tkinter objects for the Window. Normally you do not call this
|
||||
window.Read() - Read the Windows values and get the button / key that caused the Read to return. Can have an optional timeout
|
||||
window.ReadNonBlocking() - NO LONGER USED!
|
||||
|
@ -1567,8 +1568,7 @@ There are a few methods (functions) that you will see in this document that act
|
|||
window.Close() - To close your window, if a button hasn't already closed it
|
||||
window.Disable() - Use to disable the window inputwhen opening another window on top of the primnary Window
|
||||
window.Enable() - Re-enable a Disabled window
|
||||
window.FindElement(key, silent_on_error=False) - Returns the element that has a matching key value
|
||||
window.Element(key, silent_on_error=False) - EXACTLY the same as calling FindElement
|
||||
window.FindElement(key, silent_on_error=False) - Returns the element that has a matching key value
|
||||
window.Move(x,y) - Moves window to location x,y on screen'
|
||||
window.SetAlpha(alpha) - Changes window transparency
|
||||
window.BringToFront() - Brings the window to the top of other windows on the screen
|
||||
|
@ -1588,9 +1588,8 @@ There are a number of operations you can do on a window after you've created the
|
|||
Call to set the window layout. Must be called prior to Read. Most likely "chained" in line with the Window creation.
|
||||
|
||||
```python
|
||||
window = sg.Window('My window title', layout)
|
||||
window = sg.Window('My window title').Layout(layout)
|
||||
```
|
||||
|
||||
#### Finalize()
|
||||
|
||||
Call to force a window to go through the final stages of initialization. This will cause the tkinter resources to be allocated so that they can then be modified. This also causes your window to appear. If you do not want your window to appear when Finalize is called, then set the Alpha to 0 in your window's creation parameters.
|
||||
|
@ -1821,24 +1820,24 @@ Many of the main method calls and Element names have shortcuts. This enables yo
|
|||
The most basic element is the Text element. It simply displays text. Many of the 'options' that can be set for a Text element are shared by other elements.
|
||||
```python
|
||||
Text(text,
|
||||
size=(None, None),
|
||||
auto_size_text=None,
|
||||
click_submits=False,
|
||||
size=(None, None),
|
||||
auto_size_text=None,
|
||||
click_submits=False,
|
||||
enable_events=False,
|
||||
relief=None,
|
||||
font=None,
|
||||
text_color=None,
|
||||
background_color=None,
|
||||
justification=None,
|
||||
pad=None,
|
||||
key=None,
|
||||
right_click_menu=None,
|
||||
tooltip=None,
|
||||
visible=True)
|
||||
```
|
||||
.
|
||||
|
||||
Text - The text that's displayed
|
||||
font=None,
|
||||
text_color=None,
|
||||
background_color=None,
|
||||
justification=None,
|
||||
pad=None,
|
||||
key=None,
|
||||
right_click_menu=None,
|
||||
tooltip=None,
|
||||
visible=True)
|
||||
```
|
||||
.
|
||||
|
||||
Text - The text that's displayed
|
||||
size - Element's size
|
||||
click_submits - if clicked will cause a read call to return the key value of the button
|
||||
enable_events - same as click_submits
|
||||
|
@ -1921,8 +1920,8 @@ Multiline( default_text='',
|
|||
tooltip=None
|
||||
right_click_menu=None,
|
||||
visible=True)
|
||||
```
|
||||
|
||||
'''
|
||||
```
|
||||
|
||||
default_text - Text to display in the text box
|
||||
change_submits - Bool. If True, pressing Enter key submits window
|
||||
|
@ -2701,9 +2700,10 @@ gui_rows = [[sg.Text('Robotics Remote Control')],
|
|||
[sg.RealtimeButton('Left'), sg.T(' ' * 15), sg.RealtimeButton('Right')],
|
||||
[sg.T(' ' * 10), sg.RealtimeButton('Reverse')],
|
||||
[sg.T('')],
|
||||
[sg.Quit(button_color=('black', 'orange'))]]
|
||||
[sg.Quit(button_color=('black', 'orange'))]
|
||||
]
|
||||
|
||||
window = sg.Window('Robotics Remote Control', gui_rows, auto_size_text=True)
|
||||
window = sg.Window('Robotics Remote Control', auto_size_text=True).Layout(gui_rows)
|
||||
|
||||
#
|
||||
# Some place later in your code...
|
||||
|
@ -2840,8 +2840,8 @@ Another way of using a Progress Meter with PySimpleGUI is to build a custom wind
|
|||
[sg.Cancel()]]
|
||||
|
||||
# create the window`
|
||||
window = sg.Window('Custom Progress Meter', layout)
|
||||
progress_bar = window.Element('progressbar')
|
||||
window = sg.Window('Custom Progress Meter').Layout(layout)
|
||||
progress_bar = window.FindElement('progressbar')
|
||||
# loop that would normally do something useful
|
||||
for i in range(10000):
|
||||
# check to see if the cancel button was clicked and exit loop if clicked
|
||||
|
@ -2871,7 +2871,7 @@ Here's a complete solution for a chat-window using an Async window with an Outpu
|
|||
sg.RButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])),
|
||||
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
|
||||
|
||||
window = sg.Window('Chat Window', layout, default_element_size=(30, 2))
|
||||
window = sg.Window('Chat Window', default_element_size=(30, 2)).Layout(layout)
|
||||
|
||||
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
|
||||
while True:
|
||||
|
@ -2944,7 +2944,7 @@ This code produced the above window.
|
|||
# Display the window and get values
|
||||
# If you're willing to not use the "context manager" design pattern, then it's possible
|
||||
# to collapse the window display and read down to a single line of code.
|
||||
event, values = sg.Window('Compact 1-line window with column', layout).Read()
|
||||
event, values = sg.Window('Compact 1-line window with column').Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, values, line_width=200)
|
||||
|
||||
|
@ -3003,7 +3003,7 @@ This code creates a window with a Frame and 2 buttons.
|
|||
[sg.Submit(), sg.Cancel()]
|
||||
]
|
||||
|
||||
window = sg.Window('Frame with buttons', layout, font=("Helvetica", 12))
|
||||
window = sg.Window('Frame with buttons', font=("Helvetica", 12)).Layout(layout)
|
||||
|
||||
|
||||
![frame element](https://user-images.githubusercontent.com/13696193/45889173-c2245700-bd8d-11e8-8f73-1e5f1be3ddb1.jpg)
|
||||
|
@ -3049,11 +3049,11 @@ The order of operations to obtain a tkinter Canvas Widget is:
|
|||
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
|
||||
|
||||
# create the window and show it without the plot
|
||||
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', layout).Finalize()
|
||||
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
|
||||
|
||||
|
||||
|
||||
# add the plot to the window
|
||||
fig_photo = draw_figure(window.Element('canvas').TKCanvas, fig)
|
||||
fig_photo = draw_figure(window.FindElement('canvas').TKCanvas, fig)
|
||||
|
||||
# show it all again and get buttons
|
||||
event, values = window.Read()
|
||||
|
@ -3752,7 +3752,7 @@ layout = [[sg.Text('Persistent window')],
|
|||
[sg.Input()],
|
||||
[sg.RButton('Read'), sg.Exit()]]
|
||||
|
||||
window = sg.Window('Window that stays open', layout)
|
||||
window = sg.Window('Window that stays open').Layout(layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
|
@ -3880,7 +3880,7 @@ See the sample code on the GitHub named Demo Media Player for another example of
|
|||
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
|
||||
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
|
||||
|
||||
window = sg.Window('Running Timer', layout, no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True)
|
||||
window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout)
|
||||
|
||||
# ---------------- main loop ----------------
|
||||
current_time = 0
|
||||
|
@ -3891,7 +3891,7 @@ See the sample code on the GitHub named Demo Media Player for another example of
|
|||
event, values = window.Read(timeout=10)
|
||||
current_time = int(round(time.time() * 100)) - start_time
|
||||
# --------- Display timer in window --------
|
||||
window.Element('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
|
||||
window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
|
||||
(current_time // 100) % 60,
|
||||
current_time % 100))
|
||||
```
|
||||
|
@ -3929,7 +3929,7 @@ import PySimpleGUI as sg
|
|||
layout = [ [sg.Text('My layout', key='_TEXT_')],
|
||||
[sg.Button('Read')]]
|
||||
|
||||
window = sg.Window('My new window', layout)
|
||||
window = sg.Window('My new window').Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
|
@ -3948,7 +3948,7 @@ layout = [ [sg.Text('My layout', key='_TEXT_')],
|
|||
[sg.Button('Read')]
|
||||
]
|
||||
|
||||
window = sg.Window('My new window', layout).Finalize()
|
||||
window = sg.Window('My new window').Layout(layout).Finalize()
|
||||
|
||||
window.Element('_TEXT_').Update('My new text value')
|
||||
|
||||
|
@ -3989,7 +3989,7 @@ In some programs these updates happen in response to another Element. This prog
|
|||
sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]]
|
||||
|
||||
sz = fontSize
|
||||
window = sg.Window("Font size selector", layout, grab_anywhere=False)
|
||||
window = sg.Window("Font size selector", grab_anywhere=False).Layout(layout)
|
||||
# Event Loop
|
||||
while True:
|
||||
event, values= window.Read()
|
||||
|
@ -4001,9 +4001,9 @@ In some programs these updates happen in response to another Element. This prog
|
|||
if sz != fontSize:
|
||||
fontSize = sz
|
||||
font = "Helvetica " + str(fontSize)
|
||||
window.Element('text').Update(font=font)
|
||||
window.Element('slider').Update(sz)
|
||||
window.Element('spin').Update(sz)
|
||||
window.FindElement('text').Update(font=font)
|
||||
window.FindElement('slider').Update(sz)
|
||||
window.FindElement('spin').Update(sz)
|
||||
|
||||
print("Done.")
|
||||
|
||||
|
@ -4013,15 +4013,15 @@ For example, `values['slider']` is the value of the Slider Element.
|
|||
|
||||
This program changes all 3 elements if either the Slider or the Spinner changes. This is done with these statements:
|
||||
|
||||
window.Element('text').Update(font=font)
|
||||
window.Element('slider').Update(sz)
|
||||
window.Element('spin').Update(sz)
|
||||
window.FindElement('text').Update(font=font)
|
||||
window.FindElement('slider').Update(sz)
|
||||
window.FindElement('spin').Update(sz)
|
||||
|
||||
Remember this design pattern because you will use it OFTEN if you use persistent windows.
|
||||
|
||||
It works as follows. The call to `window.FindElement` returns the Element object represented by they provided `key`. This element is then updated by calling it's `Update` method. This is another example of Python's "chaining" feature. We could write this code using the long-form:
|
||||
|
||||
text_element = window.Element('text')
|
||||
text_element = window.FindElement('text')
|
||||
text_element.Update(font=font)
|
||||
|
||||
The takeaway from this exercise is that keys are key in PySimpleGUI's design. They are used to both read the values of the window and also to identify elements. As already mentioned, they are used as targets in Button calls.
|
||||
|
@ -4054,28 +4054,28 @@ Keyboard keys return 2 types of key events. For "normal" keys (a,b,c, etc), a si
|
|||
Key Sym:Key Code
|
||||
|
||||
Key Sym is a string such as 'Control_L'. The Key Code is a numeric representation of that key. The left control key, when pressed will return the value 'Control_L:17'
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
||||
# Recipe for getting keys, one at a time as they are released
|
||||
# If want to use the space bar, then be sure and disable the "default focus"
|
||||
|
||||
with sg.Window("Keyboard Test", return_keyboard_events=True, use_default_focus=False) as window:
|
||||
text_elem = sg.Text("", size=(18, 1))
|
||||
layout = [[sg.Text("Press a key or scroll mouse")],
|
||||
[text_elem],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window.Layout(layout)
|
||||
# ---===--- Loop taking in user input --- #
|
||||
while True:
|
||||
event, value = window.Read()
|
||||
|
||||
if event == "OK" or event is None:
|
||||
print(event, "exiting")
|
||||
break
|
||||
text_elem.Update(event)
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
# Recipe for getting keys, one at a time as they are released
|
||||
# If want to use the space bar, then be sure and disable the "default focus"
|
||||
|
||||
text_elem = sg.Text("", size=(18, 1))
|
||||
layout = [[sg.Text("Press a key or scroll mouse")],
|
||||
[text_elem],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window = sg.Window("Keyboard Test", layout, return_keyboard_events=True, use_default_focus=False)
|
||||
# ---===--- Loop taking in user input --- #
|
||||
while True:
|
||||
event, value = window.Read()
|
||||
|
||||
if event == "OK" or event is None:
|
||||
print(event, "exiting")
|
||||
break
|
||||
text_elem.Update(event)
|
||||
```
|
||||
|
||||
|
||||
You want to turn off the default focus so that there no buttons that will be selected should you press the spacebar.
|
||||
|
@ -4083,20 +4083,21 @@ You want to turn off the default focus so that there no buttons that will be sel
|
|||
### Realtime Keyboard Capture
|
||||
Use realtime keyboard capture by calling
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[sg.Text("Hold down a key")],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window = sg.Window("Realtime Keyboard Test", layout, return_keyboard_events=True, use_default_focus=False)
|
||||
|
||||
while True:
|
||||
event, value = window.Read(timeout=0)
|
||||
if event == "OK" or event is None:
|
||||
print(event, value, "exiting")
|
||||
break
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
print(event)
|
||||
import PySimpleGUI as sg
|
||||
|
||||
with sg.Window("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False) as window:
|
||||
layout = [[sg.Text("Hold down a key")],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window.Layout(layout)
|
||||
|
||||
while True:
|
||||
event, value = window.Read(timeout=0)
|
||||
if event == "OK" or event is None:
|
||||
print(event, value, "exiting")
|
||||
break
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
print(event)
|
||||
|
||||
|
||||
# Menus
|
||||
|
@ -4211,12 +4212,12 @@ layout = [[ sg.Text('Window 1'),],
|
|||
[sg.Text('', key='_OUTPUT_')],
|
||||
[sg.Button('Launch 2'), sg.Button('Exit')]]
|
||||
|
||||
win1 = sg.Window('Window 1', layout)
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
|
||||
win2_active = False
|
||||
while True:
|
||||
ev1, vals1 = win1.Read(timeout=100)
|
||||
win1.Element('_OUTPUT_').Update(vals1[0])
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
if ev1 is None or ev1 == 'Exit':
|
||||
break
|
||||
|
||||
|
@ -4225,7 +4226,7 @@ while True:
|
|||
layout2 = [[sg.Text('Window 2')],
|
||||
[sg.Button('Exit')]]
|
||||
|
||||
win2 = sg.Window('Window 2', layout2)
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
|
||||
if win2_active:
|
||||
ev2, vals2 = win2.Read(timeout=100)
|
||||
|
@ -4247,13 +4248,13 @@ layout = [[ sg.Text('Window 1'),],
|
|||
[sg.Text('', key='_OUTPUT_')],
|
||||
[sg.Button('Launch 2')]]
|
||||
|
||||
win1 = sg.Window('Window 1', layout)
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
win2_active=False
|
||||
while True:
|
||||
ev1, vals1 = win1.Read(timeout=100)
|
||||
if ev1 is None:
|
||||
break
|
||||
win1.Element('_OUTPUT_').Update(vals1[0])
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
|
||||
if ev1 == 'Launch 2' and not win2_active:
|
||||
win2_active = True
|
||||
|
@ -4261,7 +4262,7 @@ while True:
|
|||
layout2 = [[sg.Text('Window 2')], # note must create a layout from scratch every time. No reuse
|
||||
[sg.Button('Exit')]]
|
||||
|
||||
win2 = sg.Window('Window 2', layout2)
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
while True:
|
||||
ev2, vals2 = win2.Read()
|
||||
if ev2 is None or ev2 == 'Exit':
|
||||
|
@ -4933,6 +4934,37 @@ Mixup.... 3.26 changes don't appear to have been correctly released so releasing
|
|||
* New method for `Graph` - `RelocateFigure`
|
||||
* Output Element no longer accepts focus
|
||||
|
||||
## 3.30.0 PySimpleGUI 24-May-2019
|
||||
|
||||
* Rework of ALLL Tooltips. Was always displaying at uttuper left part of element. Not displays closer to where mouse entered or edited
|
||||
* New Element.Widget base class variable. Brings tkinter into the newer architecture of user accessibility to underlying GUI Frameworks' widgets
|
||||
* New SetTooltip Element method. Means all Elements gain this method. Can set the tooltip on the fly now for all elements
|
||||
* Include scroll bar when making visible / invisible Listbox Elements
|
||||
* New Radio Element method - `Radio.ResetGroup()` sets all elements in the Radio Group to False
|
||||
* Added borderwidth to Multiline Element
|
||||
* `Button.Click()` - new method - Generates a button click even as if a user clicked a button (at the tkinter level)
|
||||
* Made a Graph.Images dictionary to keep track of images being used in a graph. When graph is deleted, all of the accociated images should be deleted too.'
|
||||
* Added `Graph.SetFocus()` to give a Graph Element the focus just as you can input elements
|
||||
* Table new parameter - `hide_vertical_scroll` if True will hide the table's vertical bars
|
||||
* Window - new parameter - `transparent_color`. Causes a single color to become completely transparent such that you see through the window, you can click through the window. Its like tineows never was there.
|
||||
* The new `Window.AllKeysDict = {}` has been adopted by all PySimpleGUI ports. It's a new method of automatically creating missing keys, storing and retrieving keys in general for a window.
|
||||
* Changed how `window.Maximize` is implemented previously used the '-fullscreen' attribute. Now uses the 'zoomed' state
|
||||
* Window gets a new `Normal()` method to return from Maximize state. Sets root.state('normal')
|
||||
* Window.Close() now closes the special `Window.hidden_master_root` window when the "last" window is closed
|
||||
* `Window.SetTransparentColor` method added. Same effect as if window was created with parameter set
|
||||
* An Element's Widget stored in `.Widget` attribute
|
||||
* Making ComboBox's ID unique by using it's Key
|
||||
* Changed Multiline to be sunken and have a border depth setting now
|
||||
* Removed a second canvas that was being used for Graph element.
|
||||
* Changed how no titlebar is implemented running on Linux versus Windows. -type splash now used for Linux
|
||||
* PopupScrolled - Added back using CloseButton to close the window
|
||||
* Fixed PopupGetFolder to use correct PySimpleGUI program constructs (keys)
|
||||
* PopupGetText populated values carrectly using the value variable, used keys
|
||||
* PopupAnimated finally gets a completely transparent background
|
||||
|
||||
|
||||
|
||||
|
||||
### Upcoming
|
||||
Make suggestions people! Future release features
|
||||
|
||||
|
@ -4965,7 +4997,7 @@ It seemed quite natural to use Python's powerful list constructs when possible.
|
|||
**Dictionaries**
|
||||
Want to view your form's results as a dictionary instead of a list... no problem, just use the `key` keyword on your elements. For complex forms with a lot of values that need to be changed frequently, this is by far the best way of consuming the results.
|
||||
|
||||
You can also look up elements using their keys. This is an excellent way to update elements in reaction to another element. Call `form.Element(key)` to get the Element.
|
||||
You can also look up elements using their keys. This is an excellent way to update elements in reaction to another element. Call `form.FindElement(key)` to get the Element.
|
||||
|
||||
**Named / Optional Parameters**
|
||||
This is a language feature that is featured **heavily** in all of the API calls, both functions and classes. Elements are configured, in-place, by setting one or more optional parameters. For example, a Text element's color is chosen by setting the optional `text_color` parameter.
|
||||
|
|
252
readme.md
252
readme.md
|
@ -33,9 +33,9 @@
|
|||
|
||||
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.29.0-red.svg?longCache=true&style=for-the-badge)
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.39.0-red.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.29.0-blue.svg?longCache=true&style=for-the-badge)
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.30.0-blue.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
![Python Version](https://img.shields.io/badge/PySimpleGUIQt_Version-0.26.0-orange.svg?longCache=true&style=for-the-badge)
|
||||
|
||||
|
@ -324,7 +324,7 @@ An example of many widgets used on a single window. A little further down you'l
|
|||
sg.FolderBrowse()],
|
||||
[sg.Submit(), sg.Cancel(), sg.Button('Customized', button_color=('white', 'green'))]]
|
||||
|
||||
event, values = sg.Window('Everything bagel', layout, auto_size_text=True, default_element_size=(40, 1)).Read()
|
||||
event, values = sg.Window('Everything bagel', auto_size_text=True, default_element_size=(40, 1)).Layout(layout).Read()
|
||||
|
||||
|
||||
|
||||
|
@ -942,7 +942,7 @@ Finally we can put it all together into a program that will display our window.
|
|||
[sg.Input()],
|
||||
[sg.OK()] ]
|
||||
|
||||
event, (number,) = sg.Window('Enter a number example', layout).Read()
|
||||
event, (number,) = sg.Window('Enter a number example').Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, number)
|
||||
|
||||
|
@ -960,7 +960,7 @@ Writing the code for this one is just as straightforward. There is one tricky t
|
|||
[sg.Input(), sg.FileBrowse()],
|
||||
[sg.OK(), sg.Cancel()] ]
|
||||
|
||||
event, (number,) = sg.Window('Get filename example', layout).Read()
|
||||
event, (number,) = sg.Window('Get filename example').Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, number)
|
||||
|
||||
|
@ -987,7 +987,7 @@ window_rows = [[sg.Text('SHA-1 and SHA-256 Hashes for the file')],
|
|||
[sg.InputText(), sg.FileBrowse()],
|
||||
[sg.Submit(), sg.Cancel()]]
|
||||
|
||||
window = sg.Window('SHA-1 & 256 Hash', window_rows)
|
||||
window = sg.Window('SHA-1 & 256 Hash').Layout(window_rows)
|
||||
|
||||
event, values = window.Read()
|
||||
window.Close()
|
||||
|
@ -1009,7 +1009,7 @@ layout = [[sg.Text('Persistent window')],
|
|||
[sg.Input(do_not_clear=True)],
|
||||
[sg.Button('Read'), sg.Exit()]]
|
||||
|
||||
window = sg.Window('Window that stays open', layout)
|
||||
window = sg.Window('Window that stays open').Layout(layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
|
@ -1039,7 +1039,7 @@ layout = [[sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_')
|
|||
[sg.Input(do_not_clear=True, key='_IN_')],
|
||||
[sg.Button('Show'), sg.Button('Exit')]]
|
||||
|
||||
window = sg.Window('Window Title', layout)
|
||||
window = sg.Window('Window Title').Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
|
@ -1048,7 +1048,7 @@ while True: # Event Loop
|
|||
break
|
||||
if event == 'Show':
|
||||
# change the "output" element to be the value of "input" element
|
||||
window.Element('_OUTPUT_').Update(values['_IN_'])
|
||||
window.FindElement('_OUTPUT_').Update(values['_IN_'])
|
||||
|
||||
window.Close()
|
||||
```
|
||||
|
@ -1070,9 +1070,9 @@ The key to custom windows in PySimpleGUI is to view windows as ROWS of GUI Elem
|
|||
[sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
|
||||
[sg.Submit(), sg.Cancel()]]
|
||||
|
||||
window = sg.Window('Rename Files or Folders', layout)
|
||||
window = sg.Window('Rename Files or Folders')
|
||||
|
||||
event, values = window.Read()
|
||||
event, values = window.Layout(layout).Read()
|
||||
```
|
||||
|
||||
|
||||
|
@ -1233,13 +1233,13 @@ The second parameter from a Read call is either a list or a dictionary of the in
|
|||
|
||||
Each of the Elements that are Input Elements will have a value in the list of return values. You can unpack your GUI directly into the variables you want to use.
|
||||
|
||||
event, (filename, folder1, folder2, should_overwrite) = sg.Window('My title', window_rows).Read()
|
||||
event, (filename, folder1, folder2, should_overwrite) = sg.Window('My title').Layout(window_rows).Read()
|
||||
|
||||
Or, more commonly, you can unpack the return results separately.
|
||||
|
||||
```python
|
||||
event, values = sg.Window('My title', window_rows).Read()
|
||||
event, value_list = window.Read()
|
||||
event, values = sg.Window('My title').Layout(window_rows).Read()
|
||||
event, value_list = window.Layout(window_rows).Read()
|
||||
value1 = value_list[0]
|
||||
value2 = value_list[1]
|
||||
...
|
||||
|
@ -1256,7 +1256,7 @@ For windows longer than 3 or 4 fields you will want to use a dictionary to help
|
|||
|
||||
The most common window read statement you'll encounter looks something like this:
|
||||
|
||||
window = sg.Window("My title", layout).Read()
|
||||
window = sg.Window("My title").Layout(layout).Read()
|
||||
|
||||
|
||||
To use a dictionary, you will need to:
|
||||
|
@ -1267,7 +1267,7 @@ If **any** element in the window has a `key`, then **all** of the return values
|
|||
Let's take a look at your first dictionary-based window.
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
||||
window = sg.Window('Simple data entry window')
|
||||
layout = [
|
||||
[sg.Text('Please enter your Name, Address, Phone')],
|
||||
[sg.Text('Name', size=(15, 1)), sg.InputText('1', key='_name_')],
|
||||
|
@ -1275,9 +1275,8 @@ Let's take a look at your first dictionary-based window.
|
|||
[sg.Text('Phone', size=(15, 1)), sg.InputText('3', key='_phone_')],
|
||||
[sg.Submit(), sg.Cancel()]
|
||||
]
|
||||
|
||||
window = sg.Window('Simple data entry window', layout)
|
||||
event, values = window.Read()
|
||||
|
||||
event, values = window.Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, values, values['_name_'], values['_address_'], values['_phone_'])
|
||||
|
||||
|
@ -1296,6 +1295,8 @@ The reason for this naming convention is that when you are scanning the code, th
|
|||
`key = '_name_'`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## The Event Loop / Callback Functions
|
||||
|
||||
|
@ -1320,7 +1321,7 @@ layout = [[sg.Text('Click read to read the input value')],
|
|||
[sg.Input()],
|
||||
[sg.RButton('Read'), sg.Exit()]]
|
||||
|
||||
window = sg.Window('Persistent GUI Window', layout)
|
||||
window = sg.Window('Persistent GUI Window').Layout(layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
|
@ -1388,7 +1389,7 @@ This code utilizes many of the common Elements. It does not include Tabs/Tab Gr
|
|||
]
|
||||
|
||||
|
||||
window = sg.Window('Everything bagel', layout, default_element_size=(40, 1), grab_anywhere=False)
|
||||
window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
|
||||
|
||||
event, values = window.Read()
|
||||
|
||||
|
@ -1508,7 +1509,7 @@ You can get your window's size by access the `Size`property. The window has to
|
|||
To finalize your window:
|
||||
|
||||
```pytyhon
|
||||
window = Window('My Title', layout).Finalize()
|
||||
window = Window('My Title').Layout(layout).Finalize()
|
||||
```
|
||||
|
||||
|
||||
|
@ -1556,7 +1557,7 @@ PySimpleGUI will set a default focus location for you. This generally means the
|
|||
|
||||
There are a few methods (functions) that you will see in this document that act on Windows. The ones you will primarily be calling are:
|
||||
|
||||
window.Layout(layout) - Recommend moving away from this nethod and using layout parameter instead. Turns your definition of the Window into Window
|
||||
window.Layout(layout) - Turns your definition of the Window into Window
|
||||
window.Finalize() - creates the tkinter objects for the Window. Normally you do not call this
|
||||
window.Read() - Read the Windows values and get the button / key that caused the Read to return. Can have an optional timeout
|
||||
window.ReadNonBlocking() - NO LONGER USED!
|
||||
|
@ -1567,8 +1568,7 @@ There are a few methods (functions) that you will see in this document that act
|
|||
window.Close() - To close your window, if a button hasn't already closed it
|
||||
window.Disable() - Use to disable the window inputwhen opening another window on top of the primnary Window
|
||||
window.Enable() - Re-enable a Disabled window
|
||||
window.FindElement(key, silent_on_error=False) - Returns the element that has a matching key value
|
||||
window.Element(key, silent_on_error=False) - EXACTLY the same as calling FindElement
|
||||
window.FindElement(key, silent_on_error=False) - Returns the element that has a matching key value
|
||||
window.Move(x,y) - Moves window to location x,y on screen'
|
||||
window.SetAlpha(alpha) - Changes window transparency
|
||||
window.BringToFront() - Brings the window to the top of other windows on the screen
|
||||
|
@ -1588,9 +1588,8 @@ There are a number of operations you can do on a window after you've created the
|
|||
Call to set the window layout. Must be called prior to Read. Most likely "chained" in line with the Window creation.
|
||||
|
||||
```python
|
||||
window = sg.Window('My window title', layout)
|
||||
window = sg.Window('My window title').Layout(layout)
|
||||
```
|
||||
|
||||
#### Finalize()
|
||||
|
||||
Call to force a window to go through the final stages of initialization. This will cause the tkinter resources to be allocated so that they can then be modified. This also causes your window to appear. If you do not want your window to appear when Finalize is called, then set the Alpha to 0 in your window's creation parameters.
|
||||
|
@ -1821,24 +1820,24 @@ Many of the main method calls and Element names have shortcuts. This enables yo
|
|||
The most basic element is the Text element. It simply displays text. Many of the 'options' that can be set for a Text element are shared by other elements.
|
||||
```python
|
||||
Text(text,
|
||||
size=(None, None),
|
||||
auto_size_text=None,
|
||||
click_submits=False,
|
||||
size=(None, None),
|
||||
auto_size_text=None,
|
||||
click_submits=False,
|
||||
enable_events=False,
|
||||
relief=None,
|
||||
font=None,
|
||||
text_color=None,
|
||||
background_color=None,
|
||||
justification=None,
|
||||
pad=None,
|
||||
key=None,
|
||||
right_click_menu=None,
|
||||
tooltip=None,
|
||||
visible=True)
|
||||
```
|
||||
.
|
||||
|
||||
Text - The text that's displayed
|
||||
font=None,
|
||||
text_color=None,
|
||||
background_color=None,
|
||||
justification=None,
|
||||
pad=None,
|
||||
key=None,
|
||||
right_click_menu=None,
|
||||
tooltip=None,
|
||||
visible=True)
|
||||
```
|
||||
.
|
||||
|
||||
Text - The text that's displayed
|
||||
size - Element's size
|
||||
click_submits - if clicked will cause a read call to return the key value of the button
|
||||
enable_events - same as click_submits
|
||||
|
@ -1921,8 +1920,8 @@ Multiline( default_text='',
|
|||
tooltip=None
|
||||
right_click_menu=None,
|
||||
visible=True)
|
||||
```
|
||||
|
||||
'''
|
||||
```
|
||||
|
||||
default_text - Text to display in the text box
|
||||
change_submits - Bool. If True, pressing Enter key submits window
|
||||
|
@ -2701,9 +2700,10 @@ gui_rows = [[sg.Text('Robotics Remote Control')],
|
|||
[sg.RealtimeButton('Left'), sg.T(' ' * 15), sg.RealtimeButton('Right')],
|
||||
[sg.T(' ' * 10), sg.RealtimeButton('Reverse')],
|
||||
[sg.T('')],
|
||||
[sg.Quit(button_color=('black', 'orange'))]]
|
||||
[sg.Quit(button_color=('black', 'orange'))]
|
||||
]
|
||||
|
||||
window = sg.Window('Robotics Remote Control', gui_rows, auto_size_text=True)
|
||||
window = sg.Window('Robotics Remote Control', auto_size_text=True).Layout(gui_rows)
|
||||
|
||||
#
|
||||
# Some place later in your code...
|
||||
|
@ -2840,8 +2840,8 @@ Another way of using a Progress Meter with PySimpleGUI is to build a custom wind
|
|||
[sg.Cancel()]]
|
||||
|
||||
# create the window`
|
||||
window = sg.Window('Custom Progress Meter', layout)
|
||||
progress_bar = window.Element('progressbar')
|
||||
window = sg.Window('Custom Progress Meter').Layout(layout)
|
||||
progress_bar = window.FindElement('progressbar')
|
||||
# loop that would normally do something useful
|
||||
for i in range(10000):
|
||||
# check to see if the cancel button was clicked and exit loop if clicked
|
||||
|
@ -2871,7 +2871,7 @@ Here's a complete solution for a chat-window using an Async window with an Outpu
|
|||
sg.RButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])),
|
||||
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
|
||||
|
||||
window = sg.Window('Chat Window', layout, default_element_size=(30, 2))
|
||||
window = sg.Window('Chat Window', default_element_size=(30, 2)).Layout(layout)
|
||||
|
||||
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
|
||||
while True:
|
||||
|
@ -2944,7 +2944,7 @@ This code produced the above window.
|
|||
# Display the window and get values
|
||||
# If you're willing to not use the "context manager" design pattern, then it's possible
|
||||
# to collapse the window display and read down to a single line of code.
|
||||
event, values = sg.Window('Compact 1-line window with column', layout).Read()
|
||||
event, values = sg.Window('Compact 1-line window with column').Layout(layout).Read()
|
||||
|
||||
sg.Popup(event, values, line_width=200)
|
||||
|
||||
|
@ -3003,7 +3003,7 @@ This code creates a window with a Frame and 2 buttons.
|
|||
[sg.Submit(), sg.Cancel()]
|
||||
]
|
||||
|
||||
window = sg.Window('Frame with buttons', layout, font=("Helvetica", 12))
|
||||
window = sg.Window('Frame with buttons', font=("Helvetica", 12)).Layout(layout)
|
||||
|
||||
|
||||
![frame element](https://user-images.githubusercontent.com/13696193/45889173-c2245700-bd8d-11e8-8f73-1e5f1be3ddb1.jpg)
|
||||
|
@ -3049,11 +3049,11 @@ The order of operations to obtain a tkinter Canvas Widget is:
|
|||
[sg.OK(pad=((figure_w / 2, 0), 3), size=(4, 2))]]
|
||||
|
||||
# create the window and show it without the plot
|
||||
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI', layout).Finalize()
|
||||
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI').Layout(layout).Finalize()
|
||||
|
||||
|
||||
|
||||
# add the plot to the window
|
||||
fig_photo = draw_figure(window.Element('canvas').TKCanvas, fig)
|
||||
fig_photo = draw_figure(window.FindElement('canvas').TKCanvas, fig)
|
||||
|
||||
# show it all again and get buttons
|
||||
event, values = window.Read()
|
||||
|
@ -3752,7 +3752,7 @@ layout = [[sg.Text('Persistent window')],
|
|||
[sg.Input()],
|
||||
[sg.RButton('Read'), sg.Exit()]]
|
||||
|
||||
window = sg.Window('Window that stays open', layout)
|
||||
window = sg.Window('Window that stays open').Layout(layout)
|
||||
|
||||
while True:
|
||||
event, values = window.Read()
|
||||
|
@ -3880,7 +3880,7 @@ See the sample code on the GitHub named Demo Media Player for another example of
|
|||
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
|
||||
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
|
||||
|
||||
window = sg.Window('Running Timer', layout, no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True)
|
||||
window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout)
|
||||
|
||||
# ---------------- main loop ----------------
|
||||
current_time = 0
|
||||
|
@ -3891,7 +3891,7 @@ See the sample code on the GitHub named Demo Media Player for another example of
|
|||
event, values = window.Read(timeout=10)
|
||||
current_time = int(round(time.time() * 100)) - start_time
|
||||
# --------- Display timer in window --------
|
||||
window.Element('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
|
||||
window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
|
||||
(current_time // 100) % 60,
|
||||
current_time % 100))
|
||||
```
|
||||
|
@ -3929,7 +3929,7 @@ import PySimpleGUI as sg
|
|||
layout = [ [sg.Text('My layout', key='_TEXT_')],
|
||||
[sg.Button('Read')]]
|
||||
|
||||
window = sg.Window('My new window', layout)
|
||||
window = sg.Window('My new window').Layout(layout)
|
||||
|
||||
while True: # Event Loop
|
||||
event, values = window.Read()
|
||||
|
@ -3948,7 +3948,7 @@ layout = [ [sg.Text('My layout', key='_TEXT_')],
|
|||
[sg.Button('Read')]
|
||||
]
|
||||
|
||||
window = sg.Window('My new window', layout).Finalize()
|
||||
window = sg.Window('My new window').Layout(layout).Finalize()
|
||||
|
||||
window.Element('_TEXT_').Update('My new text value')
|
||||
|
||||
|
@ -3989,7 +3989,7 @@ In some programs these updates happen in response to another Element. This prog
|
|||
sg.Text("Aa", size=(2, 1), font="Helvetica " + str(fontSize), key='text')]]
|
||||
|
||||
sz = fontSize
|
||||
window = sg.Window("Font size selector", layout, grab_anywhere=False)
|
||||
window = sg.Window("Font size selector", grab_anywhere=False).Layout(layout)
|
||||
# Event Loop
|
||||
while True:
|
||||
event, values= window.Read()
|
||||
|
@ -4001,9 +4001,9 @@ In some programs these updates happen in response to another Element. This prog
|
|||
if sz != fontSize:
|
||||
fontSize = sz
|
||||
font = "Helvetica " + str(fontSize)
|
||||
window.Element('text').Update(font=font)
|
||||
window.Element('slider').Update(sz)
|
||||
window.Element('spin').Update(sz)
|
||||
window.FindElement('text').Update(font=font)
|
||||
window.FindElement('slider').Update(sz)
|
||||
window.FindElement('spin').Update(sz)
|
||||
|
||||
print("Done.")
|
||||
|
||||
|
@ -4013,15 +4013,15 @@ For example, `values['slider']` is the value of the Slider Element.
|
|||
|
||||
This program changes all 3 elements if either the Slider or the Spinner changes. This is done with these statements:
|
||||
|
||||
window.Element('text').Update(font=font)
|
||||
window.Element('slider').Update(sz)
|
||||
window.Element('spin').Update(sz)
|
||||
window.FindElement('text').Update(font=font)
|
||||
window.FindElement('slider').Update(sz)
|
||||
window.FindElement('spin').Update(sz)
|
||||
|
||||
Remember this design pattern because you will use it OFTEN if you use persistent windows.
|
||||
|
||||
It works as follows. The call to `window.FindElement` returns the Element object represented by they provided `key`. This element is then updated by calling it's `Update` method. This is another example of Python's "chaining" feature. We could write this code using the long-form:
|
||||
|
||||
text_element = window.Element('text')
|
||||
text_element = window.FindElement('text')
|
||||
text_element.Update(font=font)
|
||||
|
||||
The takeaway from this exercise is that keys are key in PySimpleGUI's design. They are used to both read the values of the window and also to identify elements. As already mentioned, they are used as targets in Button calls.
|
||||
|
@ -4054,28 +4054,28 @@ Keyboard keys return 2 types of key events. For "normal" keys (a,b,c, etc), a si
|
|||
Key Sym:Key Code
|
||||
|
||||
Key Sym is a string such as 'Control_L'. The Key Code is a numeric representation of that key. The left control key, when pressed will return the value 'Control_L:17'
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
||||
# Recipe for getting keys, one at a time as they are released
|
||||
# If want to use the space bar, then be sure and disable the "default focus"
|
||||
|
||||
with sg.Window("Keyboard Test", return_keyboard_events=True, use_default_focus=False) as window:
|
||||
text_elem = sg.Text("", size=(18, 1))
|
||||
layout = [[sg.Text("Press a key or scroll mouse")],
|
||||
[text_elem],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window.Layout(layout)
|
||||
# ---===--- Loop taking in user input --- #
|
||||
while True:
|
||||
event, value = window.Read()
|
||||
|
||||
if event == "OK" or event is None:
|
||||
print(event, "exiting")
|
||||
break
|
||||
text_elem.Update(event)
|
||||
|
||||
```python
|
||||
import PySimpleGUI as sg
|
||||
|
||||
# Recipe for getting keys, one at a time as they are released
|
||||
# If want to use the space bar, then be sure and disable the "default focus"
|
||||
|
||||
text_elem = sg.Text("", size=(18, 1))
|
||||
layout = [[sg.Text("Press a key or scroll mouse")],
|
||||
[text_elem],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window = sg.Window("Keyboard Test", layout, return_keyboard_events=True, use_default_focus=False)
|
||||
# ---===--- Loop taking in user input --- #
|
||||
while True:
|
||||
event, value = window.Read()
|
||||
|
||||
if event == "OK" or event is None:
|
||||
print(event, "exiting")
|
||||
break
|
||||
text_elem.Update(event)
|
||||
```
|
||||
|
||||
|
||||
You want to turn off the default focus so that there no buttons that will be selected should you press the spacebar.
|
||||
|
@ -4083,20 +4083,21 @@ You want to turn off the default focus so that there no buttons that will be sel
|
|||
### Realtime Keyboard Capture
|
||||
Use realtime keyboard capture by calling
|
||||
|
||||
import PySimpleGUI as sg
|
||||
|
||||
layout = [[sg.Text("Hold down a key")],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window = sg.Window("Realtime Keyboard Test", layout, return_keyboard_events=True, use_default_focus=False)
|
||||
|
||||
while True:
|
||||
event, value = window.Read(timeout=0)
|
||||
if event == "OK" or event is None:
|
||||
print(event, value, "exiting")
|
||||
break
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
print(event)
|
||||
import PySimpleGUI as sg
|
||||
|
||||
with sg.Window("Realtime Keyboard Test", return_keyboard_events=True, use_default_focus=False) as window:
|
||||
layout = [[sg.Text("Hold down a key")],
|
||||
[sg.Button("OK")]]
|
||||
|
||||
window.Layout(layout)
|
||||
|
||||
while True:
|
||||
event, value = window.Read(timeout=0)
|
||||
if event == "OK" or event is None:
|
||||
print(event, value, "exiting")
|
||||
break
|
||||
if event != sg.TIMEOUT_KEY:
|
||||
print(event)
|
||||
|
||||
|
||||
# Menus
|
||||
|
@ -4211,12 +4212,12 @@ layout = [[ sg.Text('Window 1'),],
|
|||
[sg.Text('', key='_OUTPUT_')],
|
||||
[sg.Button('Launch 2'), sg.Button('Exit')]]
|
||||
|
||||
win1 = sg.Window('Window 1', layout)
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
|
||||
win2_active = False
|
||||
while True:
|
||||
ev1, vals1 = win1.Read(timeout=100)
|
||||
win1.Element('_OUTPUT_').Update(vals1[0])
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
if ev1 is None or ev1 == 'Exit':
|
||||
break
|
||||
|
||||
|
@ -4225,7 +4226,7 @@ while True:
|
|||
layout2 = [[sg.Text('Window 2')],
|
||||
[sg.Button('Exit')]]
|
||||
|
||||
win2 = sg.Window('Window 2', layout2)
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
|
||||
if win2_active:
|
||||
ev2, vals2 = win2.Read(timeout=100)
|
||||
|
@ -4247,13 +4248,13 @@ layout = [[ sg.Text('Window 1'),],
|
|||
[sg.Text('', key='_OUTPUT_')],
|
||||
[sg.Button('Launch 2')]]
|
||||
|
||||
win1 = sg.Window('Window 1', layout)
|
||||
win1 = sg.Window('Window 1').Layout(layout)
|
||||
win2_active=False
|
||||
while True:
|
||||
ev1, vals1 = win1.Read(timeout=100)
|
||||
if ev1 is None:
|
||||
break
|
||||
win1.Element('_OUTPUT_').Update(vals1[0])
|
||||
win1.FindElement('_OUTPUT_').Update(vals1[0])
|
||||
|
||||
if ev1 == 'Launch 2' and not win2_active:
|
||||
win2_active = True
|
||||
|
@ -4261,7 +4262,7 @@ while True:
|
|||
layout2 = [[sg.Text('Window 2')], # note must create a layout from scratch every time. No reuse
|
||||
[sg.Button('Exit')]]
|
||||
|
||||
win2 = sg.Window('Window 2', layout2)
|
||||
win2 = sg.Window('Window 2').Layout(layout2)
|
||||
while True:
|
||||
ev2, vals2 = win2.Read()
|
||||
if ev2 is None or ev2 == 'Exit':
|
||||
|
@ -4933,6 +4934,37 @@ Mixup.... 3.26 changes don't appear to have been correctly released so releasing
|
|||
* New method for `Graph` - `RelocateFigure`
|
||||
* Output Element no longer accepts focus
|
||||
|
||||
## 3.30.0 PySimpleGUI 24-May-2019
|
||||
|
||||
* Rework of ALLL Tooltips. Was always displaying at uttuper left part of element. Not displays closer to where mouse entered or edited
|
||||
* New Element.Widget base class variable. Brings tkinter into the newer architecture of user accessibility to underlying GUI Frameworks' widgets
|
||||
* New SetTooltip Element method. Means all Elements gain this method. Can set the tooltip on the fly now for all elements
|
||||
* Include scroll bar when making visible / invisible Listbox Elements
|
||||
* New Radio Element method - `Radio.ResetGroup()` sets all elements in the Radio Group to False
|
||||
* Added borderwidth to Multiline Element
|
||||
* `Button.Click()` - new method - Generates a button click even as if a user clicked a button (at the tkinter level)
|
||||
* Made a Graph.Images dictionary to keep track of images being used in a graph. When graph is deleted, all of the accociated images should be deleted too.'
|
||||
* Added `Graph.SetFocus()` to give a Graph Element the focus just as you can input elements
|
||||
* Table new parameter - `hide_vertical_scroll` if True will hide the table's vertical bars
|
||||
* Window - new parameter - `transparent_color`. Causes a single color to become completely transparent such that you see through the window, you can click through the window. Its like tineows never was there.
|
||||
* The new `Window.AllKeysDict = {}` has been adopted by all PySimpleGUI ports. It's a new method of automatically creating missing keys, storing and retrieving keys in general for a window.
|
||||
* Changed how `window.Maximize` is implemented previously used the '-fullscreen' attribute. Now uses the 'zoomed' state
|
||||
* Window gets a new `Normal()` method to return from Maximize state. Sets root.state('normal')
|
||||
* Window.Close() now closes the special `Window.hidden_master_root` window when the "last" window is closed
|
||||
* `Window.SetTransparentColor` method added. Same effect as if window was created with parameter set
|
||||
* An Element's Widget stored in `.Widget` attribute
|
||||
* Making ComboBox's ID unique by using it's Key
|
||||
* Changed Multiline to be sunken and have a border depth setting now
|
||||
* Removed a second canvas that was being used for Graph element.
|
||||
* Changed how no titlebar is implemented running on Linux versus Windows. -type splash now used for Linux
|
||||
* PopupScrolled - Added back using CloseButton to close the window
|
||||
* Fixed PopupGetFolder to use correct PySimpleGUI program constructs (keys)
|
||||
* PopupGetText populated values carrectly using the value variable, used keys
|
||||
* PopupAnimated finally gets a completely transparent background
|
||||
|
||||
|
||||
|
||||
|
||||
### Upcoming
|
||||
Make suggestions people! Future release features
|
||||
|
||||
|
@ -4965,7 +4997,7 @@ It seemed quite natural to use Python's powerful list constructs when possible.
|
|||
**Dictionaries**
|
||||
Want to view your form's results as a dictionary instead of a list... no problem, just use the `key` keyword on your elements. For complex forms with a lot of values that need to be changed frequently, this is by far the best way of consuming the results.
|
||||
|
||||
You can also look up elements using their keys. This is an excellent way to update elements in reaction to another element. Call `form.Element(key)` to get the Element.
|
||||
You can also look up elements using their keys. This is an excellent way to update elements in reaction to another element. Call `form.FindElement(key)` to get the Element.
|
||||
|
||||
**Named / Optional Parameters**
|
||||
This is a language feature that is featured **heavily** in all of the API calls, both functions and classes. Elements are configured, in-place, by setting one or more optional parameters. For example, a Text element's color is chosen by setting the optional `text_color` parameter.
|
||||
|
|
Loading…
Reference in New Issue