Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tkinter - Preload window?

I started building a python tkinter gui, the problem is, after adding many features to the gui, the loading started looking really ugly. When starting the mainloop, a blank window shows up for a few miniseconds before the widgets are loaded, the same happens with the other Toplevel windows ( except the ones with few static elements that don't need updates ).

My question is

Is there a way to 'preload' the window so that when I would call it, it would start smoothly?

For example

root = MyTkRoot()
root.build_stuff_before_calling()
root.mainloop() # Smooth without widget loading lag

Part of my code

from root_gui import Root
import threading
from time import sleep
from data_handler import DataHandler


def handle_conn():
    DataHandler.try_connect()
    smtp_client.refresh()


def conn_manager(): # Working pretty well
    while 'smtp_client' in locals():
        sleep(3)
        handle_conn()


smtp_client = Root()
handle_conn()


MyConnManager = threading.Thread(target=conn_manager)
MyConnManager.start() # Thanks to the second thread, the tk window doesn't have to wait for 3 seconds before loading the widgets.
smtp_client.mainloop()

The full project on git

Too much code to put in here...

https://github.com/cernyd/smtp_client

like image 338
David Černý Avatar asked Aug 29 '16 17:08

David Černý


2 Answers

On your operating system tkinter seems to show the base Tk window before loading it's widgets causing that few milliseconds of blank window. To make it appear with all the widgets already loaded you will need to hide the window when starting and .after it has loaded show it again.

There are a number of ways to show and hide a window, personally I'd use .withdraw() to remove the window from the window manager (like it was never there) then .deiconify() (basically "unminimize") to show the window again.

class TEST(tk.Tk):
    def __init__(self,*args,**kw):
        tk.Tk.__init__(self,*args,**kw)
        self.withdraw() #hide the window
        self.after(0,self.deiconify) #as soon as possible (after app starts) show again
        #setup app...

An alternative to withdrawing the window completely is to start it minimized with .iconify() so it will appear on the taskbar/dock but won't open the window until it is completely loaded.

Another way to hide/show the window is by changing the -alpha property as @double_j has done but I would not recommend that in production code because the window is technically still there, it (and the close button etc.) can be clicked on /interacted with for a brief moment before showing which could be undesired, as well it's behaviour can be ambiguous amongst operating systems, from http://wiki.tcl.tk/9457:

Macintosh Attributes

-alpha double controls opacity (from 0.0 to 1.0)
...

Unix/X11 Attributes

-alpha double controls opacity (from 0.0 to 1.0).
This requires a compositing window manager to have any effect. [compiz] is one such, and xfwm4 (for the XFCE desktop) is another.
...

Windows Attributes

-alpha double how opaque the overall window is; note that changing between 1.0 and any other value may cause the window to flash (Tk changes the class of window used to implement the toplevel).

So on some unix machines -alpha may have no effect and on windows it may cause the window to flash (probably not an issue before it is even opened but still worth noting)

Where as withdraw and deiconify works identically amongst all platforms as far as I am aware.

like image 173
Tadhg McDonald-Jensen Avatar answered Sep 29 '22 14:09

Tadhg McDonald-Jensen


The short answer is yes. You can set the window to appear as though you can't see it. Perform any code that takes time, and then display the window.

Here's how:

class MyTkRoot(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.attributes('-alpha', 0.0)  # make window transparent
        # build gui...
        self.after(0, self.attributes, "-alpha", 1.0)  # back to normal
root = MyTkRoot()
root.mainloop()

Here's a reference to the code with the example of a Toplevel. It will also work with the root however.

like image 28
double_j Avatar answered Sep 29 '22 16:09

double_j