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()
