python 笔记 PySimpleGUI 图形界面8-Python的GUI编程应如何显示?至少对于初学者?

Python的GUI编程应如何显示?至少对于初学者?

整理 PySimpleGUI 官方网站
原文google翻译过来的

https://pysimplegui.readthedocs.io/en/latest/

一个目标是使创建GUI变得简单,而另一个重要目标是以Python方式进行。它是否实现了这些目标仍有待商,,但这是一次尝试。

PySimpleGUI中自定义窗口的关键是将窗口视为GUI元素的行。每行被指定为这些元素的列表。将各行放在一起,您将获得一个窗口。这意味着GUI被定义为一系列列表,这是一种Python的事物查看方式。

让我们剖析这个小程序

import PySimpleGUI as sg

sg.theme('Dark Blue 3')  # please make your windows colorful

layout = [[sg.Text('Rename files or folders')],
            [sg.Text('Source for Folders', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
            [sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
            [sg.Submit(), sg.Cancel()]]

window = sg.Window('Rename Files or Folders', layout)

event, values = window.read()
window.close()
folder_path, file_path = values[0], values[1]       # get the data from the values dictionary
print(folder_path, file_path)

主题

在这里插入图片描述
导入后的第一行代码是对主题 的 theme。

直到2019年12月,PySimpleGUI中“主题”的特定调用方式change_look_and_feel。该调用已由更简单的函数代替theme。

窗口内容(布局)
让我们同意该窗口有4行。

第一行的文字为Rename files or folders

第二行包含3个元素。首先是文本 Source for Folders,然后是输入字段,然后是浏览按钮。

现在,让我们看看Python代码中的这两行和另外两行:

layout = [[sg.Text('Rename files or folders')],
          [sg.Text('Source for Folders', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
          [sg.Text('Source for Files ', size=(15, 1)), sg.InputText(), sg.FolderBrowse()],
          [sg.Submit(), sg.Cancel()]]

看看源代码如何反映布局?您只需为每行列出一个列表,然后将该表提交到PySimpleGUI即可显示并从中获取值。

那那些返回值呢?大多数人只是想显示一个窗口,获取输入值并对其进行处理。因此,当我只是希望将窗口的输入值提供给我时,为什么将代码分解为按钮回调等。

对于返回值,从上到下,从左到右扫描窗口。作为输入字段的每个字段都将在返回值中占据一个位置。

在我们的示例窗口中,有2个字段,因此该窗口的返回值将是其中包含2个值的字典。请记住,如果key在创建元素时未指定a ,则会为您创建一个。它们是从0开始的整数。在此示例中,我们有2个输入元素。它们可以作为值[0]和值[1]寻址

“读取”窗口的值(也显示窗口)

event, values = window.read()
folder_path, file_path = values[0], values[1]

在一个语句中,我们既显示窗口,又读取用户的输入。在下一行代码中,返回值的字典分为多个单独的变量folder_path和file_path。

这不是寻找GUI的Python程序员想要的吗?可以轻松进行操作以获取值并继续进行实际操作的程序的其余部分。当使用PySimpleGUI以3或4行代码实现相同的布局时,为什么还要编写GUI代码页面。4行还是40行?大多数人会选择4。

返回值
有从调用2个返回值Window.read(),一个event引起的Read返回和values列表或字典中的值。如果布局中没有带键的元素,那么它将是一个列表。但是,某些元素(如某些按钮)会自动添加一个键。 最好在所有输入类型元素上使用键。

两个返回值
所有“窗口读取”调用均返回2个值。按照惯例,将读取语句写入:

event, values = window.read()
您不必以这种方式写您的读物。您可以根据需要命名变量。但是,如果您要使用其他使用PySimpleGUI的程序员习惯的方式对它们进行编码,请使用此语句。

大事记

第一个参数event描述读取为何完成。事件是其中之一:

对于所有Windows:

  • List item
  • List item

点击按钮
使用X关闭窗口
对于专门启用了这些功能的Windows。请参阅本文档的适当部分,以了解如何启用这些功能以及事件返回值是什么。

  • 键盘按键
  • 鼠标滚轮上/下
  • 选单项目
  • 元素已更改(滑块,微调器等)
  • 单击列表项
  • 在输入元素中按下了返回键
  • 等待事件超时
  • 点击了文字
  • 选择了组合框项目
  • 表格行已选择
  • 等等
  • 大多数情况下,事件是单击按钮或关闭窗口。enable_events=True当 您创建元素时进行设置时,还会发生其他特定于元素的事件。

窗口关闭事件

要遵循的另一个约定是检查是否用X关闭窗口。 这是非常重要的事件。如果不检查此内容而尝试使用该窗口,则程序将崩溃。请检查关闭的窗口并正常退出程序。您的用户会喜欢您的。

完成操作后,请关闭窗口,即使退出程序也会关闭它们。如果您不关闭窗口,则tkinter有时会生成错误/警告。对于其他端口,例如PySimpleGUIWeb,不关闭Window可能会导致您的程序继续在后台运行。

要检查关闭的窗口,请使用以下代码:

if event is None:

放在一起,我们最终得到一个“事件循环”,看起来像这样:

while True:
    event, values = window.read()
    if event is None:
        break
window.close()

您会经常看到示例,演示程序将此检查写为:

event, values = window.read()
if event in (None, 'Exit'):
    break

该if语句与:

if event is None or event == 'Exit':
    break

而不是’Exit’使用按钮的名称/键,而是要退出窗口(取消,退出等)。

按钮单击事件

默认情况下,按钮将始终返回单击事件,对于实时按钮,则始终返回按钮按下事件。您无需执行任何操作即可启用按钮单击。要禁用事件,请使用其Update方法禁用按钮。

您可以通过enable_events=True在Button调用中进行设置来启用其他“ Button Modified”事件。这些事件在某个按钮“写入”时触发,通常是因为该按钮在另一个按钮中被列为“目标”。

“读取”调用中的按钮值将是2个值之一:1.按钮的文本-默认值2.按钮的键-如果指定了键

如果在创建按钮时设置了键,则无论该按钮上显示什么文本,都将返回该键。如果未设置任何键,则返回按钮文本。如果未单击任何按钮,但是窗口仍然返回,则事件值是导致事件生成的键。例如,如果在Input元素上设置,并且有人在该Input框中键入字符,则事件将是输入框的键。

当用户单击X关闭窗口时,不返回任何内容。

如果您的窗口有一个事件循环,需要反复读取它,请记住给您的用户一个“输出”。您应该始终检查None值,并且提供某种“退出”按钮是一种好习惯。因此,设计模式通常类似于以下事件循环:

while True:
    event, values = window.read()
    if event is None or event == 'Quit':
        break

实际上,大多数演示程序和示例中都使用了更多的“ Pythonic版本”。他们做的 完全一样。

while True:
    event, values = window.read()
    if event in (None, 'Quit'):
        break

元素事件

一些元素能够在事件发生时生成事件。例如,当移动滑块或单击列表项或单击表行时。这些事件默认情况下未启用。要为元素启用事件,请设置参数enable_events=True。这与旧click_submits参数相同。您会发现click_submits参数仍在函数定义中。您可以继续使用它。它们是相同的设置。使用两个值的“或”。将来,click_submits将被删除,因此请将您的代码迁移到using enable_events。
| | |

| | |

名称大事记
输入文本任何改变
组合选择的项目
列表框选择已更改
无线电选择已更改
复选框选择已更改
微调器选择新项目
多行任何改变
文本点击了
状态栏点击了
图形点击了
图形拖了
图形拖动结束(向上移动)
标签组标签点击
滑杆滑块已移动
选择行
选定的节点
按钮菜单选择菜单项
右键菜单选择菜单项

其他活动

为MenuBar菜单和ButtonMenu菜单选择的Menubar菜单项
您将收到MenuBar和ButtonMenu的键。使用该键读取返回值字典中的值。显示的值将是所选菜单项的全文加号。请记住,您可以将按键放在菜单项上。您将在菜单定义中定义的文本和键一起获得。

选择右键菜单项

与菜单栏和按钮菜单不同,您将直接接收菜单项文本及其键值。您不会进行字典查找来获取值。它是WindowRead()返回的事件代码。

Windows-键盘,鼠标滚轮

Windows能够返回键盘事件。如果是特殊键,则以单个字符或字符串形式返回。我只能说实验。鼠标滚轮事件也是字符串。在代码中放入打印件以查看返回的内容。

超时时间

如果您在读取中设置了超时参数,则将返回系统TIMEOUT_KEY。如果您在Read调用中指定了自己的超时键,则将返回该值。

该values变量-返回值的列表

Read调用中的第二个参数是Window上输入字段的列表或字典。

默认情况下,返回值是值的列表,每个输入字段一个条目,但是对于除最简单的Windows之外的所有窗口,返回值都是字典。这是因为您可能会在布局中使用“键”。当您这样做时,它将强制返回值成为字典。

作为输入元素的每个元素在返回值列表中都有一个值。如果您确定这些值将作为列表返回,那么您可能会很聪明并将其直接解压缩为变量。

事件,(文件名,folder1,folder2,should_overwrite)= sg.Window(‘我的标题’,window_rows).read()

或者,更常见的是,您可以单独打开返回结果的包装。这是首选的方法,因为它适用于这两个列表和字典的返回值。

event, values = sg.Window('My title', window_rows).read()
event, value_list = window.read()
value1 = value_list[0]
value2 = value_list[1]
 ...

但是,当您有很多输入字段时,此方法不好。如果您在窗口中插入一个新元素,那么您将不得不重新整理您的解压缩包,将每个语句修改为reference value_list[x]。

更为常见的方法是通过将键放在“重要”元素(您希望从中获取值并希望与之交互的元素)上来将您的值作为字典返回。
values 变量-以字典形式返回值
对于那些还没有遇到过Python字典的人,请不要害怕!只需复制并粘贴示例代码并对其进行修改。遵循这种设计模式,您会没事的。您可能会在此过程中学到一些东西。

对于长度超过3或4个字段的窗口,您将需要使用字典来帮助您组织返回值。在几乎所有(如果不是全部)演示程序中,您都将找到作为字典传递的返回值。这不是一个很难理解的概念,其语法易于理解,并且使代码可读性强。

您将遇到的最常见的窗口读取语句如下所示:

window = sg.Window(“My title”, layout).read()

要使用字典,您将需要:*用关键字标记要在字典中的每个输入元素key。

如果窗口中的任何元素都有一个key,则所有返回值都将通过字典返回。如果某些元素没有键,则将它们从零开始编号。

让我们来看看您的第一个基于字典的窗口。

import PySimpleGUI as sg

sg.theme('Dark Blue 3')  # please make your windows colorful

layout = [
            [sg.Text('Please enter your Name, Address, Phone')],
            [sg.Text('Name', size=(15, 1)), sg.InputText('1', key='-NAME-')],
            [sg.Text('Address', size=(15, 1)), sg.InputText('2', key='-ADDRESS-')],
            [sg.Text('Phone', size=(15, 1)), sg.InputText('3', key='-PHONE-')],
            [sg.Submit(), sg.Cancel()]
            ]

window = sg.Window('Simple data entry window', layout)
event, values = window.read()
window.close()

sg.Popup(event, values, values['-NAME-'], values['-ADDRESS-'], values['-PHONE-'])

要获取输入字段的值,请使用用作该key值的任何值作为索引值。因此,要获取名称字段的值,将其写为

values['-NAME-']

以与列表相同的方式考虑变量值,但是,使用键的值而不是使用0,1,2来引用列表中的每个项目。上方窗口中的“名称”字段由引用values[’-NAME-’]。

除非该窗口非常简单,否则您会发现在大多数PySimpleGUI窗口中使用非常频繁的键字段。

您会在许多演示程序中看到的一种约定是,键名全部用大写字母开头,结尾处带有下划线。您无需执行此操作…您的键值可能如下所示: key = ‘-NAME-’

这种命名约定的原因是,当您扫描代码时,这些键值会跳出来。您立即知道这是关键。尝试扫描上面的代码,看看是否弹出这些键。 key = ‘-NAME-’

事件循环/回调函数

所有GUI都有一个共同点,即“事件循环”。通常,GUI框架会为您运行事件循环,但有时您需要更大的控制权,并且会运行自己的事件循环。在讨论嵌入式系统或在Raspberry Pi上时,您经常会听到术语“事件循环”。

使用PySimpleGUI,如果您的窗口在单击按钮后保持打开状态,则您的代码将具有事件循环。如果您的程序显示一个“一次性”窗口,收集数据,然后没有其他GUI交互,则您不需要事件循环。

事件循环没有什么神秘之处……它们是您要照顾的循环……等待它…… 事件。事件包括按钮单击,按键,鼠标上/下滚轮等。

这个小程序有一个典型的PySimpleGUI事件循环。

一个PySimpleGUI事件循环的解剖结构如下,一般来说。实际的“循环”部分是一个while True循环“读取”事件和窗口具有的任何输入值检查窗口是否关闭或用户希望退出一系列if event …语句

这是一个完整的简短程序,用于演示每个概念。

import PySimpleGUI as sg

sg.ChangeLookAndFeel('GreenTan')

# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']],
            ['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
            ['&Help', '&About...'], ]

# ------ Column Definition ------ #
column1 = [[sg.Text('Column 1', background_color='lightblue', justification='center', size=(10, 1))],
           [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')],
           [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')],
           [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]]

layout = [
    [sg.Menu(menu_def, tearoff=True)],
    [sg.Text('(Almost) All widgets in one Window!', size=(30, 1), justification='center', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)],
    [sg.Text('Here is some text.... and a place to enter text')],
    [sg.InputText('This is my text')],
    [sg.Frame(layout=[
    [sg.Checkbox('Checkbox', size=(10,1)),  sg.Checkbox('My second checkbox!', default=True)],
    [sg.Radio('My first Radio!     ', "RADIO1", default=True, size=(10,1)), sg.Radio('My second Radio!', "RADIO1")]], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags')],
    [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)),
     sg.Multiline(default_text='A second multi-line', size=(35, 3))],
    [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)),
     sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)],
    [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))],
    [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)),
     sg.Frame('Labelled Group',[[
     sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, tick_interval=25),
     sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75),
     sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10),
     sg.Column(column1, background_color='lightblue')]])],
    [sg.Text('_' * 80)],
    [sg.Text('Choose A Folder', size=(35, 1))],
    [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'),
     sg.InputText('Default Folder'), sg.FolderBrowse()],
    [sg.Submit(tooltip='Click to submit this form'), sg.Cancel()]]

window = sg.Window('Everything bagel', layout, default_element_size=(40, 1), grab_anywhere=False)
event, values = window.read()
window.close()

sg.Popup('Title',
         'The results of the window.',
         'The button clicked was "{}"'.format(event),
         'The values are', values)

这是一个复杂的窗口,具有很多自定义大小,可以使内容排列整齐。这是您只需要编写一次的代码。查看代码时,请记住,您看到的是列表列表。每行包含一个用于创建窗口的图形元素列表。如果看到一对方括号[],则说明您正在读取其中的一行。GUI的每一行都是这些列表之一。

该窗口对您来说可能看起来“丑陋”,这是因为未进行任何努力使其看起来不错。它是纯粹的功能。窗口中有30个元素。三十个要素。考虑到它的作用,它是奇迹般的,或者至少是令人难以置信的令人印象深刻。为什么?因为在少于50行的代码中创建,显示了该窗口,所以收集了结果并将结果显示在另一个窗口中。

50行。如果可以的话,将需要50行tkinter或Qt代码来编写窗口的前3个元素。

不,让我们在这里澄清一下。使用传统的Python GUI软件包,此窗口将包含大量代码。这是事实,如果您想证明我是错的,那么请务必这样做。请使用tkinter,Qt或WxPython编写此窗口,然后发送代码!

请注意,该窗口甚至在顶部都有一个菜单栏,容易错过。
在这里插入图片描述
单击提交按钮导致窗口调用返回。对Popup的调用导致出现此窗口。
在这里插入图片描述
Note, event values can be None* 。的值event将是创建按钮元素时在按钮元素上显示的文本或按钮的键。如果用户使用窗口右上角的“ X”关闭窗口,event则将为None。这是极其 重要的***你的代码包含无适当的检查。

对于“永久窗口”,请 始终为您的用户提供一个离开窗口的方法。否则,您将得到永远无法正确关闭的窗口。在每个演示程序中,实际上都是两行代码。在进行此操作时,请确保window.close()在事件循环后进行了调用,以确保窗口关闭。

您可以在结果弹出窗口中看到返回的值是一个字典。窗口中的每个输入字段都会在返回值列表中生成一项。输入字段通常返回string。复选框和单选按钮返回bool。滑块会根据您的配置方式或使用的端口返回float或int值。

如果您的窗口没有按键,并且没有“浏览”按钮类型的按钮,那么它将以列表而不是字典的形式返回值给您。如果可能,PySimpleGUI会尝试将值作为列表返回,以保持简单。

注意,在此示例的返回值列表中,许多键是数字。这是因为没有在任何元素上指定键(尽管为您自动创建了键)。如果您没有为元素指定键,则将顺序分配一个数字。对于您不打算修改或读取值的元素(例如文本元素),可以跳过添加键。对于其他元素,您可能需要添加键,以便可以轻松访问值并对其执行操作。

需要“长时间”的操作

如果您是Windows用户,您会看到窗口在其标题栏中显示为“ Not Responding”(不响应),然后很快出现Windows弹出窗口,指出“您的程序已停止响应”。好吧,如果您愿意,您也可以使该消息和弹出窗口出现!您需要做的就是执行一个在事件循环内花费“太长”(即几秒钟)的操作。

您有几种选择可以解决这个问题。如果您的操作可以分解成较小的部分,则可以Window.Refresh()不定期致电以避免此消息。例如,如果您正在运行循环,则将该呼叫与其他工作放在一起。这将使GUI保持愉快状态,并且Window不会抱怨。

另一方面,如果您的操作不受控制或无法添加Refresh调用,则下一个可用的选择是将长操作移入线程。

有几个演示程序可供您查看如何执行此操作。您基本上将工作放在了线程中。线程完成后,它将通过队列发送消息来告知GUI。事件循环将在计时器设置为一个值的情况下运行,该值表示您希望GUI对完成工作的“响应”程度。

这两个演示程序称为

Demo_Threaded_Work.py - Best documented.  Single thread used for long task
Demo_Multithreaded_Long_Tasks.py - Similar to above, but with less fancy GUI. Allows you to set amount of time

这2个特定的演示中有很多注释,向您显示在何处添加代码等。执行此操作的代码量实际上很小,如果您只是按照以前的演示进行操作,则无需了解所使用的机制。为您准备。

多线程程序

在讨论多线程的同时,准备了另一个演示,演示了如何在程序中运行所有与事件循环通信的多个线程,以便在GUI窗口中显示内容。回想一下,对于PySimpleGUI(至少是tkinter端口),您不能在除主程序线程之外的其他线程中进行PySimpleGUI调用。

这些线程程序的关键是从线程到事件循环的通信。这些演示选择的机制使用Python内置queue模块。事件循环轮询这些队列,以查看是否已从要显示的线程之一发送了某些内容。

您会发现演示将多个线程与一个GUI进行通信的演示称为:

Demo_Multithreaded_Queued.py
再次发出警告以使用纯PySimpleGUI(基于tkinter)-您的GUI绝不能以除主程序线程之外的任何方式运行,并且任何线程都不能直接调用PySimpleGUI调用。

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值