Merge pull request #643 from MikeTheWatchGuy/Dev-latest

Dev latest
This commit is contained in:
MikeTheWatchGuy 2018-11-02 13:33:01 -04:00 committed by GitHub
commit fc2672d32c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 343 additions and 124 deletions

31
Demo_Stdout.py Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Demo program that reroutes stdout and stderr.
Type something in the input box and click Print
Whatever you typed is "printed" using a standard print statement
Use the Output Element in your window layout to reroute stdout
You will see the output of the print in the Output Element in the center of the window
"""
layout = [
[sg.Text('Type something in input field and click print')],
[sg.In()],
[sg.Output()],
[sg.Button('Print')]
]
window = sg.Window('Reroute stdout').Layout(layout)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
print('You typed: ', values[0])

View File

@ -190,15 +190,18 @@ ThisRow = 555666777 # magic number
# DEFAULT_WINDOW_ICON = '' # DEFAULT_WINDOW_ICON = ''
MESSAGE_BOX_LINE_WIDTH = 60 MESSAGE_BOX_LINE_WIDTH = 60
# "Special" Key Values.. reserved
# Key representing a Read timeout # Key representing a Read timeout
TIMEOUT_KEY = '__timeout__' TIMEOUT_KEY = '__TIMEOUT__'
# Key indicating should not create any return values for element
WRITE_ONLY_KEY = '__WRITE ONLY__'
# a shameful global variable. This represents the top-level window information. Needed because opening a second window is different than opening the first. # a shameful global variable. This represents the top-level window information. Needed because opening a second window is different than opening the first.
class MyWindows(object): class MyWindows(object):
def __init__(self): def __init__(self):
self.NumOpenWindows = 0 self.NumOpenWindows = 0
self.user_defined_icon = None self.user_defined_icon = None
self.hidden_master_root = None
def Decrement(self): def Decrement(self):
self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0 self.NumOpenWindows -= 1 * (self.NumOpenWindows != 0) # decrement if not 0
@ -405,7 +408,6 @@ class Element(object):
button_element.ButtonCallBack() button_element.ButtonCallBack()
def ListboxSelectHandler(self, event): def ListboxSelectHandler(self, event):
MyForm = self.ParentForm
# first, get the results table built # first, get the results table built
# modify the Results table in the parent FlexForm object # modify the Results table in the parent FlexForm object
if self.Key is not None: if self.Key is not None:
@ -417,7 +419,6 @@ class Element(object):
self.ParentForm.TKroot.quit() # kick the users out of the mainloop self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def ComboboxSelectHandler(self, event): def ComboboxSelectHandler(self, event):
MyForm = self.ParentForm
# first, get the results table built # first, get the results table built
# modify the Results table in the parent FlexForm object # modify the Results table in the parent FlexForm object
if self.Key is not None: if self.Key is not None:
@ -428,8 +429,16 @@ class Element(object):
if self.ParentForm.CurrentlyRunningMainloop: if self.ParentForm.CurrentlyRunningMainloop:
self.ParentForm.TKroot.quit() # kick the users out of the mainloop self.ParentForm.TKroot.quit() # kick the users out of the mainloop
def RadioHandler(self):
if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key
else:
self.ParentForm.LastButtonClicked = ''
self.ParentForm.FormRemainedOpen = True
if self.ParentForm.CurrentlyRunningMainloop:
self.ParentForm.TKroot.quit()
def CheckboxHandler(self): def CheckboxHandler(self):
MyForm = self.ParentForm
if self.Key is not None: if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key self.ParentForm.LastButtonClicked = self.Key
else: else:
@ -439,7 +448,6 @@ class Element(object):
self.ParentForm.TKroot.quit() self.ParentForm.TKroot.quit()
def TabGroupSelectHandler(self, event): def TabGroupSelectHandler(self, event):
MyForm = self.ParentForm
if self.Key is not None: if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key self.ParentForm.LastButtonClicked = self.Key
else: else:
@ -449,7 +457,6 @@ class Element(object):
self.ParentForm.TKroot.quit() self.ParentForm.TKroot.quit()
def KeyboardHandler(self, event): def KeyboardHandler(self, event):
MyForm = self.ParentForm
if self.Key is not None: if self.Key is not None:
self.ParentForm.LastButtonClicked = self.Key self.ParentForm.LastButtonClicked = self.Key
else: else:
@ -520,6 +527,13 @@ class InputText(Element):
def Get(self): def Get(self):
return self.TKStringVar.get() return self.TKStringVar.get()
def SetFocus(self):
try:
self.TKEntry.focus_set()
except:
pass
def __del__(self): def __del__(self):
super().__del__() super().__del__()
@ -535,7 +549,7 @@ Input = InputText
class InputCombo(Element): class InputCombo(Element):
def __init__(self, values, default_value=None, size=(None, None), auto_size_text=None, background_color=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, text_color=None, change_submits=False, disabled=False, key=None, pad=None, tooltip=None,
readonly=False): readonly=False, font=None):
''' '''
Input Combo Box Element (also called Dropdown box) Input Combo Box Element (also called Dropdown box)
:param values: :param values:
@ -554,9 +568,9 @@ class InputCombo(Element):
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, 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) text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT)
def Update(self, value=None, values=None, set_to_index=None, disabled=None, readonly=None): def Update(self, value=None, values=None, set_to_index=None, disabled=None, readonly=None, font=None):
if values is not None: if values is not None:
try: try:
self.TKCombo['values'] = values self.TKCombo['values'] = values
@ -587,6 +601,8 @@ class InputCombo(Element):
self.Readonly = readonly self.Readonly = readonly
if self.Readonly: if self.Readonly:
self.TKCombo['state'] = 'readonly' self.TKCombo['state'] = 'readonly'
if font is not None:
self.TKText.configure(font=font)
def __del__(self): def __del__(self):
try: try:
@ -745,7 +761,7 @@ class Listbox(Element):
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Radio(Element): class Radio(Element):
def __init__(self, text, group_id, default=False, disabled=False, size=(None, None), auto_size_text=None, def __init__(self, text, group_id, default=False, disabled=False, size=(None, None), auto_size_text=None,
background_color=None, text_color=None, font=None, key=None, pad=None, tooltip=None): background_color=None, text_color=None, font=None, key=None, pad=None, tooltip=None, change_submits=False):
''' '''
Radio Button Element Radio Button Element
:param text: :param text:
@ -760,6 +776,7 @@ class Radio(Element):
:param key: :param key:
:param pad: :param pad:
:param tooltip: :param tooltip:
:param change_submits:
''' '''
self.InitialState = default self.InitialState = default
self.Text = text self.Text = text
@ -767,7 +784,8 @@ class Radio(Element):
self.GroupID = group_id self.GroupID = group_id
self.Value = None self.Value = None
self.Disabled = disabled self.Disabled = disabled
self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR self.TextColor = text_color or DEFAULT_TEXT_COLOR
self.ChangeSubmits = change_submits
super().__init__(ELEM_TYPE_INPUT_RADIO, size=size, auto_size_text=auto_size_text, font=font, super().__init__(ELEM_TYPE_INPUT_RADIO, size=size, auto_size_text=auto_size_text, font=font,
background_color=background_color, text_color=self.TextColor, key=key, pad=pad, background_color=background_color, text_color=self.TextColor, key=key, pad=pad,
@ -931,7 +949,7 @@ class Spin(Element):
class Multiline(Element): class Multiline(Element):
def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, size=(None, None), def __init__(self, default_text='', enter_submits=False, disabled=False, autoscroll=False, size=(None, None),
auto_size_text=None, background_color=None, text_color=None, change_submits=False, do_not_clear=False, key=None, focus=False, auto_size_text=None, background_color=None, text_color=None, change_submits=False, do_not_clear=False, key=None, focus=False,
pad=None, tooltip=None): font=None, pad=None, tooltip=None):
''' '''
Multiline Element Multiline Element
:param default_text: :param default_text:
@ -947,6 +965,7 @@ class Multiline(Element):
:param focus: :param focus:
:param pad: :param pad:
:param tooltip: :param tooltip:
:param font:
''' '''
self.DefaultText = default_text self.DefaultText = default_text
self.EnterSubmits = enter_submits self.EnterSubmits = enter_submits
@ -959,10 +978,10 @@ class Multiline(Element):
self.ChangeSubmits = change_submits self.ChangeSubmits = change_submits
super().__init__(ELEM_TYPE_INPUT_MULTILINE, size=size, auto_size_text=auto_size_text, background_color=bg, super().__init__(ELEM_TYPE_INPUT_MULTILINE, size=size, auto_size_text=auto_size_text, background_color=bg,
text_color=fg, key=key, pad=pad, tooltip=tooltip) text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT)
return return
def Update(self, value=None, disabled=None, append=False): def Update(self, value=None, disabled=None, append=False, font=None):
if value is not None: if value is not None:
try: try:
if not append: if not append:
@ -977,10 +996,19 @@ class Multiline(Element):
self.TKText.configure(state='disabled') self.TKText.configure(state='disabled')
elif disabled == False: elif disabled == False:
self.TKText.configure(state='normal') self.TKText.configure(state='normal')
if font is not None:
self.TKText.configure(font=font)
def Get(self): def Get(self):
return self.TKText.get(1.0, tk.END) return self.TKText.get(1.0, tk.END)
def SetFocus(self):
try:
self.TKText.focus_set()
except:
pass
def __del__(self): def __del__(self):
super().__del__() super().__del__()
@ -1240,6 +1268,7 @@ class Button(Element):
self.Focus = focus self.Focus = focus
self.TKCal = None self.TKCal = None
self.CalendarCloseWhenChosen = None self.CalendarCloseWhenChosen = None
self.DefaultDate_M_D_Y = (None, None, None)
self.InitialFolder = initial_folder self.InitialFolder = initial_folder
self.Disabled = disabled self.Disabled = disabled
@ -1369,7 +1398,7 @@ class Button(Element):
should_submit_window = False should_submit_window = False
root = tk.Toplevel() root = tk.Toplevel()
root.title('Calendar Chooser') root.title('Calendar Chooser')
self.TKCal = TKCalendar(master=root, firstweekday=calendar.SUNDAY, target_element=target_element, close_when_chosen=self.CalendarCloseWhenChosen ) self.TKCal = TKCalendar(master=root, firstweekday=calendar.SUNDAY, target_element=target_element, close_when_chosen=self.CalendarCloseWhenChosen, default_date=self.DefaultDate_M_D_Y )
self.TKCal.pack(expand=1, fill='both') self.TKCal.pack(expand=1, fill='both')
root.update() root.update()
@ -2067,6 +2096,12 @@ class TkScrollableFrame(tk.Frame):
self.TKFrame.config(borderwidth=0, highlightthickness=0) self.TKFrame.config(borderwidth=0, highlightthickness=0)
self.config(borderwidth=0, highlightthickness=0) self.config(borderwidth=0, highlightthickness=0)
# scrollbar = tk.Scrollbar(frame)
# scrollbar.pack(side=tk.RIGHT, fill='y')
# scrollbar.config(command=treeview.yview)
# self.vscrollbar.config(command=self.TKFrame.yview)
# self.TKFrame.configure(yscrollcommand=self.vscrollbar.set)
self.bind('<Configure>', self.set_scrollregion) self.bind('<Configure>', self.set_scrollregion)
self.bind_mouse_scroll(self.canvas, self.yscroll) self.bind_mouse_scroll(self.canvas, self.yscroll)
@ -2182,7 +2217,7 @@ class TKCalendar(tkinter.ttk.Frame):
datetime = calendar.datetime.datetime datetime = calendar.datetime.datetime
timedelta = calendar.datetime.timedelta timedelta = calendar.datetime.timedelta
def __init__(self, master=None, target_element=None, close_when_chosen=True, **kw): def __init__(self, master=None, target_element=None, close_when_chosen=True, default_date=(None, None, None), **kw):
""" """
WIDGET-SPECIFIC OPTIONS WIDGET-SPECIFIC OPTIONS
@ -2190,15 +2225,16 @@ class TKCalendar(tkinter.ttk.Frame):
selectforeground selectforeground
""" """
self._TargetElement = target_element self._TargetElement = target_element
default_mon, default_day, default_year = default_date
# remove custom options from kw before initializating ttk.Frame # remove custom options from kw before initializating ttk.Frame
fwday = kw.pop('firstweekday', calendar.MONDAY) fwday = kw.pop('firstweekday', calendar.MONDAY)
year = kw.pop('year', self.datetime.now().year) year = kw.pop('year', default_year or self.datetime.now().year)
month = kw.pop('month', self.datetime.now().month) month = kw.pop('month', default_mon or self.datetime.now().month)
locale = kw.pop('locale', None) locale = kw.pop('locale', None)
sel_bg = kw.pop('selectbackground', '#ecffc4') sel_bg = kw.pop('selectbackground', '#ecffc4')
sel_fg = kw.pop('selectforeground', '#05640e') sel_fg = kw.pop('selectforeground', '#05640e')
self._date = self.datetime(year, month, 1) self._date = self.datetime(year, month, default_day or 1)
self._selection = None # no date selected self._selection = None # no date selected
self._master = master self._master = master
self.close_when_chosen = close_when_chosen self.close_when_chosen = close_when_chosen
@ -2582,17 +2618,25 @@ class Tree(Element):
self.add_treeview_data(node) self.add_treeview_data(node)
def Update(self, values=None): def Update(self, values=None, key=None, value=None, text=None):
if values is None: if values is not None:
return children = self.TKTreeview.get_children()
children = self.TKTreeview.get_children() for i in children:
for i in children: self.TKTreeview.detach(i)
self.TKTreeview.detach(i) self.TKTreeview.delete(i)
self.TKTreeview.delete(i) children = self.TKTreeview.get_children()
children = self.TKTreeview.get_children() self.TreeData = values
self.TreeData = values self.add_treeview_data(self.TreeData.root_node)
self.add_treeview_data(self.TreeData.root_node) self.SelectedRows = []
self.SelectedRows = [] if key is not None:
item = self.TKTreeview.item(key)
if value is not None:
self.TKTreeview.item(key, values=value)
if text is not None:
self.TKTreeview.item(key, text=text)
item = self.TKTreeview.item(key)
return self
def __del__(self): def __del__(self):
super().__del__() super().__del__()
@ -2624,11 +2668,6 @@ class TreeData(object):
parent_node = self.tree_dict[parent] parent_node = self.tree_dict[parent]
parent_node._Add(node) parent_node._Add(node)
# def _print_node(self, node):
# # print(f'Node: {node.text}')
# # print(f'Children = {[c.text for c in node.children]}')
# for node in node.children:
# self._print_node(node)
def __repr__(self): def __repr__(self):
return self._NodeStr(self.root_node, 1) return self._NodeStr(self.root_node, 1)
@ -2940,13 +2979,11 @@ class Window(object):
else: else:
return self.ReturnValues return self.ReturnValues
def ReadNonBlocking(self, Message=''): def ReadNonBlocking(self):
if self.TKrootDestroyed: if self.TKrootDestroyed:
return None, None return None, None
if not self.Shown: if not self.Shown:
self.Show(non_blocking=True) self.Show(non_blocking=True)
if Message:
print(Message)
try: try:
rc = self.TKroot.update() rc = self.TKroot.update()
except: except:
@ -3086,7 +3123,8 @@ class Window(object):
self.RootNeedsDestroying = True self.RootNeedsDestroying = True
return None return None
def CloseNonBlocking(self):
def Close(self):
if self.TKrootDestroyed: if self.TKrootDestroyed:
return return
try: try:
@ -3095,16 +3133,16 @@ class Window(object):
except: except:
pass pass
CloseNonBlockingForm = CloseNonBlocking CloseNonBlockingForm = Close
Close = CloseNonBlockingForm CloseNonBlocking = Close
# IT FINALLY WORKED! 29-Oct-2018 was the first time this damned thing got called # IT FINALLY WORKED! 29-Oct-2018 was the first time this damned thing got called
def OnClosingCallback(self): def OnClosingCallback(self):
print('Got closing callback') # print('Got closing callback')
self.TKroot.quit() # kick the users out of the mainloop self.TKroot.quit() # kick the users out of the mainloop
if self.CurrentlyRunningMainloop: # quit if this is the current mainloop, otherwise don't quit! if self.CurrentlyRunningMainloop: # quit if this is the current mainloop, otherwise don't quit!
self.TKroot.destroy() # kick the users out of the mainloop self.TKroot.destroy() # kick the users out of the mainloop
self.TKrootDestroyed = True
return return
@ -3150,6 +3188,10 @@ class Window(object):
except: except:
pass pass
def CurrentLocation(self):
return int(self.TKroot.winfo_x()), int(self.TKroot.winfo_y())
def __enter__(self): def __enter__(self):
return self return self
@ -3161,10 +3203,7 @@ class Window(object):
for row in self.Rows: for row in self.Rows:
for element in row: for element in row:
element.__del__() element.__del__()
# try:
# del(self.TKroot)
# except:
# pass
FlexForm = Window FlexForm = Window
@ -3370,7 +3409,7 @@ def DummyButton(button_text, image_filename=None, image_data=None, image_size=(N
# ------------------------- Calendar Chooser Button lazy function ------------------------- # # ------------------------- Calendar Chooser Button lazy function ------------------------- #
def CalendarButton(button_text, target=(None, None), close_when_date_chosen=True, image_filename=None, image_data=None, image_size=(None, None), def CalendarButton(button_text, target=(None, None), close_when_date_chosen=True, default_date_m_d_y=(None,None,None), image_filename=None, image_data=None, image_size=(None, None),
image_subsample=None, tooltip=None, border_width=None, size=(None, None), auto_size_button=None, image_subsample=None, tooltip=None, border_width=None, size=(None, None), auto_size_button=None,
button_color=None, disabled=False, font=None, bind_return_key=False, focus=False, pad=None, button_color=None, disabled=False, font=None, bind_return_key=False, focus=False, pad=None,
key=None): key=None):
@ -3380,6 +3419,7 @@ def CalendarButton(button_text, target=(None, None), close_when_date_chosen=True
auto_size_button=auto_size_button, button_color=button_color, font=font, disabled=disabled, auto_size_button=auto_size_button, button_color=button_color, font=font, disabled=disabled,
bind_return_key=bind_return_key, focus=focus, pad=pad, key=key) bind_return_key=bind_return_key, focus=focus, pad=pad, key=key)
button.CalendarCloseWhenChosen = close_when_date_chosen button.CalendarCloseWhenChosen = close_when_date_chosen
button.DefaultDate_M_D_Y = default_date_m_d_y
return button return button
@ -3453,6 +3493,8 @@ def BuildResultsForSubform(form, initialize_only, top_level_form):
button_pressed_text = top_level_form.LastButtonClicked button_pressed_text = top_level_form.LastButtonClicked
for row_num, row in enumerate(form.Rows): for row_num, row in enumerate(form.Rows):
for col_num, element in enumerate(row): for col_num, element in enumerate(row):
if element.Key is not None and WRITE_ONLY_KEY in str(element.Key):
continue
value = None value = None
if element.Type == ELEM_TYPE_COLUMN: if element.Type == ELEM_TYPE_COLUMN:
element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter
@ -4199,8 +4241,13 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.TKIntVar = RadVar # store the RadVar in Radio object element.TKIntVar = RadVar # store the RadVar in Radio object
if default_value: # if this radio is the one selected, set RadVar to match if default_value: # if this radio is the one selected, set RadVar to match
element.TKIntVar.set(value) element.TKIntVar.set(value)
element.TKRadio = tk.Radiobutton(tk_row_frame, anchor=tk.NW, text=element.Text, width=width, if element.ChangeSubmits:
variable=element.TKIntVar, value=value, bd=border_depth, font=font) element.TKRadio = tk.Radiobutton(tk_row_frame, anchor=tk.NW, text=element.Text, width=width,
variable=element.TKIntVar, value=value, bd=border_depth, font=font,
command=element.RadioHandler)
else:
element.TKRadio = tk.Radiobutton(tk_row_frame, anchor=tk.NW, text=element.Text, width=width,
variable=element.TKIntVar, value=value, bd=border_depth, font=font)
if not element.BackgroundColor in (None, COLOR_SYSTEM_DEFAULT): if not element.BackgroundColor in (None, COLOR_SYSTEM_DEFAULT):
element.TKRadio.configure(background=element.BackgroundColor) element.TKRadio.configure(background=element.BackgroundColor)
element.TKRadio.configure(selectcolor=element.BackgroundColor) element.TKRadio.configure(selectcolor=element.BackgroundColor)
@ -4643,7 +4690,16 @@ def StartupTK(my_flex_form):
# print('Starting TK open Windows = {}'.format(ow)) # print('Starting TK open Windows = {}'.format(ow))
if not ow and not my_flex_form.ForceTopLevel: if not ow and not my_flex_form.ForceTopLevel:
root = tk.Tk() # if first window being created, make a throwaway, hidden master root. This stops one user
# window from becoming the child of another user window. All windows are children of this
# hidden window
_my_windows.Increment()
_my_windows.hidden_master_root = tk.Tk()
_my_windows.hidden_master_root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
_my_windows.hidden_master_root.wm_overrideredirect(True)
# root = tk.Tk() # users windows are no longer using tk.Tk. They are all Toplevel windows
root = tk.Toplevel()
else: else:
root = tk.Toplevel() root = tk.Toplevel()
@ -5061,16 +5117,20 @@ _easy_print_data = None # global variable... I'm cheating
class DebugWin(object): class DebugWin(object):
def __init__(self, size=(None, None)): def __init__(self, size=(None, None), location=(None, None), font=None, no_titlebar=False, no_button=False, grab_anywhere=False, keep_on_top=False):
# Show a form that's a running counter # Show a form that's a running counter
win_size = size if size != (None, None) else DEFAULT_DEBUG_WINDOW_SIZE win_size = size if size != (None, None) else DEFAULT_DEBUG_WINDOW_SIZE
self.form = Window('Debug Window', auto_size_text=True, font=('Courier New', 12)) self.window = Window('Debug Window', no_titlebar=no_titlebar, auto_size_text=True, location=location, font=font or ('Courier New', 10), grab_anywhere=grab_anywhere, keep_on_top=keep_on_top)
self.output_element = Output(size=win_size) self.output_element = Output(size=win_size)
self.form_rows = [[Text('EasyPrint Output')], if no_button:
[self.output_element], self.layout = [[self.output_element]]
[DummyButton('Quit')]] else:
self.form.AddRows(self.form_rows) self.layout = [
self.form.Show(non_blocking=True) # Show a ;non-blocking form, returns immediately [self.output_element],
[DummyButton('Quit')]
]
self.window.AddRows(self.layout)
self.window.Read(timeout=0) # Show a non-blocking form, returns immediately
return return
def Print(self, *args, **_3to2kwargs): def Print(self, *args, **_3to2kwargs):
@ -5080,45 +5140,43 @@ class DebugWin(object):
else: end = None else: end = None
sepchar = sep if sep is not None else ' ' sepchar = sep if sep is not None else ' '
endchar = end if end is not None else '\n' endchar = end if end is not None else '\n'
if self.window is None: # if window was destroyed already, just print
print(*args, sep=sepchar, end=endchar)
return
event, values = self.window.Read(timeout=0)
if event == 'Quit' or event is None:
self.Close()
print(*args, sep=sepchar, end=endchar) print(*args, sep=sepchar, end=endchar)
# for a in args: # Add extra check to see if the window was closed... if closed by X sometimes am not told
# msg = str(a) try:
# print(msg, end="", sep=sepchar) state = self.window.TKroot.state()
# print(1, 2, 3, sep='-') except:
# if end is None: self.Close()
# print("")
self.form.ReadNonBlocking()
def Close(self): def Close(self):
self.form.CloseNonBlockingForm() self.window.Close()
self.form.__del__() self.window.__del__()
self.window = None
def Print(*args, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None, None)
EasyPrint(*args, size=size, end=end, sep=sep)
def PrintClose(): def PrintClose():
EasyPrintClose() EasyPrintClose()
def eprint(*args, **_3to2kwargs):
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None, None)
EasyPrint(*args, size=size, end=end, sep=sep)
def EasyPrint(*args, **_3to2kwargs): def EasyPrint(*args, **_3to2kwargs):
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_button' in _3to2kwargs: no_button = _3to2kwargs['no_button']; del _3to2kwargs['no_button']
else: no_button = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = False
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None, None)
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep'] if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end'] if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
@ -5128,30 +5186,18 @@ def EasyPrint(*args, **_3to2kwargs):
global _easy_print_data global _easy_print_data
if _easy_print_data is None: if _easy_print_data is None:
_easy_print_data = DebugWin(size=size) _easy_print_data = DebugWin(size=size, location=location, font=font, no_titlebar=no_titlebar, no_button=no_button, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top)
_easy_print_data.Print(*args, end=end, sep=sep) _easy_print_data.Print(*args, end=end, sep=sep)
Print = EasyPrint
def EasyPrintold(*args, **_3to2kwargs): eprint = EasyPrint
if 'sep' in _3to2kwargs: sep = _3to2kwargs['sep']; del _3to2kwargs['sep']
else: sep = None
if 'end' in _3to2kwargs: end = _3to2kwargs['end']; del _3to2kwargs['end']
else: end = None
if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size']
else: size = (None, None)
if 'easy_print_data' not in EasyPrint.__dict__: # use a function property to save DebugWin object (static variable)
EasyPrint.easy_print_data = DebugWin(size=size)
if EasyPrint.easy_print_data is None:
EasyPrint.easy_print_data = DebugWin(size=size)
EasyPrint.easy_print_data.Print(*args, end=end, sep=sep)
def EasyPrintClose(): def EasyPrintClose():
if 'easy_print_data' in EasyPrint.__dict__: global _easy_print_data
if EasyPrint.easy_print_data is not None: if _easy_print_data is not None:
EasyPrint.easy_print_data._Close() _easy_print_data.Close()
EasyPrint.easy_print_data = None _easy_print_data = None
# del EasyPrint.easy_print_data
# ======================== Scrolled Text Box =====# # ======================== Scrolled Text Box =====#
@ -5825,7 +5871,7 @@ def Popup(*args, **_3to2kwargs):
pad=((20, 0), 3))) pad=((20, 0), 3)))
if non_blocking: if non_blocking:
button, values = window.ReadNonBlocking() button, values = window.Read(timeout=0)
else: else:
button, values = window.Read() button, values = window.Read()
@ -5972,7 +6018,7 @@ def PopupQuick(*args, **_3to2kwargs):
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking'] if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = True else: non_blocking = True
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration'] if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = 1 else: auto_close_duration = 2
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close'] if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = True else: auto_close = True
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color'] if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
@ -6009,6 +6055,62 @@ def PopupQuick(*args, **_3to2kwargs):
font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location) font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupQuick - a NonBlocking, Self-closing Popup with no titlebar and no buttons ---------------------------
def PopupQuickMessage(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
else: location = (None, None)
if 'keep_on_top' in _3to2kwargs: keep_on_top = _3to2kwargs['keep_on_top']; del _3to2kwargs['keep_on_top']
else: keep_on_top = False
if 'grab_anywhere' in _3to2kwargs: grab_anywhere = _3to2kwargs['grab_anywhere']; del _3to2kwargs['grab_anywhere']
else: grab_anywhere = False
if 'no_titlebar' in _3to2kwargs: no_titlebar = _3to2kwargs['no_titlebar']; del _3to2kwargs['no_titlebar']
else: no_titlebar = True
if 'font' in _3to2kwargs: font = _3to2kwargs['font']; del _3to2kwargs['font']
else: font = None
if 'line_width' in _3to2kwargs: line_width = _3to2kwargs['line_width']; del _3to2kwargs['line_width']
else: line_width = None
if 'icon' in _3to2kwargs: icon = _3to2kwargs['icon']; del _3to2kwargs['icon']
else: icon = DEFAULT_WINDOW_ICON
if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking']
else: non_blocking = True
if 'auto_close_duration' in _3to2kwargs: auto_close_duration = _3to2kwargs['auto_close_duration']; del _3to2kwargs['auto_close_duration']
else: auto_close_duration = 2
if 'auto_close' in _3to2kwargs: auto_close = _3to2kwargs['auto_close']; del _3to2kwargs['auto_close']
else: auto_close = True
if 'text_color' in _3to2kwargs: text_color = _3to2kwargs['text_color']; del _3to2kwargs['text_color']
else: text_color = None
if 'background_color' in _3to2kwargs: background_color = _3to2kwargs['background_color']; del _3to2kwargs['background_color']
else: background_color = None
if 'button_color' in _3to2kwargs: button_color = _3to2kwargs['button_color']; del _3to2kwargs['button_color']
else: button_color = None
if 'button_type' in _3to2kwargs: button_type = _3to2kwargs['button_type']; del _3to2kwargs['button_type']
else: button_type = POPUP_BUTTONS_NO_BUTTONS
"""
Show Popup box that doesn't block and closes itself
:param args:
:param button_type:
:param button_color:
:param background_color:
:param text_color:
:param auto_close:
:param auto_close_duration:
:param non_blocking:
:param icon:
:param line_width:
:param font:
:param no_titlebar:
:param grab_anywhere:
:param keep_on_top:
:param location:
:return:
"""
Popup(*args, button_color=button_color, background_color=background_color, text_color=text_color,
button_type=button_type,
auto_close=auto_close, auto_close_duration=auto_close_duration, non_blocking=non_blocking, icon=icon,
line_width=line_width,
font=font, no_titlebar=no_titlebar, grab_anywhere=grab_anywhere, keep_on_top=keep_on_top, location=location)
# --------------------------- PopupNoTitlebar --------------------------- # --------------------------- PopupNoTitlebar ---------------------------
def PopupNoTitlebar(*args, **_3to2kwargs): def PopupNoTitlebar(*args, **_3to2kwargs):
if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location'] if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location']
@ -6395,7 +6497,7 @@ def PopupYesNo(*args, **_3to2kwargs):
def PopupGetFolder(message, default_path='', no_window=False, size=(None, None), button_color=None, def PopupGetFolder(message, default_path='', no_window=False, size=(None, None), button_color=None,
background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, background_color=None, text_color=None, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False,
grab_anywhere=False, keep_on_top=False, location=(None, None)): grab_anywhere=False, keep_on_top=False, location=(None, None), initial_folder=None):
""" """
Display popup with text entry field and browse button. Browse for folder Display popup with text entry field and browse button. Browse for folder
:param message: :param message:
@ -6413,8 +6515,14 @@ def PopupGetFolder(message, default_path='', no_window=False, size=(None, None),
:param location: :param location:
:return: Contents of text field. None if closed using X or cancelled :return: Contents of text field. None if closed using X or cancelled
""" """
global _my_windows
if no_window: if no_window:
root = tk.Tk() if _my_windows.NumOpenWindows:
root = tk.Toplevel()
else:
root = tk.Tk()
try: try:
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint' root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
except: except:
@ -6424,7 +6532,7 @@ def PopupGetFolder(message, default_path='', no_window=False, size=(None, None),
return folder_name return folder_name
layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)],
[InputText(default_text=default_path, size=size), FolderBrowse()], [InputText(default_text=default_path, size=size), FolderBrowse(initial_folder=initial_folder)],
[CloseButton('Ok', size=(5, 1), bind_return_key=True), CloseButton('Cancel', size=(5, 1))]] [CloseButton('Ok', size=(5, 1), bind_return_key=True), CloseButton('Cancel', size=(5, 1))]]
window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color, window = Window(title=message, icon=icon, auto_size_text=True, button_color=button_color,
@ -6446,7 +6554,7 @@ def PopupGetFolder(message, default_path='', no_window=False, size=(None, None),
def PopupGetFile(message, default_path='', default_extension='', save_as=False, file_types=(("ALL Files", "*.*"),), def PopupGetFile(message, default_path='', default_extension='', save_as=False, file_types=(("ALL Files", "*.*"),),
no_window=False, size=(None, None), button_color=None, background_color=None, text_color=None, no_window=False, size=(None, None), button_color=None, background_color=None, text_color=None,
icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False, icon=DEFAULT_WINDOW_ICON, font=None, no_titlebar=False, grab_anywhere=False, keep_on_top=False,
location=(None, None)): location=(None, None), initial_folder=None):
""" """
Display popup with text entry field and browse button. Browse for file Display popup with text entry field and browse button. Browse for file
:param message: :param message:
@ -6467,8 +6575,14 @@ def PopupGetFile(message, default_path='', default_extension='', save_as=False,
:param location: :param location:
:return: string representing the path chosen, None if cancelled or window closed with X :return: string representing the path chosen, None if cancelled or window closed with X
""" """
global _my_windows
if no_window: if no_window:
root = tk.Tk() if _my_windows.NumOpenWindows:
root = tk.Toplevel()
else:
root = tk.Tk()
try: try:
root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint' root.attributes('-alpha', 0) # hide window while building it. makes for smoother 'paint'
except: except:
@ -6482,7 +6596,7 @@ def PopupGetFile(message, default_path='', default_extension='', save_as=False,
root.destroy() root.destroy()
return filename return filename
browse_button = SaveAs(file_types=file_types) if save_as else FileBrowse(file_types=file_types) browse_button = SaveAs(file_types=file_types, initial_folder=initial_folder) if save_as else FileBrowse(file_types=file_types, initial_folder=initial_folder)
layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)], layout = [[Text(message, auto_size_text=True, text_color=text_color, background_color=background_color)],
[InputText(default_text=default_path, size=size), browse_button], [InputText(default_text=default_path, size=size), browse_button],

View File

@ -24,9 +24,9 @@
## Now supports both Python 2.7 & 3 ## Now supports both Python 2.7 & 3
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.13.0-red.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.14.0-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.13.0-blue.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.14.0-blue.svg?longCache=true&style=for-the-badge)
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -3569,6 +3569,8 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 3.11.0 & 1.11.0 | Oct 28, 2018 | 3.11.0 & 1.11.0 | Oct 28, 2018
| 3.12.0 & 1.12.0 | Oct 28, 2018 | 3.12.0 & 1.12.0 | Oct 28, 2018
| 3.13.0 & 1.13.0 | Oct 29, 2018 | 3.13.0 & 1.13.0 | Oct 29, 2018
| 3.14.0 & 1.14.0 | Nov 2, 2018
## Release Notes ## Release Notes
2.3 - Sliders, Listbox's and Image elements (oh my!) 2.3 - Sliders, Listbox's and Image elements (oh my!)
@ -3797,7 +3799,7 @@ Emergency patch release... going out same day as previous release
* Progress meter uses new CloseButton * Progress meter uses new CloseButton
* Popups use new CloseButton * Popups use new CloseButton
## 3.13.0 & 1.13.0 ### 3.13.0 & 1.13.0
* Improved multiple window handling of Popups when the X is used to close * Improved multiple window handling of Popups when the X is used to close
* Change submits added for: * Change submits added for:
* Multiline * Multiline
@ -3809,6 +3811,39 @@ Emergency patch release... going out same day as previous release
* Scroll bars for Trees * Scroll bars for Trees
### 3.14.0 & 1.14.0
- More windowing changes...
using a hidden root windowing (Tk())
all children are Toplevel() windows
Read only setting for:
Input Text
Multiline
Font setting for InputCombo, Multiline
change_submits settinf for Radio Element
SetFocus for multiline, input elements
Default mon, day, year for calendar chooser button
Tree element update, added ability to change a single key
Message parm removed from ReadNonBlocking
Fix for closing windows using X
CurrentLocation method for Windows
Debug Window options
location
font
no_button
no_titlebar
grab_anywhere
keep_on_top
New Print / EasyPrint options
location
font
no_button
no_titlebar
grab_anywhere
keep_on_top
New popup, PopupQuickMessage
PopupGetFolder, PopupGetFile new initial_folder parm
### Upcoming ### Upcoming
Make suggestions people! Future release features Make suggestions people! Future release features
@ -3885,6 +3920,8 @@ GNU Lesser General Public License (LGPL 3) +
* Tony Crewe (anthony.crewe@gmail.com) Generously provided his classroom materials that he has written to teach a GUI course. If you're an educator and want to trade materials with Tony, he would like to hear from you. * Tony Crewe (anthony.crewe@gmail.com) Generously provided his classroom materials that he has written to teach a GUI course. If you're an educator and want to trade materials with Tony, he would like to hear from you.
* [spectre6000](https://github.com/spectre6000) - Readme updates * [spectre6000](https://github.com/spectre6000) - Readme updates
* [jackyOO7](https://github.com/jackyOO7) - Demo programs. OpenCV with realtime image processing, popup keyboard, input Combo read only option. * [jackyOO7](https://github.com/jackyOO7) - Demo programs. OpenCV with realtime image processing, popup keyboard, input Combo read only option.
* [AltoRetrato](https://github.com/AltoRetrato) - Fonts for multiline and combo
## How Do I ## How Do I
Finally, I must thank the fine folks at How Do I. Finally, I must thank the fine folks at How Do I.

View File

@ -24,9 +24,9 @@
## Now supports both Python 2.7 & 3 ## Now supports both Python 2.7 & 3
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.13.0-red.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.14.0-red.svg?longCache=true&style=for-the-badge)
![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.13.0-blue.svg?longCache=true&style=for-the-badge) ![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.14.0-blue.svg?longCache=true&style=for-the-badge)
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -3569,6 +3569,8 @@ A MikeTheWatchGuy production... entirely responsible for this code.... unless it
| 3.11.0 & 1.11.0 | Oct 28, 2018 | 3.11.0 & 1.11.0 | Oct 28, 2018
| 3.12.0 & 1.12.0 | Oct 28, 2018 | 3.12.0 & 1.12.0 | Oct 28, 2018
| 3.13.0 & 1.13.0 | Oct 29, 2018 | 3.13.0 & 1.13.0 | Oct 29, 2018
| 3.14.0 & 1.14.0 | Nov 2, 2018
## Release Notes ## Release Notes
2.3 - Sliders, Listbox's and Image elements (oh my!) 2.3 - Sliders, Listbox's and Image elements (oh my!)
@ -3797,7 +3799,7 @@ Emergency patch release... going out same day as previous release
* Progress meter uses new CloseButton * Progress meter uses new CloseButton
* Popups use new CloseButton * Popups use new CloseButton
## 3.13.0 & 1.13.0 ### 3.13.0 & 1.13.0
* Improved multiple window handling of Popups when the X is used to close * Improved multiple window handling of Popups when the X is used to close
* Change submits added for: * Change submits added for:
* Multiline * Multiline
@ -3809,6 +3811,39 @@ Emergency patch release... going out same day as previous release
* Scroll bars for Trees * Scroll bars for Trees
### 3.14.0 & 1.14.0
- More windowing changes...
using a hidden root windowing (Tk())
all children are Toplevel() windows
Read only setting for:
Input Text
Multiline
Font setting for InputCombo, Multiline
change_submits settinf for Radio Element
SetFocus for multiline, input elements
Default mon, day, year for calendar chooser button
Tree element update, added ability to change a single key
Message parm removed from ReadNonBlocking
Fix for closing windows using X
CurrentLocation method for Windows
Debug Window options
location
font
no_button
no_titlebar
grab_anywhere
keep_on_top
New Print / EasyPrint options
location
font
no_button
no_titlebar
grab_anywhere
keep_on_top
New popup, PopupQuickMessage
PopupGetFolder, PopupGetFile new initial_folder parm
### Upcoming ### Upcoming
Make suggestions people! Future release features Make suggestions people! Future release features
@ -3885,6 +3920,8 @@ GNU Lesser General Public License (LGPL 3) +
* Tony Crewe (anthony.crewe@gmail.com) Generously provided his classroom materials that he has written to teach a GUI course. If you're an educator and want to trade materials with Tony, he would like to hear from you. * Tony Crewe (anthony.crewe@gmail.com) Generously provided his classroom materials that he has written to teach a GUI course. If you're an educator and want to trade materials with Tony, he would like to hear from you.
* [spectre6000](https://github.com/spectre6000) - Readme updates * [spectre6000](https://github.com/spectre6000) - Readme updates
* [jackyOO7](https://github.com/jackyOO7) - Demo programs. OpenCV with realtime image processing, popup keyboard, input Combo read only option. * [jackyOO7](https://github.com/jackyOO7) - Demo programs. OpenCV with realtime image processing, popup keyboard, input Combo read only option.
* [AltoRetrato](https://github.com/AltoRetrato) - Fonts for multiline and combo
## How Do I ## How Do I
Finally, I must thank the fine folks at How Do I. Finally, I must thank the fine folks at How Do I.