Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug/Question] First Window Finalize is Slower Than Subsequent #4977

Closed
7 tasks done
elibroftw opened this issue Nov 24, 2021 · 17 comments
Closed
7 tasks done

[Bug/Question] First Window Finalize is Slower Than Subsequent #4977

elibroftw opened this issue Nov 24, 2021 · 17 comments
Labels
question Further information is requested

Comments

@elibroftw
Copy link

elibroftw commented Nov 24, 2021

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

Question / bug

Operating System

Windows

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

Latest

Python version (sg.sys.version)

3.10

PySimpleGUI Version (sg.__version__)

Latest

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

Latest tkinter


Your Experience In Months or Years (optional)

Years Python programming experience ~6

Years Programming experience overall ~6

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

Anything else you think would be helpful?

No

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

The issue is that the first time the Gui needs to open, it is Slower to open that if I close and open it the second time. I think this has to do with tkinter being initialized the first time I create the window so I was wondering if it's possible to preload tkinter in a thread as soon as psg is imported in order to avoid the 1 second extra delay when the Gui needs to be opened for the first time?

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:

from functools import lru_cache
import PySimpleGUI as sg
import sys
# https://raw.githubusercontent.com/elibroftw/music-caster/master/src/b64_images.py
from b64_images import *
from PIL import Image
import io


@lru_cache(maxsize=None)
def resize_img(base64data, bg, new_size=(255, 255)) -> bytes:
    """ Resize and return b64 img data to new_size (w, h). (use .decode() on return statement for str) """
    img_data = io.BytesIO(b64decode(base64data))
    art_img: Image = Image.open(img_data)
    w, h = art_img.size
    if w == h:
        img = art_img.resize(new_size, Image.ANTIALIAS)
    else:
        ratio = h / w if w > h else w / h
        to_change = 1 if w > h else 0
        ratio_size = list(new_size)
        ratio_size[to_change] = round(new_size[to_change] * ratio)
        art_img = art_img.resize(ratio_size, Image.ANTIALIAS)
        paste_width = (new_size[0] - ratio_size[0]) // 2
        paste_height = (new_size[1] - ratio_size[1]) // 2
        img = Image.new('RGB', new_size, color=bg)
        img.paste(art_img, (paste_width, paste_height))
    data = io.BytesIO()
    img.save(data, format='png')
    return b64encode(data.getvalue())

DEFAULT_ART = resize_img(DEFAULT_ART, 'black')
WINDOW_ICON = resize_img(WINDOW_ICON, 'black')
VOLUME_IMG = resize_img(VOLUME_IMG, 'black')
NEXT_BUTTON_IMG = resize_img(NEXT_BUTTON_IMG, 'black')

def make_window():
    return [
        [sg.Image(data=DEFAULT_ART)],
        [sg.Image(data=WINDOW_ICON)],
        [sg.Image(data=VOLUME_IMG)],
        [sg.Image(data=NEXT_BUTTON_IMG)]
        ]


import time


while True:
    print('Enter command: ', end='')
    user_input= input()
    if user_input == 'open':
        t1 = time.time()
        window = sg.Window('test', make_window(), finalize=True)
        print(time.time() - t1)
        event, values = window.read()
        window.close()
    elif user_input == 'close':
        sys.exit()

Screenshot, Sketch, or Drawing


Watcha Makin?

If you care to share something about your project, it would be awesome to hear what you're building.

@jason990420
Copy link
Collaborator

jason990420 commented Nov 24, 2021

IMO, the import time for tkinter or PySimpleGUI should be less than 0.2 seconds.
The key is the startup time of your script or Python in your platform.

Script is ready to accept user input only after the GUI ready.

@jason990420 jason990420 added the question Further information is requested label Nov 24, 2021
@PySimpleGUI
Copy link
Owner

tkinter cannot be run as a thread.

Can you share some quantified data? What's slow to one person is incredibly fast to another. It would be helpful to know the values we're working with. Maybe take a moment to write the code described in the block of text. I can't run a block of text.

So many things, as I'm sure you're very aware, impact the speed. Everything from your hardware to caching at various levels, the specific version of PySimpleGUI and tkinter, etc.

I'm interested in the timing values you get from the initial load and then the subsequent ones as well. Thanks for taking the time to open this issue. It's a good discussion to have.

@elibroftw
Copy link
Author

The Sg.Window(finalize=True) takes 0.95 for the first time and 0.28 seconds the second time for my application.

This is not a problem in the all elements demo program so I suspect it's a combination of images that's causing the issue.

It's extremely difficult to recreate a demo that mimics my application without requiring the bloat. But I'll try to do my best.

@elibroftw
Copy link
Author

elibroftw commented Nov 24, 2021

Okay I updated the code, you will also need to download https://raw.githubusercontent.com/elibroftw/music-caster/master/src/b64_images.py for the base64 images I used in the example.
On my machine:

Enter command: open
0.17172527313232422
Enter command: open
0.05617642402648926
Enter command: open
0.05700063705444336
Enter command: close

@elibroftw
Copy link
Author

One source (0.047 seconds) comes from _refresh_debugger(). In particular:

frame, *others = inspect.stack()[1]  # 0.046 seconds for first run, 

This can be replaced with

frame = inspect.currentframe().f_back  # 0 seconds
# OR
frame = inspect.stack(0)[1][0]  # 0 seconds

@elibroftw
Copy link
Author

elibroftw commented Nov 24, 2021

StartupTK takes 0.158 seconds first time and 0.064 seconds the second time. About 0.09 seconds difference.
Conclusion: tk.Tk() takes more time the first time than subsequent calls.

@elibroftw
Copy link
Author

I got all the way to PackFormIntoFrame

@PySimpleGUI
Copy link
Owner

PySimpleGUI commented Nov 24, 2021

StartupTK takes 0.158 seconds first time and 0.064 seconds the second time. About 0.09 seconds difference.
Conclusion: tk.Tk() takes more time the first time than subsequent calls.

Makes sense and they're reasonable numbers overall. 158ms startup time is faster than I thought it would be.

Performance tuning has had 0 cycles spent to date. I've fixed a couple of spots that were slow, but no further than that. Still in the first 2 of the "1. Make it run. 2. Make it right. 3. Make it fast." progression.

.. and thank you for tracking down a couple of the slower spots currently. You've gone further than I am able to at the moment so thanks for taking the time.

@PySimpleGUI
Copy link
Owner

PySimpleGUI commented Nov 24, 2021

you will also need to download https://raw.githubusercontent.com/elibroftw/music-caster/master/src/b64_images.py for the base64 images I used in the example.

💗 that you've really utilized this capability. None of my projects have separate image files except to provide the originals that were used to make the encoded byte-strings, and lately some ICO files so that the psgshortcut program can create shortcuts with nice looking icons. I'm sure your users appreciate that your code doesn't break due to images that are missing or in the wrong location (they just may not know to tell you they appreciate it). I don't see many people doing this in general though. Not sure why it's not taught for Python programs more often.

@elibroftw
Copy link
Author

elibroftw commented Nov 24, 2021

Okay in that case, I'll ask you a question about Combo's.
image
You see this combo box? At a previous PSG version (I have no idea), the dropdown icon used to be the same color as the accent color. Do you know how to change it back in the latest version?
image
This is how it used to look like.

@elibroftw
Copy link
Author

elibroftw commented Nov 24, 2021

Sorry do you know how to change it back without changing the button theme?

@PySimpleGUI
Copy link
Owner

do you know how to change it back without changing the button theme?

I don't know what that means. I would open another issue if you're stumped.

There are detailed release notes for each release going back to July 2018 in the documentation, and PyPI has releases going back a number of years.

https://pysimplegui.readthedocs.io/en/latest/#release-notes

Maybe pip install PySimpleGUI==4.xx.yy until you find what you're looking for? I dunno. As far as I know it's matched since 2019. Run sg.main() to quickly test a release.

image

Combo Elements are tricky because they use a TTK widget, which means a complex can of worms. They're not as simple as the Tk widgets.

@elibroftw
Copy link
Author

I found a workaround. I manually set the button colors for some buttons but set the button color theme to what I want for combos.

@PySimpleGUI
Copy link
Owner

If there's not an enhancement on being able to set the colors, feel free to open one

@PySimpleGUI
Copy link
Owner

I'm thinking this kind of interface:

layout = [  [sg.Text('My Window')],
            [sg.Input(key='-IN-')],
            [sg.Combo((1,2,3,4,5,6,7,8,9), size=(5,5))],
            [sg.Combo((1,2,3,4,5,6,7,8,9), size=(5,5), button_arrow_color='yellow', button_background_color='red')],
            [sg.Combo((1,2,3,4,5,6,7,8,9), size=(5,5), button_arrow_color='red', button_background_color='yellow')],
            [sg.Combo((1,2,3,4,5,6,7,8,9), size=(5,5))],
            [sg.Text(size=(12,1), key='-OUT-')],
            [sg.Button('Go'), sg.Button('Exit')]  ]

With this resulting window:
image

@elibroftw
Copy link
Author

Yeah, that'd be optimal, but implement at your own pace.

@elibroftw elibroftw changed the title [ Bug/Question] First Window Finalize is Slower Than Subsequent [Bug/Question] First Window Finalize is Slower Than Subsequent Nov 24, 2021
@PySimpleGUI
Copy link
Owner

I've added the details of the combo change to this enhancement issue:
#3254

Enjoy!

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants