From 0cb17f2a3126269899957c260b046404a59f1850 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sat, 17 Aug 2019 17:00:10 -0400 Subject: [PATCH] New Demo - Design Patterns for Layout Generation --- DemoPrograms/Demo_Layout_Generation.py | 251 +++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 DemoPrograms/Demo_Layout_Generation.py diff --git a/DemoPrograms/Demo_Layout_Generation.py b/DemoPrograms/Demo_Layout_Generation.py new file mode 100644 index 00000000..076a8f30 --- /dev/null +++ b/DemoPrograms/Demo_Layout_Generation.py @@ -0,0 +1,251 @@ +import PySimpleGUI as sg + +""" + PySimpleGUI is designed & authored in Python to take full advantage the awesome Python constructs & capabilities. + Layouts are represented as lists to PySimpleGUI. Lists are fundamental in Python and have a number of powerful + capabilities that PySimpleGUI exploits. + + Many times PySimpleGUI programs can benefit from using CODE to GENERATE your layouts. This Demo illustrates + a number of ways of "building" a layout. Some work on 3.5 and up. Some are basic and show concatenation of rows + to build up a layout. Some utilize generators. + + These 8 "Constructs" or Design Patterns demonstrate numerous ways of "generating" or building your layouts + 0 - A simple list comprehension to build a row of buttons + 1 - A simple list comprehension to build a column of buttons + 2 - Concatenation of rows within a layout + 3 - Concatenation of 2 complete layouts [[ ]] + [[ ]] = [[ ]] + 4 - Concatenation of elements to form a single row [ [] + [] + [] ] = [[ ]] + 5 - Questionnaire - Using a double list comprehension to build both rows and columns in a single line of code + 6 - Questionnaire - Unwinding the comprehensions into 2 for loops instead + 7 - Using the * operator to unpack generated items onto a single row +""" + +""" + Construct #0 - List comprehension to generate a row of Buttons + + Comprehensions are super-powers of Python. In this example we're using a comprehension to create 4 buttons that + are all on the same row. +""" + + +def layout0(): + layout = [[sg.Button(i) for i in range(4)]] # A list of buttons is created + + window = sg.Window('Generated Layouts', layout) + + event, values = window.Read() + + print(event, values) + window.Close() + +""" + Construct #1 - List comprehension to generate rows of Buttons + + More list super-power, this time used to build a series of buttons doing DOWN the window instead of across + +""" + +def layout1(): + layout = [[sg.Button(i)] for i in range(4)] # a List of lists of buttons. Notice the ] after Button + + window = sg.Window('Generated Layouts', layout) + + event, values = window.Read() + + print(event, values) + window.Close() + + +""" + Construct #2 - List comprehension to generate a row of Buttons and concatenation of more lines of elements + + Comprehensions are super-powers of Python. In this example we're using a comprehension to create 4 buttons that + are all on the same row, just like the previous example. + However, here, we want to not just have a row of buttons, we want have an OK button at the bottom. + To do this, you "add" the rest of the GUI layout onto the end of the generated part. + + Note - you can't end the layout line after the +. If you wanted to put the OK button on the next line, you need + to add a \ at the end of the first line. + See next Construct on how to not use a \ that also results in a VISUALLY similar to a norma layout +""" + +def layout2(): + layout = [[sg.Button(i) for i in range(4)]] + [[sg.OK()]] # if want to split, can't add newline after + to do it + + window = sg.Window('Generated Layouts', layout) + + event, values = window.Read() + + print(event, values) + window.Close() + + +""" + Construct # 3 - Adding together what appears to be 2 layouts + + Same as layout2, except that the OK button is put on another line without using a \ so that the layout appears to + look like a normal, multiline layout without a \ at the end + + Also shown is the OLD tried and true way, using layout.append. You will see the append technique in many of the + Demo programs and probably elsewhere. Hoping to remove these and instead use this more explicit method of +=. + + Using the + operator, as you've already seen, can be used in the middle of the layout. A call to append you cannot + use this way because it modifies the layout list directly. +""" + +def layout3(): + # in terms of formatting, the layout to the RIGHT of the = sign looks like a 2-line GUI (ignore the layout += + layout = [[sg.Button(i) for i in range(4)]] + layout += [[sg.OK()]] # this row is better than, but is the same as + layout.append([sg.Cancel()]) # .. this row in that they both add a new ROW with a button on it + + window = sg.Window('Generated Layouts', layout) + + event, values = window.Read() + + print(event, values) + window.Close() + + +""" + Construct 4 - Using + to place Elements on the same row + + If you want to put elements on the same row, you can simply add them together. All that is happening is that the + items in one list are added to the items in another. That's true for all these contructs using + +""" + +def layout4(): + layout = [[sg.Text('Enter some info')] + [sg.Input()] + [sg.Exit()]] + + window = sg.Window('Generated Layouts', layout) + + event, values = window.Read() + + print(event, values) + window.Close() + + +""" + Construct #5 - Simple "concatenation" of layouts + Layouts are lists of lists. Some of the examples and demo programs use a .append method to add rows to layouts. + These will soono be replaced with this new technique. It's so simple that I don't know why it took so long to + find it. + This layout uses list comprehensions heavily, and even uses 2 of them. So, if you find them confusing, skip down + to the next Construct and you'll see the same layout built except for loops are used rather than comprehensions + + The next 3 examples all use this same window that is layed out like this: + Each row is: + Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5 + Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5 + ... + + It shows, in particular, this handy bit of layout building, a += to add on additional rows. + layout = [[stuff on row 1], [stuff on row 2]] + layout += [[stuff on row 3]] + + Works as long as the things you are adding together look like this [[ ]] (the famous double bracket layouts of PSG) +""" + +def layout5(): + questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?', + 'Get along with people in your family?', 'Get along with people outside your family?', + 'Get along well in social situations?', 'Feel close to another person', + 'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?') + + layout = [[sg.T(qnum + 1, size=(2, 2)), sg.T(q, size=(30, 2))] + [sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, col)) for col in range(5)] for qnum, q in enumerate(questions)] + layout += [[sg.OK()]] + + window = sg.Window('Computed Layout Questionnaire', layout) + event, values = window.Read() + + print(event, values) + window.Close() + + +""" + Construct #6 - Computed layout without using list comprehensions + This layout is identical to Contruct #5. The difference is that rather than use list comprehensions, this code + uses for loops. Perhaps if you're a beginner this version makes more sense? + + In this example we start with a "blank layout" [[ ]] and add onto it. + + Works as long as the things you are adding together look like this [[ ]] (the famous double bracket layouts of PSG) +""" + + +def layout6(): + questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?', + 'Get along with people in your family?', 'Get along with people outside your family?', + 'Get along well in social situations?', 'Feel close to another person', + 'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?') + + layout = [[]] + for qnum, question in enumerate(questions): # loop through questions + row_layout = [sg.T(qnum + 1, size=(2, 2)), sg.T(question, size=(30, 2))] # rows start with # and question + for radio_num in range(5): # loop through 5 radio buttons and add to row + row_layout += [sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, radio_num))] + layout += [row_layout] # after row is completed layout, tack it onto the end of final layout + + layout += [[sg.OK()]] # and finally, add a row to the bottom that has an OK button + + window = sg.Window('Computed Layout Questionnaire', layout) + event, values = window.Read() + + print(event, values) + window.Close() + + + +""" + Construct #7 - * operator and list comprehensions + Using the * operator from inside the layout + List comprehension inside the layout + Addition of rows to layouts + All in a single variable assignment + + NOTE - this particular code, using the * operator, will not work on Python 2 and think it was added in Python 3.5 + + This code shows a bunch of questions with Radio Button choices + + There is a double-loop comprehension used. One that loops through the questions (rows) and the other loops through + the Radio Button choices. + Thus each row is: + Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5 + Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5 + Text1, Text2, Radio1, Radio2, Radio3, Radio4, Radio5 + + What the * operator is doing in these cases is expanding the list they are in front of into a SERIES of items + from the list... one after another, as if they are separated with comma. It's a way of "unpacking" from within + a statement. + + The result is a beautifully compact way to make a layout, still using a layout variable, that consists of a + variable number of rows and a variable number of columns in each row. +""" + +def layout7(): + questions = ('Managing your day-to-day life', 'Coping with problems in your life?', 'Concentrating?', + 'Get along with people in your family?', 'Get along with people outside your family?', + 'Get along well in social situations?', 'Feel close to another person', + 'Feel like you had someone to turn to if you needed help?', 'Felt confident in yourself?') + + layout = [[*[sg.T(qnum + 1, size=(2, 2)), sg.T(q, size=(30, 2))], # These are the question # and the question text + *[sg.Radio('', group_id=qnum, size=(7, 2), key=(qnum, col)) for col in range(5)]] for qnum, q in enumerate(questions)] + [[sg.OK()]] # finally add an OK button at the very bottom by using the '+' operator + + window = sg.Window('Questionnaire', layout) + + event, values = window.Read() + + print(event, values) + window.Close() + + +# ------------------------- Call each of the Constructs ------------------------- + +layout0() +layout1() +layout2() +layout3() +layout4() +layout5() +layout6() +layout7() \ No newline at end of file