Search code examples
user-inputpysimplegui

Understanding layout rules in PySimpleGUI


I am trying to build a GUI with PySimpleGUI for the first time and am struggling with the layout reuse rules.

The overall aim of the script will be to make entering data into a CSV table easier for users. For this purpose, the users will be able to enter data into a form, submit the data, and decide if they want to add another data set or exit.

I have tried to work with nested functions but am constantly breaking the script because I am supposed to use a new layout for each window. I have defined 3 different windows with their own layouts so far:

window1 = main window where data can be added window2 = window with error notification window3 = window opening after SUBMIT, asking users if they want to continue

In addition, I am trying to call the function for window1 again if users decide to add more data (repeatGUI()), but this is not permitted.

I am aware that there are other issues in the script, too, but I would mainly appreciate some input on the layout-reuse issue. What is the proper way of opening an input window multiple times?

DRAFT SCRIPT:

import csv
import PySimpleGUI as sg

sg.main_get_debug_data()

myfile="C:\\######\\File.csv"

counter=0

def buildGUI(): # defining GUI frame, title, fields and buttons

    sg.ChangeLookAndFeel('GreenTan')
    window1 = sg.FlexForm('MY DB', size = (900,700), resizable=True) 

    layout1 = [
              [sg.Text('Please enter a new factoid:')],
              [sg.Text('Factoid ID', size=(15, 1), key="-FACTOID-")],
              [sg.Text('Person ID', size=(15, 1), key="-PERSON-")],
              [sg.Text('Exact Date', size=(10, 1)), sg.InputText()],
              [sg.Text('Event Before', size=(10, 1)), sg.InputText()], 
              [sg.Text('Event After', size=(10, 1)), sg.InputText()],
              [sg.Text('Event Start', size=(10, 1)), sg.InputText()],
              [sg.Text('Event End', size=(10, 1)), sg.InputText()],
              [sg.Text('Event Type', size=(15, 1)), sg.InputText()],
              [sg.Text('Person Name', size=(15, 1)), sg.InputText()],
              [sg.Text('Person Title', size=(15, 1)), sg.InputText()], 
              [sg.Text('Person Function', size=(15, 1)), sg.InputText()],
              [sg.Text('Place Name', size=(15, 1)), sg.InputText()], 
              [sg.Text('Institution Name', size=(15, 1)), sg.InputText()], 
              [sg.Text('Related Persons', size=(15, 1)), sg.InputText()], 
              [sg.Text('Alternative Names', size=(15, 1)), sg.InputText()], 
              [sg.Text('Source Quote', size=(10, 1)), sg.InputText()], 
              [sg.Text('Additional Info', size=(10, 1)), sg.InputText()], 
              [sg.Text('Comment', size=(10, 1)), sg.InputText()], 
              [sg.Text('Source', size=(10, 1)), sg.InputText()], 
              [sg.Text('Source URL', size=(10, 1)), sg.InputText()], 
              [sg.Text('Info Dump', size=(10, 1)), sg.InputText()], 
              [sg.Text(size=(70,1), key='-MESSAGE1-')],
              [sg.Submit(), sg.Button('Clear Input')]
              ]
    
    while True: # The Event Loop
        event1, values1 = window1.Layout(layout1).Read()
        print(layout1)
                
        def startGUI(event1, values1): # start GUI and get data
            # interact with user to get input
            if event1 == 'Submit': # user clicked "Submit"
                def submitGUI(values1): # submitting new data via GUI
                    fact_id=("#") # place holder: to be calculated later
                    pers_id=("#") # place holder: to be calculated later
                    entries=[fact_id, pers_id]
                    for v in values1.values():
                        entries.append(v)
                    try:
                        with open(myfile, 'a', encoding="utf-8") as f:
                            w=csv.writer(f)
                            w.writerow(entries) # write list items to CSV file
                            f.close()   
                        try:
                            window3 = sg.FlexForm('NEW DATA?', size = (500,100)) 
                            layout3 = [
                                      [sg.Text("Your input has been saved! Do you want to add a new factoid?")],
                                      [sg.Text(size=(70,1), key='-MESSAGE2-')],
                                      [sg.Button("YES"), sg.Button("NO"), sg.Exit()]
                                      ]
                            while True:
                                event3, values3 = window3.Layout(layout3).Read()
                        
                                if event3 == 'YES': # user clicked YES
                                    window1.close()
                                    try:
                                        repeatGUI() # this breaks layout rules!
                                    except:
                                        print("Not allowed!")
                                        pass
                                elif event3 == 'NO':
                                    window3['-MESSAGE2-'].update("See you again soon!")
                                    window1.close()
                                elif event3 in (sg.WINDOW_CLOSE_ATTEMPTED_EVENT, 'Exit'):
                                    window3.close()
                        except:
                            pass
                
                    
                    except PermissionError:
                        window2 = sg.FlexForm('DB ERROR', size = (500,100)) 
                        layout2 = [
                                  [sg.Text("Someone else is using the file! Please try again later.")],
                                  [sg.Exit()]
                                  ]
                        event2, values2 = window2.Layout(layout2).Read()
                        
                        if event2 in (sg.WINDOW_CLOSE_ATTEMPTED_EVENT, 'Exit'):
                            window2.close() # user clicked "Exit"
                    
                submitGUI(values1)
                
            elif event1 == 'Clear Input': # user clicked "Cancel"
                window1.Refresh()
            elif event1  == sg.WINDOW_CLOSE_ATTEMPTED_EVENT: # user closed window
                window1.close() # AttributeError: module 'PySimpleGUI' has no attribute 'WIN_CLOSED'
        startGUI(event1, values1)  
buildGUI()

def repeatGUI():
    counter+=1
    print(counter)
    buildGUI()

Solution

  • Following way show that you use the variable layout1 as layout of sg.Window again and again, none of element in layout1 initialized after 1st time using.

        while True: # The Event Loop
            event1, values1 = window1.Layout(layout1).Read()
        ...
    

    Following code preferred,

    window1 = sg.Window('Title1', layout1, finalize=True)
    
    while True:
        event1, values1 = window1.read()
    ...
    

    or

    def make_window1():
        layout = [
                  [sg.Text('Please enter a new factoid:')],
        ....
                 ]
        return sg.Window('Title1', layout, finalized=True)
    
    window1 = make_window1()
    
    while True:
        event1, values1 = window1.read()
    ...