Ubuntu – Getting icon of window, and then changing icon

iconswindowxorg

My goal is to get the icon which is currently on the window. (Then using some img tools I'll modify that icon, then save it to a location, I can do this part no need for help here please). Then I would like to set the window icon to the icon i have saved at a location on hard drive.

I do this in js-ctypes so it takes me a long time so if I am going in wrong direction I waste a ton of time so it really helps to ask about it. As I can't do a quick test. If you all can just point me in right direction I would really appreciate it.

Here was how I was thinking of doing it:

  1. I already have an array of windows (got it with XQuerySubtree)
  2. Get RGBA data of currently applied icon with XGetWindowProperty and atom of _NET_WM_ICON
  3. (use my img tools to modify the icon and save to hard drive, lets take for example on the desktop /usr/noida/Desktop/new icon.png)
  4. Apply icon from /usr/noida/Desktop/new icon.png to all of the windows in the array, so it changes icon displayed on window (if there is one, in ubuntu there isnt) and changes icon in Alt + Tab menu, and changes icon on dock.
    • apply path of icon with XSendEvent of a XClientMessageEvent with atom _NET_WM_ICON?

Am I thinking in the right direction?

Thanks


Image of goal – we see here that the window doesnt have an icon dispplayed on it, but its icon is displayed in the dock at the left. And in the Alt + Tab menu. So i was hoping by changing icon on all windows to my customized icon it will change on the dock and alt+tab menu. And for linux distributions that do show icon in window I hope that to be changed as well. In image here the blue arrow points to the three related icon areas.

showing i want to change which icons

Best Answer

Where the icon is defined

The representation of an application in Dash and the the Unity launcher is defined in a .desktop file. Such a .desktop files includes a line to set the command to run the application, a line to set the icon of the application and a varying number of possible lines to set additional properties.

To see the current Unity launcher's content by command:

gsettings get com.canonical.Unity.Launcher favorites 

If you run this command, you will (a.o.) get a list of references to .desktop files, in the order like they appear in the launcher. An application's mention in the list looks like: application://thunderbird.desktop. It refers (in this example) to the file, thunderbird.desktop.

When Unity gathers its information on login, it first looks into the local directory for .desktop files, (~/.local/share/applications), and secondly in /usr/share/applications. If a .desktop file exists in both directory, the local one has preference. Normally, during your session, the launcher's icon stays linked to the .desktop file in either one of these directories.

When the content of the linked desktop file is edited during your session, the result is applied immediately. However, the icon as it appears in the Unity launcher does not change until you either:

  • log out / login
  • remove the icon from the launcher and lock it again

Since you cannot remove the icon of a running application from the launcher, changing the icon of a running application is not possible.
To change the icon of an application in the launcher which is not running however, you can use the two scripts below.
The first one copies the .desktop file to the local directory and edits the Icon= line in the local copy. The second one refreshes the icon in the launcher (and relinks it if necessary).


Script 1; change the icon

how to use

Copy the script below into an empty file, save it as change_icon.py, run it by the command:

python3 change_icon.py <applicationnamme.desktop> </path/to/new/icon>

for example:

python3 change_icon.py thunderbird.desktop </path/to/new/icon>

for smoother use:

create a directory ~/bin, copy the script into the directory, remove the language extension from the script, make it executable and (after logout / login), you can simly use the script by the command:

change_icon <applicationnamme.desktop> </path/to/new/icon>

More info on where to store and how to define icons, you can find here

The script

#!/usr/bin/env python3

import os
import shutil
import sys

file = sys.argv[1]
new_icon = sys.argv[2]

user_home = os.environ["HOME"]
dir_1 = user_home+"/.local/share/applications/"
dir_2 = "/usr/share/applications/"
dtfile_list1 = os.listdir(dir_1)
dtfile_list2 = os.listdir(dir_2)

subject = dir_1+file

if not os.path.exists(subject):
    try:
        shutil.copyfile(dir_2+file, dir_1+file)
    except FileNotFoundError:
        print("the file "+file+" does not exist")
        
def read_file(file):
    with open(file) as edit:
        return edit.readlines()

def write_file(file, linelist):
    with open(file, "wt") as edit:
        for line in linelist:
            edit.write(line)

subject_lines = read_file(subject)
index = [i for i in range(len(subject_lines)) \
         if subject_lines[i].startswith("Icon=")][0]
subject_lines[index] = "Icon="+new_icon+"\n"
write_file(subject, subject_lines)

Script 2; refresh the icon in the launcher

how to use

Copy the script below into an empty file, save it as refresh.py, run it by the command:

python3 refresh.py <applicationnamme.desktop>

for smoother use:

Like in the script above, copy the script into ~/bin, remove the language extension, make it executable and (after logout / login), you can simly use the script by the command:

refresh <applicationnamme.desktop>

for example:

refresh thunderbird.desktop

The script

#!/usr/bin/env python3

import subprocess
import time
import sys

desktopfile = sys.argv[-1]

def read_currentlauncher():
    # reads the current launcher contents
    return subprocess.check_output([
        "gsettings", "get", "com.canonical.Unity.Launcher", "favorites"
        ]).decode("utf-8")

def set_launcher(llist):
    # sets a defined unity launcher list
    current_launcher = str(llist).replace(", ", ",")
    subprocess.Popen([
        "gsettings", "set", "com.canonical.Unity.Launcher", "favorites",
        current_launcher,
        ])

def refresh_icon(desktopfile):
    current_launcher = read_currentlauncher()
    current_launcher_temp = eval(current_launcher)
    item = [item for item in current_launcher_temp if desktopfile in item][0]
    index = current_launcher_temp.index(item)
    current_launcher_temp.pop(index)
    set_launcher(current_launcher_temp)
    time.sleep(2)
    set_launcher(current_launcher)

refresh_icon(desktopfile)
Related Question