From 8b4185988f7f7b3a742bc306ff259d42ecc95182 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Thu, 27 Sep 2018 05:18:35 -0400 Subject: [PATCH] Programming course with PySimpleGUI lessons --- .../1a PSG (Entry and PopUp).py | 29 ++++ ProgrammingClassExamples/1b PSG (Format).py | 37 +++++ .../1c PSG (persistent form and bind key).py | 32 +++++ ...PSG (named input keys and catch errors).py | 35 +++++ .../1e PSG (validation and Look and Feel).py | 34 +++++ .../2a. PSG (checkbox and radiobuttons).py | 40 ++++++ .../2b_makewinexe_file.py | 36 +++++ .../3 PSG (multiline display).py | 40 ++++++ .../4a PSG (Sliders and combo).py | 43 ++++++ .../4b PSG (Spinner and combo) .py | 38 +++++ .../6 PSG sort and search .py | 130 ++++++++++++++++++ ProgrammingClassExamples/Text files.py | 88 ++++++++++++ 12 files changed, 582 insertions(+) create mode 100644 ProgrammingClassExamples/1a PSG (Entry and PopUp).py create mode 100644 ProgrammingClassExamples/1b PSG (Format).py create mode 100644 ProgrammingClassExamples/1c PSG (persistent form and bind key).py create mode 100644 ProgrammingClassExamples/1d PSG (named input keys and catch errors).py create mode 100644 ProgrammingClassExamples/1e PSG (validation and Look and Feel).py create mode 100644 ProgrammingClassExamples/2a. PSG (checkbox and radiobuttons).py create mode 100644 ProgrammingClassExamples/2b_makewinexe_file.py create mode 100644 ProgrammingClassExamples/3 PSG (multiline display).py create mode 100644 ProgrammingClassExamples/4a PSG (Sliders and combo).py create mode 100644 ProgrammingClassExamples/4b PSG (Spinner and combo) .py create mode 100644 ProgrammingClassExamples/6 PSG sort and search .py create mode 100644 ProgrammingClassExamples/Text files.py diff --git a/ProgrammingClassExamples/1a PSG (Entry and PopUp).py b/ProgrammingClassExamples/1a PSG (Entry and PopUp).py new file mode 100644 index 00000000..ed6600fc --- /dev/null +++ b/ProgrammingClassExamples/1a PSG (Entry and PopUp).py @@ -0,0 +1,29 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +layout = [ + [sg.Text('Celcius'), sg.InputText()], #layout, Text, Input + [sg.Submit()], #button on line below + ] + +#setup window with Title +window = sg.Window('Temperature Converter').Layout(layout) + +button, value = window.Read() #get value (part of a list) + +fahrenheit = round(9/5*float(value[0]) +32, 1) #convert and create string +result = 'Temperature in Fahrenheit is: ' + str(fahrenheit) +sg.Popup('Result', result) #display in Popup + + + + + + + + + + diff --git a/ProgrammingClassExamples/1b PSG (Format).py b/ProgrammingClassExamples/1b PSG (Format).py new file mode 100644 index 00000000..218be402 --- /dev/null +++ b/ProgrammingClassExamples/1b PSG (Format).py @@ -0,0 +1,37 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +#Set formatting options for all elements rather than individually. +sg.SetOptions (background_color = 'LightBlue', + element_background_color = 'LightBlue', + text_element_background_color = 'LightBlue', + font = ('Arial', 10, 'bold'), + text_color = 'Blue', + input_text_color ='Blue', + button_color = ('White', 'Blue') + ) +#adjust widths +layout = [ + [sg.Text('Celcius', size =(12,1)), sg.InputText(size = (8,1))], + [sg.Submit()], + ] + +window = sg.Window('Converter').Layout(layout) +button, value = window.Read() + +fahrenheit = round(9/5*float(value[0]) +32, 1) +result = 'Temperature in Fahrenheit is: ' + str(fahrenheit) +sg.Popup('Result',result) + + + + + + + + + + diff --git a/ProgrammingClassExamples/1c PSG (persistent form and bind key).py b/ProgrammingClassExamples/1c PSG (persistent form and bind key).py new file mode 100644 index 00000000..bae93423 --- /dev/null +++ b/ProgrammingClassExamples/1c PSG (persistent form and bind key).py @@ -0,0 +1,32 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +sg.SetOptions (background_color = 'LightBlue', + element_background_color = 'LightBlue', + text_element_background_color = 'LightBlue', + font = ('Arial', 10, 'bold'), + text_color = 'Blue', + input_text_color ='Blue', + button_color = ('White', 'Blue') + ) +#update (via list) values and and display answers +#value[0] is celcius input, value[1] is input to place result. +#Use ReadButton with while true: - keeps window open. + +layout = [ [sg.Text('Enter a Temperature in Celcius')], + [sg.Text('Celcius', size =(8,1)), sg.InputText(size = (15,1))], + [sg.Text('Result', size =(8,1)), sg.InputText(size = (15,1))], + [sg.ReadButton('Submit', bind_return_key = True)]] #Return = button press + +window = sg.Window('Converter').Layout(layout) + +while True: + button, value = window.Read() #get result + if button is not None: #break out of loop is button not pressed. + fahrenheit = round(9/5*float(value[0]) +32, 1) + window.FindElement(1).Update(fahrenheit) #put result in 2nd input box + else: + break diff --git a/ProgrammingClassExamples/1d PSG (named input keys and catch errors).py b/ProgrammingClassExamples/1d PSG (named input keys and catch errors).py new file mode 100644 index 00000000..e62fae8d --- /dev/null +++ b/ProgrammingClassExamples/1d PSG (named input keys and catch errors).py @@ -0,0 +1,35 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +sg.SetOptions (background_color = 'LightBlue', + element_background_color = 'LightBlue', + text_element_background_color = 'LightBlue', + font = ('Arial', 10, 'bold'), + text_color = 'Blue', + input_text_color ='Blue', + button_color = ('White', 'Blue') + ) +#name inputs (key) uses dictionary- easy to see updating of results +#value[input] first input value te c... +layout = [ [sg.Text('Enter a Temperature in Celcius')], + [sg.Text('Celcius', size =(8,1)), sg.InputText(size = (15,1),key = 'input')], + [sg.Text('Result', size =(8,1)), sg.InputText(size = (15,1),key = 'result')], + [sg.ReadButton('Submit', bind_return_key = True)]] + +window = sg.FlexForm('Temp Converter').Layout(layout) + +while True: + button, value = window.Read() + if button is not None: + #catch program errors for text or blank entry: + try: + fahrenheit = round(9/5*float(value['input']) +32, 1) + window.FindElement('result').Update(fahrenheit) #put result in text box + except ValueError: + sg.Popup('Error','Please try again') #display error + + else: + break diff --git a/ProgrammingClassExamples/1e PSG (validation and Look and Feel).py b/ProgrammingClassExamples/1e PSG (validation and Look and Feel).py new file mode 100644 index 00000000..e6b435a3 --- /dev/null +++ b/ProgrammingClassExamples/1e PSG (validation and Look and Feel).py @@ -0,0 +1,34 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +#Can use a variety of themes - plus individual options +sg.ChangeLookAndFeel('SandyBeach') +sg.SetOptions (font = ('Arial', 10, 'bold')) + + +layout = [ [sg.Text('Enter a Temperature in Celcius')], + [sg.Text('Celcius', size =(8,1)), sg.InputText(size = (15,1),key = 'input')], + [sg.Text('Result', size =(8,1)), sg.InputText(size = (15,1),key = 'result')], + [sg.ReadButton('Submit', bind_return_key = True)]] + +window = sg.FlexForm('Temp Converter').Layout(layout) + +while True: + button, value = window.Read() + if button is not None: + #catch program errors for text, floats or blank entry: + #Also validation for range [0, 50] + try: + if float(value['input']) > 50 or float(value['input']) <0: + sg.Popup('Error','Out of range') + else: + fahrenheit = round(9/5*int(value['input']) +32, 1) + window.FindElement('result').Update(fahrenheit) #put result in text box + except ValueError: + sg.Popup('Error','Please try again') #display error + + else: + break diff --git a/ProgrammingClassExamples/2a. PSG (checkbox and radiobuttons).py b/ProgrammingClassExamples/2a. PSG (checkbox and radiobuttons).py new file mode 100644 index 00000000..6c54f600 --- /dev/null +++ b/ProgrammingClassExamples/2a. PSG (checkbox and radiobuttons).py @@ -0,0 +1,40 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +sg.ChangeLookAndFeel('GreenTan') #Set colour scheme +sg.SetOptions (font =('Calibri',12,'bold') ) #and font + + + +#One checkbox and three radio buttons (grouped as 'Radio1' +layout = [[sg.Text('Membership Calculator', font = ('Calibri', 16, 'bold'))], + [sg.Checkbox(' Student? 10% off', size = (25,1)), #value[0] + sg.ReadButton('Display Cost', size = (14,1))], + [sg.Radio('1 month $50', 'Radio1', default = True), #value[1] + sg.Radio('3 months $100', 'Radio1'), #value[2] + sg.Radio('1 year $300', 'Radio1')], #value[3] + [sg.Text('', size = (30,1), justification = 'center', font =('Calibri', 16, 'bold'), key = 'result')]] + +window = sg.Window('Gym Membership').Layout(layout) + +while True: + button, value = window.Read() + if button is not None: + if value[1]: + cost = 50 + elif value[2]: + cost = 100 + else: + cost = 300 + if value[0]: + cost = cost*0.9 #apply discount + + #format as currency $ symbol and 2 d.p. - make a string + result = str(' Cost: ' + '${:.2f}'.format(cost)) + window.FindElement('result').Update(result) #put the result in Textbox + + else: + break diff --git a/ProgrammingClassExamples/2b_makewinexe_file.py b/ProgrammingClassExamples/2b_makewinexe_file.py new file mode 100644 index 00000000..351f082d --- /dev/null +++ b/ProgrammingClassExamples/2b_makewinexe_file.py @@ -0,0 +1,36 @@ +import PySimpleGUI as sg +#pip install PyInstaller +#windows command prompt pyinstaller -wF 2b_makewinexe_file.py +#must CD to directory where py file is + +sg.ChangeLookAndFeel('GreenTan') #Set colour scheme +sg.SetOptions (font =('Calibri',12,'bold') ) #and font + +form = sg.FlexForm('Gym Membership') + + +layout = [[sg.Text('Membership Calculator', font = ('Calibri', 16, 'bold'))], + [sg.Checkbox('CGS student?', size = (22,1)), #value[0] + sg.ReadButton('Display Cost', size = (14,1))], + [sg.Radio('One Month', 'Radio1', default = True), #value[1] + sg.Radio('Three Month', 'Radio1'), #value[2] + sg.Radio('One Year', 'Radio1')], #value[3] + [sg.Text('', size = (30,1), justification = 'center', font =('Calibri', 16, 'bold'), key = 'result')]] + +form.Layout(layout) +while True: + button, value = form.Read() + if button is not None: + if value[1]: + cost = 50 + elif value[2]: + cost = 100 + else: + cost = 300 + if value[0]: + cost = cost*0.9 + result = str(' Cost: ' + '${:.2f}'.format(cost)) #format as currency - make a string + form.FindElement('result').Update(result) #put the result in Textbox + + else: + break diff --git a/ProgrammingClassExamples/3 PSG (multiline display).py b/ProgrammingClassExamples/3 PSG (multiline display).py new file mode 100644 index 00000000..91ac114a --- /dev/null +++ b/ProgrammingClassExamples/3 PSG (multiline display).py @@ -0,0 +1,40 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +sg.ChangeLookAndFeel('GreenTan') + +sg.SetOptions(font = ('Courier New', 12)) + + + +layout = [ + [sg.Text('Enter and Add Data to Display', font = ('Calibri', 14,'bold'))], + [sg.Text('Race:', size = (5,1)), sg.InputText(size = (8,1)), + sg.Text('Club:', size = (5,1)), sg.InputText(size = (8,1))], + [sg.Text('Name:', size = (5,1)), sg.InputText(size = (8,1)), + sg.Text('Time:', size = (5,1)), sg.InputText(size = (8,1)),sg.Text(' '), + sg.ReadButton('Add Data', font = ('Calibri', 12, 'bold'))], + [sg.Text('_'*40)], + [sg.Text(' Race Club Name Time')], + [sg.Multiline(size =(40,6),key = 'Multiline')] + ] + +window = sg.Window('Enter & Display Data').Layout(layout) + +string = '' +S=[] +while True: + + button, value = window.Read() + if button is not None: + #use string formatting - best way? plus Courier New font - non-proportional font + S = S + ['{:^9s}{:<11s}{:<10s}{:>8s}'.format(value[0],value[1],value[2],value[3])] + for s in S: + string = string + s + '\n' + window.FindElement('Multiline').Update(string) + string ='' + else: + break diff --git a/ProgrammingClassExamples/4a PSG (Sliders and combo).py b/ProgrammingClassExamples/4a PSG (Sliders and combo).py new file mode 100644 index 00000000..67a930fd --- /dev/null +++ b/ProgrammingClassExamples/4a PSG (Sliders and combo).py @@ -0,0 +1,43 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +column1 = [ + [sg.Text('Pick operation', size = (15,1), font = ('Calibri', 12, 'bold'))], +[sg.InputCombo(['Add','Subtract','Multiply','Divide'], size = (10,6))], + [sg.Text('', size =(1,4))]] +column2 = [ + [sg.ReadButton('Submit', font = ('Calibri', 12, 'bold'), button_color = ('White', 'Red'))], + [sg.Text('Result:', font = ('Calibri', 12, 'bold'))],[sg.InputText(size = (12,1), key = 'result')] + ] + + +layout = [ + [sg.Text('Slider and Combo box demo', font = ('Calibri', 14,'bold'))], + [sg.Slider(range = (-9, 9),orientation = 'v', size = (5,20), default_value = 0), + sg.Slider(range = (-9, 9),orientation = 'v', size = (5, 20), default_value = 0), + sg.Text(' '), sg.Column(column1), sg.Column(column2)]] + +window = sg.Window('Enter & Display Data', grab_anywhere=False).Layout(layout) + +while True: + button, value = window.Read() + if button is not None: + if value[2] == 'Add': + result = value[0] + value[1] + elif value[2] == 'Multiply': + result = value[0] * value[1] + elif value[2] == 'Subtract': + result = value[0] - value[1] + elif value[2] == 'Divide': + if value[1] ==0: + sg.Popup('Second value can\'t be zero') + if value[0] == 0: + result = 'NA' + else: + result = value[0] / value[1] + window.FindElement('result').Update(result) + else: + break diff --git a/ProgrammingClassExamples/4b PSG (Spinner and combo) .py b/ProgrammingClassExamples/4b PSG (Spinner and combo) .py new file mode 100644 index 00000000..b4b61fc0 --- /dev/null +++ b/ProgrammingClassExamples/4b PSG (Spinner and combo) .py @@ -0,0 +1,38 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg +sg.SetOptions(font= ('Calibri', 12, 'bold')) + +layout = [ + [sg.Text('Spinner and Combo box demo', font = ('Calibri', 14, 'bold'))], + [sg.Spin([sz for sz in range (-9,10)], initial_value = 0), + sg.Spin([sz for sz in range (-9,10)], initial_value = 0), + sg.Text('Pick operation', size = (13,1)), + sg.InputCombo(['Add','Subtract','Multiply','Divide'], size = (8,6))], + [sg.Text('Result: ')],[sg.InputText(size = (6,1), key = 'result'), + sg.ReadButton('Calculate', button_color = ('White', 'Red'))]] + +window = sg.Window('Enter & Display Data', grab_anywhere=False).Layout(layout) + +while True: + button, value = window.Read() + + if button is not None: + val = [int(value[0]), int(value[1])] + if value[2] == 'Add': + result = val[0] + val[1] + elif value[2] == 'Multiply': + result = val[0] * val[1] + elif value[2] == 'Subtract': + result = val[0] - val[1] + elif value[2] == 'Divide': + if val[1] ==0: + sg.Popup('Second value can\'t be zero') + result = 'NA' + else: + result = round( val[0] / val[1], 3) + window.FindElement('result').Update(result) + else: + break diff --git a/ProgrammingClassExamples/6 PSG sort and search .py b/ProgrammingClassExamples/6 PSG sort and search .py new file mode 100644 index 00000000..70541403 --- /dev/null +++ b/ProgrammingClassExamples/6 PSG sort and search .py @@ -0,0 +1,130 @@ +#PySimple examples (v 3.8) +#Tony Crewe +#Sep 2018 + +import PySimpleGUI as sg + +sg.SetOptions (font =('Calibri',12,'bold')) + + +#setup column (called column1) of buttons to sue in layout + +column1 = [[sg.ReadButton('Original list', size = (13,1))], + [sg.ReadButton('Default sort', size = (13,1))], + [sg.ReadButton('Sort: selection',size = (13,1))], + [sg.ReadButton('Sort: quick', size = (13,1))]] + +layout =[[sg.Text('Search and Sort Demo', font =('Calibri', 20, 'bold'))], +[sg.Text('',size = (14, 11),relief=sg.RELIEF_SOLID,font = ('Calibri', 12), background_color ='White',key = 'display'), sg.Column(column1)], + [sg.Text('_'*32,font = ('Calibri', 12))], + [sg.InputText(size = (13,1), key = 'linear'), sg.Text(' '), sg.InputText(size = (13,1), key = 'binary')], + [sg.ReadButton('Linear Search', size = (13,1)), sg.Text(' '), sg.ReadButton('Binary Search', size = (13,1))], + ] + +window = sg.Window('Search and Sort Demo').Layout(layout) + +#names for Demo, could be loaded from a file +Names= ['Roberta', 'Kylie', 'Jenny', 'Helen', + 'Andrea', 'Meredith','Deborah','Pauline', + 'Belinda', 'Wendy'] + +#function to display list +def displayList(List): + global ListDisplayed #store list in Multiline text globally + ListDisplayed = List + display = '' + for l in List: #add list elements with new line + display = display + l + '\n' + window.FindElement('display').Update(display) + +#use inbuilt python sort +def default(Names): + L = Names[:] + L.sort() #inbuilt sort + displayList(L) + +#Selection sort - See Janson Ch 7 +def selSort(Names): + L = Names[:] + for i in range(len(L)): + smallest = i + for j in range(i+1, len(L)): + if L[j] < L[smallest]: #find smallest value + smallest = j #swap it to front + L[smallest], L[i] = L[i], L[smallest] #repeat from next poistion + displayList(L) + +#Quick sort - See Janson Ch 7 +def qsortHolder(Names): + L = Names[:] #pass List, first and last + quick_sort(L, 0, len(L) -1) #Start process + displayList(L) + +def quick_sort(L, first, last): #Quicksort is a partition sort + if first >= last: + return L + pivot = L[first] + low = first + high = last + while low < high: + while L[high] > pivot: + high = high -1 + while L[low] < pivot: + low = low + 1 + if low <= high: + L[high], L[low] = L[low], L[high] + low = low + 1 + high = high -1 + quick_sort(L, first, low -1) #continue splitting - sort small lsist + quick_sort(L, low, last) + +#Linear Search - no need for Ordered list +def linearSearch(): + L = Names[:] + found = False + for l in L: + if l == value['linear']: #Check each value + found = True + window.FindElement('display').Update('Linear search\n' + l + ' found.') + break + if not found: + window.FindElement('display').Update(value['linear'] + ' was \nNot found') + +#Binary Search - only works fot ordered lists +def binarySearch(): + L = ListDisplayed[:] #get List currently in multiline display + lo = 0 + hi = len(L)-1 + found = False #Start with found is Flase + while lo <= hi: + mid = (lo + hi) //2 #Start in middle + if L[mid] == value['binary']: #get the value from the search box + window.FindElement('display').Update('Binary search\n' + L[mid] + ' found.') + found = True #If found display + break #and stop + elif L[mid] < value['binary']: + lo = mid + 1 #Search in top half + else: + hi = mid - 1 #Search in lower half + if not found: #If we get to end - display not found + window.FindElement('display').Update(value['binary'] + ' was \nNot found') + + +while True: + button, value = window.Read() + if button is not None: + if button == 'Original list': + displayList(Names) + if button == 'Default sort': + default(Names) + if button == 'Sort: selection': + selSort(Names) + if button == 'Sort: quick': + qsortHolder(Names) + if button == 'Linear Search': + linearSearch() + if button == 'Binary Search': + binarySearch() + else: + break + diff --git a/ProgrammingClassExamples/Text files.py b/ProgrammingClassExamples/Text files.py new file mode 100644 index 00000000..cc1d2233 --- /dev/null +++ b/ProgrammingClassExamples/Text files.py @@ -0,0 +1,88 @@ +# TCC +#20/1/18 Oz date + +from tkinter import * + +def callback(event): #used to allow Return key as well as + calculate() # button press for mark entry + + +def calculate(): + global i, total,name + name = entry_name.get() #get the name and prevent another + mark[i] = entry_mark.get() + label_name.configure(state = DISABLED) + entry_name.configure(state = DISABLED) + mark[i] = entry_mark.get() #get and store in mark list and clear entry + entry_mark.delete(0,END) + i = i + 1 #get total i - needs to be global + if i == 4: #if four marks - stop + button_done.configure(state = NORMAL) + button_calculate.configure(state = DISABLED) + +def done(): + total = 0 + for m in mark: #total marks - convery to integer + total += int(m) + average = total/4 #calculate average + f = open(pathname, 'w') + print(name, file= f) + print(total, file= f) #write to file + print(average, file= f) + f.close() + button_done.configure(state = DISABLED) #stop button being pressed again + button_display.configure(state = NORMAL) + + +def display(): + #create list of three valuesand combine elemnets into one string - use \n for new line + data = [line.strip() for line in open(pathname)] + s= 'Name: ' + data[0] +'\nTotal: ' + str(data[1]) + '\nAverage: ' + str(data[2]) + label_displayresults.configure(text = s) + + +root = Tk() +root.title('text files') + +#set up controls +label_instructs = Label(justify = LEFT, padx = 10, pady=10,width = 30, height =4, text = 'Enter a Name then a Mark then press\nCalculate, do this 4 times.Then press\nDone to Save Name, Total and Average.') +label_name = Label(text='Name: ', width = 8) +entry_name = Entry(width = 8) +label_mark = Label(text='Mark: ', width = 8) +entry_mark = Entry(width = 8) +button_calculate = Button(text = 'Calculate', command=calculate) +button_done= Button(pady = 8, text='Done', command = done, state = DISABLED) +button_display = Button(pady =8,text = 'Display', command=display, state = DISABLED) +label_displaytext = Label(justify = LEFT, text='Press display to\nretrieve recent\nTotal & Average') +label_displayresults=Label(justify = LEFT, padx = 10, height = 5,) + +#set up positioning of controls +label_instructs.grid(row = 0, columnspan = 3) +label_name.grid(row = 1, column = 0) +entry_name.grid(row = 1, column = 1) +label_mark.grid(row = 2, column = 0) +entry_mark.grid(row = 2, column = 1) + +entry_mark.bind('', callback) #create binding for Return key for mark entry box + +button_calculate.grid(row =3, column = 0) +button_done.grid(row = 3, column = 1) +button_display.grid(row = 4, column = 0) +label_displaytext.grid(row = 4, column = 1) +label_displayresults.grid(row = 5, columnspan = 2) + +#global variables when used in more than one function +global i +global mark +global total +global average +i=total=0 +mark = [0,0,0,0] +average = 0.0 +entry_name.focus() #set initial focus + +global pathname + +pathname = "C:\\Users\\tcrewe\\Dropbox\\01 Teaching folders\\07 TCC Python stuff\\TCC py files\\TCC sample files\wordlist.txt" + +mainloop()