CustomTkinter icon indicating copy to clipboard operation
CustomTkinter copied to clipboard

Default window popping-up before main window - workaround

Open lachesis17 opened this issue 2 years ago • 1 comments

I've seen a couple of other users encountering this in the latest version 5.0.3: https://github.com/TomSchimansky/CustomTkinter/issues/1021 https://github.com/TomSchimansky/CustomTkinter/issues/960

I'm not sure exactly where the problem is but I know its when creating a customtkinter.CTk() window, which is inside the ctk_tk.py script of the the customtkinter package.

I found some of the issues relate to wm_iconbitmap() but I wasn't calling this method.

https://stackoverflow.com/questions/55890931/python-tkinter-small-window-pops-up-momentarily-before-main-window https://stackoverflow.com/questions/15496835/python-tkinter-child-window-issue?rq=1

A workaround is to create your main window as a regular tkinter.Tk() window instead of a customtkinter window, and then create a customtkinter.CTkFrame() inside the main window so you can still use all the customtkinter methods and dependencies. This stops the small default window from popping up as the tk.Tk() window doesn't experience this issue.

Example:

import tkinter as tk
import customtkinter as ct

class AppFrame(ct.CTkFrame):

    def __init__(self):
        super(AppFrame, self).__init__(main_window)

        main_window.geometry('250x100+600+200')
        main_window.title("Main Window")
        
        self.button = ct.CTkButton(self, text="Button!", command=self.button_command, fg_color="#000000", corner_radius=5, hover_color="#27af42", text_color="#FFFFFF", font=("Tahoma",11), width=15)
        self.button.pack(pady=8, padx=8)

        self.pack(pady=25)

    def button_command(self):
        print('hello world')

if __name__ == '__main__':
    main_window = tk.Tk()
    app_frame = AppFrame()
    main_window.mainloop()

lachesis17 avatar Jan 11 '23 11:01 lachesis17

Workaround

I found another workaround, by overriding and restoring tkinter iconbitmap variable.

Example:

import sys,os
import customtkinter as ct


# get customtkinder dir absolute path
customtkinter_directory= os.path.dirname(os.path.dirname(os.path.abspath(sys.modules[ct.CTk.__module__].__file__)))

# I like the customtkinter icon but it can be any icon path. just store the path in bitmap
bitmap = os.path.join(customtkinter_directory, "assets", "icons", "CustomTkinter_icon_Windows.ico")

class App(ct.CTk):
    def __init__(self):

        self.iconbitmap = lambda bitmap: self.wm_iconbitmap()  # override iconbitmap prevent CTk.__init__ from updating the icon
        
        super().__init__()
        self.geometry(f"{600}x{500}")
        self.title("CTk example")

app = App()
app.iconbitmap = app.wm_iconbitmap  # restore iconbitmap value
app.iconbitmap(bitmap) # change the icon as usual
app.mainloop()

Bug Fix Suggestions

I figure out that this issue comes from calling self.iconbitmap at CTk,init:

       try:
            # Set Windows titlebar icon
            if sys.platform.startswith("win"):
                customtkinter_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
                self.iconbitmap(os.path.join(customtkinter_directory, "assets", "icons", "CustomTkinter_icon_Windows.ico"))
        except Exception:
            pass

That actually calls the tk.call in tk.wm_iconbitmap method:

 def wm_iconbitmap(self, bitmap=None, default=None):
        """Set bitmap for the iconified widget to BITMAP. Return
        the bitmap if None is given.

        Under Windows, the DEFAULT parameter can be used to set the icon
        for the widget and any descendants that don't have an icon set
        explicitly.  DEFAULT can be the relative path to a .ico file
        (example: root.iconbitmap(default='myicon.ico') ).  See Tk
        documentation for more information."""
        if default:
            return self.tk.call('wm', 'iconbitmap', self._w, '-default', default)
        else:
            return self.tk.call('wm', 'iconbitmap', self._w, bitmap)

modifying wm_iconbitmap so it will always use the default icon, makes the issue gone. so it might actually be an issue with Tkinter?

Anyway, I found two solutions, not sure how good they are.

  1. By using after() or after_idle() method when calling self.iconbitmap in the CTk.__init__ code above, it seems to be solved:
        try:
            # Set Windows titlebar icon
            if sys.platform.startswith("win"):
                customtkinter_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
                self.after(100, lambda: self.iconbitmap(os.path.join(customtkinter_directory, "assets", "icons", "CustomTkinter_icon_Windows.ico")))
        except Exception:
            pass
  1. moving the code from CTk.__init__ to CTk.mainloop:
def mainloop(self, *args, **kwargs):
        try:
            # Set Windows titlebar icon
            if sys.platform.startswith("win"):
                customtkinter_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
                self.iconbitmap(
                    os.path.join(customtkinter_directory, "assets", "icons", "CustomTkinter_icon_Windows.ico"))
        except Exception:
            pass
        
        if not self._window_exists:
            self._window_exists = True

            if sys.platform.startswith("win"):
                if not self._withdraw_called_before_window_exists and not self._iconify_called_before_window_exists:
                    # print("window dont exists -> deiconify in mainloop")
                    self.deiconify()

        super().mainloop(*args, **kwargs)

I hope that will help.

m-schwob avatar Jan 20 '23 12:01 m-schwob