Starten Sie die App nur, wenn sie noch nicht geöffnet ist

16

Ich möchte die Verwendung von Alfred unter Mac OS X nachahmen. Wenn Sie versuchen, eine App nach der Suche zu öffnen, wird nur dann ein neues Fenster geöffnet, wenn das Programm noch nicht ausgeführt wird derzeit laufende Instanz dieser Anwendung. Gibt es eine Möglichkeit, das Standardverhalten des Launchers zu ändern, um dies zu überprüfen, bevor ein neues Fenster geöffnet wird?

Dan Jenson
quelle
Auch @pidge Dies zu tun wäre nicht schwierig, würde aber auch das Rechtsklick-Verhalten "Öffnen mit" bewirken. Ich denke, das ist ein inakzeptabler Nebeneffekt.
Jacob Vlijm
1
Ich denke, Sie sollten in der Lage sein, ein Skript zu erstellen, mit dem Sie überprüfen können, ob ein bestimmter Prozess bereits ausgeführt wird, und entscheiden können, ob ein neuer Prozess gestartet oder der Fokus auf das vorhandene Fenster gelegt wird. Leider bin ich noch nicht sehr gut im Schreiben von Skripten ... Aber @JacobVlijm ist bekannt als ein Typ mit Skripten für jeden Zweck;) Sie müssten jedoch alle Originalstarter durch das jeweilige Skript ersetzen. Ich bin mir nicht sicher, ob Sie das wollen / können - ich würde nicht ...
Byte Commander
2
Wenn Sie sich für die Skriptroute entscheiden, können Sie dieses Skript als Ausgangspunkt verwenden. Ich habe es ursprünglich für LXDE / Openbox geschrieben, aber es sollte auch in Unity funktionieren. Weitere Informationen zum Skript und seiner Verwendung finden Sie hier .
Glutanimate
1
@ByteCommander genau das hatte ich mir vorgestellt. Sie können sogar die Befehle in den .desktopDateien per Skript ersetzen . Wenn Sie jedoch den Befehl in der .desktopDatei ersetzen , ist die Option zum Öffnen mit der rechten Maustaste ungültig.
Jacob Vlijm
1
Für welche Desktop-Umgebung?
06.04.15 Uhr

Antworten:

6

Update 7. April: Eine andere Version hinzugefügt und Albert gefunden, siehe Update und Bonus unten !!!

Bezüglich der Dash-Funktionalität : Sie haben gefragt, ob Sie das Standardverhalten des Launchers ändern möchten, um dies zu überprüfen, bevor Sie ein neues Fenster öffnen . Die grundlegende Antwort lautet: Nein, als normaler Benutzer haben Sie keine Möglichkeit, dieses Verhalten zu Dash hinzuzufügen. Wenn es jedoch einen Unity-Scope-Entwickler geben würde, der bereit wäre, dies umzusetzen, könnten Sie sich an ihn wenden oder selbst einen entwickeln, wenn Sie entschlossen und lernbereit sind. Meine Programmierkenntnisse sind sehr bescheiden, daher verwende ich Shell-Skripte und das verfügbare grafische Front-End für die Skripte als Workaround.

Verwandte Informationen

Ursprünglicher Beitrag:

Ich habe ein Skript geschrieben, das Zenity-Dialog und wmctrl verwendet, um das zu erreichen, wonach Sie gefragt haben. Beachten Sie, dass dies ein grafisches Skript ist, das heißt, es funktioniert nur mit Windows in der GUI und nicht, wenn Sie versuchen, etwas in tty zu starten. Soweit ich weiß, macht Alfred genau das Gleiche. Sie können eine Desktop-Verknüpfung oder eine Starter-Verknüpfung erstellen, wie hier und hier beschrieben .

Das Drehbuch:

#!/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 /ubuntu//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

Nebenbemerkungen: In der vorherigen Version verwendete das Skript echo $?, Um zu testen, ob frühere Ausdrücke erfolgreich beendet wurden. Gemäß Murus Vorschlag (aus der Bearbeitung) habe ich den Code in eine etwas kompaktere Version geändert, daher schlage ich vor, dass Sie sich die vorherige Version und die aktuelle Version ansehen.

Außerdem hat wmctrl -a $MYPROGdas Testen von Google-Chrome oder Chrom-Browser bisher nicht funktioniert. Aus irgendeinem dummen Grund haben einige Programme die WM_CLASS-Eigenschaft des Fensters in Großbuchstaben geschrieben, während das von aufgeführte Programm in dpkg --get-selectionsKleinbuchstaben geschrieben ist (Sie müssen nur lesen man wmctrlund ausführen wmctrl -lx, Sie werden es wissen). Das Hinzufügen von -ax sollte sich darum kümmern. Das Skript ruft das bereits geöffnete Chromfenster wie gewünscht auf

Eine andere Sache - wmctlr ist insofern etwas seltsam, als es manchmal eine Verzögerung braucht (ich hatte Erfahrung damit in einem anderen Skript), also musste ich sleep 1Zeile hinzufügen . Früher war es mit Firefox ein und aus, aber jetzt funktioniert es schwimmend.

Das Skript in Aktion

In der folgenden Animation sehen Sie, dass beim ersten Ausführen des Skripts eine Instanz von Firefox geöffnet ist und der Fokus des Skripts auf dieses Fenster wechselt. Beim zweiten Test öffne ich eine neue Instanz von Google-Chrome, die zuvor noch nicht geöffnet war. (Randnotiz: Wenn Sie über den Desktop neugierig sind, ist das übrigens Openbox mit Cairo Dock)

Per Vorschlag in den Kommentaren, eingebettete Animation entfernt, nur Link gepostet. Bitte melden, wenn es kaputt ist! http://i.stack.imgur.com/puuPZ.gif

Update, 7. April

Ich habe das Skript etwas verbessert, damit alle Programme im Dropdown-Eingabefeld von zenity aufgelistet werden. Jetzt muss sich der Benutzer nicht mehr jedes Programm merken, sondern kann mit den Pfeiltasten durch eine Liste blättern oder einfach das Dropdown-Menü öffnen. Diese verbesserte Version erhöht Fenster nicht nach Namen, sondern nach Fenster-ID, wodurch die Leistung erheblich verbessert wird. Beachten Sie, dass die Art und Weise, wie ich .desktop-Dateien durchsuche, redundant ist, wenn ich den Befehl cut zweimal verwende, aber da mein Skript-Fu bisher nicht so gut ist, ist dies alles, was ich tun kann. Verbesserungsvorschläge sind willkommen!

#!/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 /ubuntu//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

Bildbeschreibung hier eingeben

Bonus:

Ich habe tatsächlich Albert gefunden , die Linux-Version von Alfred, habe es aber selbst nicht ausprobiert. Ein Besuch lohnt sich jedoch. Wie Jacob jedoch bereits bemerkte, ist es immer noch fehlerhaft.

Es gibt eine App namens Gnome-Do, die Alfred grafisch ähnelt, jedoch nicht die gleiche Funktionalität wie dieses Skript hat.

Bildbeschreibung hier eingeben

Lassen Sie mich wissen, ob Ihnen dieses Skript gefällt, ob es Probleme gibt, die behoben werden müssen, und vergessen Sie nicht, die Antwort zu aktualisieren, wenn Sie sie nützlich finden

Sergiy Kolodyazhnyy
quelle
Übrigens beachten Sie, wie ich die Namen der Programme eintippe - genau wie in aufgeführt dpkg --get-selectons. Das Starten von libreoffice writer durch Eingabe von "writer" funktioniert nicht, Sie können jedoch einen Symlink in Ihrem Ordner "~ / bin", "/ bin" oder "/ usr / bin" erstellen oder einen Alias ​​in ".bashrc" oder ".profile" verwenden.
Sergiy Kolodyazhnyy
Beachten Sie auch, dass Sie wmctl installiert haben müssen, es ist nicht standardmäßig vorhanden, aber ziemlich praktisch. Ich habe es auch verwendet, um dies
Sergiy Kolodyazhnyy
Könnten Sie die Annimation durch ein Bild ersetzen, das mit der Annimation verknüpft ist? Mein Browser "lädt" die Seite weiter, sodass ich sie nicht aktualisieren kann. (Und die Animation läuft nicht :))
Jacob Vlijm
Vielen Dank! Der Link funktioniert gut in Chrome, nicht in Firefox.
Jacob Vlijm
@JacobVlijm Ugh, so ist es. Ich bin mir nicht sicher, warum Firefox es ablehnt, es zu spielen. Es ist nur ein Link zu dem, was ich ursprünglich hochgeladen habe
Sergiy Kolodyazhnyy
5

1. Dash the Second

Unten ein Skript, das als Alternative zu Dash verwendet werden kann, wenn es darum geht, Anwendungen wie in Ihrer Frage beschrieben auszuführen.

Es besteht aus einem Fenster mit der gleichen Funktionalität wie Dash. Wenn Sie ein oder mehrere Zeichen der Anwendung eingeben, wird die Anwendung in der Liste angezeigt. Drücken Sie Enter, um die Anwendung zu starten oder zu starten, je nachdem, ob sie bereits ausgeführt wird oder nicht.

Sie können es über eine Tastenkombination aufrufen oder ein Symbol im Launcher festlegen, um es ähnlich wie Dash (siehe weiter unten) zu verwenden, oder beides.

Bildbeschreibung hier eingeben

Das Drehbuch

#!/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

Wie benutzt man

Das Skript muss wmctrlinstalliert sein:

sudo apt-get install wmctrl

Dann:

  1. Fügen Sie das obige Skript in eine leere Datei ein und speichern Sie es unter dash_alternative.py
  2. Hinzufügen zu einer Tastenkombination: Wählen Sie: Systemeinstellungen> "Tastatur"> "Verknüpfungen"> "Benutzerdefinierte Verknüpfungen". Klicken Sie auf das "+" und fügen Sie den Befehl hinzu:

    python3 /path/to/dash_alternative.py
    

Erläuterung

Wenn das Skript ausgeführt wird, werden alle in dargestellten Anwendungen aufgelistet /usr/share/applications. Es durchsucht die .dektopDateien und erstellt eine Liste aller Anwendungsnamen (ab der ersten Zeile "Name =") und den Befehl zum Ausführen der Anwendung (ab der ersten Zeile "Exec =").

Anschließend wird eine Zenity-Liste erstellt, in der alle Anwendungen sortiert dargestellt werden.

Bei jeder Auswahl einer Anwendung wird das Skript in der Liste der ausgeführten Prozesse angezeigt, wenn die Anwendung ausgeführt wird. In diesem Fall wird das entsprechende Fenster geöffnet. Wenn nicht, wird eine neue Instanz geöffnet.

Anmerkungen

  1. Um das Skript am 12.04 auszuführen (da die ursprüngliche Frage markiert wurde 12.04, ändern Sie einfach den shebang in #!/usr/bin/env pythonund führen Sie ihn mit dem Befehl aus

    python /path/to/dash_alternative.py
    
  2. Soweit ich es getestet habe, funktioniert das Skript einwandfrei. Befehle und ihre (nicht-) entsprechenden Prozessnamen (zB LibreOffice<> soffice.bin), verschiedene Fenstertypen ( nautilushat mehrere verschiedene Fenstertypen, außer "echten" Fenstern), mehrere Pids pro Anwendung ( Chromium, Google-chrome) können Ausnahmen verursachen, die ich in den Beispielen behoben habe über. Wenn jemand auf ein Problem stößt, erwähnen Sie es bitte.

2. Zusätzlich: Einstellung als Alternative zum "echten" Dash zum Ausführen von Anwendungen

  1. Kopieren und speichern Sie das Skript wie oben beschrieben
  2. Speichern Sie das Symbol unten (Rechtsklick> Sichern unter) als dash_alternative.png

    Bildbeschreibung hier eingeben

  3. Kopieren Sie den folgenden Code in eine leere Datei und speichern Sie ihn ~/.local/share/applicationsunter dash_thesecond.desktop. Stellen Sie die richtigen Pfade für /path/to/dash_alternative.py(das Skript) und /path/to/dash_alternative.png(das Symbol) ein

    [Desktop Entry]
    Name=Dash the Second
    Exec=python3 /path/to/dash_alternative.py
    Icon=/path/to/dash_alternative.png
    Type=Application
    Hidden=false
    
  4. Ziehen Sie die .desktopDatei auf den Launcher:

Jacob Vlijm
quelle
1
Gut zu wissen, dass es einen ganzen Ordner mit .desktop-Dateien gibt! Ich habe mich gefragt, wie die nativen und apt-installierten Anwendungen nach Namen anstatt nach Befehlen aufgelistet werden. Gute Arbeit dort!
Sergiy Kolodyazhnyy
@ Serg Danke! gleichfalls :). Es gibt auch ein lokales Verzeichnis für .desktopDateien: ~/.local/share/applications. Ich dachte, ich würde die Suche auf global installierte Anwendungen beschränken.
Jacob Vlijm
Jacob, ich weiß nicht , Python, aber vielleicht dies wird bei der Verbesserung Ihrer Skript für Sie nützlich sein , indem sie in eine Einheit Rahmen drehen. Ich verstehe, dass dies der einzige Weg ist, die Dash-Funktionalität genau so zu verändern, wie es OP will
Sergiy Kolodyazhnyy 08.04.15
@ Serg Danke! Das ist ein interessanter Beitrag, den ich mir auf jeden Fall ansehen werde!
Jacob Vlijm
0

Für den Launcher (das vertikale Bedienfeld auf der linken Seite des Bildschirms) ist dies bereits das Standardverhalten, da es sich um die Taskwechseloberfläche handelt.

Für den Bindestrich (das große Bit, das beim Klicken auf das Ubuntu-Logo geöffnet wird) gibt es keine Möglichkeit, das Verhalten auf diese Weise zu ändern, ohne den Quellcode selbst vermutlich erheblich zu ändern.

Einige Apps tun dies jedoch möglicherweise bereits, da sie so konzipiert sind, dass sie sich so verhalten. Allerdings sind und müssen nicht alle Apps so implementiert werden.

Wenn Sie jedoch das Fenster mit Super+ öffnen Wund einen Anwendungsnamen eingeben, werden nur die Fenster für diese Anwendung angezeigt.

dobey
quelle
Ich habe tatsächlich die Linux-Version von dem gefunden, was OP wollte, siehe meinen Beitrag im Bonusbereich. Anscheinend hatte jemand anderes die Idee, Alfred zu Linux zu bringen
Sergiy Kolodyazhnyy
1
@ Serg bitte probier Albert aus; Albert hat immer noch den "LibreOffice-Bug", den "Chromium-Bug" und den "Chrome-Bug". Auch der "Files Bug" ... Albert öffnet einfach immer eine neue Instanz dieser Anwendungen. LibreOffice funktioniert einfach überhaupt nicht. In den Kommentaren unter Ihrem Link finden Sie auch eine Reihe von Problemen.
Jacob Vlijm
@Serg Nein, Sie haben eine Problemumgehung gefunden, indem Sie eine zusätzliche Software hinzugefügt haben, die im Hintergrund ausgeführt wird. Dies bietet eine völlig andere Benutzererfahrung und erfordert eine Umschulung der Suche nach Anwendungen. Die Frage lautete, wie diese Arbeit im Unity-Dash erfolgen soll. Möglicherweise können Sie eine ähnliche Funktion auch außerhalb von Unity selbst erhalten. Die einzige Möglichkeit, Unity zu ändern, besteht darin, den Quellcode zu ändern.
Dobey
@dobey Nun, das stimmt; Eine Änderung der Dash-Funktionalität ist daher außerhalb unserer Reichweite. . Ich muss mit dem, was wir haben, etwas anfangen, oder? Es sei denn, es gibt einen Entwickler, der bereit wäre, einen Unity-Bereich mit solchen Funktionen zu programmieren. . .
Sergiy Kolodyazhnyy