OnceUponATime
OnceUponATime

Reputation: 488

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

Upvotes: 0

Views: 1668

Answers (2)

OnceUponATime
OnceUponATime

Reputation: 488

Based on @jason-yang 's answer, I was able to fix my layout issue and revised the whole script to

a) define layouts outside the function that starts the GUI and b) get rid of the attempt to open the same input window several times.

Having a persistent window is much more suitable in my case, so I am using a "Clear" button to allow new data input:

elif event1 == 'Clear to add new data': # user clicked "Clear"
            window1['-MESSAGE1-'].update("Ready for your next data set!")
            # update all input fields
            window1['-DATE-'].update('')
            window1['-BEFORE-'].update('')
            window1['-AFTER-'].update('')
            window1['-START-'].update('')
            window1['-END-'].update('')
            window1['-EVENT-'].update('')
            window1['-PERSNAME-'].update('')

Upvotes: 0

Jason Yang
Jason Yang

Reputation: 13061

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

Upvotes: 2

Related Questions