diff --git a/DemoPrograms/Demo_Conways_Game_of_Life.py b/DemoPrograms/Demo_Conways_Game_of_Life.py index 9fb43ba7..ce984754 100644 --- a/DemoPrograms/Demo_Conways_Game_of_Life.py +++ b/DemoPrograms/Demo_Conways_Game_of_Life.py @@ -103,7 +103,7 @@ class GameOfLife: [self.graph], [sg.Button('Go!', key='_DONE_'), sg.Text(' Delay (ms)') , sg.Slider([0,400], orientation='h', key='_SLIDER_', size=(15,15)), - sg.Text(' Num Generations'), sg.Slider([0, 1000],default_value=200, orientation='h',size=(15,15), key='_SLIDER2_')] + sg.Text(' Num Generations'), sg.Slider([0, 3000],default_value=400, orientation='h',size=(15,15), key='_SLIDER2_')] ] self.window = sg.Window('Window Title', ).Layout(layout).Finalize() diff --git a/PySimpleGUI27.py b/PySimpleGUI27.py index c57287ac..e72a05bb 100644 --- a/PySimpleGUI27.py +++ b/PySimpleGUI27.py @@ -252,6 +252,8 @@ ELEM_TYPE_TREE = 'tree' ELEM_TYPE_ERROR = 'error' ELEM_TYPE_SEPARATOR = 'separator' ELEM_TYPE_STATUSBAR = 'statusbar' +ELEM_TYPE_PANE = 'pane' +# STRETCH == ERROR ELEMENT as a filler # ------------------------- Popup Buttons Types ------------------------- # POPUP_BUTTONS_YES_NO = 1 @@ -912,8 +914,7 @@ Check = Checkbox class Spin(Element): # Values = None # TKSpinBox = None - def __init__(self, values, initial_value=None, disabled=False, change_submits=False,enable_events=False , size=(None, None), auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, - tooltip=None, visible=True): + def __init__(self, values, initial_value=None, disabled=False, change_submits=False,enable_events=False , size=(None, None), auto_size_text=None, font=None, background_color=None, text_color=None, key=None, pad=None, tooltip=None, visible=True): ''' Spinner Element :param values: @@ -1372,6 +1373,8 @@ class Button(Element): self.TKButton = None self.Target = target self.ButtonText = button_text + if sys.platform == 'darwin' and button_color is not None: + print('Button *** WARNING - Button colors are not supported on the Mac ***') self.ButtonColor = button_color if button_color else DEFAULT_BUTTON_COLOR self.ImageFilename = image_filename self.ImageData = image_data @@ -1528,11 +1531,13 @@ class Button(Element): return - def Update(self, text=None, button_color=(None, None), disabled=None, image_data=None, image_filename=None, visible=None): + def Update(self, text=None, button_color=(None, None), disabled=None, image_data=None, image_filename=None, visible=None, image_subsample=None, image_size=None): try: if text is not None: self.TKButton.configure(text=text) self.ButtonText = text + if sys.platform == 'darwin' and button_color != (None, None): + print('Button.Update *** WARNING - Button colors are not supported on the Mac***') if button_color != (None, None): self.TKButton.config(foreground=button_color[0], background=button_color[1]) except: @@ -1543,7 +1548,12 @@ class Button(Element): self.TKButton['state'] = 'normal' if image_data is not None: image = tk.PhotoImage(data=image_data) - width, height = image.width(), image.height() + if image_size is not None: + width, height = image_size + else: + width, height = image.width(), image.height() + if image_subsample: + image = image.subsample(image_subsample) self.TKButton.config(image=image, width=width, height=height) self.TKButton.image = image if image_filename is not None: @@ -1878,6 +1888,13 @@ class Graph(Element): return None self._TKCanvas2.delete('all') + + def DeleteFigure(self, id): + try: + self._TKCanvas2.delete(id) + except: + print('DeleteFigure - bad ID {}'.format(id)) + def Update(self, background_color, visible=None): if self._TKCanvas2 is None: print('*** WARNING - The Graph element has not been finalized and cannot be drawn upon ***') @@ -2407,6 +2424,7 @@ class Column(Element): self.ReturnValuesDictionary = {} self.DictionaryKeyCounter = 0 self.ParentWindow = None + self.ParentPanedWindow = None self.Rows = [] self.TKFrame = None self.TKColFrame = None @@ -2416,7 +2434,7 @@ class Column(Element): self.Layout(layout) - super().__init__(ELEM_TYPE_COLUMN, background_color=background_color, size=size, pad=pad, key=key, visible=visible) + super().__init__(ELEM_TYPE_COLUMN, background_color=bg, size=size, pad=pad, key=key, visible=visible) return def AddRow(self, *args): @@ -2446,9 +2464,15 @@ class Column(Element): def Update(self, visible=None): if visible is False: - self.TKColFrame.pack_forget() + if self.TKColFrame: + self.TKColFrame.pack_forget() + if self.ParentPanedWindow: + self.ParentPanedWindow.remove(self.TKColFrame) elif visible is True: - self.TKColFrame.pack() + if self.TKColFrame: + self.TKColFrame.pack() + if self.ParentPanedWindow: + self.ParentPanedWindow.add(self.TKColFrame) def __del__(self): @@ -2462,6 +2486,52 @@ class Column(Element): super().__del__() +# ---------------------------------------------------------------------- # +# Pane # +# ---------------------------------------------------------------------- # +class Pane(Element): + def __init__(self, pane_list, background_color=None, size=(None, None), pad=None, orientation='vertical', show_handle=True, relief=RELIEF_RAISED, handle_size=None, border_width=None, key=None, visible=True): + ''' + Container for elements that are stacked into rows + :param layout: + :param background_color: + :param size: + :param pad: + :param scrollable: + :param vertical_scroll_only: + :param key: + ''' + self.UseDictionary = False + self.ReturnValues = None + self.ReturnValuesList = [] + self.ReturnValuesDictionary = {} + self.DictionaryKeyCounter = 0 + self.ParentWindow = None + self.Rows = [] + self.TKFrame = None + self.PanedWindow = None + self.Orientation = orientation + self.PaneList = pane_list + self.ShowHandle = show_handle + self.Relief = relief + self.HandleSize = handle_size or 8 + self.BorderDepth = border_width + bg = background_color if background_color is not None else DEFAULT_BACKGROUND_COLOR + + self.Rows = [pane_list] + + super().__init__(ELEM_TYPE_PANE, background_color=bg, size=size, pad=pad, key=key, visible=visible) + return + + + def Update(self, visible=None): + if visible is False: + self.PanedWindow.pack_forget() + elif visible is True: + self.PanedWindow.pack() + + + # ---------------------------------------------------------------------- # # Calendar # # ---------------------------------------------------------------------- # @@ -3027,7 +3097,7 @@ class Window(object): progress_bar_color=(None, None), background_color=None, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON, force_toplevel=False, alpha_channel=1, return_keyboard_events=False, use_default_focus=True, text_justification=None, - no_titlebar=False, grab_anywhere=False, keep_on_top=False, resizable=False, disable_close=False): + no_titlebar=False, grab_anywhere=False, keep_on_top=False, resizable=False, disable_close=False, disable_minimize=False): ''' Main window object where Elements will be laid out in rows :param title: @@ -3103,6 +3173,7 @@ class Window(object): self.TimeoutKey = '_timeout_' self.TimerCancelled = False self.DisableClose = disable_close + self.DisableMinimize = disable_minimize self._Hidden = False self._Size = size self.XFound = False @@ -3918,6 +3989,18 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): if element.ReturnValues[0] is not None: # if a button was clicked button_pressed_text = element.ReturnValues[0] + if element.Type == ELEM_TYPE_PANE: + element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter + element.ReturnValuesList = [] + element.ReturnValuesDictionary = {} + BuildResultsForSubform(element, initialize_only, top_level_form) + for item in element.ReturnValuesList: + AddToReturnList(top_level_form, item) + if element.UseDictionary: + top_level_form.UseDictionary = True + if element.ReturnValues[0] is not None: # if a button was clicked + button_pressed_text = element.ReturnValues[0] + if element.Type == ELEM_TYPE_TAB_GROUP: element.DictionaryKeyCounter = top_level_form.DictionaryKeyCounter element.ReturnValuesList = [] @@ -4123,6 +4206,10 @@ def _FindElementFromKeyInSubForm(form, key): matching_elem = _FindElementFromKeyInSubForm(element, key) if matching_elem is not None: return matching_elem + if element.Type == ELEM_TYPE_PANE: + matching_elem = _FindElementFromKeyInSubForm(element, key) + if matching_elem is not None: + return matching_elem if element.Type == ELEM_TYPE_TAB: matching_elem = _FindElementFromKeyInSubForm(element, key) if matching_elem is not None: @@ -4207,7 +4294,7 @@ if sys.version_info[0] >= 3: i += 1 else: def AddMenuItem(top_menu, sub_menu_info, element, is_sub_menu=False, skip=False): - if isinstance(sub_menu_info, types.StringType): + if isinstance(sub_menu_info, (str,unicode)): if not is_sub_menu and not skip: # print(f'Adding command {sub_menu_info}') pos = sub_menu_info.find('&') @@ -4234,7 +4321,7 @@ else: while i < (len(sub_menu_info)): item = sub_menu_info[i] if i != len(sub_menu_info) - 1: - if not isinstance(sub_menu_info[i + 1], types.StringType): + if not isinstance(sub_menu_info[i + 1], (str, unicode)): new_menu = tk.Menu(top_menu, tearoff=element.Tearoff) pos = sub_menu_info[i].find('&') if pos != -1: @@ -4306,31 +4393,64 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): # ------------------------- COLUMN element ------------------------- # if element_type == ELEM_TYPE_COLUMN: if element.Scrollable: - col_frame = TkScrollableFrame(tk_row_frame, element.VerticalScrollOnly) # do not use yet! not working - PackFormIntoFrame(element, col_frame.TKFrame, toplevel_form) - col_frame.TKFrame.update() + element.TKColFrame = TkScrollableFrame(tk_row_frame, element.VerticalScrollOnly) # do not use yet! not working + PackFormIntoFrame(element, element.TKColFrame.TKFrame, toplevel_form) + element.TKColFrame.TKFrame.update() if element.Size == (None, None): # if no size specified, use column width x column height/2 - col_frame.canvas.config(width=col_frame.TKFrame.winfo_reqwidth(), - height=col_frame.TKFrame.winfo_reqheight() / 2) + element.TKColFrame.canvas.config(width=element.TKColFrame.TKFrame.winfo_reqwidth(), + height=element.TKColFrame.TKFrame.winfo_reqheight() / 2) else: - col_frame.canvas.config(width=element.Size[0], height=element.Size[1]) + element.TKColFrame.canvas.config(width=element.Size[0], height=element.Size[1]) if not element.BackgroundColor in (None, COLOR_SYSTEM_DEFAULT): - col_frame.canvas.config(background=element.BackgroundColor) - col_frame.TKFrame.config(background=element.BackgroundColor, borderwidth=0, + element.TKColFrame.canvas.config(background=element.BackgroundColor) + element.TKColFrame.TKFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0) - col_frame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0) + element.TKColFrame.config(background=element.BackgroundColor, borderwidth=0, highlightthickness=0) else: - col_frame = tk.Frame(tk_row_frame) - PackFormIntoFrame(element, col_frame, toplevel_form) - col_frame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both') + element.TKColFrame = tk.Frame(tk_row_frame) + PackFormIntoFrame(element, element.TKColFrame, toplevel_form) + element.TKColFrame.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both') if element.Visible is False: - col_frame.pack_forget() + element.TKColFrame.pack_forget() - element.TKColFrame = col_frame + element.TKColFrame = element.TKColFrame if element.BackgroundColor != COLOR_SYSTEM_DEFAULT and element.BackgroundColor is not None: - col_frame.configure(background=element.BackgroundColor, highlightbackground=element.BackgroundColor, + element.TKColFrame.configure(background=element.BackgroundColor, highlightbackground=element.BackgroundColor, highlightcolor=element.BackgroundColor) + + # ------------------------- Pane element ------------------------- # + if element_type == ELEM_TYPE_PANE: + bd = element.BorderDepth if element.BorderDepth is not None else border_depth + element.PanedWindow = tk.PanedWindow(tk_row_frame, + orient=tk.VERTICAL if element.Orientation.startswith('v') else tk.HORIZONTAL, + borderwidth=bd, + bd=bd, + ) + if element.Relief is not None: + element.PanedWindow.configure(relief=element.Relief) + element.PanedWindow.configure(handlesize=element.HandleSize) + if element.ShowHandle: + element.PanedWindow.config(showhandle=True) + if element.Size != (None, None): + element.PanedWindow.config(width=element.Size[0], height=element.Size[1]) + if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: + element.PanedWindow.configure(background=element.BackgroundColor) + for pane in element.PaneList: + pane.TKColFrame = tk.Frame(element.PanedWindow) + pane.ParentPanedWindow = element.PanedWindow + PackFormIntoFrame(pane, pane.TKColFrame, toplevel_form) + if pane.Visible: + element.PanedWindow.add(pane.TKColFrame) + if pane.BackgroundColor != COLOR_SYSTEM_DEFAULT and pane.BackgroundColor is not None: + pane.TKColFrame.configure(background=pane.BackgroundColor, highlightbackground=pane.BackgroundColor, + highlightcolor=pane.BackgroundColor) + + element.PanedWindow.pack(side=tk.LEFT, padx=elementpad[0], pady=elementpad[1], expand=True, fill='both') + if element.Visible is False: + element.PanedWindow.pack_forget() + + # ------------------------- TEXT element ------------------------- # elif element_type == ELEM_TYPE_TEXT: # auto_size_text = element.AutoSizeText @@ -4419,7 +4539,9 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): tkbutton.config(foreground=bc[0], background=bc[1], activebackground=bc[1]) elif bc[1] == COLOR_SYSTEM_DEFAULT: tkbutton.config(foreground=bc[0]) - + if border_depth == 0: + tkbutton.config(relief=tk.FLAT) + tkbutton.config(highlightthickness=0) element.TKButton = tkbutton # not used yet but save the TK button in case wraplen = tkbutton.winfo_reqwidth() # width of widget in Pixels if element.ImageFilename: # if button has an image on it @@ -4502,11 +4624,25 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.TKStringVar = tk.StringVar() if element.BackgroundColor is not None and element.BackgroundColor != COLOR_SYSTEM_DEFAULT: combostyle = tkinter.ttk.Style() + # + # style.map("C.TButton", + # foreground=[('pressed', 'red'), ('active', 'blue')], + # background=[('pressed', '!disabled', 'black'), ('active', 'white')] + # ) + + # combostyle.map('PSG.TCombobox', background=[('selected', 'green')]) + # combostyle.configure('PSG.TCombobox.Listbox',fieldbackground='green') + # combostyle.configure('PSG.TCombobox', foreground=text_color) + # combostyle.configure('PSG.TCombobox', selectbackground='gray70') + # combostyle.map('PSG.TCombobox', background=[('readonly','red')]) + # combostyle.configure('PSG.TCombobox.TEntry', background='red') + # combostyle.configure('PSG.TCombobox', selectforeground=element.BackgroundColor) + # combostyle.configure('PSG.TCombobox', fieldbackground='blue') try: combostyle.theme_create('combostyle', settings={'TCombobox': {'configure': - {'selectbackground': element.BackgroundColor, + {'selectbackground': 'gray50', 'fieldbackground': element.BackgroundColor, 'foreground': text_color, 'background': element.BackgroundColor} @@ -4516,7 +4652,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): combostyle.theme_settings('combostyle', settings={'TCombobox': {'configure': - {'selectbackground': element.BackgroundColor, + {'selectbackground': 'gray50', 'fieldbackground': element.BackgroundColor, 'foreground': text_color, 'background': element.BackgroundColor} @@ -4525,6 +4661,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): pass # ATTENTION: this applies the new style 'combostyle' to all ttk.Combobox combostyle.theme_use('combostyle') + element.TKCombo = tkinter.ttk.Combobox(tk_row_frame, width=width, textvariable=element.TKStringVar, font=font) if element.Size[1] != 1 and element.Size[1] is not None: element.TKCombo.configure(height=element.Size[1]) @@ -5286,6 +5423,9 @@ def StartupTK(my_flex_form): if not my_flex_form.Resizable: root.resizable(False, False) + if my_flex_form.DisableMinimize: + root.attributes("-toolwindow", 1) + if my_flex_form.KeepOnTop: root.wm_attributes("-topmost", 1) @@ -5410,20 +5550,18 @@ class QuickMeter(object): layout = [] if self.orientation.lower().startswith('h'): col = [] - for arg in args: - col.append([T(arg)]) - col.append([T('', size=(30,10), key='_STATS_')]) - col.append([ProgressBar(max_value=self.max_value, orientation='h', key='_PROG_', size=self.size)]) - col.append([Cancel(button_color=self.button_color), Stretch()]) - layout += [Column(col)] + col += [[T(arg)] for arg in args] + col += [[T('', size=(30,10), key='_STATS_')], + [ProgressBar(max_value=self.max_value, orientation='h', key='_PROG_', size=self.size)], + [Cancel(button_color=self.button_color), Stretch()]] + layout = [Column(col)] else: col = [[ProgressBar(max_value=self.max_value, orientation='v', key='_PROG_', size=self.size)]] col2 = [] - for arg in args: - col2.append([T(arg)]) - col2.append([T('', size=(30,10), key='_STATS_')]) - col2.append([Cancel(button_color=self.button_color), Stretch()]) - layout += [Column(col), Column(col2)] + col2 += [[T(arg)] for arg in args] + col2 += [[T('', size=(30,10), key='_STATS_')], + [Cancel(button_color=self.button_color), Stretch()]] + layout = [Column(col), Column(col2)] self.window = Window(self.title, grab_anywhere=self.grab_anywhere, border_depth=self.border_width) self.window.Layout([layout]).Finalize() @@ -5524,24 +5662,33 @@ def GetComplimentaryHex(color): # ======================== EasyPrint =====# # ===================================================# -_easy_print_data = None # global variable... I'm cheating - - class DebugWin(object): debug_window = 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): + def __init__(self, size=(None, None), location=(None, None), font=None, no_titlebar=False, no_button=False, + grab_anywhere=False, keep_on_top=False, do_not_reroute_stdout=True): # Show a form that's a running counter + self.size = size + self.location = location + self.font = font + self.no_titlebar = no_titlebar + self.no_button = no_button + self.grab_anywhere = grab_anywhere + self.keep_on_top = keep_on_top + self.do_not_reroute_stdout = do_not_reroute_stdout + win_size = size if size != (None, None) else DEFAULT_DEBUG_WINDOW_SIZE - 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.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 = Multiline(size=win_size, autoscroll=True, key='_MULTILINE_') if do_not_reroute_stdout else Output(size=win_size) + if no_button: - self.layout = [[self.output_element]] + self.layout = [[self.output_element]] else: - self.layout = [ - [self.output_element], - [DummyButton('Quit')] - ] + self.layout = [ + [self.output_element], + [DummyButton('Quit'), Stretch()] + ] self.window.AddRows(self.layout) self.window.Read(timeout=0) # Show a non-blocking form, returns immediately return @@ -5554,28 +5701,28 @@ class DebugWin(object): sepchar = sep if sep is not None else ' ' endchar = end if end is not None else '\n' - if self.window is None: # if window was destroyed already, just print - self.__init__() - print(*args, sep=sepchar, end=endchar) - return - + if self.window is None: # if window was destroyed alread re-open it + self.__init__(size=self.size, location=self.location, font=self.font, no_titlebar=self.no_titlebar, no_button=self.no_button, grab_anywhere=self.grab_anywhere, keep_on_top=self.keep_on_top, do_not_reroute_stdout=self.do_not_reroute_stdout) event, values = self.window.Read(timeout=0) if event == 'Quit' or event is None: self.Close() - print(*args, sep=sepchar, end=endchar) - # Add extra check to see if the window was closed... if closed by X sometimes am not told - # try: - # state = self.window.TKroot.state() - # except: - # self.Close() + self.__init__(size=self.size, location=self.location, font=self.font, no_titlebar=self.no_titlebar, no_button=self.no_button, grab_anywhere=self.grab_anywhere, keep_on_top=self.keep_on_top, do_not_reroute_stdout=self.do_not_reroute_stdout) + if self.do_not_reroute_stdout: + outstring = '' + for arg in args: + outstring += str(arg) + sepchar + outstring += endchar + self.output_element.Update(outstring, append=True) + else: + print(*args, sep=sepchar, end=endchar) + def Close(self): - if self.window is None: - return self.window.Close() self.window.__del__() self.window = None + def PrintClose(): EasyPrintClose() @@ -5583,6 +5730,8 @@ def PrintClose(): def EasyPrint(*args, **_3to2kwargs): + if 'do_not_reroute_stdout' in _3to2kwargs: do_not_reroute_stdout = _3to2kwargs['do_not_reroute_stdout']; del _3to2kwargs['do_not_reroute_stdout'] + else: do_not_reroute_stdout = False 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'] @@ -5602,8 +5751,69 @@ def EasyPrint(*args, **_3to2kwargs): if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size'] else: size = (None, None) if DebugWin.debug_window is None: - DebugWin.debug_window = 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) + DebugWin.debug_window = 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, do_not_reroute_stdout=do_not_reroute_stdout) DebugWin.debug_window.Print(*args, end=end, sep=sep) +# +# +# +# +# +# class DebugWin(): +# debug_window = 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 +# win_size = size if size != (None, None) else DEFAULT_DEBUG_WINDOW_SIZE +# 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) +# if no_button: +# self.layout = [[self.output_element]] +# else: +# self.layout = [ +# [self.output_element], +# [DummyButton('Quit')] +# ] +# self.window.AddRows(self.layout) +# self.window.Read(timeout=0) # Show a non-blocking form, returns immediately +# return +# +# def Print(self, *args, end=None, sep=None): +# sepchar = sep if sep is not None else ' ' +# endchar = end if end is not None else '\n' +# +# if self.window is None: # if window was destroyed already, just print +# self.__init__() +# 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) +# # Add extra check to see if the window was closed... if closed by X sometimes am not told +# # try: +# # state = self.window.TKroot.state() +# # except: +# # self.Close() +# +# def Close(self): +# if self.window is None: +# return +# self.window.Close() +# self.window.__del__() +# self.window = None +# +# def PrintClose(): +# EasyPrintClose() +# +# +# def EasyPrint(*args, size=(None, None), end=None, sep=None, location=(None, None), font=None, no_titlebar=False, no_button=False, grab_anywhere=False, keep_on_top=False): +# +# +# if DebugWin.debug_window is None: +# DebugWin.debug_window = 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) +# DebugWin.debug_window.Print(*args, end=end, sep=sep) Print = EasyPrint @@ -5618,6 +5828,10 @@ def EasyPrintClose(): # ======================== Scrolled Text Box =====# # ===================================================# def PopupScrolled(*args, **_3to2kwargs): + if 'non_blocking' in _3to2kwargs: non_blocking = _3to2kwargs['non_blocking']; del _3to2kwargs['non_blocking'] + else: non_blocking = False + if 'title' in _3to2kwargs: title = _3to2kwargs['title']; del _3to2kwargs['title'] + else: title = None if 'location' in _3to2kwargs: location = _3to2kwargs['location']; del _3to2kwargs['location'] else: location = (None, None) if 'size' in _3to2kwargs: size = _3to2kwargs['size']; del _3to2kwargs['size'] @@ -5633,7 +5847,7 @@ def PopupScrolled(*args, **_3to2kwargs): if not args: return width, height = size width = width if width else MESSAGE_BOX_LINE_WIDTH - form = Window(args[0], auto_size_text=True, button_color=button_color, auto_close=auto_close, + window = Window(title=title or args[0], auto_size_text=True, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, location=location) max_line_total, max_line_width, total_lines, height_computed = 0, 0, 0, 0 complete_output = '' @@ -5652,18 +5866,19 @@ def PopupScrolled(*args, **_3to2kwargs): height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed if height: height_computed = height - form.AddRow(Multiline(complete_output, size=(max_line_width, height_computed))) + window.AddRow(Multiline(complete_output, size=(max_line_width, height_computed))) pad = max_line_total - 15 if max_line_total > 15 else 1 # show either an OK or Yes/No depending on paramater + button = DummyButton if non_blocking else Button if yes_no: - form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(), No()) - button, values = form.Read() - form.Close() - return button + window.AddRow(Text('', size=(pad, 1), auto_size_text=False), button('Yes'), button('No')) else: - form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Button('OK', size=(5, 1), button_color=button_color)) - button, values = form.Read() - form.Close() + window.AddRow(Text('', size=(pad, 1), auto_size_text=False), button('OK', size=(5, 1), button_color=button_color)) + + if non_blocking: + button, values = window.Read(timeout=0) + else: + button, values = window.Read() return button diff --git a/PySimpleGUIQt/readme.md b/PySimpleGUIQt/readme.md index 00339841..da52d251 100644 --- a/PySimpleGUIQt/readme.md +++ b/PySimpleGUIQt/readme.md @@ -16,7 +16,7 @@ ![Python Version](https://img.shields.io/badge/Python-3.x-yellow.svg) -![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-00.21.0-orange.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-00.19.0-orange.svg?longCache=true&style=for-the-badge) @@ -26,7 +26,7 @@ "Qt without the ugly" - ## The Alpha Release Version 0.21.0 + ## The Alpha Release Version 0.18.0 [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) @@ -37,7 +37,7 @@ Welcome to the Alpha Release of PySimpleGUI for Qt! You can use the exact same code that you are running on the older, tkinter, version of PySimpleGUI. -PySimpleGUIQt uses **PySide2** OR **PyQt5** for access to Qt. **PyQt5 has been having a number of problems recently however so tread lightly.** +PySimpleGUIQt uses **PySide2** OR **PyQt5** for access to Qt. PyQt5 has been having a number of problems recently however so tread lightly. ## Porting your PySimpleGUI code to PySimpleGUIQt @@ -153,14 +153,13 @@ These Elements are "complete" (a relative term... more are more complete than ot Notable MISSING features at the moment include: * Graphs Element Methods - erasing, draw arc, etc -# New PySimpleGUI Features only in Qt (or first introduced in Qt) +# New PySimpleGUI Features only in Qt There are a number of new features that are only available in PySimpleGUIQt. These include: * ButtonMenu Element * Dial Element * Stretcher Element * SystemTray feature -* "Dynamic" windows that grow and shrink (uses invisible elements) ## SystemTray @@ -219,7 +218,7 @@ You will find 3 parameters used to specify these 3 options on both the initializ ## Menu Definition ```python -menu_def = ['BLANK', ['&Open', '&Save', ['1', '2', ['a', 'b']], '!&Properties', 'E&xit']] +menu_def = ['BLANK', ['&Open', '&Save', ['1', '2', ['a', 'b']], '&Properties', 'E&xit']] ``` A menu is defined using a list. A "Menu entry" is a string that specifies: @@ -227,7 +226,7 @@ A menu is defined using a list. A "Menu entry" is a string that specifies: * keyboard shortcut * key -See section on Menu Keys for more information on using keys with menus. +See section on Menu Keys for more informatoin on using keys with menus. An entry without a key and keyboard shortcut is a simple string `'Menu Item'` @@ -244,10 +243,6 @@ The first entry can be ignored.`'BLANK`' was chosen for this example. It's this **Separators** If you want a separator between 2 items, add the entry `'---'` and it will add a separator item at that place in your menu. -**Disabled menu entries** - -If you want to disable a menu entry, place a `!` before the menu entry - ## SystemTray Methods @@ -365,18 +360,6 @@ If you want to change the separator characters from :: top something else,change When a menu item has a key and it is chosen, then entire string is returned. If Hide were selected, then Hide::key would be returned from the Read. Note that the shortcut character & is NOT returned from Reads. -## Dynamic Windows (Element Visibility) - -Finally, the ability to grow and shrink has been added as of release 0.20.0 - -While the window **appears** to be dynamic, the reality is that the elements are created up front, when you define the window layout. You will create these "extra" elements with the flag `visible=False`. Then, when you wish to show those elements, call the element's `Update` method setting `visible=True`. - -After you call the `Update` method, it's important to call `window.VisibilityChanged()` so that your window can change sizes. Without that call your window will not shrink. It will grow properly, but it will not shrink. While this could have been done by PySimpleGUI on the user's behalf, the thought was that perhaps the user wants the window size to remain the same and the element simply appears and disappears, leaving a blank spot. If the window automatically grew and shrank, this would not be possible. Just buck-up and make the call to `VisibilityChanged`. - -## `enable_events` Parameter - -All elements that are capable of producing events now have a parameter `enable_events`. This is *identical* to the old parameter `change_submits` or `click_submits`. The idea is to standardize on 1 name that all elements use. The old parameters will continue to work, but the documentation and sample programs will steer you away from them and towards enable_events. - # Release Notes: ### 0.12.0 - 20-Nov-2018 @@ -503,111 +486,6 @@ Window - Get screen dimensions Slider - disable Dial - disable -### 0.20.0 6-Dec-2018 - -* Ability to change calculations between characters and pixels -* size_px added to ALL elements that have a size parameter -* General Element.Update(widget, background_color, text_color, font, visible) -* visible parameter added to ALL elements -* enable_events flag -* Input text - enable events, visibility, size_px -* Input text update added capabilities - * ability to highlight the input string - * background, text colors and font -* Combo - enable events, visibility, size_px -* Combo - auto complete feature -* Combo - added to Update - background color, text color, font, visible -* Listbox - enable events, visibility, size_px -* Listbox - better scaling from characters to pixels -* Listbox - ability to Update with set to index, text color, font, visibility -* Radio - enable events, visibility, size_px -* Radio - Update additions - background_color, text_color, font, visibility -* Checkbox - enable events, visibility, size_px -* Checkbox - Update additions - background_color, text_color, font, visibility -* Spin - enable events, visibility, size_px -* Spin - Update additions - background_color, text_color, font, visibility -* Multiline input - enable events, visibility, size_px -* Multiline input - Update additions - background_color, text_color, font, visibility -* Multiline input better character to pixel scaling -* Multiline output - enable events, visibility, size_px -* Multiline output - Update additions - background_color, text_color, visibility -* Text - enable events, size in pixels -* Text - Update addition of visibility -* Output - visible, size_px -* Output - added update capability with new value, background_color, text_color, font, visibility -* Button - enable events, visible, size_px -* Button - Color Chooser feature completed -* Button - Color Chooser can target (None, None) which will store the value to be returned with the values from Read() -* Button - fixed bug in SaveAs button code. Bad filter variable -* Button - Updated added font, visibility -* Button - new SetFocus() method will set the focus onto the button -* ButtonMenu - Update method implemented that includes menu definition changes, text, button color, font, visibility -* ProgressBar - added visibility, size_px -* ProgressBar - added Update method for changing the visibility -* Images - events, size_pix, visibility -* Images - can now get click events for images! -* Images - Update added visibility -* Graph - visibility, size_px -* Graph - Update method for changing visibility -* Frame - visibility, size_px -* Frame - Update method added that controls visibility -* ALL elements inside of a Frame that's invisible will also be invisible -* Tab - visible parameter added, however not yet functional! -* TabGroup - enable events, visibility -* TabGroup - Update for controlling visibility -* Slider - enable events, size_px -* Slider - Update method now includes visibility -* Dial - enable events, size_px, visibility -* Dial - Update method added visibilty control -* Column - visibility added -* Column - Added Update method to control visibility -* ALL elements inside of an invisible Column Element will also be invisible -* MenuBar - added visibility -* MenuBar - Update can now change menu definitions at runtime, and control visibility -* Table - enable events, size_px, visibility -* Table - Update method can control visibility -* Tree - enable events, size_px, visibility -* Tree - Update method can control visibility -* VisibilityChanged() function that must be called when using Qt so that the window will shrink or grow -* window.GetScreenDimensions can now be called prior to window creation -* window.Size property -* enable_events added to all of the shortcut buttons and browse buttons -* Ability to set a button image from a file -* Combo - ability to set a default value -* Combo - Read only setting. Allows for user editing of value -* Menus - Ability to disable / enable any part of a menu by adding a ! before the entry name -* Tabs - ability to set tab text color, background color, background color of selected tab -* Tabs - ability to set widget area's background color -* Sliders - paging works properly (using page-up page-down or slider slider area to advance slider) -* Tree - Setting number of visible rows implemented -* Added 5 pixels to every window. Have been having issues with text being cutoff on the right side -* SetOptions - ability to change default error button color for popups - -### 0.21.0 - 9-Dec-2018 - -* Removed use of global variabels - using static class variabels instead -* Listbox.Get() will return current listbox value -* Progressbar now has color support -* Progressbar can be vertical now -* Can change bar or back and background color -* (barcolor, background color - None if use default) -* Table num_rows parameter implemented -* Table.Update - can change number of visible rows -* Window resizable parm - implemented, default changed from False to True -* Window.Move - implemented -* Window.Minimize - implemented -* Window.Disable - implemented -* Window.Enable - implemented -* Window.CurrentLocation - implemented -* Fixed too small scrollbar in Combobox -* Fixed too small scrollbar in Listbox -* Changed "text" window to a complex one for quick regression testing (try running PySimpleGUIQt.py by itself) - -### 0.22.0 - 9-Dec-2018 - -* Spin.Get method - get the current spinner value - - # Design ## Author Mike B. diff --git a/docs/index.md b/docs/index.md index e7135896..99286fbf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -31,11 +31,11 @@ -![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.19.2-red.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.20.0-red.svg?longCache=true&style=for-the-badge) -![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.18.0-blue.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.20.0-blue.svg?longCache=true&style=for-the-badge) -![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.19.0-orange.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.22.0-orange.svg?longCache=true&style=for-the-badge) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) @@ -195,6 +195,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad Listbox Option Menu Slider + Dial Graph Frame with title Icons @@ -205,6 +206,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad Trees Progress Bar Async/Non-Blocking Windows Tabbed windows + Paned windows Persistent Windows Redirect Python Output/Errors to scrolling window 'Higher level' APIs (e.g. MessageBox, YesNobox, ...) @@ -589,8 +591,7 @@ Note that you should not call Popup yourself with different button_types. Rely There is a scrolled version of Popups should you have a lot of information to display. ```python -PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None, - size=(None, None), location=(None, None)) +PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None, size=(None, None), location=(None, None), title=None, non_blocking=False) ``` Typical usage: @@ -607,7 +608,9 @@ This call will create a scrolled box 80 characters wide and a height dependent u sg.PopupScrolled(my_text, size=(80, None)) -Note that the default max number of lines before scrolling happens is set to 50. At 50 lines the scrolling will begin. +Note that the default max number of lines before scrolling happens is set to 50. At 50 lines the scrolling will begin. + +If `non_blocking` parameter is set, then the call will not blocking waiting for the user to close the window. Execution will immediately return to the user. Handy when you want to dump out debug info without disrupting the program flow. ### PopupNoWait @@ -766,6 +769,7 @@ Another call in the 'Easy' families of APIs is `EasyPrint`. It will output to a print = sg.EasyPrint at the top of your code. + There are a number of names for the same EasyPrint function. `Print` is one of the better ones to use as it's easy to remember. It is simply `print` with a capital P. import PySimpleGUI as sg @@ -787,8 +791,9 @@ Just like the standard print call, `EasyPrint` supports the `sep` and `end` keyw You can change the size of the debug window using the `SetOptions` call with the `debug_win_size` parameter. -*A word of caution.* There are known problems when multiple PySimpleGUI windows are opened. If you open one of these debug windows, if you close it using the Quit button, it can have the side-effect of causing other visible windows to also close. It's a known architectural issue. +There is an option to tell PySimpleGUI to reroute all of your stdout and stderr output to this window. To do so call EasyPrint with the parameter `do_not_reroute_stdout` set to True. After calling it once with this parameter set to True, all future calls to a normal`print` will go to the debug window. +If you close the debug window it will re-open the next time you Print to it. --- # Custom window API Calls (Your First window) @@ -1368,7 +1373,8 @@ Window( title, grab_anywhere=False, keep_on_top=False, resizable=False, - disable_close=False): + disable_close=False, + disable_minimize=False): ``` Parameter Descriptions. You will find these same parameters specified for each `Element` and some of them in `Row` specifications. The `Element` specified value will take precedence over the `Row` and `window` values. @@ -1398,6 +1404,7 @@ Parameter Descriptions. You will find these same parameters specified for each keep_on_top - if True then window will always stop on top of other windows on the screen. Great for floating toolbars. resizable - if True - user can manually changge the wize of the window. Defaults to False disable_close - if True user will not be able to close using the X. + disable_minimize - if True user will not be able to minimize the window ### Window Location @@ -2893,12 +2900,13 @@ DrawArc(self, top_left, bottom_right, extent, start_angle, style=None, arc_color DrawRectangle(self, top_left, bottom_right, fill_color=None, line_color=None) DrawText(self, text, location, color='black', font=None, angle=0) Erase(background_color) +DeleteFigure(figure_id) Update() Move(self, x_direction, y_direction) MoveFigure(self, figure, x_direction, y_direction) TKCanvas ``` -All of the Drawing methods return a "figure" that can be used move the figure +All of the Drawing methods return a "***figure***" that can be used move and delete the figure DrawLine - draws a line DrawPoint - draws a single point @@ -2911,6 +2919,7 @@ Erase - erases entire graph Update - changes background color Move - moves everything an x,y direction MoveFigure - moves an individual figure +DeleteFigure - delete an individual figure ## Table Element @@ -3123,7 +3132,28 @@ Update(disabled = None) ``` WARNING - This Update method does not yet work! - +## Pane Element + +New in version 3.20 is the Pane Element, a super-cool tkinter feature. You won't find this one in PySimpleGUIQt, only PySimpleGUI. It's difficult to describe one of these things. Think of them as "Tabs without labels" that you can slide. + +![pane3](https://user-images.githubusercontent.com/13696193/50035040-fcd50e80-ffcd-11e8-939c-df8ab8d64712.gif) + +```python + +Pane(pane_list, background_color=None, size=(None, None), pad=None, orientation='vertical', show_handle=True, relief=RELIEF_RAISED, handle_size=None, border_width=None, key=None, visible=True): +``` + +***Each "Pane" of a Pane Element must be a Column Element***. The parameter `pane_list` is a list of Column Elements. + +Calls can get a little hairy looking if you try to declare everything in-line as you can see in this example. + +```python +sg.Pane([col5, sg.Column([[sg.Pane([col1, col2, col4], handle_size=15, orientation='v', background_color=None, show_handle=True, visible=True, key='_PANE_', border_width=0, relief=sg.RELIEF_GROOVE),]]),col3 ], orientation='h', background_color=None, size=(160,160), relief=sg.RELIEF_RAISED, border_width=0) +``` + +Combing these with *visibility* make for an interesting interface with entire panes being hidden from view until neded by the user. It's one way of producing "dynamic" windows. + + ## Colors Starting in version 2.5 you can change the background colors for the window and the Elements. @@ -4256,6 +4286,16 @@ Emergency patch release... going out same day as previous release * Buttons - remove highlight when border depth == 0 * OneLineProgressMeter - better layout implementation +## 3.20.0 & 1.20.0 18-Dec-2018 + +* New Pane Element +* Graphh.DeleteFigure method +* disable_minimize - New parameter for Window +* Fix for 2.7 menus +* Debug Window no longer re-routes stdout by default +* Can re-route by specifying in Print / EasyPrint call +* New non-blocking for PopupScrolled +* Can set title for PopupScrolled window ### Upcoming diff --git a/readme.md b/readme.md index e7135896..99286fbf 100644 --- a/readme.md +++ b/readme.md @@ -31,11 +31,11 @@ -![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.19.2-red.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_3.x_Version-3.20.0-red.svg?longCache=true&style=for-the-badge) -![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.18.0-blue.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUI_For_Python_2.7_Version-1.20.0-blue.svg?longCache=true&style=for-the-badge) -![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.19.0-orange.svg?longCache=true&style=for-the-badge) +![Python Version](https://img.shields.io/badge/PySimpleGUIQt_For_Python_3.x_Version-0.22.0-orange.svg?longCache=true&style=for-the-badge) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) @@ -195,6 +195,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad Listbox Option Menu Slider + Dial Graph Frame with title Icons @@ -205,6 +206,7 @@ While simple to use, PySimpleGUI has significant depth to be explored by more ad Trees Progress Bar Async/Non-Blocking Windows Tabbed windows + Paned windows Persistent Windows Redirect Python Output/Errors to scrolling window 'Higher level' APIs (e.g. MessageBox, YesNobox, ...) @@ -589,8 +591,7 @@ Note that you should not call Popup yourself with different button_types. Rely There is a scrolled version of Popups should you have a lot of information to display. ```python -PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None, - size=(None, None), location=(None, None)) +PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None, size=(None, None), location=(None, None), title=None, non_blocking=False) ``` Typical usage: @@ -607,7 +608,9 @@ This call will create a scrolled box 80 characters wide and a height dependent u sg.PopupScrolled(my_text, size=(80, None)) -Note that the default max number of lines before scrolling happens is set to 50. At 50 lines the scrolling will begin. +Note that the default max number of lines before scrolling happens is set to 50. At 50 lines the scrolling will begin. + +If `non_blocking` parameter is set, then the call will not blocking waiting for the user to close the window. Execution will immediately return to the user. Handy when you want to dump out debug info without disrupting the program flow. ### PopupNoWait @@ -766,6 +769,7 @@ Another call in the 'Easy' families of APIs is `EasyPrint`. It will output to a print = sg.EasyPrint at the top of your code. + There are a number of names for the same EasyPrint function. `Print` is one of the better ones to use as it's easy to remember. It is simply `print` with a capital P. import PySimpleGUI as sg @@ -787,8 +791,9 @@ Just like the standard print call, `EasyPrint` supports the `sep` and `end` keyw You can change the size of the debug window using the `SetOptions` call with the `debug_win_size` parameter. -*A word of caution.* There are known problems when multiple PySimpleGUI windows are opened. If you open one of these debug windows, if you close it using the Quit button, it can have the side-effect of causing other visible windows to also close. It's a known architectural issue. +There is an option to tell PySimpleGUI to reroute all of your stdout and stderr output to this window. To do so call EasyPrint with the parameter `do_not_reroute_stdout` set to True. After calling it once with this parameter set to True, all future calls to a normal`print` will go to the debug window. +If you close the debug window it will re-open the next time you Print to it. --- # Custom window API Calls (Your First window) @@ -1368,7 +1373,8 @@ Window( title, grab_anywhere=False, keep_on_top=False, resizable=False, - disable_close=False): + disable_close=False, + disable_minimize=False): ``` Parameter Descriptions. You will find these same parameters specified for each `Element` and some of them in `Row` specifications. The `Element` specified value will take precedence over the `Row` and `window` values. @@ -1398,6 +1404,7 @@ Parameter Descriptions. You will find these same parameters specified for each keep_on_top - if True then window will always stop on top of other windows on the screen. Great for floating toolbars. resizable - if True - user can manually changge the wize of the window. Defaults to False disable_close - if True user will not be able to close using the X. + disable_minimize - if True user will not be able to minimize the window ### Window Location @@ -2893,12 +2900,13 @@ DrawArc(self, top_left, bottom_right, extent, start_angle, style=None, arc_color DrawRectangle(self, top_left, bottom_right, fill_color=None, line_color=None) DrawText(self, text, location, color='black', font=None, angle=0) Erase(background_color) +DeleteFigure(figure_id) Update() Move(self, x_direction, y_direction) MoveFigure(self, figure, x_direction, y_direction) TKCanvas ``` -All of the Drawing methods return a "figure" that can be used move the figure +All of the Drawing methods return a "***figure***" that can be used move and delete the figure DrawLine - draws a line DrawPoint - draws a single point @@ -2911,6 +2919,7 @@ Erase - erases entire graph Update - changes background color Move - moves everything an x,y direction MoveFigure - moves an individual figure +DeleteFigure - delete an individual figure ## Table Element @@ -3123,7 +3132,28 @@ Update(disabled = None) ``` WARNING - This Update method does not yet work! - +## Pane Element + +New in version 3.20 is the Pane Element, a super-cool tkinter feature. You won't find this one in PySimpleGUIQt, only PySimpleGUI. It's difficult to describe one of these things. Think of them as "Tabs without labels" that you can slide. + +![pane3](https://user-images.githubusercontent.com/13696193/50035040-fcd50e80-ffcd-11e8-939c-df8ab8d64712.gif) + +```python + +Pane(pane_list, background_color=None, size=(None, None), pad=None, orientation='vertical', show_handle=True, relief=RELIEF_RAISED, handle_size=None, border_width=None, key=None, visible=True): +``` + +***Each "Pane" of a Pane Element must be a Column Element***. The parameter `pane_list` is a list of Column Elements. + +Calls can get a little hairy looking if you try to declare everything in-line as you can see in this example. + +```python +sg.Pane([col5, sg.Column([[sg.Pane([col1, col2, col4], handle_size=15, orientation='v', background_color=None, show_handle=True, visible=True, key='_PANE_', border_width=0, relief=sg.RELIEF_GROOVE),]]),col3 ], orientation='h', background_color=None, size=(160,160), relief=sg.RELIEF_RAISED, border_width=0) +``` + +Combing these with *visibility* make for an interesting interface with entire panes being hidden from view until neded by the user. It's one way of producing "dynamic" windows. + + ## Colors Starting in version 2.5 you can change the background colors for the window and the Elements. @@ -4256,6 +4286,16 @@ Emergency patch release... going out same day as previous release * Buttons - remove highlight when border depth == 0 * OneLineProgressMeter - better layout implementation +## 3.20.0 & 1.20.0 18-Dec-2018 + +* New Pane Element +* Graphh.DeleteFigure method +* disable_minimize - New parameter for Window +* Fix for 2.7 menus +* Debug Window no longer re-routes stdout by default +* Can re-route by specifying in Print / EasyPrint call +* New non-blocking for PopupScrolled +* Can set title for PopupScrolled window ### Upcoming