RELEASE 3.10.1 & 1.2.1
This commit is contained in:
parent
20379b8a87
commit
3c807be334
|
@ -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__()
|
||||
|
||||
|
|
432
PySimpleGUI27.py
432
PySimpleGUI27.py
|
@ -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__':
|
||||
|
|
199
docs/index.md
199
docs/index.md
|
@ -23,9 +23,9 @@
|
|||
|
||||
## Now supports both Python 2.7 & 3
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
[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
199
readme.md
|
@ -23,9 +23,9 @@
|
|||
|
||||
## Now supports both Python 2.7 & 3
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
[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
|
||||
|
|
Loading…
Reference in New Issue