Icon for windows with TopLevel

4

Within a kind of application of the style of a Text Editor, I open the typical "Help" and "About" windows.

Both windows are made using the Toplevel Tkinter widget. And I would like to put an icon that appeared in the taskbar as I have done for the main window of the application (the root).

For the main window (root), I have set the icon in this way (in this case, "self", refers to ROOT):

self.img_logo = PhotoImage(file='img/anonvicom_logo.png')
self.call('wm', 'iconphoto', self, self.img_logo)

I have tried to do the same in the configuration of the two mentioned Toplevel but, apparently, because of the ERROR that I get the method "call ()" is not available for TopLevel widgets.

I've tried, too, to put it this way (for example, for the Toplevel "ayuda_top"):

ayuda_top.iconwindow('img/anonvicom_logo.png')

or, like this:

ayuda_top.iconwindow(pathName='img/anonvicom_logo.png')

But it gives me something like this ERROR:

TclError: bad window path name "img/anonvicom_logo.png"

Also, I used:

acerca_top.iconbitmap('@img/anonvicom_logo.xbm')

But this way, apart from putting the image in Black / White and negative, now, I'm giving this ERROR too:

TclError: error reading bitmap file "img/anonvicom_logo.xbm"

By giving me this ERROR, the execution stops.

The "@" is because, apparently, for Linux routes, it would be necessary to put it, and, if not, I get this message:

TclError: bitmap "img/anonvicom_logo.xbm" not defined

NOTE : As a curiosity, the ERROR of " TclError: error reading bitmap file "img/anonvicom_logo.xbm" ", occurs when I run the application by the Visual Studio Code. If I execute it through the Sublime Text, or by the interpreter of the terminal, it opens without any complaint.

So, how to put a window icon for Toplevel widgets whether or not the same icon as the icon in the parent window?
If you want to put the same icon of the main window, is there any way, too, to inherit its icon for the Toplevel son windows?

Employee environment: python 2.7.x, Linux Ubuntu 16.04, Tkinter.

Edited

For the one that interests you as I define the self (the root object), there it goes:

from Tkinter import *


class MiTkinter(Tk):

    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)
        self.img_logo = PhotoImage(file='img/anonvicom_logo.png')
        self.call('wm', 'iconphoto', self, self.img_logo)

        # y después todo el código que haga falta
        # ...


if __name__ == '__main__':
    # Tk (Raíz) objeto raíz por defecto
    # ==========================================================
    root = MiTkinter()
    # etc, etc, ...
    
asked by zacktagnan 21.05.2018 в 00:32
source

2 answers

2

I will try to complete the ChemaCortes response to the comments. In Python 3, the simplest thing is maybe to use the iconphoto or wm_iconphoto method, which have both tkinter.Tk and tkinter.Toplevel :

  • Use the image for the icon in the main window and for all your daughters ( default=True ):

    self.wm_iconphoto(True, self.img_logo)
    
  • Use the image only for the main window icon ( default=False ):

    self.wm_iconphoto(False, self.img_logo)
    
  • Use another image for a Toplevel window ( ayuda_top ):

    import tkinter as tk
    
    ayuda_top = tk.Toplevel(self, ....)
    ayuda_logo = tk.PhotoImage(...)
    ayuda_top.wm_iconphoto(False, ayuda_logo)
    

The problem is that this apparently is not valid in Python 2, I do not know the reason but Tkinter does not expose in this case the iconphoto function by means of an instance method like in Python 3 (at least in Python 2.7.14 / Tkinter 8.6 which is where I tested it). Actually the iconphoto method simply tries to simplify the use of the iconphoto function of Tk / Tcl, using the call method internally, so we can do the same thing:

  • Use the image for the icon in the main window and for all your daughters ( default=True ):

    self.tk.call('wm', 'iconphoto', self._w, "-default", self.img_logo)
    
  • Use the image only for the main window icon ( default=False ):

    self.tk.call('wm', 'iconphoto', self._w, self.img_logo)
    
  • Use another image for a Toplevel window:

    import Tkinter as tk        
    
    ayuda_top = tk.Toplevel(self, ....)
    ayuda_logo = tk.PhotoImage(...)
    self.tk.call('wm', 'iconphoto', ayuda_top._w, ayuda_logo)
    
  

Note: I've tried this on Linux (X11), I do not know how it behaves on MacOs. In Windows this changes perfectly the icon of the title bar, but the icon used in the toolbar is that of the Python interpreter. This can be solved in many ways, but I think the cleanest one is using SetCurrentProcessExplicitAppUserModelID of the Windows API via ctypes :

import ctypes

id = 'company.product.subproduct.version'
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(id)
    
answered by 21.05.2018 / 14:21
source
2

The .call() what you are doing is invoking directly the TCL interpreter that is below Tkinter and can only be done from root of Tk.

This sentence:

self.call('wm', 'iconphoto', self, self.img_logo)

equals:

self.iconphoto(self.img_logo)

In addition to being more concise, this method will be available for the rest of the windows.

But you could also have given this icon by default for all:

self.iconphoto(self.img_logo, default=True)

The errors it gives you seem to be by using relative path. Try using absolute path to see if it works.

Anyway, the best way to put icons to the windows would be using PNG files and be more careful with the routes, something like this:

import tkinter as tk
from pathlib import Path

WORKDIR = Path(__file__).parent
icon = WORKDIR/"img"/"icono.png"

root = tk.Tk()
root.iconbitmap(icon)

about = tk.Toplevel(root)
about.title("Prueba")

about.iconbitmap(icon)

root.mainloop()

Edited : in the previous code you have to modify the first two lines so that it works in python2, and installing the module pathlib2 :

import Tkinter as tk
from pathlib2 import Path

WORKDIR = Path(__file__).parent
icon = WORKDIR/"img"/"icono.png"

root = tk.Tk()
root.iconbitmap(icon)

about = tk.Toplevel(root)
about.title("Prueba")

about.iconbitmap(icon)

root.mainloop()
    
answered by 21.05.2018 в 11:04