PySimpleGUI / PySimpleGUI

Python GUIs for Humans! PySimpleGUI is the top-rated Python application development environment. Launched in 2018 and actively developed, maintained, and supported in 2024. Transforms tkinter, Qt, WxPython, and Remi into a simple, intuitive, and fun experience for both hobbyists and expert users.

Home Page:https://www.PySimpleGUI.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] Question regarding canvas.bind

Segalikeme opened this issue · comments

Type of Issue (Enhancement, Error, Bug, Question)

Question

Operating System

MacOS Monterey Version 12.3.1

PySimpleGUI Port (tkinter, Qt, Wx, Web)

tkinter


Versions

Version information can be obtained by calling sg.main_get_debug_data()
Or you can print each version shown in ()

Python version (sg.sys.version)

3.9.7

PySimpleGUI Version (sg.__version__)

4.60.0

GUI Version (tkinter (sg.tclversion_detailed), PySide2, WxPython, Remi)

8.6.11


Your Experience In Months or Years (optional)

Years Python programming experience

1 year

Years Programming experience overall

1 year

Have used another Python GUI Framework? (tkinter, Qt, etc) (yes/no is fine)

No

Anything else you think would be helpful?

First time using a GUI Framework (I have been working with PySimpleGUI for less than a week)

Troubleshooting

These items may solve your problem. Please check those you've done by changing - [ ] to - [X]

  • Searched main docs for your problem www.PySimpleGUI.org
  • Looked for Demo Programs that are similar to your goal Demos.PySimpleGUI.org
  • If not tkinter - looked for Demo Programs for specific port
  • For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi)
  • [] Run your program outside of your debugger (from a command line)
  • Searched through Issues (open and closed) to see if already reported Issues.PySimpleGUI.org
  • Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released

Detailed Description

I had a question regarding the behaviour of 'canvas.bind("", lambda event, canvas=canvas, frame_id=frame_id:configure(event, canvas, frame_id))'.

As originally posted by @jason990420 in #4752 (comment)_, I was having trouble with the expand_x of some collapsible columns inside a main scrollable column (which I am using to obtain a scrollable window) and thus implemented the fix given in that issue. This fixed that problem, but seeing how I have collapsible sections in my window, I discovered a new problem: when clicking on the "section arrow" to reveal the hidden text, the window does not show the totality of the uncollapsed section until I manually resize the window (it must be noted that the actual "resized sized" does not matter, but that simply the action of resizing fixes the window) .

As such, as I am very new to all of this, I wanted to know what the above code was actually doing, and if it is possible to set it in way such that when a collapsable section is clicked, it automatically shows the totality of the hidden section (i.e. without me having to manually resize the window).

Code To Duplicate

A short program that isolates and demonstrates the problem (Do not paste your massive program, but instead 10-20 lines that clearly show the problem)

This pre-formatted code block is all set for you to paste in your bit of code:

import PySimpleGUI as sg

SYMBOL_UP =    '▶'
SYMBOL_DOWN =  '▼'

def collapse(layout, key):
    """
    Helper function that creates a Column that can be later made hidden, thus appearing "collapsed"
    :param layout: The layout for the section
    :param key: Key used to make this seciton visible / invisible
    :return: A pinned column that can be placed directly into your layout
    :rtype: sg.pin
    """
    return sg.pin(sg.Column(layout, key=key,visible=False))

section1 = [
    [sg.Text('Hello World 1')]
]

section2 = [
    [sg.Table('Hello World 2')]
]

layout1 = [
          [sg.T(SYMBOL_UP, enable_events=True, k='OPEN SEC1', text_color='navy'),
             sg.T('First Table', enable_events=True, text_color='navy', k='OPEN SEC1-TEXT'),
                sg.T('', enable_events=True, text_color='navy', k='OPEN SEC1-TEXT',justification='right')],
            [collapse(section1, 'SEC1')]
]

layout2 = [
    [sg.T(SYMBOL_UP, enable_events=True, k='OPEN SEC2', text_color='navy'),
             sg.T('Second Table', enable_events=True, text_color='navy', k='OPEN SEC2-TEXT')],
            [collapse(section2, 'SEC2')]
]

layout3 = [
    [(sg.Column(layout1, key = 'col1',expand_x = True, expand_y = True))],
    [(sg.Column(layout2, key = 'col2',expand_x = True, expand_y = True))]
]

layout = [
      [sg.Column(layout3, key = 'col',expand_x = True, expand_y = True, scrollable = True,justification = 'left',vertical_scroll_only = True,size = (500,30),pad=20)]
  ]

def configure(event, canvas, frame_id):
    canvas.itemconfig(frame_id, width=canvas.winfo_width())

opened1, opened2 = False, False

window = sg.Window('Simple data entry GUI', auto_size_text=True, auto_size_buttons=True,
                   grab_anywhere=False, resizable=True,
                   layout=layout, finalize=True, margins = (0,0))
window['col1'].Widget.configure(borderwidth=1, relief=sg.DEFAULT_FRAME_RELIEF)
window['col2'].Widget.configure(borderwidth=1, relief=sg.DEFAULT_FRAME_RELIEF)
window['col'].Widget.configure(borderwidth=1, relief=sg.DEFAULT_FRAME_RELIEF)

frame_id = window['col'].Widget.frame_id
canvas = window['col'].Widget.canvas
canvas.bind("<Configure>", lambda event, canvas=canvas, frame_id=frame_id:configure(event, canvas, frame_id))

while True:             # Event Loop
    event, values = window.read()
    print(event, values)
    if event == sg.WIN_CLOSED or event == 'Exit':
        break

    if event.startswith('OPEN SEC1'):
        opened1 = not opened1
        window['OPEN SEC1'].update(SYMBOL_DOWN if opened1 else SYMBOL_UP)
        window['SEC1'].update(visible=opened1)
    
    if event.startswith('OPEN SEC2'):
        opened2 = not opened2
        window['OPEN SEC2'].update(SYMBOL_DOWN if opened2 else SYMBOL_UP)
        window['SEC2'].update(visible=opened2)

Screenshot, Sketch, or Drawing

Initial window:
Screen Shot 2022-05-18 at 3 39 42 PM

After clicking on 'Second Table':

Screen Shot 2022-05-18 at 3 39 50 PM

After manually-resizing the window:
Screen Shot 2022-05-18 at 3 39 59 PM


Watcha Makin?

I am trying to create an easy to access status table for the pipelines of a GitLab group.

Comments added here to explain something

# Function to set the item `frame_id` in canvas to have same width of canvas
def configure(event, canvas, frame_id):
    canvas.itemconfig(frame_id, width=canvas.winfo_width())
...
# The `frame_id` for the frame inside the Column element
frame_id = window['Scrollable Column'].Widget.frame_id
# The canvas inside the Column element for scrollable function
canvas = window['Scrollable Column'].Widget.canvas
# If size of canvas configured here, set the frame inside the canas have same width of canvas
canvas.bind("<Configure>", lambda event, canvas=canvas, frame_id=frame_id:configure(event, canvas, frame_id))

You need to call Window.refresh to update the GUI and Column.contents_changed to update the scroll region if the content of the Column element changed. Revised event loop as following,

while True:             # Event Loop
    event, values = window.read()
    print(event, values)
    if event == sg.WIN_CLOSED or event == 'Exit':
        break

    if event.startswith('OPEN SEC1'):
        opened1 = not opened1
        window['OPEN SEC1'].update(SYMBOL_DOWN if opened1 else SYMBOL_UP)
        window['SEC1'].update(visible=opened1)
        # Update the scroll region of Column element
        window.refresh()
        window['col'].contents_changed()

    if event.startswith('OPEN SEC2'):
        opened2 = not opened2
        window['OPEN SEC2'].update(SYMBOL_DOWN if opened2 else SYMBOL_UP)
        window['SEC2'].update(visible=opened2)
        # Update the scroll region of Column element
        window.refresh()
        window['col'].contents_changed()

window.close()

image

This clarifies things a lot! Thank you very much for the clarifications and the help, I really appreciate it!

Thank you very much for the "thank you very much" !
image

I'll check to see if there's a place in the docs I should add something to remind users the contents_changed call is needed

Added a reminder to the Column Element's docstring scrollable paramter.

image

First time using a GUI Framework (I have been working with PySimpleGUI for less than a week)

NICE! Welcome to the world of GUIs!

Here is still or new problem with this. @jason990420

After
windows.refresh()
gui_elements.files.contents_changed()

nothing happens.

I have the same problem as an author. I need to resize the window manually BEFORE I add ps.Input() to the ps.Column() OR AFTER.

psg.Window('YAGC', layout, resizable=True, auto_size_buttons=True, auto_size_text=True, size=(800, 400), finalize=True, metadata={'file_id': 0, 'folder_id': 0}, margins=(0, 0))
.
.
.
.
frame_id = gui_elements.files.Widget.frame_id
canvas = gui_elements.files.Widget.canvas
canvas.bind("<Configure>", lambda event, canvas=canvas, frame_id=frame_id:configure(event, canvas, frame_id))
.
.
.
.
.
window.extend_layout(
    gui_elements.files,
    [[
        psg.Input(default_text=values[GUIKeys.ADD_FILE], key=gui_key_file_id, readonly=True, expand_x=True),
        psg.FileBrowse(),
        psg.Button('-', key=GUIKeys.REMOVE_FILE, metadata=gui_key_file_id)
    ]]
)
window.refresh()
gui_elements.files.contents_changed()

Before resize
Screenshot from 2024-02-05 13-29-31
After resize
Screenshot from 2024-02-05 13-29-44

As I mention - doesn't matter when you resize, before or after adding the element (Input in my case)

Not to add your question to an old question closed at alsmost about two years ago.

Better to add one new issue with full, executable and short script, also steps how to demo your question.