PySimpleGUI/DemoPrograms old/Demo_Layout_Generation.py

290 lines
12 KiB
Python
Raw Normal View History

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
8 - Multiple Choice Test - a practical use showing list comprehension and concatenated layout
"""
"""
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 a Column 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()
"""
Construct #8 - Computed layout using list comprehension and concatenation
This layout shows one practical use, a multiple choice test. It's been left parse as to focus on the generation
part of the program. For example, default keys are used on the Radio elements. In reality you would likely
use a tuple of the question number and the answer number.
In this example we start with a "Header" Text element and build from there.
"""
def layout8():
# The questions and answers
q_and_a = [
['1. What is the thing that makes light in our solar system', ['A. The Moon', 'B. Jupiter', 'C. I dunno']],
['2. What is Pluto', ['A. The 9th planet', 'B. A dwarf-planet', 'C. The 8th planet', 'D. Goofies pet dog']],
['3. When did man step foot on the moon', ['A. 1969', 'B. 1960', 'C. 1970', 'D. 1869']], ]
layout = [[sg.Text('Astronomy Quiz #1', font='ANY 15', size=(30, 2))]] # make Header larger
# "generate" the layout for the window based on the Question and Answer information
for qa in q_and_a:
q = qa[0]
a_list = qa[1]
layout += [[sg.Text(q)]] + [[sg.Radio(a, group_id=q)] for a in a_list] + [[sg.Text('_' * 50)]]
layout += [[sg.Button('Submit Answers', key='SUBMIT')]]
window = sg.Window('Multiple Choice Test', layout)
while True: # Event Loop
event, values = window.read()
if event in (None, 'SUBMIT'):
break
sg.popup('The answers submitted were', values)
window.close()
# ------------------------- Call each of the Constructs -------------------------
layout0()
layout1()
layout2()
layout3()
layout4()
layout5()
layout6()
layout7()
layout8()