如何在 Unity 启动器中为脚本赋予自己的图标?

如何在 Unity 启动器中为脚本赋予自己的图标?

使用 Ubuntu 16.04,如果您创建一个~/.local/share/applications带有终端应用程序自定义图标的启动器(中的桌面文件)(因此使用Terminal=true),那么当您启动它时,它将产生一个新的标准终端图标,并且您的自定义图标将在几秒钟内脉动并消失。

在 14.04 中,它按预期工作。(它不会启动新的标准终端图标)。

知道如何改变这种行为吗?我有几个终端应用程序想要从 Unity 启动,但新的行为是有问题的(我记不清哪个是哪个,因为它们都以标准终端图标结尾)...

答案1

为什么它不像你那样工作

正如评论中提到的,原则上一个应用程序只能由启动器中的一个图标表示一次. 一直都是这样。

您所指的可能是 Unity 在确定哪个文件.desktop最适合代表应用程序窗口方面变得“更聪明”。因此,运行终端窗口的脚本将由 -icon 表示gnome-terminal

在此处输入图片描述

因此,您设置中过去所做的操作(简单地创建一个启动器,启动您的脚本)不再欺骗 Unity,它会选择现有的gnome-terminal启动器来代表您的窗口。

糟糕的解决方案

...是通过在启动器中添加一行来推翻 Unity 的选择(适用于 16.04):

StartupWMClass=gnome-terminal-server

在此处输入图片描述

...但是之后全部终端窗口,无论是否运行您的脚本,都分组在此图标下。

此外,一般来说,拥有多个.desktop文件并在其主命令中调用同一个应用程序是一种不好的、不干净的做法。


编辑

如何为正在运行的脚本设置单独的图标

这需要一些诡计和欺骗,但它可以为在不同的终端窗口中运行的多个脚本设置单独的图标。

实践中如何运作

  • 假设您有一个脚本,somscript.sh您想要在终端窗口中运行它,并在运行时在 Unity Launcher 中显示其专用图标。
  • 运行命令:

    showicon somescript.sh someicon.png
    

    脚本将在新打开的gnome-terminal窗口中运行,并显示图标:someicon.png

  • 如果窗口关闭,图标将再次从启动器中删除。

一个例子

  • 我想要一个脚本,/home/jacob/Bureaublad/script.sh运行,在 Unity 启动器中显示图标:/home/jacob/Thema/icon/ubu.png 运行命令:

    showicon '/home/jacob/Bureaublad/script.sh' '/home/jacob/Thema/icon/ubu.png'
    

    将会这样做:

    在此处输入图片描述

    现在让我们添加另一个:

    showicon '/home/jacob/Bureaublad/script2.sh' '/home/jacob/Thema/icon/banaan.png'
    

    结果:

    在此处输入图片描述

    一旦窗口关闭,图标就会再次被删除。

如何设置

  1. 脚本需要wmctrl

    sudo apt-get install wmctrl
    
  2. 如果目录尚不存在,则创建该目录~/bin

  3. 将以下脚本复制到一个空文件中,将其另存为showicon(无扩展名)~/bin,然后使其可执行
  4. 注销并重新登录,您的设置应该可以正常工作。使用命令进行测试

    showicon </path/to/script.sh> </path/to/icon.png>
    

    已经script.sh在终端中运行,并显示icon.png在 Unity 启动器中。

剧本

#!/usr/bin/env python3
import subprocess
import os
import sys
import time

terminal = "gnome-terminal"
key = "com.canonical.Unity.Launcher"
script = sys.argv[1]
icon = sys.argv[2]

curr = os.path.dirname(os.path.realpath(__file__))
scriptname = script.split("/")[-1]

def get(command):
    try:
        return subprocess.check_output(command).decode("utf-8")
    except subprocess.CalledProcessError:
        pass

# --- edit Unity launcher section

def current_launcher():
    return eval(get(["gsettings", "get", key, "favorites"]))

def set_launcher(desktopfile, arg):
    curr_launcher = current_launcher()
    last = [i for i, x in enumerate(curr_launcher) if x.startswith("application://")][-1]
    new_icon = "application://"+desktopfile
    if arg == "a":
        if not new_icon in curr_launcher:
            curr_launcher.insert(0, new_icon)
            subprocess.Popen(["gsettings", "set", key,"favorites",str(curr_launcher)])
    elif arg == "r":
        curr_launcher.remove(new_icon)
        subprocess.Popen(["gsettings", "set", key,"favorites",str(curr_launcher)])

# --- end section

def create_launcher(w, scriptname, icon):
    launcher = ["[Desktop Entry]", "Type=Application",
            "Exec=wmctrl -ia "+w, "Name="+scriptname, "Icon="+icon,
            "StartupNotify=False"]
    with open(l_name, "wt") as newlauncher:
        for l in launcher:
            newlauncher.write(l+"\n")

def getname():
    # create unique launcher name
    n = 1
    while True:
        nm = os.path.join(curr, "scriptlauncher_"+str(n)+".desktop")
        if os.path.exists(nm):
            n += 1
        else:
            break
    return nm    

wlist1 = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
subprocess.Popen(["gnome-terminal", "-e", script])

while True:
    time.sleep(1)
    wdata = get(["wmctrl", "-l"]).splitlines()
    if wdata:
        try:
            wlist2 = [l.split()[0] for l in wdata]
            w = [w for w in wlist2 if not w in wlist1][0]
        except IndexError:
            pass
        else:
            # check if the new window belongs to the terminal
            if terminal in get(["xprop", "-id", w]):
                # create launcher
                l_name = getname()
                create_launcher(w, scriptname, icon)
                set_launcher(l_name, "a")
                break
    wlist1 = wlist2

while True:
    time.sleep(2)
    wdata = get(["wmctrl", "-l"])
    if wdata:
        if not w in wdata:
            os.remove(l_name)
            set_launcher(l_name, "r")
            break 

笔记

  • 图标的作用:

    • 它代表gnome-terminal运行脚本的窗口
    • 单击它时,它会像往常一样升起窗口。执行此操作的命令会自动添加到临时启动器:

      wmctrl -ia <window_id>
      
  • 它能做什么不是

    • 该解决方案的唯一缺点是,由于表示是间接的,因此图标左侧不会显示正在运行的应用程序的通常箭头。

解释

无需过多赘述:

  • 该脚本是一个包装器。如果您启动脚本通过 showicon,的实例在窗口showicon中运行您的脚本gnome-terminal,类似于Terminal=true
  • 随后,showicon等待新gnome-terminal窗口出现并读取其窗口 id。
  • 然后创建一个临时启动器,使用窗口 ID 创建命令以在其行中调出窗口Exec=。您在要运行的命令中设置为参数的图标showicon将自动设置为此临时启动器的图标(在行中定义Icon=)。

    这种自动创建的(临时)启动器的示例:

    [Desktop Entry]
    Type=Application
    Exec=wmctrl -ia 0x04400b7f
    Name=script2.sh
    Icon=/home/jacob/Thema/icon/ubu.png
    StartupNotify=False
    
  • 使用与这个答案,临时启动器将被添加到 Unity Launcher 的顶部位置,以代表您正在运行的脚本。

  • 同时,showicon检查窗口是否存在。如果不存在,则临时启动器将从 Unity 启动器中移除,并不再存在根本showicon实例将被终止。

答案2

我找到了一个在 Ubuntu 20.04 中有效的简单解决方案。

这适用于您想要使用自己的图标和名称集成到 dock 中而不是仅仅在另一个终端窗口中运行的任何脚本或终端应用程序。

您不需要安装任何东西,一切都在启动器 .desktop 文件中完成。这是我启动终端文本编辑器的示例

[Desktop Entry]

Name=Micro
GenericName=Text Editor
Comment=Edit text files in a terminal

Icon=micro
Type=Application
Categories=Utility;TextEditor;Development;
Keywords=text;editor;syntax;terminal;

Exec=gnome-terminal -e "micro %F" -t "Micro" --hide-menubar --name=Micro --class=micro
StartupNotify=true
Terminal=false
MimeType=text/plain;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;text/x-java;text/x-dsrc;text/x-pascal;text/x-perl;text/x-python;application/x-php;application/x-httpd-php3;application/x-httpd-php4;application/x-httpd-php5;application/xml;text/html;text/css;text/x-sql;text/x-diff;

需要注意的主要选项是:

  • 执行官gnome-terminal使用命令 ( -e "YOUR COMMAND") 和参数启动-t "Micro" --hide-menubar --name=Micro --class=micro。这些将使用应用程序的名称(在本例中为 Micro)及其类来设置终端窗口,以便它不会合并到另一个终端类窗口中。
  • StartupNotify=true这可以确保如果您将启动器作为收藏,则启动时它会被堆叠,而不是添加额外的图标实例。
  • 终端=false使用此功能可以避免弹出额外的终端窗口,除非您确实想看到该窗口。

答案3

另一个不是答案而是解决方案。

我使用快速列表来为最常用的终端会话创建启动器,然后在 gnome-terminal 中为每个启动器创建配置文件,以执行诸如更改其颜色之类的操作,这使得您可以很容易地知道您正在使用哪个服务器。

您可以通过编辑 ~/.local/share/applications/gnome-terminal.desktop 中的 gnome-terminal.desktop 文件来执行此操作。

我的看起来像这样

[Desktop Entry]
Name=Terminal
Comment=Use the command line
Keywords=shell;prompt;command;commandline;
TryExec=gnome-terminal
Exec=gnome-terminal
Icon=utilities-terminal
Type=Application
X-GNOME-DocPath=gnome-terminal/index.html
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-terminal
X-GNOME-Bugzilla-Component=BugBuddyBugs
X-GNOME-Bugzilla-Version=3.16.2
Categories=GNOME;GTK;System;TerminalEmulator;
StartupNotify=true
X-GNOME-SingleWindow=false
OnlyShowIn=GNOME;Unity;
Actions=New;Item1;Item2
X-Ubuntu-Gettext-Domain=gnome-terminal

[Desktop Action New]
Name=New Terminal
Exec=gnome-terminal
OnlyShowIn=Unity

[Desktop Action Item1]
Name=SSH Shell type 1
Exec=gnome-terminal -e 'ssh item1' --profile 'Item1'
OnlyShowIn=Unity


[Desktop Action Item2]
Name=SSH Shell type 2
Exec=gnome-terminal -e 'ssh item2' --profile 'Item2'
OnlyShowIn=Unity

不久前我还编写了一个脚本,用于自动从 hostsfile 将条目添加到快速列表中,以便任何 ssh 命令都能获得快速列表条目。当快速列表无法自动更新时,我编写了它,但后来放弃了,因为这让它变得笨重,现在它们是即时的,可以通过 cron 作业运行。

http://blog.amias.net/articles/114

答案4

一次性修复方法是编写一个运行脚本的小型可执行程序,并为该可执行程序分配一个唯一的启动器图标。

相关内容