RELEASE 3.10.1 & 1.2.1

This commit is contained in:
MikeTheWatchGuy 2018-10-20 12:04:18 -04:00
parent 20379b8a87
commit 3c807be334
4 changed files with 661 additions and 212 deletions

View File

@ -39,6 +39,25 @@ def TimerStop():
g_time_delta = g_time_end - g_time_start
print(g_time_delta)
"""
Welcome to the "core" PySimpleGUI code....
It's a mess.... really... it's a mess internally... it's the external-facing interfaces that
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.
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
that you'll pass on this package, then that's your right and I invite you to do so. However, if
perhaps you're a practical thinker where it's the results that matter, then you'll have no
trouble with this code base. There is consisency however.
I truly hope you get a lot of enjoyment out of using PySimpleGUI. It came from good intentions.
"""
# ----====----====----==== Constants the user CAN safely change ====----====----====----#
DEFAULT_WINDOW_ICON = 'default_icon.ico'
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
@ -61,8 +80,12 @@ PURPLES = ("#480656","#4F2398","#380474")
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
YELLOWS = ("#F3FB62", "#F0F595")
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]), ('#000000','#FFFFFF'),('#FFFFFF', '#000000'), (YELLOWS[0], PURPLES[1]),
(YELLOWS[0], GREENS[3]), (YELLOWS[0], BLUES[2]))
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]),
('#000000','#FFFFFF'),
('#FFFFFF', '#000000'),
(YELLOWS[0], PURPLES[1]),
(YELLOWS[0], GREENS[3]),
(YELLOWS[0], BLUES[2]))
COLOR_SYSTEM_DEFAULT = '1234567890' # Colors should never be this long
if sys.platform == 'darwin':
@ -2282,6 +2305,21 @@ class Table(Element):
return
def Update(self, values=None):
if values is not None:
self.TKTreeview.delete(*self.TKTreeview.get_children())
for i, value in enumerate(self.Values):
if self.DisplayRowNumbers:
value = [i] + value
id = self.TKTreeview.insert('', 'end', text=value, values=value, tag=i % 2)
if i == 4:
break
if self.AlternatingRowColor is not None:
self.TKTreeview.tag_configure(1, background=self.AlternatingRowColor)
self.Values = values
def treeview_selected(self, event):
selections = self.TKTreeview.selection()
self.SelectedRows = [int(x[1:], 16)-1 for x in selections]
@ -2294,6 +2332,7 @@ class Table(Element):
# print('Selected item iid: %s' % iid)
# #self.process_directory(iid, path)
def __del__(self):
super().__del__()

View File

@ -51,6 +51,25 @@ def TimerStop():
g_time_delta = g_time_end - g_time_start
print(g_time_delta)
"""
Welcome to the "core" PySimpleGUI code....
It's a mess.... really... it's a mess internally... it's the external-facing interfaces that
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.
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
that you'll pass on this package, then that's your right and I invite you to do so. However, if
perhaps you're a practical thinker where it's the results that matter, then you'll have no
trouble with this code base. There is consisency however.
I truly hope you get a lot of enjoyment out of using PySimpleGUI. It came from good intentions.
"""
# ----====----====----==== Constants the user CAN safely change ====----====----====----#
DEFAULT_WINDOW_ICON = 'default_icon.ico'
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
@ -73,8 +92,12 @@ PURPLES = ("#480656","#4F2398","#380474")
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
YELLOWS = ("#F3FB62", "#F0F595")
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]), ('#000000','#FFFFFF'),('#FFFFFF', '#000000'), (YELLOWS[0], PURPLES[1]),
(YELLOWS[0], GREENS[3]), (YELLOWS[0], BLUES[2]))
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]),
('#000000','#FFFFFF'),
('#FFFFFF', '#000000'),
(YELLOWS[0], PURPLES[1]),
(YELLOWS[0], GREENS[3]),
(YELLOWS[0], BLUES[2]))
COLOR_SYSTEM_DEFAULT = '1234567890' # Colors should never be this long
if sys.platform == 'darwin':
@ -435,13 +458,12 @@ class Element(object):
# Input Class #
# ---------------------------------------------------------------------- #
class InputText(Element):
def __init__(self, default_text ='', size=(None, None), disabled=False, auto_size_text=None, password_char='',
def __init__(self, default_text ='', size=(None, None), disabled=False, password_char='',
justification=None, background_color=None, text_color=None, font=None, tooltip=None, do_not_clear=False, key=None, focus=False, pad=None):
'''
Input a line of text Element
:param default_text: Default value to display
:param size: Size of field in characters
:param auto_size_text: True if should shrink field to fit the default text
:param password_char: If non-blank, will display this character for every character typed
:param background_color: Color for Element. Text or RGB Hex
'''
@ -453,7 +475,7 @@ class InputText(Element):
self.do_not_clear = do_not_clear
self.Justification = justification
self.Disabled = disabled
super().__init__(ELEM_TYPE_INPUT_TEXT, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad, font=font, tooltip=tooltip)
super().__init__(ELEM_TYPE_INPUT_TEXT, size=size, background_color=bg, text_color=fg, key=key, pad=pad, font=font, tooltip=tooltip)
def Update(self, value=None, disabled=None):
@ -482,7 +504,7 @@ Input = InputText
# Combo #
# ---------------------------------------------------------------------- #
class InputCombo(Element):
def __init__(self, values, default_value=None, size=(None, None), auto_size_text=None, background_color=None, text_color=None, change_submits=False, disabled=False, key=None, pad=None, tooltip=None):
def __init__(self, values, default_value=None, size=(None, None), auto_size_text=None, background_color=None, text_color=None, change_submits=False, disabled=False, key=None, pad=None, tooltip=None, readonly=False):
'''
Input Combo Box Element (also called Dropdown box)
:param values:
@ -494,14 +516,15 @@ class InputCombo(Element):
self.DefaultValue = default_value
self.ChangeSubmits = change_submits
self.TKCombo = None
self.InitializeAsDisabled = disabled
# self.InitializeAsDisabled = disabled
self.Disabled = disabled
self.Readonly=readonly
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
super().__init__(ELEM_TYPE_INPUT_COMBO, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad, tooltip=tooltip)
def Update(self, value=None, values=None, set_to_index=None, disabled=None):
def Update(self, value=None, values=None, set_to_index=None, disabled=None, readonly=None):
if values is not None:
try:
self.TKCombo['values'] = values
@ -526,6 +549,10 @@ class InputCombo(Element):
self.TKCombo['state'] = 'disable'
elif disabled == False:
self.TKCombo['state'] = 'enable'
if readonly is not None:
self.Readonly = readonly
if self.Readonly:
self.TKCombo['state']='readonly'
def __del__(self):
@ -1326,7 +1353,6 @@ class ProgressBar(Element):
self.Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF
self.BarExpired = False
super().__init__(ELEM_TYPE_PROGRESS_BAR, size=size, auto_size_text=auto_size_text, key=key, pad=pad)
return
# returns False if update failed
def UpdateBar(self, current_count, max=None):
@ -1802,7 +1828,7 @@ class TabGroup(Element):
# Slider #
# ---------------------------------------------------------------------- #
class Slider(Element):
def __init__(self, range=(None,None), default_value=None, resolution=None, tick_interval=None, orientation=None, border_width=None, relief=None, change_submits=False, disabled=False, size=(None, None), font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
def __init__(self, range=(None,None), default_value=None, resolution=None, tick_interval=None, orientation=None, border_width=None, relief=None, change_submits=False, disabled=False, size=(None,None), font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None):
'''
Slider Element
:param range:
@ -1831,8 +1857,11 @@ class Slider(Element):
self.ChangeSubmits = change_submits
self.Disabled = disabled
self.TickInterval = tick_interval
temp_size = size
if temp_size == (None, None):
temp_size = (20,20) if orientation.startswith('h') else (8,20)
super().__init__(ELEM_TYPE_INPUT_SLIDER, size=size, font=font, background_color=background_color, text_color=text_color, key=key, pad=pad, tooltip=tooltip)
super().__init__(ELEM_TYPE_INPUT_SLIDER, size=temp_size, font=font, background_color=background_color, text_color=text_color, key=key, pad=pad, tooltip=tooltip)
return
def Update(self, value=None, range=(None, None), disabled=None):
@ -2282,11 +2311,40 @@ class Table(Element):
self.NumRows = num_rows if num_rows is not None else size[1]
self.TKTreeview = None
self.AlternatingRowColor = alternating_row_color
self.SelectedRows = []
super().__init__(ELEM_TYPE_TABLE, text_color=text_color, background_color=background_color, font=font, size=size, pad=pad, key=key, tooltip=tooltip)
return
def Update(self, values=None):
if values is not None:
self.TKTreeview.delete(*self.TKTreeview.get_children())
for i, value in enumerate(self.Values):
if self.DisplayRowNumbers:
value = [i] + value
id = self.TKTreeview.insert('', 'end', text=value, values=value, tag=i % 2)
if i == 4:
break
if self.AlternatingRowColor is not None:
self.TKTreeview.tag_configure(1, background=self.AlternatingRowColor)
self.Values = values
def treeview_selected(self, event):
selections = self.TKTreeview.selection()
self.SelectedRows = [int(x[1:], 16)-1 for x in selections]
# ttk.Treeview.selection
# print(select)
# self.TKTreeview.TreeSelection.get_selected_rows()
#
# iid = self.TKTreeview.focus()
# # item = self.Values[iid]
# print('Selected item iid: %s' % iid)
# #self.process_directory(iid, path)
def __del__(self):
super().__del__()
@ -2485,6 +2543,9 @@ class Window(object):
self.ForceTopLevel = force_toplevel
self.Resizable = resizable
self._AlphaChannel = alpha_channel
self.Timeout = None
self.TimeoutKey = '_timeout_'
self.TimerCancelled = False
# ------------------------- Add ONE Row to Form ------------------------- #
def AddRow(self, *args):
@ -2553,10 +2614,15 @@ class Window(object):
return self.ReturnValues
# ------------------------- SetIcon - set the window's fav icon ------------------------- #
def SetIcon(self, icon):
self.WindowIcon = icon
def SetIcon(self, icon=None, pngbase64=None):
if pngbase64 != None:
img = tkinter.PhotoImage(data=pngbase64)
wicon = img
else:
wicon = icon
self.WindowIcon = wicon
try:
self.TKroot.iconbitmap(icon)
self.TKroot.iconbitmap(wicon)
except: pass
def _GetElementAtLocation(self, location):
@ -2582,7 +2648,19 @@ class Window(object):
pass
def Read(self):
def _TimeoutAlarmCallback(self):
# first, get the results table built
# modify the Results table in the parent FlexForm object
if self.TimerCancelled:
return
self.LastButtonClicked = self.TimeoutKey
self.FormRemainedOpen = True
self.TKroot.quit() # kick the users out of the mainloop
def Read(self, timeout=None, timeout_key='_timeout_'):
self.Timeout = timeout
self.TimeoutKey = timeout_key
self.NonBlocking = False
if self.TKrootDestroyed:
return None, None
@ -2590,7 +2668,11 @@ class Window(object):
self.Show()
else:
InitializeResults(self)
if timeout != None:
self.TimerCancelled = False
self.TKAfterID = self.TKroot.after(timeout, self._TimeoutAlarmCallback)
self.TKroot.mainloop()
self.TimerCancelled = True
if self.RootNeedsDestroying:
self.TKroot.destroy()
_my_windows.Decrement()
@ -2652,15 +2734,17 @@ class Window(object):
element = _FindElementFromKeyInSubForm(self, key)
if element is None:
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
if element is None:
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
PopupError('Keyword error in FindElement Call',
'Bad key = {}'.format(key),
'Your bad line of code may resemble this:',
'window.FindElement("{}")'.format(key))
PopupError('Keyword error in FindElement Call',
'Bad key = {}'.format(key),
'Your bad line of code may resemble this:',
'window.FindElement("{}")'.format(key))
return ErrorElement(key=key)
return element
def FindElementWithFocus(self):
element = _FindElementWithFocusInSubForm(self)
return element
def SaveToDisk(self, filename):
try:
results = BuildResults(self, False, self)
@ -3088,19 +3172,31 @@ def BuildResultsForSubform(form, initialize_only, top_level_form):
value = tab_key
except:
value = None
elif element.Type == ELEM_TYPE_TABLE:
value = element.SelectedRows
else:
value = None
# if an input type element, update the results
if element.Type != ELEM_TYPE_BUTTON and element.Type != ELEM_TYPE_TEXT and element.Type != ELEM_TYPE_IMAGE and\
element.Type != ELEM_TYPE_OUTPUT and element.Type != ELEM_TYPE_PROGRESS_BAR and \
element.Type!= ELEM_TYPE_COLUMN and element.Type != ELEM_TYPE_FRAME \
if element.Type != ELEM_TYPE_BUTTON and \
element.Type != ELEM_TYPE_TEXT and \
element.Type != ELEM_TYPE_IMAGE and\
element.Type != ELEM_TYPE_OUTPUT and \
element.Type != ELEM_TYPE_PROGRESS_BAR and \
element.Type!= ELEM_TYPE_COLUMN and \
element.Type != ELEM_TYPE_FRAME \
and element.Type != ELEM_TYPE_TAB:
AddToReturnList(form, value)
AddToReturnDictionary(top_level_form, element, value)
elif (element.Type == ELEM_TYPE_BUTTON and element.BType == BUTTON_TYPE_CALENDAR_CHOOSER and element.Target == (None,None)) or \
(element.Type == ELEM_TYPE_BUTTON and element.BType == BUTTON_TYPE_COLOR_CHOOSER and element.Target == (None,None)) or \
(element.Type == ELEM_TYPE_BUTTON and element.Key is not None and (element.BType in (BUTTON_TYPE_SAVEAS_FILE, BUTTON_TYPE_BROWSE_FILE, BUTTON_TYPE_BROWSE_FILES, BUTTON_TYPE_BROWSE_FOLDER))):
elif (element.Type == ELEM_TYPE_BUTTON and
element.BType == BUTTON_TYPE_CALENDAR_CHOOSER and
element.Target == (None,None)) or \
(element.Type == ELEM_TYPE_BUTTON and
element.BType == BUTTON_TYPE_COLOR_CHOOSER and
element.Target == (None,None)) or \
(element.Type == ELEM_TYPE_BUTTON
and element.Key is not None and
(element.BType in (BUTTON_TYPE_SAVEAS_FILE, BUTTON_TYPE_BROWSE_FILE, BUTTON_TYPE_BROWSE_FILES, BUTTON_TYPE_BROWSE_FOLDER))):
AddToReturnList(form, value)
AddToReturnDictionary(top_level_form, element, value)
@ -3185,6 +3281,29 @@ def _FindElementFromKeyInSubForm(form, key):
return element
def _FindElementWithFocusInSubForm(form):
for row_num, row in enumerate(form.Rows):
for col_num, element in enumerate(row):
if element.Type == ELEM_TYPE_COLUMN:
matching_elem = _FindElementWithFocusInSubForm(element)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_FRAME:
matching_elem = _FindElementWithFocusInSubForm(element)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_TAB_GROUP:
matching_elem = _FindElementWithFocusInSubForm(element)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_TAB:
matching_elem = _FindElementWithFocusInSubForm(element)
if matching_elem is not None:
return matching_elem
if element.Type == ELEM_TYPE_INPUT_TEXT:
if element.TKEntry is not None:
if element.TKEntry is element.TKEntry.focus_get():
return element
if sys.version_info[0] >= 3:
def AddMenuItem(top_menu, sub_menu_info, element, is_sub_menu=False, skip=False):
@ -3501,8 +3620,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKCombo.configure(height=element.Size[1])
# element.TKCombo['state']='readonly'
element.TKCombo['values'] = element.Values
if element.InitializeAsDisabled:
element.TKCombo['state'] = 'disabled'
# if element.InitializeAsDisabled:
# element.TKCombo['state'] = 'disabled'
# if element.BackgroundColor is not None:
# element.TKCombo.configure(background=element.BackgroundColor)
element.TKCombo.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1])
@ -3515,7 +3635,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKCombo.current(0)
if element.ChangeSubmits:
element.TKCombo.bind('<<ComboboxSelected>>', element.ComboboxSelectHandler)
if element.Disabled == True:
if element.Readonly:
element.TKCombo['state']='readonly'
if element.Disabled is True: # note overrides readonly if disabled
element.TKCombo['state'] = 'disabled'
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
@ -3921,6 +4043,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
width = element.DefaultColumnWidth
treeview.column(heading, width=width*CharWidthInPixels(), anchor=anchor)
# Insert values into the tree
for i, value in enumerate(element.Values):
if element.DisplayRowNumbers:
value = [i] + value
@ -3932,6 +4055,8 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
tkinter.ttk.Style().configure("Treeview", foreground=element.TextColor)
# scrollable_frame.pack(side=tk.LEFT, padx=element.Pad[0], pady=element.Pad[1], expand=True, fill='both')
treeview.bind("<<TreeviewSelect>>", element.treeview_selected)
element.TKTreeview.pack(side=tk.LEFT,expand=True, padx=0, pady=0, fill='both')
if element.Tooltip is not None:
element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
@ -4089,12 +4214,16 @@ def StartupTK(my_flex_form):
if my_flex_form.AutoClose:
duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form.AutoCloseDuration is None else my_flex_form.AutoCloseDuration
my_flex_form.TKAfterID = root.after(duration * 1000, my_flex_form._AutoCloseAlarmCallback)
if my_flex_form.Timeout != None:
my_flex_form.TKAfterID = root.after(my_flex_form.Timeout, my_flex_form._TimeoutAlarmCallback)
if my_flex_form.NonBlocking:
pass
# my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback())
else: # it's a blocking form
# print('..... CALLING MainLoop')
my_flex_form.TKroot.mainloop()
my_flex_form.TimerCancelled = True
# print('..... BACK from MainLoop')
if not my_flex_form.FormRemainedOpen:
_my_windows.Decrement()
@ -4955,79 +5084,211 @@ LOOK_AND_FEEL_TABLE = {'SystemDefault':
'PROGRESS_DEPTH': 0},
'Dark2': {'BACKGROUND': 'gray25', 'TEXT': 'white', 'INPUT': 'white',
'TEXT_INPUT': 'black', 'SCROLL': 'gray44', 'BUTTON': ('white', '#004F00'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Black': {'BACKGROUND': 'black', 'TEXT': 'white', 'INPUT': 'gray30',
'TEXT_INPUT': 'white', 'SCROLL': 'gray44', 'BUTTON': ('black', 'white'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Tan': {'BACKGROUND': '#fdf6e3', 'TEXT': '#268bd1', 'INPUT': '#eee8d5',
'TEXT_INPUT': '#6c71c3', 'SCROLL': '#eee8d5', 'BUTTON': ('white', '#063542'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'Dark2': {'BACKGROUND': 'gray25',
'TEXT': 'white',
'INPUT': 'white',
'TEXT_INPUT': 'black',
'SCROLL': 'gray44',
'BUTTON': ('white', '#004F00'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'TanBlue': {'BACKGROUND': '#e5dece', 'TEXT': '#063289', 'INPUT': '#f9f8f4',
'TEXT_INPUT': '#242834', 'SCROLL': '#eee8d5', 'BUTTON': ('white', '#063289'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'Black': {'BACKGROUND': 'black',
'TEXT': 'white',
'INPUT': 'gray30',
'TEXT_INPUT': 'white',
'SCROLL': 'gray44',
'BUTTON': ('black', 'white'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Tan': {'BACKGROUND': '#fdf6e3',
'TEXT': '#268bd1',
'INPUT': '#eee8d5',
'TEXT_INPUT': '#6c71c3',
'SCROLL': '#eee8d5',
'BUTTON': ('white', '#063542'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkTanBlue': {'BACKGROUND': '#242834', 'TEXT': '#dfe6f8', 'INPUT': '#97755c',
'TEXT_INPUT': 'white', 'SCROLL': '#a9afbb', 'BUTTON': ('white', '#063289'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'TanBlue': {'BACKGROUND': '#e5dece',
'TEXT': '#063289',
'INPUT': '#f9f8f4',
'TEXT_INPUT': '#242834',
'SCROLL': '#eee8d5',
'BUTTON': ('white', '#063289'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkAmber': {'BACKGROUND': '#2c2825', 'TEXT': '#fdcb52', 'INPUT': '#705e52',
'TEXT_INPUT': '#fdcb52', 'SCROLL': '#705e52', 'BUTTON': ('black', '#fdcb52'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'DarkTanBlue': {'BACKGROUND': '#242834',
'TEXT': '#dfe6f8',
'INPUT': '#97755c',
'TEXT_INPUT': 'white',
'SCROLL': '#a9afbb',
'BUTTON': ('white', '#063289'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkAmber': {'BACKGROUND': '#2c2825',
'TEXT': '#fdcb52',
'INPUT': '#705e52',
'TEXT_INPUT': '#fdcb52',
'SCROLL': '#705e52',
'BUTTON': ('black', '#fdcb52'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkBlue': {'BACKGROUND': '#1a2835',
'TEXT': '#d1ecff',
'INPUT': '#335267',
'TEXT_INPUT': '#acc2d0',
'SCROLL': '#1b6497',
'BUTTON': ('black', '#fafaf8'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'DarkBlue': {'BACKGROUND': '#1a2835', 'TEXT': '#d1ecff', 'INPUT': '#335267',
'TEXT_INPUT': '#acc2d0', 'SCROLL': '#1b6497', 'BUTTON': ('black', '#fafaf8'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Reds': {'BACKGROUND': '#280001', 'TEXT': 'white', 'INPUT': '#d8d584',
'TEXT_INPUT': 'black', 'SCROLL': '#763e00', 'BUTTON': ('black', '#daad28'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'Reds': {'BACKGROUND': '#280001',
'TEXT': 'white',
'INPUT': '#d8d584',
'TEXT_INPUT': 'black',
'SCROLL': '#763e00',
'BUTTON': ('black', '#daad28'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Green': {'BACKGROUND': '#82a459', 'TEXT': 'black', 'INPUT': '#d8d584',
'TEXT_INPUT': 'black', 'SCROLL': '#e3ecf3', 'BUTTON': ('white', '#517239'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'Green': {'BACKGROUND': '#82a459',
'TEXT': 'black',
'INPUT': '#d8d584',
'TEXT_INPUT': 'black',
'SCROLL': '#e3ecf3',
'BUTTON': ('white', '#517239'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH': 0,
'PROGRESS_DEPTH': 0},
'BluePurple': {'BACKGROUND' : '#A5CADD', 'TEXT': '#6E266E', 'INPUT':'#E0F5FF','TEXT_INPUT' : 'black', 'SCROLL': '#E0F5FF','BUTTON': ('white', '#303952'),'PROGRESS':DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BluePurple': {'BACKGROUND' : '#A5CADD',
'TEXT': '#6E266E',
'INPUT':'#E0F5FF',
'TEXT_INPUT' : 'black',
'SCROLL': '#E0F5FF',
'BUTTON': ('white', '#303952'),
'PROGRESS':DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'Purple': {'BACKGROUND': '#B0AAC2', 'TEXT': 'black', 'INPUT': '#F2EFE8','SCROLL': '#F2EFE8','TEXT_INPUT' : 'black',
'BUTTON': ('black', '#C2D4D8'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'Purple': {'BACKGROUND': '#B0AAC2',
'TEXT': 'black',
'INPUT': '#F2EFE8',
'SCROLL': '#F2EFE8',
'TEXT_INPUT' : 'black',
'BUTTON': ('black', '#C2D4D8'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'BlueMono': {'BACKGROUND': '#AAB6D3', 'TEXT': 'black', 'INPUT': '#F1F4FC','SCROLL': '#F1F4FC','TEXT_INPUT' : 'black',
'BUTTON': ('white', '#7186C7'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BlueMono': {'BACKGROUND': '#AAB6D3',
'TEXT': 'black',
'INPUT': '#F1F4FC',
'SCROLL': '#F1F4FC',
'TEXT_INPUT' : 'black',
'BUTTON': ('white', '#7186C7'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'GreenMono': {'BACKGROUND': '#A8C1B4', 'TEXT': 'black', 'INPUT': '#DDE0DE', 'SCROLL': '#E3E3E3','TEXT_INPUT' : 'black',
'BUTTON': ('white', '#6D9F85'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'GreenMono': {'BACKGROUND': '#A8C1B4',
'TEXT': 'black',
'INPUT': '#DDE0DE',
'SCROLL': '#E3E3E3',
'TEXT_INPUT' : 'black',
'BUTTON': ('white', '#6D9F85'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'BrownBlue': {'BACKGROUND': '#64778d', 'TEXT': 'white', 'INPUT': '#f0f3f7', 'SCROLL': '#A6B2BE','TEXT_INPUT' : 'black', 'BUTTON': ('white', '#283b5b'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BrownBlue': {'BACKGROUND': '#64778d',
'TEXT': 'white',
'INPUT': '#f0f3f7',
'SCROLL': '#A6B2BE',
'TEXT_INPUT' : 'black',
'BUTTON': ('white', '#283b5b'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'BrightColors': {'BACKGROUND': '#b4ffb4', 'TEXT': 'black', 'INPUT': '#ffff64','SCROLL': '#ffb482','TEXT_INPUT' : 'black', 'BUTTON': ('black', '#ffa0dc'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'BrightColors': {'BACKGROUND': '#b4ffb4',
'TEXT': 'black',
'INPUT': '#ffff64',
'SCROLL': '#ffb482',
'TEXT_INPUT' : 'black',
'BUTTON': ('black', '#ffa0dc'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'NeutralBlue': {'BACKGROUND': '#92aa9d', 'TEXT': 'black', 'INPUT': '#fcfff6',
'SCROLL': '#fcfff6', 'TEXT_INPUT': 'black', 'BUTTON': ('black', '#d0dbbd'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'NeutralBlue': {'BACKGROUND': '#92aa9d',
'TEXT': 'black',
'INPUT': '#fcfff6',
'SCROLL': '#fcfff6',
'TEXT_INPUT': 'black',
'BUTTON': ('black', '#d0dbbd'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'Kayak': {'BACKGROUND': '#a7ad7f', 'TEXT': 'black', 'INPUT': '#e6d3a8',
'SCROLL': '#e6d3a8', 'TEXT_INPUT': 'black', 'BUTTON': ('white', '#5d907d'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'Kayak': {'BACKGROUND': '#a7ad7f',
'TEXT': 'black',
'INPUT': '#e6d3a8',
'SCROLL': '#e6d3a8',
'TEXT_INPUT': 'black',
'BUTTON': ('white', '#5d907d'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'SandyBeach': {'BACKGROUND': '#efeccb', 'TEXT': '#012f2f', 'INPUT': '#e6d3a8',
'SCROLL': '#e6d3a8', 'TEXT_INPUT': '#012f2f', 'BUTTON': ('white', '#046380'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
'SandyBeach': {'BACKGROUND': '#efeccb',
'TEXT': '#012f2f',
'INPUT': '#e6d3a8',
'SCROLL': '#e6d3a8',
'TEXT_INPUT': '#012f2f',
'BUTTON': ('white', '#046380'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0},
'TealMono': {'BACKGROUND': '#a8cfdd', 'TEXT': 'black', 'INPUT': '#dfedf2','SCROLL': '#dfedf2', 'TEXT_INPUT' : 'black', 'BUTTON': ('white', '#183440'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0}
'TealMono': {'BACKGROUND': '#a8cfdd',
'TEXT': 'black',
'INPUT': '#dfedf2','SCROLL': '#dfedf2',
'TEXT_INPUT' : 'black',
'BUTTON': ('white', '#183440'),
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
'BORDER': 1,
'SLIDER_DEPTH':0,
'PROGRESS_DEPTH':0}
}
def ListOfLookAndFeelValues():
@ -5724,15 +5985,14 @@ def PopupYesNo(*args, **_3to2kwargs):
def main():
window = Window('Demo window..')
window_rows = [[Text('You are running the PySimpleGUI.py file itself')],
layout = [[Text('You are running the PySimpleGUI.py file itself')],
[Text('You should be importing it rather than running it', size=(50,2))],
[Text('Here is your sample input window....')],
[Text('Source Folder', size=(15, 1), justification='right'), InputText('Source', focus=True),FolderBrowse()],
[Text('Destination Folder', size=(15, 1), justification='right'), InputText('Dest'), FolderBrowse()],
[Ok(), Cancel()]]
button, (source, dest) = window.LayoutAndRead(window_rows)
button, values = Window('Demo window..').Layout(layout).Read()
if __name__ == '__main__':

View File

@ -23,9 +23,9 @@
## Now supports both Python 2.7 & 3
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.9.4-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.10.1-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.1.4-blue.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.2.1-blue.svg?longCache=true&style=for-the-badge)
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -1202,7 +1202,8 @@ This is the definition of the Window object:
auto_close=False,
auto_close_duration=DEFAULT_AUTOCLOSE_TIME,
icon=DEFAULT_WINDOW_ICON,
force_toplevel=False
force_toplevel=False,
alpha_channel=1,
return_keyboard_events=False,
use_default_focus=True,
text_justification=None,
@ -1227,7 +1228,8 @@ Parameter Descriptions. You will find these same parameters specified for each
auto_close - Bool. If True window will autoclose
auto_close_duration - Duration in seconds before window closes
icon - .ICO file that will appear on the Task Bar and end of Title Bar
force_top_level - Bool. If set causes a tk.Tk window to be used as primary window rather than tk.TopLevel. Used to get around Matplotlib problem
force_top_level - Bool. If set causes a tk.Tk window to be used as primary window rather than tk.TopLevel. Used to get around Matplotlib problem
alpha_channel - Float 0 to 1. 0 is invisible, 1 is fully visible, Anything between will be semi-transparent
return_keyboard_events - if True key presses are returned as buttons
use_default_focus - if True and no focus set, then automatically set a focus
text_justification - Justification to use for Text Elements in this window
@ -1280,7 +1282,7 @@ There are a few methods (functions) that you will see in this document that act
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
window.Read() - Read the Windows values and get the button / key that caused the Read to return. Can have an optional timeout
window.ReadNonBlocking() - Same as Read but will return right away
window.Refresh() - Use if updating elements and want to show the updates prior to the nex Read
window.Fill(values_dict) - Fill each Element with entry from the dictionary passed in
@ -1289,7 +1291,10 @@ There are a few methods (functions) that you will see in this document that act
window.CloseNonBlocking() - When done, for good, reading a non-blocking window
window.Disable() - Use to disable the window inpurt when opening another window on top of the primnary Window
window.Enable() - Re-enable a Disabled window
window.FindElement(key) - Returns the element that has a matching key value
window.FindElement(key) - 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 Methods
@ -1306,20 +1311,20 @@ window = sg.Window('My window title').Layout(layout)
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.
#### Read()
#### Read(timeout=None, timeout_key='__timeout_ _ ')
Read the Window's input values and button clicks in a blocking-fashion
Returns event, values
Returns event, values. Adding a timeout can be achieved by setting timeout=number of milliseconds before the Read times out after which a "timeout event" is returned. The value of timeout_key will be returned as the event.
#### ReadNonBlocking()
Read the Window's input values and button clicks but without blocking. It will immediately return.
Read the Window's input values and button clicks but without blocking. It will immediately return. **Consider using Read with timeout instead!**
#### Refresh()
Cause changes to the window to be displayed on the screen. Normally not needed unless the changes are immediately required or if it's going to be a while before another call to Read.
#### SetIcon(icon)
Sets the window's icon that will be shown on the titlebar.
#### SetIcon(icon, pngbase64)
Sets the window's icon that will be shown on the titlebar. Can either be a filename or a base64 string.
#### Fill(values_dict)
Populates the windows fields with the values shown in the dictionary.
@ -1328,6 +1333,10 @@ Populates the windows fields with the values shown in the dictionary.
Rerturns the Element that has a matching key. If the key is not found, an Error Element is returned so that the program will not crash should the user try to perform an "update". A Popup message will be shown
#### FindElementWithFocus()
Returns the Element that currently has the focus. Returns None if no Elements were found.
#### SaveToDisk(filename)
Saves the window's values to disk
@ -1672,7 +1681,8 @@ Also known as a drop-down list. Only required parameter is the list of choices.
auto_size_text=None
background_color=None
text_color=None
change_submits=False
change_submits=False
readonly=True
disabled=False
key=None
pad=None
@ -1686,7 +1696,8 @@ Also known as a drop-down list. Only required parameter is the list of choices.
background_color - color to use for the input field background
text_color - color to use for the typed text
change_submits - Bool. If set causes Read to immediately return if the selected value changes
disabled - Bool. If set will disable changes
disabled - Bool. If set will disable changes
readonly - Bool. If set user cannot change the values to choose from
key - Dictionary key to use for return values
pad - (x,y) Amount of padding to put around element in pixels
tooltip - Text string. If set, hovering over field will popup the text
@ -1695,12 +1706,13 @@ Shortcut functions - Combo, DropDown, Drop
### Combo Methods
```python
Update(value=None, values=None, set_to_index=None, disabled=None)
Update(value=None, values=None, set_to_index=None, disabled=None, readonly=None)
```
value - change which value is current selected
values - change list of choices
set_to_index - change selection to a particular choice
disable - if True will disable element
readonly - if True will make element readonly
## Listbox Element
The standard listbox like you'll find in most GUIs. Note that the return values from this element will be a ***list of results, not a single result***. This is because the user can select more than 1 item from the list (if you set the right mode).
@ -2198,7 +2210,7 @@ All buttons can have their text changed by changing the `button_text` variable i
**Button Images**
Now this is an exciting feature not found in many simplified packages.... images on buttons! You can make a pretty spiffy user interface with the help of a few button images.
Your button images need to be in PNG or GIF format. When you make a button with an image, set the button background to the same color as the background. There's a button color TRANSPARENT_BUTTON that you can set your button color to in order for it to blend into the background. Note that this value is currently the same as the color as the default system background on Windows.
Your button images need to be in PNG or GIF format. When you make a button with an image, set the button background to the same color as the background. There's a button color TRANSPARENT_BUTTON that you can set your button color to in order for it to blend into the background. Note that this value is currently the same as the color as the default system background on Windows. If you want to set the button background color to the current system default, use the value COLOR_SYSTEM_DEFAULT as the background color.
This example comes from the `Demo Media Player.py` example program. Because it's a non-blocking button, it's defined as `RButton`. You also put images on blocking buttons by using `Button`.
@ -2613,7 +2625,18 @@ Let me say up front that the Table Element has Beta status. The reason is that s
key - key used to lookup element
tooltip - tooltip text
### Read return values from Table Element
The values returned from a `Window.Read` or `Window.ReadNonBlocking` call for the Tree Element are a list of row numbers that are currently highlighted.
### Update Call
There is an Update method defined in the code for the Tree Element, however it has not been completely tested so the results are not to be trusted. Use at your own risk... Removing rows from a table seem to work.
```python
def Update(self, values=None):
```
`values` is a table containing your rows just like you passed in when creating the Table Element.
## Tree Element
The Tree Element and Table Element are close cousins. Many of the parameters found in the Table Element apply to Tree Elements. In particular the heading information, column widths, etc.
@ -2873,19 +2896,61 @@ The reasoning behind this is that Persistent Windows are often "forms". When "s
## Asynchronous (Non-Blocking) windows
So you want to be a wizard do ya? Well go boldly!
Use async windows sparingly. It's possible to have a window that appears to be async, but it is not. **Please** try to find other methods before going to async windows. The reason for this plea is that async windows poll tkinter over and over. If you do not have a sleep in your loop, you will eat up 100% of the CPU time.
When to use a non-blocking window:
* A media file player like an MP3 player
* A status dashboard that's periodically updated
* Progress Meters - when you want to make your own progress meters
* Output using print to a scrolled text element. Good for debugging.
If your application doesn't follow the basic design pattern at one of those, then it shouldn't be executed as a non-blocking window.
Not so fast buddy!!
Non-blocking is generally reserved as a "last resort". Too many times I've seen people use non-blocking reads when a blocking read will do just fine.
Release 4.0 ushered in a hybrid approach to non-blocking calls. That compromise came in the form of a read with a timeout. You'll score much higher points on the impressive meter if you're able to use a lot less CPU time by using this type of read.
So you want to be a wizard do ya? Well go boldly! But a good dose of caution is advised.
Use async windows sparingly. It's possible to have a window that appears to be async, but it is not. **Please** try to find other methods before going to async windows. The reason for this plea is that async windows poll tkinter over and over. If you do not have a sleep in your loop, you will eat up 100% of the CPU time. It's important to be a good citizen. Don't chew up CPU cycles needlessly.
The most legit time to use a non-blocking window is when you're working directly with hardware. Maybe you're driving a serial bus. If you look at the Event Loop in the Demo_OpenCV_Webcam.py program, you'll see that the read is a non-blocking read. The point in the loop where you will block is the call to read frames from the webcam. When a frame is available you want to quickly deliver it to the output device, so you don't want your GUI blocking on your.
Another example can be found in the demo for controlling a robot on a Raspberry Pi. In that application you want to read the direction buttons, forward, backward, etc, and immediately take action.
However, even in this application it's not good to simply burn 100% of the CPU time in a tight spin loop. Let's say your call to the GUI takes 10ms. And your robot can react at a rate of 200ms. If you do non-blocking reads without some kind of sleep, you'll be taking 100% of the CPU time needlessly.
Adding a sleep to your event loop will at least give other processes time to execute. It will, however, starve your GUI. The entire time you're sleeping, your GUI isn't executing.
It's for this reason that a new feature was just added in version 4.0, Read with a Timeout. You can thing of this as an event loop, with a sleep, except that during the time the processor is sleeping your GUI is very operational, responding to the user for thinks like checkboxes, sliders, even button pushes will be instant feeling.
## Read(timeout = t, timeout_key='timeout')
The new Hybrid-Read! Read with a timeout is a very good thing for your GUIs to use in a read non-blocking situation, if you can use them. If your device can wait for a little while, then use this kind of read. The longer you're able to add to the timeout value, the less CPU time you'll be taking).
One way of thinking of reads with timeouts:
> During the timeout time, you are "yielding" the processor to do other tasks.
But it gets better than just being a good citizen....**your GUI will be more responsive than if you used a non-blocking read**
Let's say you had a device that you want to "poll" every 100ms. The "easy way out" and the only way out until release 4.0 was this:
```python
while True: # Event Loop
event, values = window.ReadNonBlocking()
read_my_hardware() # process my device here
time.sleep(.1) # sleep 1/10 second
```
This program will quickly test for user input, then deal with the hardware. Then it'll sleep for 100ms, while your gui is non-responsive, then it'll check in with your GUI again. I fully realize this is a crude way of doing things. We're talking dirt simple stuff without trying to use threads, etc to 'get it right'. It's for demonstration purposes.
The new and better way....
using the Read Timeout mechanism, the sleep goes away.
```python
while True: # Event Loop
event, values = window.Read(timeout = 100)
read_my_hardware() # process my device here
```
This event loop will run every 100 ms. You're making a Read call, so anything that the use does will return back to you immediately, and you're waiting up to 100ms for the user to do something. If the user doesn't do anything, then the read will timeout and execution will return to the program.
### Instead of ReadNonBlocking --- Use `change_submits = True` or return_keyboard_events = True
Any time you are thinking "I want an X Element to cause a Y Element to do something", then you want to use the `change_submits` option.
@ -2903,7 +2968,7 @@ One example is you have an input field that changes as you press buttons on an o
### Periodically Calling`ReadNonBlocking`
Periodically "refreshing" the visible GUI. The longer you wait between updates to your GUI the more sluggish your windows will feel. It is up to you to make these calls or your GUI will freeze.
Let's say you do end up using ReadNonBlocking... then you've got some housekeeping to do. It's up to you to periodically "refresh" the visible GUI. The longer you wait between updates to your GUI the more sluggish your windows will feel. It is up to you to make these calls or your GUI will freeze.
There are 2 methods of interacting with non-blocking windows.
1. Read the window just as you would a normal window
@ -2950,42 +3015,38 @@ When you are ready to close the window (assuming the window wasn't closed by the
**Example - Running timer that updates**
See the sample code on the GitHub named Demo Media Player for another example of Async windows. We're going to make a window and update one of the elements of that window every .01 seconds. Here's the entire code to do that.
import PySimpleGUI as sg
import time
import PySimpleGUI as sg
import time
# ---------------- Create Form ----------------
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0, 0))
# window that doesn't block
# Make a window, but don't use context manager
window = sg.Window('Running Timer', auto_size_text=True)
layout = [[sg.Text('')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
[sg.ReadButton('Pause', key='button', button_color=('white', '#001480')),
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
# Create the layout
window_rows = [[sg.Text('Non-blocking GUI with updates')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), key='output') ],
[sg.Button('Quit')]]
# Layout the rows of the window and perform a read. Indicate the window is non-blocking!
window.Layout(window_rows).ReadNonBlocking()
window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout)
#
# Some place later in your code...
# You need to perform a ReadNonBlocking on your window every now and then or
# else it won't refresh
#
# ---------------- main loop ----------------
current_time = 0
paused = False
start_time = int(round(time.time() * 100))
while (True):
# --------- Read and update window --------
event, values = window.Read(timeout=10)
current_time = int(round(time.time() * 100)) - start_time
# --------- Display timer in window --------
window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
(current_time // 100) % 60,
current_time % 100))
for i in range(1, 1000):
window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
event, values = window.ReadNonBlocking()
if values is None or event == 'Quit':
break
time.sleep(.01)
else:
window.CloseNonBlocking()
What we have here is the same sequence of function calls as in the description. Get a window, add rows to it, show the window, and then refresh it every now and then.
The new thing in this example is the call use of the Update method for the Text Element. The first thing we do inside the loop is "update" the text element that we made earlier. This changes the value of the text field on the window. The new value will be displayed when `window.ReadNonBlocking()` is called. if you want to have the window reflect your changes immediately, call `window.Refresh()`.
Note the `else` statement on the for loop. This is needed because we're about to exit the loop while the window is still open. The user has not closed the window using the X nor a button so it's up to the caller to close the window using `CloseNonBlocking`.
Previously this program was implemented using a sleep in the loop to control the clock tick. This version uses the new timeout parameter. The result is a window that reacts quicker then the one with the sleep and the accuracy is just as good.
@ -3382,7 +3443,8 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 03.09.01 | Oct 8, 2018
| 3.9.3 & 1.1.3 | Oct 11, 2018
| 3.9.4 & 1.1.4 | Oct 16, 2018
| 3.10.1 & 1.2.1 | Oct 20, 2018
## Release Notes
@ -3563,6 +3625,19 @@ It's official. There is a 2.7 version of PySimpleGUI!
* BUG fix in listbox double-click. First bug fix in months
* New Look And Feel capability. List predefined settings using ListOfLookAndFeelValues
### 3.10.1 & 1.2.1
* Combobox new readonly parameter in init and Update
* Better default sizes for Slider
* Read of Tables now returns which rows are selected (big damned deal feature)
* PARTIAL support of Table.Update with new values (use at your own peril)
* Alpha channel setting for Windows
* Timeout setting for Window.Read (big damned deal feature)
* Icon can be base64 image now in SetIcon call
* Window.FindElementWithFocus call
* Window.Move allows moving window anywhere on screen
* Window.Minimize will minimize to taskbar
* Button background color can be set to system default (i.e. not changed)
### Upcoming

199
readme.md
View File

@ -23,9 +23,9 @@
## Now supports both Python 2.7 & 3
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.9.4-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.10.1-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.1.4-blue.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.2.1-blue.svg?longCache=true&style=for-the-badge)
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -1202,7 +1202,8 @@ This is the definition of the Window object:
auto_close=False,
auto_close_duration=DEFAULT_AUTOCLOSE_TIME,
icon=DEFAULT_WINDOW_ICON,
force_toplevel=False
force_toplevel=False,
alpha_channel=1,
return_keyboard_events=False,
use_default_focus=True,
text_justification=None,
@ -1227,7 +1228,8 @@ Parameter Descriptions. You will find these same parameters specified for each
auto_close - Bool. If True window will autoclose
auto_close_duration - Duration in seconds before window closes
icon - .ICO file that will appear on the Task Bar and end of Title Bar
force_top_level - Bool. If set causes a tk.Tk window to be used as primary window rather than tk.TopLevel. Used to get around Matplotlib problem
force_top_level - Bool. If set causes a tk.Tk window to be used as primary window rather than tk.TopLevel. Used to get around Matplotlib problem
alpha_channel - Float 0 to 1. 0 is invisible, 1 is fully visible, Anything between will be semi-transparent
return_keyboard_events - if True key presses are returned as buttons
use_default_focus - if True and no focus set, then automatically set a focus
text_justification - Justification to use for Text Elements in this window
@ -1280,7 +1282,7 @@ There are a few methods (functions) that you will see in this document that act
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
window.Read() - Read the Windows values and get the button / key that caused the Read to return. Can have an optional timeout
window.ReadNonBlocking() - Same as Read but will return right away
window.Refresh() - Use if updating elements and want to show the updates prior to the nex Read
window.Fill(values_dict) - Fill each Element with entry from the dictionary passed in
@ -1289,7 +1291,10 @@ There are a few methods (functions) that you will see in this document that act
window.CloseNonBlocking() - When done, for good, reading a non-blocking window
window.Disable() - Use to disable the window inpurt when opening another window on top of the primnary Window
window.Enable() - Re-enable a Disabled window
window.FindElement(key) - Returns the element that has a matching key value
window.FindElement(key) - 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 Methods
@ -1306,20 +1311,20 @@ window = sg.Window('My window title').Layout(layout)
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.
#### Read()
#### Read(timeout=None, timeout_key='__timeout_ _ ')
Read the Window's input values and button clicks in a blocking-fashion
Returns event, values
Returns event, values. Adding a timeout can be achieved by setting timeout=number of milliseconds before the Read times out after which a "timeout event" is returned. The value of timeout_key will be returned as the event.
#### ReadNonBlocking()
Read the Window's input values and button clicks but without blocking. It will immediately return.
Read the Window's input values and button clicks but without blocking. It will immediately return. **Consider using Read with timeout instead!**
#### Refresh()
Cause changes to the window to be displayed on the screen. Normally not needed unless the changes are immediately required or if it's going to be a while before another call to Read.
#### SetIcon(icon)
Sets the window's icon that will be shown on the titlebar.
#### SetIcon(icon, pngbase64)
Sets the window's icon that will be shown on the titlebar. Can either be a filename or a base64 string.
#### Fill(values_dict)
Populates the windows fields with the values shown in the dictionary.
@ -1328,6 +1333,10 @@ Populates the windows fields with the values shown in the dictionary.
Rerturns the Element that has a matching key. If the key is not found, an Error Element is returned so that the program will not crash should the user try to perform an "update". A Popup message will be shown
#### FindElementWithFocus()
Returns the Element that currently has the focus. Returns None if no Elements were found.
#### SaveToDisk(filename)
Saves the window's values to disk
@ -1672,7 +1681,8 @@ Also known as a drop-down list. Only required parameter is the list of choices.
auto_size_text=None
background_color=None
text_color=None
change_submits=False
change_submits=False
readonly=True
disabled=False
key=None
pad=None
@ -1686,7 +1696,8 @@ Also known as a drop-down list. Only required parameter is the list of choices.
background_color - color to use for the input field background
text_color - color to use for the typed text
change_submits - Bool. If set causes Read to immediately return if the selected value changes
disabled - Bool. If set will disable changes
disabled - Bool. If set will disable changes
readonly - Bool. If set user cannot change the values to choose from
key - Dictionary key to use for return values
pad - (x,y) Amount of padding to put around element in pixels
tooltip - Text string. If set, hovering over field will popup the text
@ -1695,12 +1706,13 @@ Shortcut functions - Combo, DropDown, Drop
### Combo Methods
```python
Update(value=None, values=None, set_to_index=None, disabled=None)
Update(value=None, values=None, set_to_index=None, disabled=None, readonly=None)
```
value - change which value is current selected
values - change list of choices
set_to_index - change selection to a particular choice
disable - if True will disable element
readonly - if True will make element readonly
## Listbox Element
The standard listbox like you'll find in most GUIs. Note that the return values from this element will be a ***list of results, not a single result***. This is because the user can select more than 1 item from the list (if you set the right mode).
@ -2198,7 +2210,7 @@ All buttons can have their text changed by changing the `button_text` variable i
**Button Images**
Now this is an exciting feature not found in many simplified packages.... images on buttons! You can make a pretty spiffy user interface with the help of a few button images.
Your button images need to be in PNG or GIF format. When you make a button with an image, set the button background to the same color as the background. There's a button color TRANSPARENT_BUTTON that you can set your button color to in order for it to blend into the background. Note that this value is currently the same as the color as the default system background on Windows.
Your button images need to be in PNG or GIF format. When you make a button with an image, set the button background to the same color as the background. There's a button color TRANSPARENT_BUTTON that you can set your button color to in order for it to blend into the background. Note that this value is currently the same as the color as the default system background on Windows. If you want to set the button background color to the current system default, use the value COLOR_SYSTEM_DEFAULT as the background color.
This example comes from the `Demo Media Player.py` example program. Because it's a non-blocking button, it's defined as `RButton`. You also put images on blocking buttons by using `Button`.
@ -2613,7 +2625,18 @@ Let me say up front that the Table Element has Beta status. The reason is that s
key - key used to lookup element
tooltip - tooltip text
### Read return values from Table Element
The values returned from a `Window.Read` or `Window.ReadNonBlocking` call for the Tree Element are a list of row numbers that are currently highlighted.
### Update Call
There is an Update method defined in the code for the Tree Element, however it has not been completely tested so the results are not to be trusted. Use at your own risk... Removing rows from a table seem to work.
```python
def Update(self, values=None):
```
`values` is a table containing your rows just like you passed in when creating the Table Element.
## Tree Element
The Tree Element and Table Element are close cousins. Many of the parameters found in the Table Element apply to Tree Elements. In particular the heading information, column widths, etc.
@ -2873,19 +2896,61 @@ The reasoning behind this is that Persistent Windows are often "forms". When "s
## Asynchronous (Non-Blocking) windows
So you want to be a wizard do ya? Well go boldly!
Use async windows sparingly. It's possible to have a window that appears to be async, but it is not. **Please** try to find other methods before going to async windows. The reason for this plea is that async windows poll tkinter over and over. If you do not have a sleep in your loop, you will eat up 100% of the CPU time.
When to use a non-blocking window:
* A media file player like an MP3 player
* A status dashboard that's periodically updated
* Progress Meters - when you want to make your own progress meters
* Output using print to a scrolled text element. Good for debugging.
If your application doesn't follow the basic design pattern at one of those, then it shouldn't be executed as a non-blocking window.
Not so fast buddy!!
Non-blocking is generally reserved as a "last resort". Too many times I've seen people use non-blocking reads when a blocking read will do just fine.
Release 4.0 ushered in a hybrid approach to non-blocking calls. That compromise came in the form of a read with a timeout. You'll score much higher points on the impressive meter if you're able to use a lot less CPU time by using this type of read.
So you want to be a wizard do ya? Well go boldly! But a good dose of caution is advised.
Use async windows sparingly. It's possible to have a window that appears to be async, but it is not. **Please** try to find other methods before going to async windows. The reason for this plea is that async windows poll tkinter over and over. If you do not have a sleep in your loop, you will eat up 100% of the CPU time. It's important to be a good citizen. Don't chew up CPU cycles needlessly.
The most legit time to use a non-blocking window is when you're working directly with hardware. Maybe you're driving a serial bus. If you look at the Event Loop in the Demo_OpenCV_Webcam.py program, you'll see that the read is a non-blocking read. The point in the loop where you will block is the call to read frames from the webcam. When a frame is available you want to quickly deliver it to the output device, so you don't want your GUI blocking on your.
Another example can be found in the demo for controlling a robot on a Raspberry Pi. In that application you want to read the direction buttons, forward, backward, etc, and immediately take action.
However, even in this application it's not good to simply burn 100% of the CPU time in a tight spin loop. Let's say your call to the GUI takes 10ms. And your robot can react at a rate of 200ms. If you do non-blocking reads without some kind of sleep, you'll be taking 100% of the CPU time needlessly.
Adding a sleep to your event loop will at least give other processes time to execute. It will, however, starve your GUI. The entire time you're sleeping, your GUI isn't executing.
It's for this reason that a new feature was just added in version 4.0, Read with a Timeout. You can thing of this as an event loop, with a sleep, except that during the time the processor is sleeping your GUI is very operational, responding to the user for thinks like checkboxes, sliders, even button pushes will be instant feeling.
## Read(timeout = t, timeout_key='timeout')
The new Hybrid-Read! Read with a timeout is a very good thing for your GUIs to use in a read non-blocking situation, if you can use them. If your device can wait for a little while, then use this kind of read. The longer you're able to add to the timeout value, the less CPU time you'll be taking).
One way of thinking of reads with timeouts:
> During the timeout time, you are "yielding" the processor to do other tasks.
But it gets better than just being a good citizen....**your GUI will be more responsive than if you used a non-blocking read**
Let's say you had a device that you want to "poll" every 100ms. The "easy way out" and the only way out until release 4.0 was this:
```python
while True: # Event Loop
event, values = window.ReadNonBlocking()
read_my_hardware() # process my device here
time.sleep(.1) # sleep 1/10 second
```
This program will quickly test for user input, then deal with the hardware. Then it'll sleep for 100ms, while your gui is non-responsive, then it'll check in with your GUI again. I fully realize this is a crude way of doing things. We're talking dirt simple stuff without trying to use threads, etc to 'get it right'. It's for demonstration purposes.
The new and better way....
using the Read Timeout mechanism, the sleep goes away.
```python
while True: # Event Loop
event, values = window.Read(timeout = 100)
read_my_hardware() # process my device here
```
This event loop will run every 100 ms. You're making a Read call, so anything that the use does will return back to you immediately, and you're waiting up to 100ms for the user to do something. If the user doesn't do anything, then the read will timeout and execution will return to the program.
### Instead of ReadNonBlocking --- Use `change_submits = True` or return_keyboard_events = True
Any time you are thinking "I want an X Element to cause a Y Element to do something", then you want to use the `change_submits` option.
@ -2903,7 +2968,7 @@ One example is you have an input field that changes as you press buttons on an o
### Periodically Calling`ReadNonBlocking`
Periodically "refreshing" the visible GUI. The longer you wait between updates to your GUI the more sluggish your windows will feel. It is up to you to make these calls or your GUI will freeze.
Let's say you do end up using ReadNonBlocking... then you've got some housekeeping to do. It's up to you to periodically "refresh" the visible GUI. The longer you wait between updates to your GUI the more sluggish your windows will feel. It is up to you to make these calls or your GUI will freeze.
There are 2 methods of interacting with non-blocking windows.
1. Read the window just as you would a normal window
@ -2950,42 +3015,38 @@ When you are ready to close the window (assuming the window wasn't closed by the
**Example - Running timer that updates**
See the sample code on the GitHub named Demo Media Player for another example of Async windows. We're going to make a window and update one of the elements of that window every .01 seconds. Here's the entire code to do that.
import PySimpleGUI as sg
import time
import PySimpleGUI as sg
import time
# ---------------- Create Form ----------------
sg.ChangeLookAndFeel('Black')
sg.SetOptions(element_padding=(0, 0))
# window that doesn't block
# Make a window, but don't use context manager
window = sg.Window('Running Timer', auto_size_text=True)
layout = [[sg.Text('')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
[sg.ReadButton('Pause', key='button', button_color=('white', '#001480')),
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
# Create the layout
window_rows = [[sg.Text('Non-blocking GUI with updates')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20), key='output') ],
[sg.Button('Quit')]]
# Layout the rows of the window and perform a read. Indicate the window is non-blocking!
window.Layout(window_rows).ReadNonBlocking()
window = sg.Window('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True).Layout(layout)
#
# Some place later in your code...
# You need to perform a ReadNonBlocking on your window every now and then or
# else it won't refresh
#
# ---------------- main loop ----------------
current_time = 0
paused = False
start_time = int(round(time.time() * 100))
while (True):
# --------- Read and update window --------
event, values = window.Read(timeout=10)
current_time = int(round(time.time() * 100)) - start_time
# --------- Display timer in window --------
window.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
(current_time // 100) % 60,
current_time % 100))
for i in range(1, 1000):
window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
event, values = window.ReadNonBlocking()
if values is None or event == 'Quit':
break
time.sleep(.01)
else:
window.CloseNonBlocking()
What we have here is the same sequence of function calls as in the description. Get a window, add rows to it, show the window, and then refresh it every now and then.
The new thing in this example is the call use of the Update method for the Text Element. The first thing we do inside the loop is "update" the text element that we made earlier. This changes the value of the text field on the window. The new value will be displayed when `window.ReadNonBlocking()` is called. if you want to have the window reflect your changes immediately, call `window.Refresh()`.
Note the `else` statement on the for loop. This is needed because we're about to exit the loop while the window is still open. The user has not closed the window using the X nor a button so it's up to the caller to close the window using `CloseNonBlocking`.
Previously this program was implemented using a sleep in the loop to control the clock tick. This version uses the new timeout parameter. The result is a window that reacts quicker then the one with the sleep and the accuracy is just as good.
@ -3382,7 +3443,8 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 03.09.01 | Oct 8, 2018
| 3.9.3 & 1.1.3 | Oct 11, 2018
| 3.9.4 & 1.1.4 | Oct 16, 2018
| 3.10.1 & 1.2.1 | Oct 20, 2018
## Release Notes
@ -3563,6 +3625,19 @@ It's official. There is a 2.7 version of PySimpleGUI!
* BUG fix in listbox double-click. First bug fix in months
* New Look And Feel capability. List predefined settings using ListOfLookAndFeelValues
### 3.10.1 & 1.2.1
* Combobox new readonly parameter in init and Update
* Better default sizes for Slider
* Read of Tables now returns which rows are selected (big damned deal feature)
* PARTIAL support of Table.Update with new values (use at your own peril)
* Alpha channel setting for Windows
* Timeout setting for Window.Read (big damned deal feature)
* Icon can be base64 image now in SetIcon call
* Window.FindElementWithFocus call
* Window.Move allows moving window anywhere on screen
* Window.Minimize will minimize to taskbar
* Button background color can be set to system default (i.e. not changed)
### Upcoming