仅当尚未打开时才启动应用程序

仅当尚未打开时才启动应用程序

我想模仿 Mac OS X 上 Alfred 的使用方式,如果您在搜索后尝试打开某个应用程序,则只有当该程序尚未运行时,它才会打开一个新窗口,否则它会将焦点放在该应用程序的当前运行实例上。有没有办法更改启动器的默认行为,以便在打开新窗口之前检查这一点?

答案1

4月7日更新:添加了不同的版本并找到了Albert,请参阅下面的更新和奖励!!!

关于仪表盘功能:您问过“有没有办法改变启动器的默认行为,在打开新窗口之前检查这一点“。基本答案是,不,作为普通用户,您无法将该行为添加到 dash。但是,如果有 Unity 范围开发人员愿意实现这一点,您可以联系他们,或者如果您有决心并愿意学习,您可以自己开发一个。我的编码技能非常有限,因此我使用 shell 脚本和脚本的可用图形前端作为解决方法。

相关信息

原始帖子:

我编写了一个使用 zenity dialog 和 wmctrl 来实现您要求的脚本。请注意,这是一个图形脚本,这意味着它只能在 Windows 中使用 GUI,如果您尝试在 tty 中启动某些东西,它将不起作用。此外,据我所知,Alfred 的作用完全相同。您可以创建它的桌面快捷方式或启动器快捷方式,如所述这里这里

剧本:

#!/bin/bash
# Author: Serg Kolo
# Description: A launcher script that checks whether
#       or not a window of a particular program already exists
#       If a window of such program is open, bring it to focus
#       Otherwise - launch a new window
#       Written for https://askubuntu.com/q/440142/295286
# Date: April 6 , 2015
#


MYPROG=$( zenity --entry --title='MY LAUNCHER' --text='Type the name of application to run' )
sleep 0.5
wmctrl -lx | awk '{print $3}' | grep -i "$MYPROG"

if [ $? -eq 0 ]; then
    sleep 1         
    wmctrl -xa $MYPROG
   #as an alternative try the line bellow
   #wmctrl -a $MYPROG
    exit 1
else 
    $MYPROG &
    exit 0
fi

附注:在之前的版本中,脚本使用 echo $? 来测试之前的表达式是否成功退出。根据 muru 的建议(来自编辑),我将代码更改为更紧凑的版本,因此我建议您查看之前的版本和当前版本。

此外,之前wmctrl -a $MYPROG无法测试 google-chrome 或 chromium-browser;出于某些愚蠢的原因,某些程序的窗口 WM_CLASS 属性大写,而列出的程序dpkg --get-selections是小写的(只需阅读man wmctrl并运行wmctrl -lx,您就会知道)。添加 -ax 应该可以解决这个问题。脚本会按预期调出已经打开的 chromium 窗口

还有一件事 - wmctlr 有点奇怪,有时需要延迟(在另一个脚本中有过这种体验),所以我不得不添加sleep 1一行。以前它在 Firefox 中会时断时续,但现在运行顺畅。

脚本实际运行

在下面的动画中,您可以看到,在脚本的第一次运行中,有一个 Firefox 实例打开,并且脚本将焦点切换到该窗口;在第二次测试中,我打开了 google-chrome 的新实例,该实例之前从未打开过。(旁注:顺便说一句,如果您对桌面感兴趣,那就是带有 cairo dock 的 openbox)

根据评论中的建议,删除了嵌入的动画,仅发布链接。如果损坏,请报告! https://i.stack.imgur.com/puuPZ.gif

4月7日更新

我对脚本进行了一些改进,使所有程序都列在 zenity 的下拉输入框中。现在用户不必记住每个程序,只需使用箭头键滚动浏览程序列表或打开下拉菜单即可。此外,此改进版本不是按名称而是按窗口 ID 调出窗口,这样性能会好得多。请注意,我浏览 .desktop 文件的方式有点多余,使用了两次 cut 命令,但由于我的脚本功能目前还不是很好,所以我只能这样做。欢迎提出改进建议!

#!/bin/bash
# Author: Serg Kolo
# Description: Second version of a launcher script that checks whether
#       or not a window of a particular program already exists
#       If a window of such program is open, bring it to focus
#       Otherwise - launch a new window
#       Written for https://askubuntu.com/q/440142/295286
# Date: April 7 , 2015
#

set -x

MYPROG=$(zenity --entry --text 'Select program from list' --entry-text $(ls /usr/share/applications/*.desktop | cut -d'/' -f5 | cut -d'.' -f1 | xargs echo))
sleep 0.5
# Do we have a window of such program ?
wmctrl -lx| awk '{print $3}'  | grep -i $MYPROG

if [ $? -eq 0 ]; then
    sleep 0.5 # if yes, find that window id, and raise it
    WINID=$(wmctrl -lx | grep -i $MYPROG | awk 'NR==1{print $1}')
    wmctrl -ia $WINID &
 #  exit 0  
else
    echo $MYPROG | grep -i libreoffice
    if [ $? -eq 0  ]
    then
        MYPROG=$(echo $MYPROG | sed 's/-/ --/g')
    fi
    $MYPROG &

#  exit 0 
fi

在此处输入图片描述

奖金:

我确实发现阿尔伯特,这是 Alfred 的 Linux 版本,但我自己还没有尝试过。不过值得一试。然而,正如 Jacob 已经指出的那样,它仍然有缺陷。

有一个名为 Gnome-Do 的应用程序,其图形看起来与 Alfred 类似,但它不具备与该脚本相同的功能。

在此处输入图片描述

如果您喜欢这个脚本,或者有任何需要修复的地方,请告诉我,如果您发现它有用,请不要忘记点赞

答案2

1. 冲刺第二

下面的脚本可以用作 Dash 的替代品,当运行您问题中描述的应用程序时。

它有一个与 Dash 功能相同的窗口;如果输入应用程序的一个或多个字符,应用程序将出现在列表中。按Enter可启动或显示应用程序,具体取决于它是否已在运行。

您可以通过快捷键组合调用它,或者在启动器中设置一个图标以类似于 Dash 的方式使用它(请参阅下文),或者两者兼而有之。

在此处输入图片描述

剧本

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

user = getpass.getuser()
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
skip = ["%F", "%U", "%f", "%u"]; trim = ["chrome", "chromium", "nautilus"]

def apply(command):
    if "libreoffice" in command:
        proc = [l.split()[0] for l in get("ps -u "+user).splitlines() if "soffice.bin" in l]
        module = command.split("--")[-1]
        time.sleep(0.1)
        try:
            ws = sum([[w.split()[0] for w in get("wmctrl -lp").splitlines() if process in w and module in w.lower()] for process in proc], [])[0]
            subprocess.call(["wmctrl", "-ia", ws])
        except IndexError:
            subprocess.Popen(["/bin/bash", "-c", command+"&"])
    else:
        check = command.split("/")[-1][:14]
        proc = [p.split()[0] for p in get("ps -u "+user).splitlines() if check in p]
        time.sleep(0.5)
        try:
            ws = sum([[w.split()[0] for w in get("wmctrl -lp").splitlines() if process in w] for process in proc], [])
            if command == "nautilus":
                real_window = [w for w in ws if "_NET_WM_WINDOW_TYPE_NORMAL" in get("xprop -id "+w)][0]
            else:
                real_window = ws[0]
            subprocess.call(["wmctrl", "-ia", real_window])
        except IndexError:
            subprocess.Popen(["/bin/bash", "-c", command+"&"])
# default directories of .desktop files; globally, locally, LibreOffice- specific when separately installed
globally = "/usr/share/applications"; locally = os.environ["HOME"]+"/.local/share/applications"; lo_dir = "/opt/libreoffice4.4/share/xdg"
# create list of .desktop files; local ones have preference
local_files = [it for it in os.listdir(locally) if it.endswith(".desktop")]
global_files = [it for it in os.listdir(globally) if it.endswith(".desktop")]
lo_spec = [it for it in os.listdir(lo_dir) if it.endswith(".desktop")] if os.path.exists(lo_dir) else []
for f in [f for f in local_files if f in global_files]:
    global_files.remove(f)
for f in [f for f in local_files if f in lo_spec]:
    lo_spec.remove(f)
dtfiles = [globally+"/"+f for f in global_files]+[locally+"/"+f for f in local_files]+[lo_dir+"/"+f for f in lo_spec]
# create list of application names / commands
valid = []
for f in dtfiles:
    content = open(f).read()
    if all(["NoDisplay=true" not in content,"Exec=" in content]):
        lines = content.splitlines()
        name = [l.replace("Name=", "") for l in lines if "Name=" in l][0]
        command = [l.replace("Exec=", "") for l in lines if all(["Exec=" in l, not "TryExec=" in l])][0]
        valid.append((name, command))
valid.sort(key=lambda x: x[0])
# create zenity list + window
list_items = '"'+'" "'.join([f[0] for f in valid])+'"'
proposed = 'zenity --list --text "Type one or more characters... " --column="Application List" '+\
           '--title="Dash the Second" --height 450 --width 300 '+list_items
try:
    choice = subprocess.check_output(["/bin/bash", "-c", proposed]).decode("utf-8").strip().split("|")[0]
    command = [r[1] for r in valid if r[0] == choice][0]
    # command fixes:
    for s in skip:
        command = command.replace(" "+s, "")
    for t in trim:
        if t in command:
            command = t
    apply(command)
except subprocess.CalledProcessError:
    pass

如何使用

需要安装的脚本wmctrl

sudo apt-get install wmctrl

然后:

  1. 将上面的脚本粘贴到一个空文件中,保存为dash_alternative.py
  2. 将其添加到快捷键组合中:选择:系统设置 > “键盘” > “快捷键” > “自定义快捷键”。单击“+”并添加命令:

    python3 /path/to/dash_alternative.py
    

解释

当脚本运行时,它会列出所有应用程序,以 表示/usr/share/applications。它会搜索.dektop文件,创建所有应用程序名称的列表(从第一个“Name=”行开始)和运行应用程序的命令(从第一个“Exec=”行开始)。

随后,创建一个 Zenity 列表,以排序的方式呈现所有应用程序。

每当选择一个应用程序时,脚本都会在正在运行的进程列表中查找该应用程序是否正在运行。如果是,则会显示相应的窗口。如果没有,则会打开一个新实例。

笔记

  1. 运行脚本12.04(因为原来的问题被标记,12.04只需将shebang更改为#!/usr/bin/env python并通过命令运行它

    python /path/to/dash_alternative.py
    
  2. 据我测试,该脚本运行良好。命令及其(不)对应的进程名称(例如LibreOffice<> soffice.bin)、不同的窗口类型(nautilus除了“真实”窗口外,还有几种不同的窗口类型)、每个应用程序的多个 pid(Chromium, Google-chrome)可能会导致异常,我已在上面的示例中修复了这些问题。如果有人遇到问题,请提及。

2. 附加功能:将其设置为运行应用程序的“真实” Dash 的替代品

  1. 复制并保护上述脚本
  2. 将下面的图标保存为(右键单击 > 安全为)dash_alternative.png

    在此处输入图片描述

  3. 将以下代码复制到一个空文件中,另存为~/.local/share/applications。为(脚本)和(图标)dash_thesecond.desktop设置正确的路径/path/to/dash_alternative.py/path/to/dash_alternative.png

    [Desktop Entry]
    Name=Dash the Second
    Exec=python3 /path/to/dash_alternative.py
    Icon=/path/to/dash_alternative.png
    Type=Application
    Hidden=false
    
  4. .desktop文件拖到启动器上:

答案3

为了启动器(屏幕左侧的垂直面板),这已经是默认行为,因为它是任务切换界面。

为了短跑(单击 Ubuntu 徽标时打开的大位),没有办法以这种方式更改行为,除非对源代码本身进行重大修改。

然而,有些应用可能已经这样做了,因为它们的设计就是如此。不过,并非所有应用都以这种方式实现,也不一定如此。

Super但是,还有另一个功能,如果您使用+打开展开的窗口W,并开始输入应用程序名称,则将只显示该应用程序的窗口。

相关内容