了解 PySimpleGUI 中的布局规则

Understanding layout rules in PySimpleGUI

我第一次尝试使用 PySimpleGUI 构建 GUI,并且正在努力解决 布局重用规则

该脚本的总体目标是让用户更轻松地将数据输入 CSV table。为此,用户将能够在表单中输入数据、提交数据,并决定是要添加另一个数据集还是退出。

我曾尝试使用嵌套函数,但我不断地破坏脚本,因为我应该为每个 window 使用新的布局。到目前为止,我已经定义了 3 个不同的 windows 和它们自己的布局:

window1 = main window 可以添加数据的地方 window2 = window 带有错误通知 window3 = window 在 SUBMIT 之后打开,询问用户是否要继续

此外,如果用户决定添加更多数据 (repeatGUI()),我将尝试再次调用 window1 的函数,但这是不允许的。

我知道脚本中还有其他问题,但我主要希望能就布局重用问题提供一些意见。 多次window打开一个输入的正确方法是什么?

脚本草稿:

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

以下方式表明您一次又一次地使用变量 layout1 作为 sg.Window 的布局,布局 1 中元素的 none 在第一次使用后初始化。

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

首选以下代码,

window1 = sg.Window('Title1', layout1, finalize=True)

while True:
    event1, values1 = window1.read()
...

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

根据@jason-yang 的回答,我能够解决布局问题并将整个脚本修改为

a) 在启动 GUI 的函数之外定义布局 和 b) 摆脱多次打开相同输入 window 的尝试。

持久性 window 更适合我的情况,因此我使用“清除”按钮来允许输入新数据:

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