From c79b0be31deb415fcbe0a87a3a756fb6247c1599 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Tue, 11 Sep 2018 08:38:38 -0400 Subject: [PATCH] Added Enable/Disable to all input elements! Refresh of many Demo programs --- Demo_Button_States.py | 38 +++--- Demo_Cookbook_Browser.py | 111 +++++++++--------- Demo_Desktop_Widget_Timer.py | 103 ++++++++--------- Demo_Keyboard.py | 2 +- Demo_Keypad.py | 87 ++++++++------ Demo_Matplotlib_Ping_Graph.py | 2 +- Demo_PNG_Viewer.py | 56 +++++---- Demo_Password_Login.py | 4 +- Demo_Super_Simple_Form.py | 17 +-- Demo_Table_Simulation.py | 60 ++++++++-- PySimpleGUI.py | 211 +++++++++++++++++++++------------- 11 files changed, 389 insertions(+), 302 deletions(-) diff --git a/Demo_Button_States.py b/Demo_Button_States.py index 233d56dc..fa5d5d9f 100644 --- a/Demo_Button_States.py +++ b/Demo_Button_States.py @@ -8,15 +8,13 @@ states for buttons and changes the text color to show greyed-out (disabled) butt sg.ChangeLookAndFeel('Dark') sg.SetOptions(element_padding=(0,0)) -StartButton = sg.ReadFormButton('Start', button_color=('white', 'black')) -StopButton = sg.ReadFormButton('Stop', button_color=('gray34','black')) -ResetButton = sg.ReadFormButton('Reset', button_color=('gray','firebrick3')) -SubmitButton = sg.ReadFormButton('Submit', button_color=('gray34','springgreen4')) - layout = [[sg.T('User:', pad=((3,0),0)), sg.OptionMenu(values = ('User 1', 'User 2'), size=(20,1)), sg.T('0', size=(8,1))], [sg.T('Customer:', pad=((3,0),0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20,1)), sg.T('1', size=(8,1))], [sg.T('Notes:', pad=((3,0),0)), sg.In(size=(44,1), background_color='white', text_color='black')], - [StartButton, StopButton, ResetButton, SubmitButton] + [sg.ReadFormButton('Start', button_color=('white', 'black'), key='start'), + sg.ReadFormButton('Stop', button_color=('gray34', 'black'), key='stop'), + sg.ReadFormButton('Reset', button_color=('gray', 'firebrick3'), key='reset'), + sg.ReadFormButton('Submit', button_color=('gray34', 'springgreen4'), key='submit')] ] form = sg.FlexForm("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False, @@ -28,27 +26,27 @@ while True: if button is None: exit(69) if button is 'Start': - StartButton.Update(button_color=('gray34','black')) - StopButton.Update(button_color=('white', 'black')) - ResetButton.Update(button_color=('white', 'firebrick3')) + form.FindElement('start').Update(button_color=('gray34','black')) + form.FindElement('stop').Update(button_color=('white', 'black')) + form.FindElement('reset').Update(button_color=('white', 'firebrick3')) recording = True elif button is 'Stop' and recording: - StopButton.Update(button_color=('gray34','black')) - StartButton.Update(button_color=('white', 'black')) - SubmitButton.Update(button_color=('white', 'springgreen4')) + form.FindElement('stop').Update(button_color=('gray34','black')) + form.FindElement('start').Update(button_color=('white', 'black')) + form.FindElement('submit').Update(button_color=('white', 'springgreen4')) recording = False have_data = True elif button is 'Reset': - StopButton.Update(button_color=('gray34','black')) - StartButton.Update(button_color=('white', 'black')) - SubmitButton.Update(button_color=('gray34', 'springgreen4')) - ResetButton.Update(button_color=('gray34', 'firebrick3')) + form.FindElement('stop').Update(button_color=('gray34','black')) + form.FindElement('start').Update(button_color=('white', 'black')) + form.FindElement('submit').Update(button_color=('gray34', 'springgreen4')) + form.FindElement('reset').Update(button_color=('gray34', 'firebrick3')) recording = False have_data = False elif button is 'Submit' and have_data: - StopButton.Update(button_color=('gray34','black')) - StartButton.Update(button_color=('white', 'black')) - SubmitButton.Update(button_color=('gray34', 'springgreen4')) - ResetButton.Update(button_color=('gray34', 'firebrick3')) + form.FindElement('stop').Update(button_color=('gray34','black')) + form.FindElement('start').Update(button_color=('white', 'black')) + form.FindElement('submit').Update(button_color=('gray34', 'springgreen4')) + form.FindElement('reset').Update(button_color=('gray34', 'firebrick3')) recording = False diff --git a/Demo_Cookbook_Browser.py b/Demo_Cookbook_Browser.py index 0e766491..2b378c36 100644 --- a/Demo_Cookbook_Browser.py +++ b/Demo_Cookbook_Browser.py @@ -109,25 +109,34 @@ def AllWidgetsWithContext(): # sg.ChangeLookAndFeel('GreenTan') with sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False) as form: + + column1 = [[sg.Text('Column 1', background_color='#F7F3EC', justification='center', size=(10, 1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] + layout = [ [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], [sg.Text('Here is some text.... and a place to enter text')], - [sg.InputText()], - [sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)], + [sg.InputText('This is my text')], + [sg.Checkbox('Checkbox'), sg.Checkbox('My second checkbox!', default=True)], [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), sg.Multiline(default_text='A second multi-line', size=(35, 3))], - [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 3)), + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)), sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], - [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3', 'Listbox 4'), size=(30, 3)), + [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), - sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], - [sg.Text('_' * 80)], + sg.Column(column1, background_color='#F7F3EC')], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Default Folder'), sg.FolderBrowse()], - [sg.Submit(), sg.Cancel(), sg.SimpleButton('Customized', button_color=('black', '#EDE5B7'))]] + [sg.Submit(), sg.Cancel()] + ] button, values = form.LayoutAndRead(layout) @@ -137,31 +146,37 @@ def AllWidgetsNoContext(): """ import PySimpleGUI as sg - # Green & tan color scheme sg.ChangeLookAndFeel('GreenTan') form = sg.FlexForm('Everything bagel', default_element_size=(40, 1), grab_anywhere=False) + + column1 = [[sg.Text('Column 1', background_color='#F7F3EC', justification='center', size=(10, 1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] + layout = [ - [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], - [sg.Text('Here is some text.... and a place to enter text')], - [sg.InputText('This is my text')], - [sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)], - [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], - [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), - sg.Multiline(default_text='A second multi-line', size=(35, 3))], - [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 3)), - sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], - [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), - sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), - sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), - sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), - sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], - [sg.Text('_' * 80)], - [sg.Text('Choose A Folder', size=(35, 1))], - [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), - sg.InputText('Default Folder'), sg.FolderBrowse()], - [sg.Submit(), sg.Cancel(), sg.SimpleButton('Customized', button_color=('white', '#7E6C92'))] - ] + [sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25))], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text')], + [sg.Checkbox('Checkbox'), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), + sg.Multiline(default_text='A second multi-line', size=(35, 3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Column(column1, background_color='#F7F3EC')], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), + sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(), sg.Cancel()] + ] button, values = form.LayoutAndRead(layout) @@ -175,10 +190,9 @@ def NonBlockingWithUpdates(): form = sg.FlexForm('Running Timer') # create a text element that will be updated periodically - text_element = sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center') form_rows = [[sg.Text('Stopwatch', size=(20,2), justification='center')], - [text_element], + [ sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center', key='output')], [sg.T(' ' * 5), sg.ReadFormButton('Start/Stop', focus=True), sg.Quit()]] form.LayoutAndRead(form_rows, non_blocking=True) @@ -193,7 +207,7 @@ def NonBlockingWithUpdates(): break elif button == 'Start/Stop': timer_running = not timer_running - text_element.Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) + form.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) time.sleep(.01) # if the loop finished then need to close the form for the user @@ -208,14 +222,13 @@ def NonBlockingWithContext(): import time with sg.FlexForm('Running Timer') as form: - text_element = sg.Text('', size=(10, 2), font=('Helvetica', 20), text_color='red', justification='center') layout = [[sg.Text('Non blocking GUI with updates', justification='center')], - [text_element], + [sg.Text('', size=(10, 2), font=('Helvetica', 20), text_color='red', justification='center', key='output')], [sg.T(' ' * 15), sg.Quit()]] form.LayoutAndRead(layout, non_blocking=True) for i in range(1, 500): - text_element.Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) + form.FindElement('output').Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100)) button, values = form.ReadNonBlocking() if values is None or button == 'Quit': # if user closed the window using X break @@ -451,16 +464,7 @@ def MachineLearning(): """ import PySimpleGUI as sg - # Green & tan color scheme - sg.SetOptions(background_color='#9FB8AD', - text_element_background_color='#9FB8AD', - element_background_color='#9FB8AD', - input_elements_background_color='#F7F3EC', - button_color=('white', '#475841'), - border_width=0, - slider_border_width=0, - progress_meter_border_depth=0, - scrollbar_color='#F7F3EC') + sg.ChangeLookAndFeel('LightGreen') sg.SetOptions(text_justification='right') @@ -645,8 +649,6 @@ def InputElementUpdate(): """ import PySimpleGUI as g - # g.SetOptions(button_color=g.COLOR_SYSTEM_DEFAULT) # because some people like gray buttons - # Demonstrates a number of PySimpleGUI features including: # Default element size # auto_size_buttons @@ -655,17 +657,13 @@ def InputElementUpdate(): # Update of elements in form (Text, Input) # do_not_clear of Input elements - # create the 2 Elements we want to control outside the form - out_elem = g.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red') - in_elem = g.Input(size=(10, 1), do_not_clear=True, key='input') - layout = [[g.Text('Enter Your Passcode')], - [in_elem], + [g.Input(size=(10, 1), do_not_clear=True, key='input')], [g.ReadFormButton('1'), g.ReadFormButton('2'), g.ReadFormButton('3')], [g.ReadFormButton('4'), g.ReadFormButton('5'), g.ReadFormButton('6')], [g.ReadFormButton('7'), g.ReadFormButton('8'), g.ReadFormButton('9')], [g.ReadFormButton('Submit'), g.ReadFormButton('0'), g.ReadFormButton('Clear')], - [out_elem], + [ g.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='output')], ] form = g.FlexForm('Keypad', default_element_size=(5, 2), auto_size_buttons=False) @@ -684,9 +682,9 @@ def InputElementUpdate(): keys_entered += button # add the new digit elif button is 'Submit': keys_entered = values['input'] - out_elem.Update(keys_entered) # output the final string + form.FindElement('outpput').Update(keys_entered) # output the final string - in_elem.Update(keys_entered) # change the form to reflect current key string + form.FindElement('input').Update(keys_entered) # change the form to reflect current key string def TableSimulation(): @@ -751,13 +749,12 @@ fig_dict = {'Simple Data Entry':SimpleDataEntry, 'Simple Entry Return Data as Di 'Table Simulation':TableSimulation, 'Tight Layout':TightLayout} -# multiline_elem = sg.Multiline(size=(70,35),pad=(5,(3,90))) # define the form layout listbox_values = [key for key in fig_dict.keys()] while True: - # sg.ChangeLookAndFeel('Dark') - # sg.SetOptions(element_padding=(0,0)) + sg.ChangeLookAndFeel('Dark') + sg.SetOptions(element_padding=(0,0)) col_listbox = [[sg.Listbox(values=listbox_values, size=(max(len(x) for x in listbox_values),min(len(listbox_values), 20)), change_submits=False, key='func')], [sg.ReadFormButton('Run', pad=(0,0)), sg.ReadFormButton('Show Code', button_color=('white', 'gray25'), pad=(0,0)), sg.Exit(button_color=('white', 'firebrick4'), pad=(0,0))]] diff --git a/Demo_Desktop_Widget_Timer.py b/Demo_Desktop_Widget_Timer.py index 149f89d7..76040c50 100644 --- a/Demo_Desktop_Widget_Timer.py +++ b/Demo_Desktop_Widget_Timer.py @@ -7,63 +7,62 @@ import time You move it by grabbing anywhere on the window Good example of how to do a non-blocking, polling program using PySimpleGUI Can be used to poll hardware when running on a Pi - + NOTE - you will get a warning message printed when you exit using exit button. It will look something like: invalid command name "1616802625480StopMove" """ -def Timer(): - # ---------------- Create Form ---------------- - sg.ChangeLookAndFeel('Black') - sg.SetOptions(element_padding=(0,0)) - # Make a form, but don't use context manager - # Create the form layout - form_rows = [[sg.Text('')], - [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], - [sg.ReadFormButton('Pause', key='button', button_color=('white', '#001480')), sg.ReadFormButton('Reset', button_color=('white', '#007339')), sg.Exit(button_color=('white','firebrick4'))]] - # Layout the rows of the form and perform a read. Indicate the form is non-blocking! - form = sg.FlexForm('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True) - form.Layout(form_rows) - # - # ---------------- main loop ---------------- - current_time = 0 - paused = False - start_time = int(round(time.time()*100)) - while (True): - # --------- Read and update window -------- - if not paused: - button, values = form.ReadNonBlocking() - current_time = int(round(time.time()*100)) - start_time - else: - button, values = form.Read() - # --------- Do Button Operations -------- - if values is None or button == 'Exit': - break - if button is 'Reset': - start_time = int(round(time.time()*100)) - current_time = 0 - paused_time = start_time - elif button == 'Pause': - paused = True - paused_time = int(round(time.time()*100)) - element = form.FindElement('button') - element.Update(new_text='Run') - elif button == 'Run': - paused = False - start_time = start_time + int(round(time.time()*100)) - paused_time - element = form.FindElement('button') - element.Update(new_text='Pause') +# ---------------- Create Form ---------------- +sg.ChangeLookAndFeel('Black') +sg.SetOptions(element_padding=(0, 0)) +# Make a form, but don't use context manager +# Create the form layout +form_rows = [[sg.Text('')], + [sg.Text('', size=(8, 2), font=('Helvetica', 20), justification='center', key='text')], + [sg.ReadFormButton('Pause', key='button', button_color=('white', '#001480')), + sg.ReadFormButton('Reset', button_color=('white', '#007339')), + sg.Exit(button_color=('white', 'firebrick4'))]] +# Layout the rows of the form and perform a read. Indicate the form is non-blocking! +form = sg.FlexForm('Running Timer', no_titlebar=True, auto_size_buttons=False, keep_on_top=True, grab_anywhere=True) +form.Layout(form_rows) +# +# ---------------- main loop ---------------- +current_time = 0 +paused = False +start_time = int(round(time.time() * 100)) +while (True): + # --------- Read and update window -------- + if not paused: + button, values = form.ReadNonBlocking() + current_time = int(round(time.time() * 100)) - start_time + else: + button, values = form.Read() + # --------- Do Button Operations -------- + if values is None or button == 'Exit': + break + if button is 'Reset': + start_time = int(round(time.time() * 100)) + current_time = 0 + paused_time = start_time + elif button == 'Pause': + paused = True + paused_time = int(round(time.time() * 100)) + element = form.FindElement('button') + element.Update(text='Run') + elif button == 'Run': + paused = False + start_time = start_time + int(round(time.time() * 100)) - paused_time + element = form.FindElement('button') + element.Update(text='Pause') - # --------- Display timer in window -------- - form.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60, - (current_time // 100) % 60, - current_time % 100)) - time.sleep(.01) + # --------- Display timer in window -------- + form.FindElement('text').Update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60, + (current_time // 100) % 60, + current_time % 100)) + time.sleep(.01) - # --------- After loop -------- +# --------- After loop -------- - # Broke out of main loop. Close the window. - form.CloseNonBlockingForm() - -Timer() \ No newline at end of file +# Broke out of main loop. Close the window. +form.CloseNonBlockingForm() diff --git a/Demo_Keyboard.py b/Demo_Keyboard.py index a2ac060c..bde67fdb 100644 --- a/Demo_Keyboard.py +++ b/Demo_Keyboard.py @@ -18,7 +18,7 @@ with sg.FlexForm("Keyboard Test", return_keyboard_events=True, use_default_focus print(button, "exiting") break if len(button) == 1: - text_elem.Update(new_value='%s - %s'%(button, ord(button))) + text_elem.Update(value='%s - %s' % (button, ord(button))) if button is not None: text_elem.Update(button) diff --git a/Demo_Keypad.py b/Demo_Keypad.py index 9efa683a..fe036363 100644 --- a/Demo_Keypad.py +++ b/Demo_Keypad.py @@ -1,43 +1,56 @@ -import PySimpleGUI as sg - -# g.SetOptions(button_color=g.COLOR_SYSTEM_DEFAULT) # because some people like gray buttons - -# Demonstrates a number of PySimpleGUI features including: -# Default element size -# auto_size_buttons -# ReadFormButton -# Dictionary return values -# Update of elements in form (Text, Input) -# do_not_clear of Input elements +from tkinter import * +from random import randint +import PySimpleGUI as g +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, FigureCanvasAgg +from matplotlib.figure import Figure +import matplotlib.backends.tkagg as tkagg +import tkinter as Tk -# create the 2 Elements we want to control outside the form +def main(): + fig = Figure() -layout = [[sg.Text('Enter Your Passcode')], - [sg.Input(size=(10, 1), do_not_clear=True, key='input')], - [sg.ReadFormButton('1'), sg.ReadFormButton('2'), sg.ReadFormButton('3')], - [sg.ReadFormButton('4'), sg.ReadFormButton('5'), sg.ReadFormButton('6')], - [sg.ReadFormButton('7'), sg.ReadFormButton('8'), sg.ReadFormButton('9')], - [sg.ReadFormButton('Submit'), sg.ReadFormButton('0'), sg.ReadFormButton('Clear')], - [sg.Text('', size=(15, 1), font=('Helvetica', 18), text_color='red', key='out')], - ] + ax = fig.add_subplot(111) + ax.set_xlabel("X axis") + ax.set_ylabel("Y axis") + ax.grid() -form = sg.FlexForm('Keypad', default_button_element_size=(5, 2), auto_size_buttons=False) -form.Layout(layout) + layout = [[g.Text('Animated Matplotlib', size=(40, 1), justification='center', font='Helvetica 20')], + [g.Canvas(size=(640, 480), key='canvas')], + [g.ReadFormButton('Exit', size=(10, 2), pad=((280, 0), 3), font='Helvetica 14')]] -# Loop forever reading the form's values, updating the Input field -keys_entered = '' -while True: - button, values = form.Read() # read the form - if button is None: # if the X button clicked, just exit - break - if button is 'Clear': # clear keys if clear button - keys_entered = '' - elif button in '1234567890': - keys_entered = values['input'] # get what's been entered so far - keys_entered += button # add the new digit - elif button is 'Submit': - keys_entered = values['input'] - form.FindElement('out').Update(keys_entered) # output the final string + # create the form and show it without the plot + form = g.FlexForm('Demo Application - Embedding Matplotlib In PySimpleGUI') + form.Layout(layout) + form.ReadNonBlocking() - form.FindElement('input').Update(keys_entered) # change the form to reflect current key string \ No newline at end of file + canvas_elem = form.FindElement('canvas') + + graph = FigureCanvasTkAgg(fig, master=canvas_elem.TKCanvas) + canvas = canvas_elem.TKCanvas + + dpts = [randint(0, 10) for x in range(10000)] + for i in range(len(dpts)): + button, values = form.ReadNonBlocking() + if button is 'Exit' or values is None: + exit(69) + + ax.cla() + ax.grid() + + ax.plot(range(20), dpts[i:i + 20], color='purple') + graph.draw() + figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds + figure_w, figure_h = int(figure_w), int(figure_h) + photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h) + + canvas.create_image(640 / 2, 480 / 2, image=photo) + + figure_canvas_agg = FigureCanvasAgg(fig) + figure_canvas_agg.draw() + + tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2) + # time.sleep(.1) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Demo_Matplotlib_Ping_Graph.py b/Demo_Matplotlib_Ping_Graph.py index fc8f7cdd..c58c2874 100644 --- a/Demo_Matplotlib_Ping_Graph.py +++ b/Demo_Matplotlib_Ping_Graph.py @@ -645,7 +645,7 @@ def main(): layout = [[canvas_elem, sg.ReadFormButton('Exit', pad=(0, (210, 0)))]] # create the form and show it without the plot - form = sg.FlexForm('Ping Graph', background_color='white') + form = sg.FlexForm('Ping Graph', background_color='white', grab_anywhere=True) form.Layout(layout) form.ReadNonBlocking() diff --git a/Demo_PNG_Viewer.py b/Demo_PNG_Viewer.py index 7d6bcd7d..1af28f72 100644 --- a/Demo_PNG_Viewer.py +++ b/Demo_PNG_Viewer.py @@ -4,9 +4,9 @@ import os # Simple Image Browser based on PySimpleGUI # Get the folder containing the images from the user -rc, folder = sg.GetPathBox('Image Browser', 'Image folder to open', default_path='') -if rc is False or folder is '': - sg.MsgBoxCancel('Cancelling') +folder = sg.PopupGetFolder('Image folder to open') +if folder is None: + sg.PopupCancel('Cancelling') exit(0) # get list of PNG files in folder @@ -14,52 +14,62 @@ png_files = [folder + '\\' + f for f in os.listdir(folder) if '.png' in f] filenames_only = [f for f in os.listdir(folder) if '.png' in f] if len(png_files) == 0: - sg.MsgBox('No PNG images in folder') + sg.Popup('No PNG images in folder') exit(0) + +# define menu layout +menu = [['File', ['Open Folder', 'Exit']], ['Help', ['About',]]] # create the form that also returns keyboard events form = sg.FlexForm('Image Browser', return_keyboard_events=True, location=(0,0), use_default_focus=False ) -# make these 2 elements outside the layout because want to "update" them later -# initialize to the first PNG file in the list -image_elem = sg.Image(filename=png_files[0]) -filename_display_elem = sg.Text(png_files[0], size=(80, 3)) -file_num_display_elem = sg.Text('File 1 of {}'.format(len(png_files)), size=(15,1)) - # define layout, show and read the form -col = [[filename_display_elem], - [image_elem], - [sg.ReadFormButton('Next', size=(8,2)), sg.ReadFormButton('Prev', size=(8,2)), file_num_display_elem]] +col = [[sg.Text(png_files[0], size=(80, 3), key='filename')], + [sg.Image(filename=png_files[0], key='image')], + [sg.ReadFormButton('Next', size=(8,2)), sg.ReadFormButton('Prev', size=(8,2)), + sg.Text('File 1 of {}'.format(len(png_files)), size=(15,1), key='filenum')]] col_files = [[sg.Listbox(values=filenames_only, size=(60,30), key='listbox')], [sg.ReadFormButton('Read')]] -layout = [[sg.Column(col_files), sg.Column(col)]] +layout = [[sg.Menu(menu)], [sg.Column(col_files), sg.Column(col)]] button, values = form.LayoutAndRead(layout) # Shows form on screen # loop reading the user input and displaying image, filename i=0 while True: - # perform button and keyboard operations + # --------------------- Button & Keyboard --------------------- if button is None: break elif button in ('Next', 'MouseWheel:Down', 'Down:40', 'Next:34') and i < len(png_files)-1: i += 1 elif button in ('Prev', 'MouseWheel:Up', 'Up:38', 'Prior:33') and i > 0: i -= 1 + elif button == 'Exit': + exit(69) - if button == 'Read': - filename = folder + '\\' + values['listbox'][0] - # print(filename) - else: - filename = png_files[i] + filename = folder + '/' + values['listbox'][0] if button == 'Read' else png_files[i] + + # ----------------- Menu choices ----------------- + if button == 'Open Folder': + newfolder = sg.PopupGetFolder('New folder', no_window=True) + if newfolder is None: + continue + folder = newfolder + png_files = [folder + '/' + f for f in os.listdir(folder) if '.png' in f] + filenames_only = [f for f in os.listdir(folder) if '.png' in f] + form.FindElement('listbox').Update(values=filenames_only) + form.Refresh() + i = 0 + elif button == 'About': + sg.Popup('Demo PNG Viewer Program', 'Please give PySimpleGUI a try!') # update window with new image - image_elem.Update(filename=filename) + form.FindElement('image').Update(filename=filename) # update window with filename - filename_display_elem.Update(filename) + form.FindElement('filename').Update(filename) # update page display - file_num_display_elem.Update('File {} of {}'.format(i+1, len(png_files))) + form.FindElement('filenum').Update('File {} of {}'.format(i+1, len(png_files))) # read the form button, values = form.Read() diff --git a/Demo_Password_Login.py b/Demo_Password_Login.py index c6040e3d..be3e764a 100644 --- a/Demo_Password_Login.py +++ b/Demo_Password_Login.py @@ -9,6 +9,8 @@ import hashlib 3. Type password into the GUI 4. Copy and paste hash code form GUI into variable named login_password_hash 5. Run program again and test your login! + 6. Are you paying attention? The first person that can post an issue on GitHub with the + matching password to the hash code in this example gets a $5 PayPal payment """ # Use this GUI to get your password's hash code @@ -47,7 +49,7 @@ def PasswordMatches(password, hash): return password_hash == hash -login_password_hash = '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8' +login_password_hash = 'e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4' password = sg.PopupGetText('Password', password_char='*') if password == 'gui': # Remove when pasting into your program HashGeneratorGUI() # Remove when pasting into your program diff --git a/Demo_Super_Simple_Form.py b/Demo_Super_Simple_Form.py index 4498feea..a1039249 100644 --- a/Demo_Super_Simple_Form.py +++ b/Demo_Super_Simple_Form.py @@ -13,19 +13,4 @@ layout = [ button, values = form.LayoutAndRead(layout) -print(button, values, values['name'], values['address'], values['phone']) - -form = sg.FlexForm('Simple data entry form') # begin with a blank form - -layout = [ - [sg.Text('Please enter your Name, Address, Phone')], - [sg.Text('Name', size=(15, 1)), sg.InputText('name')], - [sg.Text('Address', size=(15, 1)), sg.InputText('address')], - [sg.Text('Phone', size=(15, 1)), sg.InputText('phone')], - [sg.Ok(), sg.Cancel()] - ] - -button, values = form.LayoutAndRead(layout) -print(values) -name, address, phone = values -sg.MsgBox(button, values[0], values[1], values[2]) \ No newline at end of file +sg.Popup(button, values, values['name'], values['address'], values['phone']) diff --git a/Demo_Table_Simulation.py b/Demo_Table_Simulation.py index a96ef955..b8d9ed0b 100644 --- a/Demo_Table_Simulation.py +++ b/Demo_Table_Simulation.py @@ -1,30 +1,68 @@ +import csv import PySimpleGUI as sg def TableSimulation(): """ Display data in a table format """ - # sg.ChangeLookAndFeel('Dark') sg.SetOptions(element_padding=(0,0)) - layout = [[sg.T('Table Using Combos and Input Elements', font='Any 18')], - [sg.T('Row, Cal to change'), + + menu_def = [['File', ['Open', 'Save', 'Exit']], + ['Edit', ['Paste', ['Special', 'Normal',], 'Undo'],], + ['Help', 'About...'],] + + columm_layout = [[]] + + MAX_ROWS = 60 + MAX_COL = 10 + for i in range(MAX_ROWS): + inputs = [sg.T('{}'.format(i), size=(4,1), justification='right')] + [sg.In(size=(10, 1), pad=(1, 1), justification='right', key=(i,j), do_not_clear=True) for j in range(MAX_COL)] + columm_layout.append(inputs) + + layout = [ [sg.Menu(menu_def)], + [sg.T('Table Using Combos and Input Elements', font='Any 18')], + [sg.T('Type in a row, column and value. The form will update the values in realtime as you type'), sg.In(key='inputrow', justification='right', size=(8,1), pad=(1,1), do_not_clear=True), sg.In(key='inputcol', size=(8,1), pad=(1,1), justification='right', do_not_clear=True), - sg.In(key='value', size=(8,1), pad=(1,1), justification='right', do_not_clear=True)]] - - for i in range(20): - inputs = [sg.In('{}{}'.format(i,j), size=(8, 1), pad=(1, 1), justification='right', key=(i,j), do_not_clear=True) for j in range(10)] - line = [sg.Combo(('Customer ID', 'Customer Name', 'Customer Info'))] - line.append(inputs) - layout.append(inputs) + sg.In(key='value', size=(8,1), pad=(1,1), justification='right', do_not_clear=True)], + [sg.Column(columm_layout, size=(815,600), scrollable=True)]] form = sg.FlexForm('Table', return_keyboard_events=True, grab_anywhere=False) form.Layout(layout) while True: button, values = form.Read() - if button is None: + # --- Process buttons --- # + if button is None or button == 'Exit': break + elif button == 'About...': + sg.Popup('Demo of table capabilities') + elif button == 'Open': + filename = sg.PopupGetFile('filename to open', no_window=True, file_types=(("CSV Files","*.csv"),)) + # --- populate table with file contents --- # + if filename is not None: + with open(filename, "r") as infile: + reader = csv.reader(infile) + try: + data = list(reader) # read everything else into a list of rows + except: + sg.PopupError('Error reading file') + continue + # clear the table + [form.FindElement((i,j)).Update('') for j in range(MAX_COL) for i in range(MAX_ROWS)] + + for i, row in enumerate(data): + for j, item in enumerate(row): + location = (i,j) + try: # try the best we can at reading and filling the table + target_element = form.FindElement(location) + new_value = item + if target_element is not None and new_value != '': + target_element.Update(new_value) + except: + pass + + # if a valid table location entered, change that location's value try: location = (int(values['inputrow']), int(values['inputcol'])) target_element = form.FindElement(location) diff --git a/PySimpleGUI.py b/PySimpleGUI.py index ab4dbcdc..3c0946a0 100644 --- a/PySimpleGUI.py +++ b/PySimpleGUI.py @@ -282,11 +282,16 @@ class InputText(Element): super().__init__(ELEM_TYPE_INPUT_TEXT, scale=scale, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad) - def Update(self, new_value): - try: - self.TKStringVar.set(new_value) - except: pass - self.DefaultText = new_value + def Update(self, value=None, disable=None): + if disable is True: + self.TKEntry['state'] = 'disabled' + elif disable is False: + self.TKEntry['state'] = 'normal' + if value is not None: + try: + self.TKStringVar.set(value) + except: pass + self.DefaultText = value def Get(self): return self.TKStringVar.get() @@ -317,23 +322,24 @@ class InputCombo(Element): super().__init__(ELEM_TYPE_INPUT_COMBO, scale=scale, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad) - def Update(self, value=None, values=None, disabled=False): + def Update(self, value=None, values=None, disable=None): if values is not None: try: self.TKCombo['values'] = values self.TKCombo.current(0) except: pass self.Values = values - - self.TKCombo['state'] = 'disable' if disabled else 'enable' - - for index, v in enumerate(self.Values): - if v == value: - try: - self.TKCombo.current(index) - except: pass - self.DefaultValue = value - break + for index, v in enumerate(self.Values): + if v == value: + try: + self.TKCombo.current(index) + except: pass + self.DefaultValue = value + break + if disable == True: + self.TKCombo['state'] = 'disable' + elif disable == False: + self.TKCombo['state'] = 'enable' def __del__(self): @@ -365,14 +371,21 @@ class InputOptionMenu(Element): super().__init__(ELEM_TYPE_INPUT_OPTION_MENU, scale=scale, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad) - def Update(self, value): - for index, v in enumerate(self.Values): - if v == value: - try: - self.TKStringVar.set(value) - except: pass - self.DefaultValue = value - break + def Update(self, value=None, values=None, disable=None): + if values is not None: + self.Values = values + if self.Values is not None: + for index, v in enumerate(self.Values): + if v == value: + try: + self.TKStringVar.set(value) + except: pass + self.DefaultValue = value + break + if disable == True: + self.TKOptionMenu['state'] = 'disabled' + elif disable == False: + self.TKOptionMenu['state'] = 'normal' def __del__(self): @@ -415,12 +428,19 @@ class Listbox(Element): super().__init__(ELEM_TYPE_INPUT_LISTBOX, scale=scale, size=size, auto_size_text=auto_size_text, font=font, background_color=bg, text_color=fg, key=key, pad=pad) - def Update(self, values): - self.TKListbox.delete(0, 'end') - for item in values: - self.TKListbox.insert(tk.END, item) - self.TKListbox.selection_set(0, 0) - self.Values = values + def Update(self, values=None, disable=None): + if disable == True: + self.TKListbox.configure(state='disabled') + elif disable == False: + self.TKListbox.configure(state='normal') + if values is not None: + self.TKListbox.delete(0, 'end') + for item in values: + self.TKListbox.insert(tk.END, item) + self.TKListbox.selection_set(0, 0) + self.Values = values + + def SetValue(self, values): for index, item in enumerate(self.Values): @@ -466,14 +486,17 @@ class Radio(Element): super().__init__(ELEM_TYPE_INPUT_RADIO, scale=scale , size=size, auto_size_text=auto_size_text, font=font, background_color=background_color, text_color=self.TextColor, key=key, pad=pad) - def Update(self, value): - if not value: - return + def Update(self, value=None, disable=None): location = EncodeRadioRowCol(self.Position[0], self.Position[1]) - try: - self.TKIntVar.set(location) - except: pass - self.InitialState = value + if value is not None: + try: + self.TKIntVar.set(location) + except: pass + self.InitialState = value + if disable == True: + self.TKRadio['state'] = 'disabled' + elif disable == False: + self.TKRadio['state'] = 'normal' def __del__(self): try: @@ -508,15 +531,16 @@ class Checkbox(Element): def Get(self): return self.TKIntVar.get() - def Update(self, value): - try: - if value is None: - self.TKCheckbutton.configure(state='disabled') - else: - self.TKCheckbutton.configure(state='normal') + def Update(self, value=None, disable=None): + if value is not None: + try: self.TKIntVar.set(value) - except: pass - self.InitialState = value + self.InitialState = value + except: pass + if disable == True: + self.TKCheckbutton.configure(state='disabled') + elif disable == False: + self.TKCheckbutton.configure(state='normal') def __del__(self): @@ -550,17 +574,21 @@ class Spin(Element): super().__init__(ELEM_TYPE_INPUT_SPIN, scale, size, auto_size_text, font=font,background_color=bg, text_color=fg, key=key, pad=pad) return - def Update(self, new_value=None, new_values=None ): - if new_values != None: + def Update(self, value=None, values=None, disable=None): + if values != None: old_value = self.TKStringVar.get() - self.Values = new_values - self.TKSpinBox.configure(values=new_values) + self.Values = values + self.TKSpinBox.configure(values=values) self.TKStringVar.set(old_value) - if new_value is not None: + if value is not None: try: - self.TKStringVar.set(new_value) + self.TKStringVar.set(value) except: pass - self.DefaultValue = new_value + self.DefaultValue = value + if disable == True: + self.TKSpinBox.configure(state='disabled') + elif disable == False: + self.TKSpinBox.configure(state='normal') def SpinChangedHandler(self, event): @@ -601,12 +629,17 @@ class Multiline(Element): super().__init__(ELEM_TYPE_INPUT_MULTILINE, scale=scale, size=size, auto_size_text=auto_size_text, background_color=bg, text_color=fg, key=key, pad=pad) return - def Update(self, new_value): - try: - self.TKText.delete('1.0', tk.END) - self.TKText.insert(1.0, new_value) - except: pass - self.DefaultText = new_value + def Update(self, value=None, disable=None): + if value is not None: + try: + self.TKText.delete('1.0', tk.END) + self.TKText.insert(1.0, value) + except: pass + self.DefaultText = value + if disable == True: + self.TKText.configure(state='disabled') + elif disable == False: + self.TKText.configure(state='normal') def Get(self): return self.TKText.get(1.0, tk.END) @@ -641,11 +674,11 @@ class Text(Element): super().__init__(ELEM_TYPE_TEXT, scale, size, auto_size_text, background_color=bg, font=font if font else DEFAULT_FONT, text_color=self.TextColor, pad=pad, key=key) return - def Update(self, new_value = None, background_color=None, text_color=None, font=None): - if new_value is not None: - self.DisplayText=new_value + def Update(self, value = None, background_color=None, text_color=None, font=None): + if value is not None: + self.DisplayText=value stringvar = self.TKStringVar - stringvar.set(new_value) + stringvar.set(value) if background_color is not None: self.TKText.configure(background=background_color) if text_color is not None: @@ -690,17 +723,18 @@ class TKProgressBar(): s.configure(str(length)+str(width)+"my.Vertical.TProgressbar", troughrelief=relief, borderwidth=border_width, thickness=width) self.TKProgressBarForReal = ttk.Progressbar(root, maximum=self.Max, style=str(length)+str(width)+'my.Vertical.TProgressbar', length=length, orient=tk.VERTICAL, mode='determinate') - def Update(self, count, max=None): + def Update(self, count=None, max=None): if max is not None: self.Max = max try: self.TKProgressBarForReal.config(maximum=max) except: return False - if count > self.Max: return False - try: - self.TKProgressBarForReal['value'] = count - except: return False + if count is not None and count > self.Max: return False + if count is not None: + try: + self.TKProgressBarForReal['value'] = count + except: return False return True def __del__(self): @@ -922,16 +956,23 @@ class Button(Element): return - def Update(self, value=None, new_text=None, button_color=(None, None)): + def Update(self, value=None, text=None, button_color=(None, None), disable=None): try: - if new_text is not None: - self.TKButton.configure(text=new_text) - self.ButtonText = new_text + if text is not None: + self.TKButton.configure(text=text) + self.ButtonText = text if button_color != (None, None): self.TKButton.config(foreground=button_color[0], background=button_color[1]) except: return - self.DefaultValue = value + if value is not None: + self.DefaultValue = value + if disable == True: + self.TKButton['state'] = 'disabled' + elif disable == False: + self.TKButton['state'] = 'normal' + + def __del__(self): try: @@ -1089,13 +1130,18 @@ class Slider(Element): super().__init__(ELEM_TYPE_INPUT_SLIDER, scale=scale, size=size, font=font, background_color=background_color, text_color=text_color, key=key, pad=pad) return - def Update(self, value, range=(None, None)): - try: - self.TKIntVar.set(value) - if range != (None, None): - self.TKScale.config(from_ = range[0], to_ = range[1]) - except: pass - self.DefaultValue = value + def Update(self, value=None, range=(None, None), disable=None): + if value is not None: + try: + self.TKIntVar.set(value) + if range != (None, None): + self.TKScale.config(from_ = range[0], to_ = range[1]) + except: pass + self.DefaultValue = value + if disable == True: + self.TKScale['state'] = 'disabled' + elif disable == False: + self.TKScale['state'] = 'normal' def SliderChangedHandler(self, event): # first, get the results table built @@ -2737,8 +2783,7 @@ def StartupTK(my_flex_form): my_flex_form.TKroot = root # Make moveable window - if ((my_flex_form.NoTitleBar or my_flex_form.GrabAnywhere in (None, True)) and not my_flex_form.NonBlocking) or \ - (my_flex_form.GrabAnywhere == True and my_flex_form.NonBlocking): + if (my_flex_form.GrabAnywhere is not False and not (my_flex_form.NonBlocking and my_flex_form.GrabAnywhere is not True)): root.bind("", my_flex_form.StartMove) root.bind("", my_flex_form.StopMove) root.bind("", my_flex_form.OnMotion) @@ -3556,7 +3601,7 @@ def ObjToString(obj, extra=' '): # Exits via an OK button2 press # # Returns nothing # # ===================================================# -def Popup(*args, button_color=None, button_type=MSG_BOX_OK, auto_close=False, auto_close_duration=None, non_blocking=False, icon=DEFAULT_WINDOW_ICON, line_width=None, font=None, no_titlebar=False): +def Popup(*args, button_color=None, button_type=MSG_BOX_OK, auto_close=False, auto_close_duration=None, non_blocking=False, icon=DEFAULT_WINDOW_ICON, line_width=None, font=None, no_titlebar=False, keep_on_top=False): ''' Show message box. Displays one line per user supplied argument. Takes any Type of variable to display. :param args: @@ -3578,7 +3623,7 @@ def Popup(*args, button_color=None, button_type=MSG_BOX_OK, auto_close=False, au else: local_line_width = MESSAGE_BOX_LINE_WIDTH title = args_to_print[0] if args_to_print[0] is not None else 'None' - with FlexForm(title, auto_size_text=True, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, icon=icon, font=font, no_titlebar=no_titlebar) as form: + with FlexForm(title, auto_size_text=True, button_color=button_color, auto_close=auto_close, auto_close_duration=auto_close_duration, icon=icon, font=font, no_titlebar=no_titlebar, keep_on_top=keep_on_top) as form: max_line_total, total_lines = 0,0 for message in args_to_print: # fancy code to check if string and convert if not is not need. Just always convert to string :-)