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
|
g_time_delta = g_time_end - g_time_start
|
||||||
print(g_time_delta)
|
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 ====----====----====----#
|
# ----====----====----==== Constants the user CAN safely change ====----====----====----#
|
||||||
DEFAULT_WINDOW_ICON = 'default_icon.ico'
|
DEFAULT_WINDOW_ICON = 'default_icon.ico'
|
||||||
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
|
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
|
||||||
|
@ -61,8 +80,12 @@ PURPLES = ("#480656","#4F2398","#380474")
|
||||||
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
|
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
|
||||||
YELLOWS = ("#F3FB62", "#F0F595")
|
YELLOWS = ("#F3FB62", "#F0F595")
|
||||||
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
|
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
|
||||||
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]), ('#000000','#FFFFFF'),('#FFFFFF', '#000000'), (YELLOWS[0], PURPLES[1]),
|
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]),
|
||||||
(YELLOWS[0], GREENS[3]), (YELLOWS[0], BLUES[2]))
|
('#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
|
COLOR_SYSTEM_DEFAULT = '1234567890' # Colors should never be this long
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
|
@ -2282,6 +2305,21 @@ class Table(Element):
|
||||||
return
|
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):
|
def treeview_selected(self, event):
|
||||||
selections = self.TKTreeview.selection()
|
selections = self.TKTreeview.selection()
|
||||||
self.SelectedRows = [int(x[1:], 16)-1 for x in selections]
|
self.SelectedRows = [int(x[1:], 16)-1 for x in selections]
|
||||||
|
@ -2294,6 +2332,7 @@ class Table(Element):
|
||||||
# print('Selected item iid: %s' % iid)
|
# print('Selected item iid: %s' % iid)
|
||||||
# #self.process_directory(iid, path)
|
# #self.process_directory(iid, path)
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
super().__del__()
|
super().__del__()
|
||||||
|
|
||||||
|
|
408
PySimpleGUI27.py
408
PySimpleGUI27.py
|
@ -51,6 +51,25 @@ def TimerStop():
|
||||||
g_time_delta = g_time_end - g_time_start
|
g_time_delta = g_time_end - g_time_start
|
||||||
print(g_time_delta)
|
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 ====----====----====----#
|
# ----====----====----==== Constants the user CAN safely change ====----====----====----#
|
||||||
DEFAULT_WINDOW_ICON = 'default_icon.ico'
|
DEFAULT_WINDOW_ICON = 'default_icon.ico'
|
||||||
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
|
DEFAULT_ELEMENT_SIZE = (45,1) # In CHARACTERS
|
||||||
|
@ -73,8 +92,12 @@ PURPLES = ("#480656","#4F2398","#380474")
|
||||||
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
|
GREENS = ("#01826B","#40A860","#96D2AB", "#00A949","#003532")
|
||||||
YELLOWS = ("#F3FB62", "#F0F595")
|
YELLOWS = ("#F3FB62", "#F0F595")
|
||||||
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
|
TANS = ("#FFF9D5","#F4EFCF","#DDD8BA")
|
||||||
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]), ('#000000','#FFFFFF'),('#FFFFFF', '#000000'), (YELLOWS[0], PURPLES[1]),
|
NICE_BUTTON_COLORS = ((GREENS[3], TANS[0]),
|
||||||
(YELLOWS[0], GREENS[3]), (YELLOWS[0], BLUES[2]))
|
('#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
|
COLOR_SYSTEM_DEFAULT = '1234567890' # Colors should never be this long
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
|
@ -435,13 +458,12 @@ class Element(object):
|
||||||
# Input Class #
|
# Input Class #
|
||||||
# ---------------------------------------------------------------------- #
|
# ---------------------------------------------------------------------- #
|
||||||
class InputText(Element):
|
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):
|
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
|
Input a line of text Element
|
||||||
:param default_text: Default value to display
|
:param default_text: Default value to display
|
||||||
:param size: Size of field in characters
|
: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 password_char: If non-blank, will display this character for every character typed
|
||||||
:param background_color: Color for Element. Text or RGB Hex
|
: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.do_not_clear = do_not_clear
|
||||||
self.Justification = justification
|
self.Justification = justification
|
||||||
self.Disabled = disabled
|
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):
|
def Update(self, value=None, disabled=None):
|
||||||
|
@ -482,7 +504,7 @@ Input = InputText
|
||||||
# Combo #
|
# Combo #
|
||||||
# ---------------------------------------------------------------------- #
|
# ---------------------------------------------------------------------- #
|
||||||
class InputCombo(Element):
|
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)
|
Input Combo Box Element (also called Dropdown box)
|
||||||
:param values:
|
:param values:
|
||||||
|
@ -494,14 +516,15 @@ class InputCombo(Element):
|
||||||
self.DefaultValue = default_value
|
self.DefaultValue = default_value
|
||||||
self.ChangeSubmits = change_submits
|
self.ChangeSubmits = change_submits
|
||||||
self.TKCombo = None
|
self.TKCombo = None
|
||||||
self.InitializeAsDisabled = disabled
|
# self.InitializeAsDisabled = disabled
|
||||||
self.Disabled = disabled
|
self.Disabled = disabled
|
||||||
|
self.Readonly=readonly
|
||||||
bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR
|
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
|
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)
|
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:
|
if values is not None:
|
||||||
try:
|
try:
|
||||||
self.TKCombo['values'] = values
|
self.TKCombo['values'] = values
|
||||||
|
@ -526,6 +549,10 @@ class InputCombo(Element):
|
||||||
self.TKCombo['state'] = 'disable'
|
self.TKCombo['state'] = 'disable'
|
||||||
elif disabled == False:
|
elif disabled == False:
|
||||||
self.TKCombo['state'] = 'enable'
|
self.TKCombo['state'] = 'enable'
|
||||||
|
if readonly is not None:
|
||||||
|
self.Readonly = readonly
|
||||||
|
if self.Readonly:
|
||||||
|
self.TKCombo['state']='readonly'
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
@ -1326,7 +1353,6 @@ class ProgressBar(Element):
|
||||||
self.Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF
|
self.Relief = relief if relief else DEFAULT_PROGRESS_BAR_RELIEF
|
||||||
self.BarExpired = False
|
self.BarExpired = False
|
||||||
super().__init__(ELEM_TYPE_PROGRESS_BAR, size=size, auto_size_text=auto_size_text, key=key, pad=pad)
|
super().__init__(ELEM_TYPE_PROGRESS_BAR, size=size, auto_size_text=auto_size_text, key=key, pad=pad)
|
||||||
return
|
|
||||||
|
|
||||||
# returns False if update failed
|
# returns False if update failed
|
||||||
def UpdateBar(self, current_count, max=None):
|
def UpdateBar(self, current_count, max=None):
|
||||||
|
@ -1831,8 +1857,11 @@ class Slider(Element):
|
||||||
self.ChangeSubmits = change_submits
|
self.ChangeSubmits = change_submits
|
||||||
self.Disabled = disabled
|
self.Disabled = disabled
|
||||||
self.TickInterval = tick_interval
|
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
|
return
|
||||||
|
|
||||||
def Update(self, value=None, range=(None, None), disabled=None):
|
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.NumRows = num_rows if num_rows is not None else size[1]
|
||||||
self.TKTreeview = None
|
self.TKTreeview = None
|
||||||
self.AlternatingRowColor = alternating_row_color
|
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)
|
super().__init__(ELEM_TYPE_TABLE, text_color=text_color, background_color=background_color, font=font, size=size, pad=pad, key=key, tooltip=tooltip)
|
||||||
return
|
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):
|
def __del__(self):
|
||||||
super().__del__()
|
super().__del__()
|
||||||
|
|
||||||
|
@ -2485,6 +2543,9 @@ class Window(object):
|
||||||
self.ForceTopLevel = force_toplevel
|
self.ForceTopLevel = force_toplevel
|
||||||
self.Resizable = resizable
|
self.Resizable = resizable
|
||||||
self._AlphaChannel = alpha_channel
|
self._AlphaChannel = alpha_channel
|
||||||
|
self.Timeout = None
|
||||||
|
self.TimeoutKey = '_timeout_'
|
||||||
|
self.TimerCancelled = False
|
||||||
|
|
||||||
# ------------------------- Add ONE Row to Form ------------------------- #
|
# ------------------------- Add ONE Row to Form ------------------------- #
|
||||||
def AddRow(self, *args):
|
def AddRow(self, *args):
|
||||||
|
@ -2553,10 +2614,15 @@ class Window(object):
|
||||||
return self.ReturnValues
|
return self.ReturnValues
|
||||||
|
|
||||||
# ------------------------- SetIcon - set the window's fav icon ------------------------- #
|
# ------------------------- SetIcon - set the window's fav icon ------------------------- #
|
||||||
def SetIcon(self, icon):
|
def SetIcon(self, icon=None, pngbase64=None):
|
||||||
self.WindowIcon = icon
|
if pngbase64 != None:
|
||||||
|
img = tkinter.PhotoImage(data=pngbase64)
|
||||||
|
wicon = img
|
||||||
|
else:
|
||||||
|
wicon = icon
|
||||||
|
self.WindowIcon = wicon
|
||||||
try:
|
try:
|
||||||
self.TKroot.iconbitmap(icon)
|
self.TKroot.iconbitmap(wicon)
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
def _GetElementAtLocation(self, location):
|
def _GetElementAtLocation(self, location):
|
||||||
|
@ -2582,7 +2648,19 @@ class Window(object):
|
||||||
pass
|
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
|
self.NonBlocking = False
|
||||||
if self.TKrootDestroyed:
|
if self.TKrootDestroyed:
|
||||||
return None, None
|
return None, None
|
||||||
|
@ -2590,7 +2668,11 @@ class Window(object):
|
||||||
self.Show()
|
self.Show()
|
||||||
else:
|
else:
|
||||||
InitializeResults(self)
|
InitializeResults(self)
|
||||||
|
if timeout != None:
|
||||||
|
self.TimerCancelled = False
|
||||||
|
self.TKAfterID = self.TKroot.after(timeout, self._TimeoutAlarmCallback)
|
||||||
self.TKroot.mainloop()
|
self.TKroot.mainloop()
|
||||||
|
self.TimerCancelled = True
|
||||||
if self.RootNeedsDestroying:
|
if self.RootNeedsDestroying:
|
||||||
self.TKroot.destroy()
|
self.TKroot.destroy()
|
||||||
_my_windows.Decrement()
|
_my_windows.Decrement()
|
||||||
|
@ -2650,8 +2732,6 @@ class Window(object):
|
||||||
|
|
||||||
def FindElement(self, key):
|
def FindElement(self, key):
|
||||||
element = _FindElementFromKeyInSubForm(self, key)
|
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:
|
if element is None:
|
||||||
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
|
print('*** WARNING = FindElement did not find the key. Please check your key\'s spelling ***')
|
||||||
PopupError('Keyword error in FindElement Call',
|
PopupError('Keyword error in FindElement Call',
|
||||||
|
@ -2661,6 +2741,10 @@ class Window(object):
|
||||||
return ErrorElement(key=key)
|
return ErrorElement(key=key)
|
||||||
return element
|
return element
|
||||||
|
|
||||||
|
def FindElementWithFocus(self):
|
||||||
|
element = _FindElementWithFocusInSubForm(self)
|
||||||
|
return element
|
||||||
|
|
||||||
def SaveToDisk(self, filename):
|
def SaveToDisk(self, filename):
|
||||||
try:
|
try:
|
||||||
results = BuildResults(self, False, self)
|
results = BuildResults(self, False, self)
|
||||||
|
@ -3088,19 +3172,31 @@ def BuildResultsForSubform(form, initialize_only, top_level_form):
|
||||||
value = tab_key
|
value = tab_key
|
||||||
except:
|
except:
|
||||||
value = None
|
value = None
|
||||||
|
elif element.Type == ELEM_TYPE_TABLE:
|
||||||
|
value = element.SelectedRows
|
||||||
else:
|
else:
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
# if an input type element, update the results
|
# 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\
|
if element.Type != ELEM_TYPE_BUTTON and \
|
||||||
element.Type != ELEM_TYPE_OUTPUT and element.Type != ELEM_TYPE_PROGRESS_BAR and \
|
element.Type != ELEM_TYPE_TEXT and \
|
||||||
element.Type!= ELEM_TYPE_COLUMN and element.Type != ELEM_TYPE_FRAME \
|
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:
|
and element.Type != ELEM_TYPE_TAB:
|
||||||
AddToReturnList(form, value)
|
AddToReturnList(form, value)
|
||||||
AddToReturnDictionary(top_level_form, element, 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 \
|
elif (element.Type == ELEM_TYPE_BUTTON and
|
||||||
(element.Type == ELEM_TYPE_BUTTON and element.BType == BUTTON_TYPE_COLOR_CHOOSER and element.Target == (None,None)) or \
|
element.BType == BUTTON_TYPE_CALENDAR_CHOOSER and
|
||||||
(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))):
|
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)
|
AddToReturnList(form, value)
|
||||||
AddToReturnDictionary(top_level_form, element, value)
|
AddToReturnDictionary(top_level_form, element, value)
|
||||||
|
|
||||||
|
@ -3185,6 +3281,29 @@ def _FindElementFromKeyInSubForm(form, key):
|
||||||
return element
|
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:
|
if sys.version_info[0] >= 3:
|
||||||
def AddMenuItem(top_menu, sub_menu_info, element, is_sub_menu=False, skip=False):
|
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.configure(height=element.Size[1])
|
||||||
# element.TKCombo['state']='readonly'
|
# element.TKCombo['state']='readonly'
|
||||||
element.TKCombo['values'] = element.Values
|
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:
|
# if element.BackgroundColor is not None:
|
||||||
# element.TKCombo.configure(background=element.BackgroundColor)
|
# element.TKCombo.configure(background=element.BackgroundColor)
|
||||||
element.TKCombo.pack(side=tk.LEFT,padx=element.Pad[0], pady=element.Pad[1])
|
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)
|
element.TKCombo.current(0)
|
||||||
if element.ChangeSubmits:
|
if element.ChangeSubmits:
|
||||||
element.TKCombo.bind('<<ComboboxSelected>>', element.ComboboxSelectHandler)
|
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'
|
element.TKCombo['state'] = 'disabled'
|
||||||
if element.Tooltip is not None:
|
if element.Tooltip is not None:
|
||||||
element.TooltipObject = ToolTip(element.TKCombo, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
|
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
|
width = element.DefaultColumnWidth
|
||||||
|
|
||||||
treeview.column(heading, width=width*CharWidthInPixels(), anchor=anchor)
|
treeview.column(heading, width=width*CharWidthInPixels(), anchor=anchor)
|
||||||
|
# Insert values into the tree
|
||||||
for i, value in enumerate(element.Values):
|
for i, value in enumerate(element.Values):
|
||||||
if element.DisplayRowNumbers:
|
if element.DisplayRowNumbers:
|
||||||
value = [i] + value
|
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:
|
if element.TextColor is not None and element.TextColor != COLOR_SYSTEM_DEFAULT:
|
||||||
tkinter.ttk.Style().configure("Treeview", foreground=element.TextColor)
|
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')
|
# 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')
|
element.TKTreeview.pack(side=tk.LEFT,expand=True, padx=0, pady=0, fill='both')
|
||||||
if element.Tooltip is not None:
|
if element.Tooltip is not None:
|
||||||
element.TooltipObject = ToolTip(element.TKTreeview, text=element.Tooltip, timeout=DEFAULT_TOOLTIP_TIME)
|
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:
|
if my_flex_form.AutoClose:
|
||||||
duration = DEFAULT_AUTOCLOSE_TIME if my_flex_form.AutoCloseDuration is None else my_flex_form.AutoCloseDuration
|
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)
|
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:
|
if my_flex_form.NonBlocking:
|
||||||
pass
|
pass
|
||||||
# my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback())
|
# my_flex_form.TKroot.protocol("WM_DELETE_WINDOW", my_flex_form.OnClosingCallback())
|
||||||
else: # it's a blocking form
|
else: # it's a blocking form
|
||||||
# print('..... CALLING MainLoop')
|
# print('..... CALLING MainLoop')
|
||||||
my_flex_form.TKroot.mainloop()
|
my_flex_form.TKroot.mainloop()
|
||||||
|
my_flex_form.TimerCancelled = True
|
||||||
# print('..... BACK from MainLoop')
|
# print('..... BACK from MainLoop')
|
||||||
if not my_flex_form.FormRemainedOpen:
|
if not my_flex_form.FormRemainedOpen:
|
||||||
_my_windows.Decrement()
|
_my_windows.Decrement()
|
||||||
|
@ -4955,79 +5084,211 @@ LOOK_AND_FEEL_TABLE = {'SystemDefault':
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
|
|
||||||
'Dark2': {'BACKGROUND': 'gray25', 'TEXT': 'white', 'INPUT': 'white',
|
'Dark2': {'BACKGROUND': 'gray25',
|
||||||
'TEXT_INPUT': 'black', 'SCROLL': 'gray44', 'BUTTON': ('white', '#004F00'),
|
'TEXT': 'white',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': 'white',
|
||||||
|
'TEXT_INPUT': 'black',
|
||||||
|
'SCROLL': 'gray44',
|
||||||
|
'BUTTON': ('white', '#004F00'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'Black': {'BACKGROUND': 'black', 'TEXT': 'white', 'INPUT': 'gray30',
|
'Black': {'BACKGROUND': 'black',
|
||||||
'TEXT_INPUT': 'white', 'SCROLL': 'gray44', 'BUTTON': ('black', 'white'),
|
'TEXT': 'white',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': 'gray30',
|
||||||
|
'TEXT_INPUT': 'white',
|
||||||
|
'SCROLL': 'gray44',
|
||||||
|
'BUTTON': ('black', 'white'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'Tan': {'BACKGROUND': '#fdf6e3', 'TEXT': '#268bd1', 'INPUT': '#eee8d5',
|
'Tan': {'BACKGROUND': '#fdf6e3',
|
||||||
'TEXT_INPUT': '#6c71c3', 'SCROLL': '#eee8d5', 'BUTTON': ('white', '#063542'),
|
'TEXT': '#268bd1',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#eee8d5',
|
||||||
|
'TEXT_INPUT': '#6c71c3',
|
||||||
|
'SCROLL': '#eee8d5',
|
||||||
|
'BUTTON': ('white', '#063542'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'TanBlue': {'BACKGROUND': '#e5dece', 'TEXT': '#063289', 'INPUT': '#f9f8f4',
|
'TanBlue': {'BACKGROUND': '#e5dece',
|
||||||
'TEXT_INPUT': '#242834', 'SCROLL': '#eee8d5', 'BUTTON': ('white', '#063289'),
|
'TEXT': '#063289',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#f9f8f4',
|
||||||
|
'TEXT_INPUT': '#242834',
|
||||||
|
'SCROLL': '#eee8d5',
|
||||||
|
'BUTTON': ('white', '#063289'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'DarkTanBlue': {'BACKGROUND': '#242834', 'TEXT': '#dfe6f8', 'INPUT': '#97755c',
|
'DarkTanBlue': {'BACKGROUND': '#242834',
|
||||||
'TEXT_INPUT': 'white', 'SCROLL': '#a9afbb', 'BUTTON': ('white', '#063289'),
|
'TEXT': '#dfe6f8',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#97755c',
|
||||||
|
'TEXT_INPUT': 'white',
|
||||||
|
'SCROLL': '#a9afbb',
|
||||||
|
'BUTTON': ('white', '#063289'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'DarkAmber': {'BACKGROUND': '#2c2825', 'TEXT': '#fdcb52', 'INPUT': '#705e52',
|
'DarkAmber': {'BACKGROUND': '#2c2825',
|
||||||
'TEXT_INPUT': '#fdcb52', 'SCROLL': '#705e52', 'BUTTON': ('black', '#fdcb52'),
|
'TEXT': '#fdcb52',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#705e52',
|
||||||
|
'TEXT_INPUT': '#fdcb52',
|
||||||
|
'SCROLL': '#705e52',
|
||||||
|
'BUTTON': ('black', '#fdcb52'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'DarkBlue': {'BACKGROUND': '#1a2835', 'TEXT': '#d1ecff', 'INPUT': '#335267',
|
'DarkBlue': {'BACKGROUND': '#1a2835',
|
||||||
'TEXT_INPUT': '#acc2d0', 'SCROLL': '#1b6497', 'BUTTON': ('black', '#fafaf8'),
|
'TEXT': '#d1ecff',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#335267',
|
||||||
|
'TEXT_INPUT': '#acc2d0',
|
||||||
|
'SCROLL': '#1b6497',
|
||||||
|
'BUTTON': ('black', '#fafaf8'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1, 'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'Reds': {'BACKGROUND': '#280001', 'TEXT': 'white', 'INPUT': '#d8d584',
|
'Reds': {'BACKGROUND': '#280001',
|
||||||
'TEXT_INPUT': 'black', 'SCROLL': '#763e00', 'BUTTON': ('black', '#daad28'),
|
'TEXT': 'white',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#d8d584',
|
||||||
|
'TEXT_INPUT': 'black',
|
||||||
|
'SCROLL': '#763e00',
|
||||||
|
'BUTTON': ('black', '#daad28'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_DEPTH': 0},
|
'PROGRESS_DEPTH': 0},
|
||||||
|
|
||||||
'Green': {'BACKGROUND': '#82a459', 'TEXT': 'black', 'INPUT': '#d8d584',
|
'Green': {'BACKGROUND': '#82a459',
|
||||||
'TEXT_INPUT': 'black', 'SCROLL': '#e3ecf3', 'BUTTON': ('white', '#517239'),
|
'TEXT': 'black',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1, 'SLIDER_DEPTH': 0,
|
'INPUT': '#d8d584',
|
||||||
|
'TEXT_INPUT': 'black',
|
||||||
|
'SCROLL': '#e3ecf3',
|
||||||
|
'BUTTON': ('white', '#517239'),
|
||||||
|
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR,
|
||||||
|
'BORDER': 1,
|
||||||
|
'SLIDER_DEPTH': 0,
|
||||||
'PROGRESS_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',
|
'Purple': {'BACKGROUND': '#B0AAC2',
|
||||||
'BUTTON': ('black', '#C2D4D8'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
|
'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',
|
'BlueMono': {'BACKGROUND': '#AAB6D3',
|
||||||
'BUTTON': ('white', '#7186C7'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
|
'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',
|
'GreenMono': {'BACKGROUND': '#A8C1B4',
|
||||||
'BUTTON': ('white', '#6D9F85'), 'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
|
'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',
|
'NeutralBlue': {'BACKGROUND': '#92aa9d',
|
||||||
'SCROLL': '#fcfff6', 'TEXT_INPUT': 'black', 'BUTTON': ('black', '#d0dbbd'),
|
'TEXT': 'black',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
|
'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',
|
'Kayak': {'BACKGROUND': '#a7ad7f',
|
||||||
'SCROLL': '#e6d3a8', 'TEXT_INPUT': 'black', 'BUTTON': ('white', '#5d907d'),
|
'TEXT': 'black',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
|
'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',
|
'SandyBeach': {'BACKGROUND': '#efeccb',
|
||||||
'SCROLL': '#e6d3a8', 'TEXT_INPUT': '#012f2f', 'BUTTON': ('white', '#046380'),
|
'TEXT': '#012f2f',
|
||||||
'PROGRESS': DEFAULT_PROGRESS_BAR_COLOR, 'BORDER': 1,'SLIDER_DEPTH':0, 'PROGRESS_DEPTH':0},
|
'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():
|
def ListOfLookAndFeelValues():
|
||||||
|
@ -5724,15 +5985,14 @@ def PopupYesNo(*args, **_3to2kwargs):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
window = Window('Demo window..')
|
layout = [[Text('You are running the PySimpleGUI.py file itself')],
|
||||||
window_rows = [[Text('You are running the PySimpleGUI.py file itself')],
|
|
||||||
[Text('You should be importing it rather than running it', size=(50,2))],
|
[Text('You should be importing it rather than running it', size=(50,2))],
|
||||||
[Text('Here is your sample input window....')],
|
[Text('Here is your sample input window....')],
|
||||||
[Text('Source Folder', size=(15, 1), justification='right'), InputText('Source', focus=True),FolderBrowse()],
|
[Text('Source Folder', size=(15, 1), justification='right'), InputText('Source', focus=True),FolderBrowse()],
|
||||||
[Text('Destination Folder', size=(15, 1), justification='right'), InputText('Dest'), FolderBrowse()],
|
[Text('Destination Folder', size=(15, 1), justification='right'), InputText('Dest'), FolderBrowse()],
|
||||||
[Ok(), Cancel()]]
|
[Ok(), Cancel()]]
|
||||||
|
|
||||||
button, (source, dest) = window.LayoutAndRead(window_rows)
|
button, values = Window('Demo window..').Layout(layout).Read()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
167
docs/index.md
167
docs/index.md
|
@ -23,9 +23,9 @@
|
||||||
|
|
||||||
## Now supports both Python 2.7 & 3
|
## Now supports both Python 2.7 & 3
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
|
[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=False,
|
||||||
auto_close_duration=DEFAULT_AUTOCLOSE_TIME,
|
auto_close_duration=DEFAULT_AUTOCLOSE_TIME,
|
||||||
icon=DEFAULT_WINDOW_ICON,
|
icon=DEFAULT_WINDOW_ICON,
|
||||||
force_toplevel=False
|
force_toplevel=False,
|
||||||
|
alpha_channel=1,
|
||||||
return_keyboard_events=False,
|
return_keyboard_events=False,
|
||||||
use_default_focus=True,
|
use_default_focus=True,
|
||||||
text_justification=None,
|
text_justification=None,
|
||||||
|
@ -1228,6 +1229,7 @@ Parameter Descriptions. You will find these same parameters specified for each
|
||||||
auto_close_duration - Duration in seconds before window closes
|
auto_close_duration - Duration in seconds before window closes
|
||||||
icon - .ICO file that will appear on the Task Bar and end of Title Bar
|
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
|
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
|
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
|
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.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.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.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.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
|
window.Fill(values_dict) - Fill each Element with entry from the dictionary passed in
|
||||||
|
@ -1290,6 +1292,9 @@ There are a few methods (functions) that you will see in this document that act
|
||||||
window.Disable() - Use to disable the window inpurt when opening another window on top of the primnary 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.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
|
## 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.
|
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
|
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()
|
#### 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()
|
#### 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.
|
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)
|
#### SetIcon(icon, pngbase64)
|
||||||
Sets the window's icon that will be shown on the titlebar.
|
Sets the window's icon that will be shown on the titlebar. Can either be a filename or a base64 string.
|
||||||
|
|
||||||
#### Fill(values_dict)
|
#### Fill(values_dict)
|
||||||
Populates the windows fields with the values shown in the dictionary.
|
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
|
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)
|
#### SaveToDisk(filename)
|
||||||
|
|
||||||
Saves the window's values to disk
|
Saves the window's values to disk
|
||||||
|
@ -1673,6 +1682,7 @@ Also known as a drop-down list. Only required parameter is the list of choices.
|
||||||
background_color=None
|
background_color=None
|
||||||
text_color=None
|
text_color=None
|
||||||
change_submits=False
|
change_submits=False
|
||||||
|
readonly=True
|
||||||
disabled=False
|
disabled=False
|
||||||
key=None
|
key=None
|
||||||
pad=None
|
pad=None
|
||||||
|
@ -1687,6 +1697,7 @@ Also known as a drop-down list. Only required parameter is the list of choices.
|
||||||
text_color - color to use for the typed text
|
text_color - color to use for the typed text
|
||||||
change_submits - Bool. If set causes Read to immediately return if the selected value changes
|
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
|
key - Dictionary key to use for return values
|
||||||
pad - (x,y) Amount of padding to put around element in pixels
|
pad - (x,y) Amount of padding to put around element in pixels
|
||||||
tooltip - Text string. If set, hovering over field will popup the text
|
tooltip - Text string. If set, hovering over field will popup the text
|
||||||
|
@ -1695,12 +1706,13 @@ Shortcut functions - Combo, DropDown, Drop
|
||||||
|
|
||||||
### Combo Methods
|
### Combo Methods
|
||||||
```python
|
```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
|
value - change which value is current selected
|
||||||
values - change list of choices
|
values - change list of choices
|
||||||
set_to_index - change selection to a particular choice
|
set_to_index - change selection to a particular choice
|
||||||
disable - if True will disable element
|
disable - if True will disable element
|
||||||
|
readonly - if True will make element readonly
|
||||||
|
|
||||||
## Listbox Element
|
## 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).
|
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**
|
**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.
|
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`.
|
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,6 +2625,17 @@ Let me say up front that the Table Element has Beta status. The reason is that s
|
||||||
key - key used to lookup element
|
key - key used to lookup element
|
||||||
tooltip - tooltip text
|
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
|
## Tree Element
|
||||||
|
|
||||||
|
@ -2873,17 +2896,59 @@ The reasoning behind this is that Persistent Windows are often "forms". When "s
|
||||||
|
|
||||||
|
|
||||||
## Asynchronous (Non-Blocking) windows
|
## Asynchronous (Non-Blocking) windows
|
||||||
So you want to be a wizard do ya? Well go boldly!
|
Not so fast buddy!!
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
|
||||||
|
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
|
### Instead of ReadNonBlocking --- Use `change_submits = True` or return_keyboard_events = True
|
||||||
|
@ -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 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.
|
There are 2 methods of interacting with non-blocking windows.
|
||||||
1. Read the window just as you would a normal window
|
1. Read the window just as you would a normal window
|
||||||
|
@ -2954,38 +3019,34 @@ See the sample code on the GitHub named Demo Media Player for another example of
|
||||||
import PySimpleGUI as sg
|
import PySimpleGUI as sg
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# window that doesn't block
|
# ---------------- Create Form ----------------
|
||||||
# Make a window, but don't use context manager
|
sg.ChangeLookAndFeel('Black')
|
||||||
window = sg.Window('Running Timer', auto_size_text=True)
|
sg.SetOptions(element_padding=(0, 0))
|
||||||
|
|
||||||
# Create the layout
|
layout = [[sg.Text('')],
|
||||||
window_rows = [[sg.Text('Non-blocking GUI with updates')],
|
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
|
||||||
[sg.Text('', size=(8, 2), font=('Helvetica', 20), key='output') ],
|
[sg.ReadButton('Pause', key='button', button_color=('white', '#001480')),
|
||||||
[sg.Button('Quit')]]
|
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
|
||||||
# Layout the rows of the window and perform a read. Indicate the window is non-blocking!
|
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
|
||||||
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
|
|
||||||
#
|
|
||||||
|
|
||||||
for i in range(1, 1000):
|
# ---------------- main loop ----------------
|
||||||
window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
|
current_time = 0
|
||||||
event, values = window.ReadNonBlocking()
|
paused = False
|
||||||
if values is None or event == 'Quit':
|
start_time = int(round(time.time() * 100))
|
||||||
break
|
while (True):
|
||||||
time.sleep(.01)
|
# --------- Read and update window --------
|
||||||
else:
|
event, values = window.Read(timeout=10)
|
||||||
window.CloseNonBlocking()
|
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))
|
||||||
|
|
||||||
|
|
||||||
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()`.
|
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.
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3382,6 +3443,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
|
||||||
| 03.09.01 | Oct 8, 2018
|
| 03.09.01 | Oct 8, 2018
|
||||||
| 3.9.3 & 1.1.3 | Oct 11, 2018
|
| 3.9.3 & 1.1.3 | Oct 11, 2018
|
||||||
| 3.9.4 & 1.1.4 | Oct 16, 2018
|
| 3.9.4 & 1.1.4 | Oct 16, 2018
|
||||||
|
| 3.10.1 & 1.2.1 | Oct 20, 2018
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
* BUG fix in listbox double-click. First bug fix in months
|
||||||
* New Look And Feel capability. List predefined settings using ListOfLookAndFeelValues
|
* 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
|
### Upcoming
|
||||||
|
|
167
readme.md
167
readme.md
|
@ -23,9 +23,9 @@
|
||||||
|
|
||||||
## Now supports both Python 2.7 & 3
|
## Now supports both Python 2.7 & 3
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
|
[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=False,
|
||||||
auto_close_duration=DEFAULT_AUTOCLOSE_TIME,
|
auto_close_duration=DEFAULT_AUTOCLOSE_TIME,
|
||||||
icon=DEFAULT_WINDOW_ICON,
|
icon=DEFAULT_WINDOW_ICON,
|
||||||
force_toplevel=False
|
force_toplevel=False,
|
||||||
|
alpha_channel=1,
|
||||||
return_keyboard_events=False,
|
return_keyboard_events=False,
|
||||||
use_default_focus=True,
|
use_default_focus=True,
|
||||||
text_justification=None,
|
text_justification=None,
|
||||||
|
@ -1228,6 +1229,7 @@ Parameter Descriptions. You will find these same parameters specified for each
|
||||||
auto_close_duration - Duration in seconds before window closes
|
auto_close_duration - Duration in seconds before window closes
|
||||||
icon - .ICO file that will appear on the Task Bar and end of Title Bar
|
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
|
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
|
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
|
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.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.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.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.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
|
window.Fill(values_dict) - Fill each Element with entry from the dictionary passed in
|
||||||
|
@ -1290,6 +1292,9 @@ There are a few methods (functions) that you will see in this document that act
|
||||||
window.Disable() - Use to disable the window inpurt when opening another window on top of the primnary 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.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
|
## 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.
|
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
|
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()
|
#### 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()
|
#### 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.
|
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)
|
#### SetIcon(icon, pngbase64)
|
||||||
Sets the window's icon that will be shown on the titlebar.
|
Sets the window's icon that will be shown on the titlebar. Can either be a filename or a base64 string.
|
||||||
|
|
||||||
#### Fill(values_dict)
|
#### Fill(values_dict)
|
||||||
Populates the windows fields with the values shown in the dictionary.
|
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
|
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)
|
#### SaveToDisk(filename)
|
||||||
|
|
||||||
Saves the window's values to disk
|
Saves the window's values to disk
|
||||||
|
@ -1673,6 +1682,7 @@ Also known as a drop-down list. Only required parameter is the list of choices.
|
||||||
background_color=None
|
background_color=None
|
||||||
text_color=None
|
text_color=None
|
||||||
change_submits=False
|
change_submits=False
|
||||||
|
readonly=True
|
||||||
disabled=False
|
disabled=False
|
||||||
key=None
|
key=None
|
||||||
pad=None
|
pad=None
|
||||||
|
@ -1687,6 +1697,7 @@ Also known as a drop-down list. Only required parameter is the list of choices.
|
||||||
text_color - color to use for the typed text
|
text_color - color to use for the typed text
|
||||||
change_submits - Bool. If set causes Read to immediately return if the selected value changes
|
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
|
key - Dictionary key to use for return values
|
||||||
pad - (x,y) Amount of padding to put around element in pixels
|
pad - (x,y) Amount of padding to put around element in pixels
|
||||||
tooltip - Text string. If set, hovering over field will popup the text
|
tooltip - Text string. If set, hovering over field will popup the text
|
||||||
|
@ -1695,12 +1706,13 @@ Shortcut functions - Combo, DropDown, Drop
|
||||||
|
|
||||||
### Combo Methods
|
### Combo Methods
|
||||||
```python
|
```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
|
value - change which value is current selected
|
||||||
values - change list of choices
|
values - change list of choices
|
||||||
set_to_index - change selection to a particular choice
|
set_to_index - change selection to a particular choice
|
||||||
disable - if True will disable element
|
disable - if True will disable element
|
||||||
|
readonly - if True will make element readonly
|
||||||
|
|
||||||
## Listbox Element
|
## 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).
|
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**
|
**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.
|
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`.
|
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,6 +2625,17 @@ Let me say up front that the Table Element has Beta status. The reason is that s
|
||||||
key - key used to lookup element
|
key - key used to lookup element
|
||||||
tooltip - tooltip text
|
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
|
## Tree Element
|
||||||
|
|
||||||
|
@ -2873,17 +2896,59 @@ The reasoning behind this is that Persistent Windows are often "forms". When "s
|
||||||
|
|
||||||
|
|
||||||
## Asynchronous (Non-Blocking) windows
|
## Asynchronous (Non-Blocking) windows
|
||||||
So you want to be a wizard do ya? Well go boldly!
|
Not so fast buddy!!
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
|
||||||
|
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
|
### Instead of ReadNonBlocking --- Use `change_submits = True` or return_keyboard_events = True
|
||||||
|
@ -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 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.
|
There are 2 methods of interacting with non-blocking windows.
|
||||||
1. Read the window just as you would a normal window
|
1. Read the window just as you would a normal window
|
||||||
|
@ -2954,38 +3019,34 @@ See the sample code on the GitHub named Demo Media Player for another example of
|
||||||
import PySimpleGUI as sg
|
import PySimpleGUI as sg
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# window that doesn't block
|
# ---------------- Create Form ----------------
|
||||||
# Make a window, but don't use context manager
|
sg.ChangeLookAndFeel('Black')
|
||||||
window = sg.Window('Running Timer', auto_size_text=True)
|
sg.SetOptions(element_padding=(0, 0))
|
||||||
|
|
||||||
# Create the layout
|
layout = [[sg.Text('')],
|
||||||
window_rows = [[sg.Text('Non-blocking GUI with updates')],
|
[sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')],
|
||||||
[sg.Text('', size=(8, 2), font=('Helvetica', 20), key='output') ],
|
[sg.ReadButton('Pause', key='button', button_color=('white', '#001480')),
|
||||||
[sg.Button('Quit')]]
|
sg.ReadButton('Reset', button_color=('white', '#007339'), key='Reset'),
|
||||||
# Layout the rows of the window and perform a read. Indicate the window is non-blocking!
|
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
|
||||||
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
|
|
||||||
#
|
|
||||||
|
|
||||||
for i in range(1, 1000):
|
# ---------------- main loop ----------------
|
||||||
window.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
|
current_time = 0
|
||||||
event, values = window.ReadNonBlocking()
|
paused = False
|
||||||
if values is None or event == 'Quit':
|
start_time = int(round(time.time() * 100))
|
||||||
break
|
while (True):
|
||||||
time.sleep(.01)
|
# --------- Read and update window --------
|
||||||
else:
|
event, values = window.Read(timeout=10)
|
||||||
window.CloseNonBlocking()
|
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))
|
||||||
|
|
||||||
|
|
||||||
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()`.
|
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.
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -3382,6 +3443,7 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
|
||||||
| 03.09.01 | Oct 8, 2018
|
| 03.09.01 | Oct 8, 2018
|
||||||
| 3.9.3 & 1.1.3 | Oct 11, 2018
|
| 3.9.3 & 1.1.3 | Oct 11, 2018
|
||||||
| 3.9.4 & 1.1.4 | Oct 16, 2018
|
| 3.9.4 & 1.1.4 | Oct 16, 2018
|
||||||
|
| 3.10.1 & 1.2.1 | Oct 20, 2018
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
* BUG fix in listbox double-click. First bug fix in months
|
||||||
* New Look And Feel capability. List predefined settings using ListOfLookAndFeelValues
|
* 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
|
### Upcoming
|
||||||
|
|
Loading…
Reference in New Issue