Pysimplegui: Updating form layouts

Created on 4 Sep 2018  路  36Comments  路  Source: PySimpleGUI/PySimpleGUI

Is it possible for the form layouts to be updated on the go?

Eg. A checkbox hiding and displaying another element on a non-blocking form.

So far as I've read the documentation, there doesnt seem to be a method (unless I've missed it?)

If not, could this be a possible update in the future?

enhancement

Most helpful comment

import PySimpleGUI as sg

sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))

layout = [
          [sg.ReadFormButton('Numbers', button_color=('white', 'black'), key='start'),
           sg.ReadFormButton('Letters', button_color=('white', 'firebrick4'), key='stop')],
          [sg.Combo(('Numbers', 'Letters'), key='combo1', change_submits=True), sg.Combo(('a', 'b', 'c', 'd'), key='combo2')]
          ]

form = sg.FlexForm("Change Combo Values", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
                   default_button_element_size=(12,1))
form.Layout(layout)
while True:
    button, values = form.Read()
    if button is None:
        exit(69)
    if button is 'Numbers':
        form.FindElement('combo1').Update(values=(7,8,9,0))
        form.FindElement('combo2').Update(values=(1,2,3,4))
    elif button is 'Letters':
        form.FindElement('combo1').Update(values=('a','b','c','d'))
        form.FindElement('combo2').Update(values=('e','f','g','h'))

    if values['combo1'] == 'Numbers':
        form.FindElement('combo2').Update(values=(1,2,3,4))
    elif values['combo1'] == 'Letters':
        form.FindElement('combo2').Update(values=('e','f','g','h'))

OK, it's DONE!

Please get a new PySimpleGUI.py file the Dev-active branch.

If it works for you I'll merge to master

Thanks for your patience and for explaining this to me. I still have more of these change_submits to do. I've getting them one by one.

Thanks for the compliments! And keep the feature requests coming.

All 36 comments

You are correct that at the moment there is no way to add or remove elements on the fly.

Adding this to the list of feature requests.

In the meantime, the best you can do it disable checkboxes on the fly.

@MikeTheWatchGuy, Hi!
similar question - can i update combobox values on the fly?
ideally, for example

  • if i click a value 'char' on combobox1, then combobox2 getting the values 'a','b','c',
  • if i click a value 'digits' on combobox1, then combobox2 getting the values '1','2','3',
    etc

It is already possible, or maybe planned?

This is already possible 馃憤 Finally, a feature that's done ahead of a user's need!

You can call Combobox.Update with 2 values now, the value you want to change it to and the list of options new options. You do not have to call it with both parameters (i.e. you are not forced to supply the list of options every time)

All of the Elements need to have a full list of parameters available to be updated.
See this enhancement for more details:
https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/109

Please see the Wiki for a great new feature for dealing with updating of Elements that already exist. You can use form.FindElement(key) to get the element directly. You do not need to keep a variable in your program containing the element. Let me know if this is confusing as it's not fully documented yet.

This code demonstrates using the form.FindElement call

def TimeTracker():
    import PySimpleGUI as sg

    sg.ChangeLookAndFeel('Dark')
    sg.SetOptions(element_padding=(0,0))

    layout = [[sg.T('User:', pad=((3,0),0)), sg.OptionMenu(values = ('User 1', 'User 2'), size=(20,1)), sg.T('0', size=(8,1))],
              [sg.T('Customer:', pad=((3,0),0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20,1)), sg.T('1', size=(8,1))],
              [sg.T('Notes:', pad=((3,0),0)), sg.In(size=(44,1), background_color='white', text_color='black')],
              [sg.ReadFormButton('Start', button_color=('white', 'black'), key='start'),
               sg.ReadFormButton('Stop', button_color=('gray34', 'black'), key='stop'),
               sg.ReadFormButton('Reset', button_color=('gray','firebrick3'), key='reset'),
               sg.ReadFormButton('Submit', button_color=('gray34','springgreen4'), key='submit')]
              ]

    form = sg.FlexForm("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
                       default_button_element_size=(12,1))
    form.Layout(layout)
    recording = have_data = False
    while True:
        button, values = form.Read()
        if button is None:
            exit(69)
        if button is 'Start':
            form.FindElement('start').Update(button_color=('gray34','black'))
            form.FindElement('stop').Update(button_color=('white', 'black'))
            form.FindElement('reset').Update(button_color=('white', 'firebrick3'))
            recording = True
        elif button is 'Stop' and recording:
            form.FindElement('stop').Update(button_color=('gray34','black'))
            form.FindElement('start').Update(button_color=('white', 'black'))
            form.FindElement('submit').Update(button_color=('white', 'springgreen4'))
            recording = False
            have_data = True
        elif button is 'Reset':
            form.FindElement('stop').Update(button_color=('gray34','black'))
            form.FindElement('start').Update(button_color=('white', 'black'))
            form.FindElement('submit').Update(button_color=('gray34', 'springgreen4'))
            recording = False
            have_data = False
        elif button is 'Submit' and have_data:
            form.FindElement('stop').Update(button_color=('gray34','black'))
            form.FindElement('start').Update(button_color=('white', 'black'))
            form.FindElement('submit').Update(button_color=('gray34', 'springgreen4'))
            form.FindElement('reset').Update(button_color=('gray34', 'firebrick3'))
            recording = False

TimeTracker()

@MikeTheWatchGuy Thanks, i did the trick with form.Findelement() on button click event
https://imgur.com/a/1ejHMES
But i cant understand, how to apply this event with selecting value on combobox without need a button :(

I want make the event a click on value in combo1 to change the available values in combo2.
All examples in documentation describes buttonclick events

Can you explain me this point ? Thanks in advance)

Can you post the code? Looks like perhaps bug if the combo isn't being found.

Try this code.... it's working OK for me and changes the values:

import PySimpleGUI as sg

sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))

layout = [
          [sg.ReadFormButton('Numbers', button_color=('white', 'black'), key='start'),
           sg.ReadFormButton('Letters', button_color=('white', 'firebrick4'), key='stop')],
          [sg.Combo((1,2,3,4), key='combo1'), sg.Combo(('a', 'b', 'c', 'd'), key='combo2')]
          ]

form = sg.FlexForm("Change Combo Values", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
                   default_button_element_size=(12,1))
form.Layout(layout)
while True:
    button, values = form.Read()
    if button is None:
        exit(69)
    if button is 'Numbers':
        form.FindElement('combo1').Update(values=(7,8,9,0))
        form.FindElement('combo2').Update(values=(1,2,3,4))
    elif button is 'Letters':
        form.FindElement('combo1').Update(values=('a','b','c','d'))
        form.FindElement('combo2').Update(values=('e','f','g','h'))

https://gist.github.com/Chocobo-ts/a68f8ab15d03e97d504fe7bc9eea77c9
but this code works fine, as on gif above.

I just don't know how make this combo values changing without buttons (

Ah, now I understand point about buttons...
Can you post the code that isn't working?

Perhaps you need to call form.Refresh() ?

Oh!!! Wait!!

I think I understand...

You want to have a change to a combo box change the values in another combobox, without a button.

What you need is for there to be a change_submits options for combo boxes....

I need to implement that.

"You want to have a change to a combo box change the values in another combobox,"
Exactly :)

Ok, i will follow the news while waiting this feature)
your project is really awesome useful , and very cool cookbook.
Thanks again for your work!

import PySimpleGUI as sg

sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))

layout = [
          [sg.ReadFormButton('Numbers', button_color=('white', 'black'), key='start'),
           sg.ReadFormButton('Letters', button_color=('white', 'firebrick4'), key='stop')],
          [sg.Combo(('Numbers', 'Letters'), key='combo1', change_submits=True), sg.Combo(('a', 'b', 'c', 'd'), key='combo2')]
          ]

form = sg.FlexForm("Change Combo Values", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
                   default_button_element_size=(12,1))
form.Layout(layout)
while True:
    button, values = form.Read()
    if button is None:
        exit(69)
    if button is 'Numbers':
        form.FindElement('combo1').Update(values=(7,8,9,0))
        form.FindElement('combo2').Update(values=(1,2,3,4))
    elif button is 'Letters':
        form.FindElement('combo1').Update(values=('a','b','c','d'))
        form.FindElement('combo2').Update(values=('e','f','g','h'))

    if values['combo1'] == 'Numbers':
        form.FindElement('combo2').Update(values=(1,2,3,4))
    elif values['combo1'] == 'Letters':
        form.FindElement('combo2').Update(values=('e','f','g','h'))

OK, it's DONE!

Please get a new PySimpleGUI.py file the Dev-active branch.

If it works for you I'll merge to master

Thanks for your patience and for explaining this to me. I still have more of these change_submits to do. I've getting them one by one.

Thanks for the compliments! And keep the feature requests coming.

I confirm,
That's exactly what i wanted!!!

Awesome!

See, people, it never hurts to speak up! You may get what you ask for right away.

Since you're clearly an advanced software engineer, can I ask a few questions?

  1. What did you use to make the animated image you posted in this Issue?
  2. What do you think about the "event loop" design?

The second question is one I have gotten complaints about. At least for now, rather than create a fancy inter-element communications mechanism or a true callback architecture, I've chosen to require the user to run their own event loop.

The reason for this was to keep things "simple" (it is named PySimpleGUI for a reason).

I think that having all of the GUI code in a single loop keeps the logic together and is easier to understand. I can see right there in the code that one element is causing a change to another element. I don't have to go look for a callback function and check to see what it does.

I argue that this architecture create a lot less code than if callsbacks were used. In general, for the exact same functioning GUI interface, the code for PySimpleGUI will be significantly less than if used tkinter or WxPython.

What do you think about the overall architecture and in particular this event loop?

Merged into Master Branch

  1. I'm under Linux and software it's a Peek - https://github.com/phw/peek
  2. Well, I'm with you there. I thik that event loop make it simply for understanding and handling. As not a "severe" python-programmer but unix engineer - it didn't take much time to understand and start works with it) to form an some concrete opinion about architecture - i'm need more time to take a look at this in work tasks. But this event implementation very attractive)

All of these changes were released in 2.20 today on PyPI. Lots of stuff in there so hoping noting broke!

Sorry that this issue strayed so far from the initial request. At least all those things inbetween are released and can be ignored now.

Some interesting stuff happened while I was away I see haha.

Other than the original issue, may I also recommend that grey out features be implemented into all or at least most of the elements.

Afaik, currently only the checkbox elements have this feature. Unless I've missed it out of course, do correct me if I have.

I don't think this is possible.

Once the form is "packed" into the frame and shown, I know of no way of deleting tkinter widgets from the frame.

If someone can shoe me how it's done using only tkinter code then perhaps I have a chance. I don't think completely removing an element is going to be possible.

I've been adding Update methods to all of the Elements. They're not all fully functional, but many do the basics. You can change the items in a list for example. What you can't do is delete the list element entirely.

It's going to be up to the caller to do many of these 'graying out' things. I posted the Demo_Button_States.py to show people a way of doing this using today's features. To 'grey out' a button it's a 1-line change to the your code to call Update with a new button color.

I don't know of many windows programs that delete widgets from their user interface on the fly. Can you show me an example so I can better understand the use?

This one may simply be impossible.

I've just tested Demo_Button_States, and I would say that it could be a workaround for my current issue.

My objective of the deleting/graying out of elements was to prevent users from being confused with multiple elements on the screen. Ideally, being able to 'disable' elements when a specific checkbox was unchecked.

Personally, I haven't been dabbling much into the Windows scene so I may have been asking for the impossible with this feature. Probably was too far into the idea of a web app instead of what a GUI can do?

Disable is different from remove. I thought you meant remove it from the window so that it's hidden. There's no way of doing it and reshuffling the window around. You could kinda simulate it by setting text color to same as background.

Disabling, however, is potentially possible. At the moment, only the checkbox can be disabled. It becomes inoperable when set to the value None. It may be possible to do that with other elements. I have not investigated the various "states" that are possible with tkinter widgets. It may be possible to get what you're looking for.

You can current change the listbox's values to nothing, same with combobox and options menu.

Nothing possible on slider yet.

Ah I see. Sorry about the miscommunication! I had assumed they would both be similar in idea.

I am aware of the checkbox's disable functionality. I was looking for a way to disable the rest of the elements with this issue.

OK, another element down.

You can now disable ComboBoxes. You can also create them in a disabled state.

Change has been checked into GitHub Master branch.

When calling InputCombo or Combo (both are the same), set the flag disabled=Trueif you want to create the listbox in a disabled state. Same flag was added to Update. Set 'disabled=TTrue` when calling update and it'll disable the widget.

Getting them one at a time. Thought this one was already done for Combobox but found it wasn't.

All of the input-type elements have had their Update methods updated. They all contain a parameter disabledthat is used to enable/disable. If you don't specify either, nothing is changed. This was done so you can change values without changing state.

Hopefully this is a start towards this Issue.

Having thought about the problem for a bit of wanting to completely blank out an area, the way to do that may be is to disable the Element, then change the element's color to match the background. Unfortunately my Update methods do have the ability the change the text colors... yet.... soon. In the meantime, you can at least get them so they can't be changed and they are grayed out.

Perfect :)

On a side note, your announcement for this feature had a bit of a typo on the parameter names.
'disable' instead of 'disabled'. May cause a bit of confusion for some.

Other than that, thanks for your help and hope you continue the great work on the project!

I named it disable=True on purpose, as a verb.

Perhaps I should have made it disabled??

There is still time as it has not hit PyPI yet.

Guess disabled would be better.

Ah. The previous ones for the combobox and checkbox were under 'disabled' so I assumed the rest would follow.

Also, I feel that 'disabled' does seem to fit better in the context its used.

Renamed all of the Update methods to use disabled keyword.

This follows the other conventions which generally treat parameters as if they were "states" rather than "actions". Set the element's "state to disabled" rather than "disable the element".

I need to somehow remember to stay away from verb keywords :-) or maybe there are already places there I do this and can't be changed so easily.

I'll push this out to PyPI later today.

Get a new Demo_Disable_Elements.py

Just tested out the new disabling features, they work great!

However, I've noticed that many of the elements are not able to be created with disabled as a parameter. Was this intended?

Thanks in advance!

I hope to convert all of Elements to have a disabled parameter.

The disabled state is a new concept for PySimpleGUI. I did not design it in from the beginning. I only added it as part of the Update function.

What needs to happen is that all of the Elements need to add the disabled flag to their init method so that they can be created disabled. At the moment it's possible to create an element and then immediately disable it. To do this you would code it like this:

form = sg.FlexForm(....)
form.Layout(layout)
form.Finalize()
element.Update(disabled=True)

while True:
   b,v = form.Read()

It is going to take a little while to get all the elements updated to using a disabled parameter. In the meantime, you can use the above design pattern as a way to create disabled elements.

What is your application that you're using PySimpleGUI with?

I get new feature ideas and make improvements based on requests like yours so keep them coming.

Ah okay! I'll do that for now.

I'm currently working on a small project, an accounting invoice/inventory handling tool. Thought of adding a GUI to it to make it more user-friendly.

@Chocobo-ts can you help out with this issue's Mint installation problem?

https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/226

@fyraiga I'm starting to look into more flexible layouts. Would having the ability to create invisible elements help you? The window will expand as you create them if you're using PySimpleGUIQt.

See issue #846

Oops, didn't look here for quite a while.
I see the enhancement has already been completed. After thinking about it, I feel that the disabled functionality would be more fitting for my use case, but invisible elements would probably come in handy someday too.

Can this one be closed now?

Sure. I'll go ahead and do that.

Was this page helpful?
0 / 5 - 0 ratings