Uruchom aplikację tylko wtedy, gdy jeszcze nie jest otwarta

16

Chciałbym naśladować użycie Alfreda w systemie Mac OS X, gdzie jeśli spróbujesz otworzyć aplikację po jej wyszukaniu, otworzy się nowe okno tylko wtedy, gdy program nie jest jeszcze uruchomiony, w przeciwnym razie ustawi się fokus na aktualnie działająca instancja tej aplikacji. Czy w ogóle można zmienić domyślne zachowanie programu uruchamiającego, aby to sprawdzić przed otwarciem nowego okna?

Dan Jenson
źródło
Również @pidge Wykonanie tego nie byłoby trudne, ale wpłynęłoby również na zachowanie „otwórz za pomocą” kliknięcia prawym przyciskiem myszy. Myślę, że to niedopuszczalny efekt uboczny.
Jacob Vlijm
1
Myślę, że powinieneś być w stanie stworzyć skrypt, który może sprawdzić, czy określony proces już działa, i zdecydować o uruchomieniu nowego procesu lub skupieniu się na istniejącym oknie. Niestety, nie jestem jeszcze zbyt dobry w pisaniu skryptów ... Ale @JacobVlijm jest znany jako facet ze skryptami do każdego celu;) Trzeba jednak zastąpić wszystkie oryginalne programy uruchamiające odpowiednim skryptem. Nie jestem pewien, czy chcesz / możesz to zrobić - nie zrobiłbym ...
Byte Commander
2
Jeśli wybierzesz trasę skryptu, możesz użyć tego skryptu jako punktu początkowego. Początkowo napisałem to dla LXDE / Openbox, ale powinno również działać w Unity. Więcej informacji o skrypcie i jego użyciu tutaj .
Glutanimate
1
@ByteCommander dokładnie to miałem na myśli. Możesz nawet zamienić polecenia w .desktopplikach. Jeśli jednak zastąpisz polecenie w .desktoppliku, kliknięcie prawym przyciskiem myszy Otwórz z opcją jest zepsute.
Jacob Vlijm,
1
Dla jakiego środowiska pulpitu?
j0h

Odpowiedzi:

6

Aktualizacja 7 kwietnia: Dodano inną wersję i znaleziono Alberta, zobacz aktualizację i Bonus poniżej !!!

Dotyczące funkcjonalności deski rozdzielczej : Zapytałeś: „ Czy mimo to można zmienić domyślne zachowanie programu uruchamiającego, aby to sprawdzić przed otwarciem nowego okna ”? Podstawowa odpowiedź brzmi: nie, jako zwykły użytkownik nie masz możliwości dodania tego zachowania do myślnika. Jeśli jednak byłby programista zakresu jedności, który byłby skłonny to zaimplementować, możesz do nich podejść lub sam go opracować, jeśli masz determinację i chcesz się uczyć. Moje umiejętności kodowania są bardzo skromne, dlatego używam skryptu powłoki i dostępnego graficznego interfejsu dla skryptów jako obejścia.

Powiązana informacja

Oryginalny post:

Napisałem skrypt, który używa dialogu zenity i wmctrl, aby osiągnąć to, o co prosiłeś. Zauważ, że jest to skrypt graficzny, co oznacza, że ​​będzie działał tylko z oknami, w GUI i nie będzie działać, jeśli spróbujesz uruchomić coś w tty. Poza tym z tego, co rozumiem, Alfred robi dokładnie to samo. Możesz utworzyć do niego skrót na pulpicie lub skrót do programu uruchamiającego, jak opisano tutaj i tutaj .

Scenariusz:

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

Dodatkowe uwagi: w poprzedniej wersji skrypt używał echa $ ?, aby sprawdzić, czy poprzednie wyrażenia zakończyły się powodzeniem. Zgodnie z sugestią Muru (z edycji) zmieniłem kod na nieco bardziej kompaktową, więc proponuję spojrzeć na poprzednią i aktualną.

Ponadto wcześniej wmctrl -a $MYPROGnie działał z testowaniem google-chrome lub chrome-browser; z jakiegoś głupiego powodu niektóre programy mają właściwość WM_CLASS okna pisanego wielkimi literami, podczas gdy program wymieniony przez dpkg --get-selectionsma małe litery (po prostu czytaj man wmctrli uruchamiaj wmctrl -lx, będziesz wiedział). Dodanie tego -ax powinno o to zadbać. Skrypt wyświetla już otwarte okno chromu tak, jak powinno

Kolejna rzecz - wmctlr jest nieco dziwny, ponieważ czasami potrzebuje opóźnienia (miał doświadczenie z innym skryptem), więc musiałem dodać sleep 1linię. Wcześniej byłby to rodzaj włączania i wyłączania Firefoksa, ale teraz działa płynnie.

Skrypt w akcji

W poniższej animacji widać, że przy pierwszym uruchomieniu skryptu jest jedna instancja firefox, a skrypt przełącza fokus na to okno; w drugim teście otwieram nowe wystąpienie google-chrome, które nie było wcześniej otwarte. (Nota boczna: Nawiasem mówiąc, jeśli interesujesz się pulpitem, to jest to openbox z doku Cairo)

Zgodnie z sugestią w komentarzach usunięto osadzoną animację, opublikowano tylko link. Zgłoś, jeśli jest zepsuty, proszę! http://i.stack.imgur.com/puuPZ.gif

Aktualizacja, 7 kwietnia

Ulepszyłem nieco skrypt, aby wszystkie programy wymienione w rozwijanym polu wprowadzania zenity. Teraz użytkownik nie musi zapamiętywać każdego programu, ale może po prostu przewijać listę za pomocą klawiszy strzałek lub po prostu otworzyć menu rozwijane. Ponadto ta ulepszona wersja podnosi okna nie według nazwy, ale według identyfikatora okna, co zapewnia znacznie lepszą wydajność. Zauważ, że sposób, w jaki przechodzę przez pliki .desktop jest trochę zbędny, używając dwukrotnie polecenia cut, ale ponieważ mój skrypt-fu nie jest do tej pory tak dobry, to wszystko, co mogę zrobić. Sugestie dotyczące ulepszeń są mile widziane!

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

wprowadź opis zdjęcia tutaj

Premia:

Znalazłem Alberta , który jest wersją Alfreda dla Linuksa, ale sam tego nie próbowałem. Warto jednak to sprawdzić. Jednak, jak już zauważył Jacob, nadal jest wadliwy.

Istnieje aplikacja o nazwie Gnome-Do, która graficznie wygląda podobnie do Alfreda, jednak nie ma takiej samej funkcjonalności jak ten skrypt.

wprowadź opis zdjęcia tutaj

Daj mi znać, jeśli podoba Ci się ten skrypt, jeśli coś wymaga naprawy, i nie zapomnij głosować odpowiedzi, jeśli okażą się przydatne

Sergiy Kolodyazhnyy
źródło
Przy okazji zauważ, jak wpisuję nazwy programów - dokładnie tak, jak podano w dpkg --get-selectons. Uruchamianie programu libreoffice writer przez wpisanie „writer” nie zadziała, ale możesz utworzyć dowiązanie symboliczne do niego w folderze ~ / bin, / bin lub / usr / bin lub użyć aliasu w .bashrc lub .profile.
Sergiy Kolodyazhnyy
Zauważ też, że będziesz musiał zainstalować wmctl, domyślnie nie jest on dostarczany, ale całkiem przydatny. Użyłem go również do tego
Sergiy Kolodyazhnyy
Czy możesz zastąpić wypowiedź obrazem, łącząc się z ogłoszeniem? Moja przeglądarka „ładuje” stronę, więc nie mogę jej odświeżyć. (a zawiadomienie nie działa :))
Jacob Vlijm,
Dzięki! Link działa dobrze w Chrome, a nie w Firefox.
Jacob Vlijm,
@JacobVlijm Ugh, więc to robi. Nie jestem pewien, dlaczego Firefox nie chce w to grać. To tylko imgur link do tego, co pierwotnie przesłałem
Sergiy Kolodyazhnyy
5

1. Dash the Second

Poniżej skrypt, który może być używany jako alternatywa dla Dasha, jeśli chodzi o uruchamianie aplikacji zgodnie z opisem w pytaniu.

Istnieje okno o takiej samej funkcjonalności jak Dash; jeśli wpiszesz co najmniej jeden znak aplikacji, aplikacja pojawi się na liście. Naciśnij, Enteraby uruchomić lub podnieść aplikację, w zależności od tego, czy jest już uruchomiona, czy nie.

Możesz zadzwonić z kombinacji klawiszy skrótu lub ustawić ikonę w programie uruchamiającym, aby używać jej podobnie do Dasha (patrz poniżej) lub obu.

wprowadź opis zdjęcia tutaj

Scenariusz

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

Jak używać

Skrypt wymaga wmctrlzainstalowania:

sudo apt-get install wmctrl

Następnie:

  1. Wklej powyższy skrypt do pustego pliku i zapisz go jako dash_alternative.py
  2. Dodaj go do kombinacji klawiszy skrótu: Wybierz: Ustawienia systemu> „Klawiatura”> „Skróty”> „Skróty niestandardowe”. Kliknij „+” i dodaj polecenie:

    python3 /path/to/dash_alternative.py
    

Wyjaśnienie

Po uruchomieniu skryptu wyświetla wszystkie aplikacje reprezentowane w /usr/share/applications. Przeszukuje .dektoppliki, tworząc listę wszystkich nazw aplikacji (z pierwszego wiersza „Name =”) oraz polecenie uruchomienia aplikacji (z pierwszego wiersza „Exec =”).

Następnie tworzona jest lista Zenity, przedstawiająca wszystkie aplikacje w uporządkowany sposób.

Po wybraniu aplikacji skrypt sprawdza listę uruchomionych procesów, jeśli aplikacja jest uruchomiona. Jeśli tak, odpowiednie okno zostanie podniesione. Jeśli nie, zostanie otwarta nowa instancja.

Notatki

  1. Aby uruchomić skrypt 12.04 (ponieważ pierwotne pytanie zostało oznaczone, 12.04wystarczy zmienić shebang na #!/usr/bin/env pythoni uruchomić je za pomocą polecenia

    python /path/to/dash_alternative.py
    
  2. O ile go przetestowałem, skrypt działa dobrze. Polecenia i ich (nie-) odpowiadające nazwy procesów (np. LibreOffice<> soffice.bin), Różne typy okien ( nautilusma kilka różnych typów okien, oprócz „prawdziwych” okien), wiele pidów na aplikację ( Chromium, Google-chrome) może powodować wyjątki, które naprawiłem w przykładach powyżej. Jeśli ktoś napotka problem, proszę o tym wspomnieć.

2. Dodatkowe: ustawienie go jako alternatywy dla „prawdziwego” Dasha do uruchamiania aplikacji

  1. Skopiuj i zabezpiecz skrypt, jak wspomniano powyżej
  2. Zapisz poniższą ikonę (kliknij prawym przyciskiem myszy> bezpieczne jako) jako dash_alternative.png

    wprowadź opis zdjęcia tutaj

  3. Skopiuj poniższy kod do pustego pliku i zapisz go ~/.local/share/applicationsjako dash_thesecond.desktop. Ustaw poprawne ścieżki dla /path/to/dash_alternative.py(skryptu) i /path/to/dash_alternative.png(ikony)

    [Desktop Entry]
    Name=Dash the Second
    Exec=python3 /path/to/dash_alternative.py
    Icon=/path/to/dash_alternative.png
    Type=Application
    Hidden=false
    
  4. Przeciągnij .desktopplik do programu uruchamiającego:

Jacob Vlijm
źródło
1
Miło wiedzieć, że jest cały folder plików .desktop! Zastanawiałem się, w jaki sposób natywne i apt-zainstalowane aplikacje są wyświetlane według nazwy zamiast poleceń. Dobra robota!
Sergiy Kolodyazhnyy,
@Serg Dzięki! i nawzajem :). Istnieje również lokalny katalog dla .desktopplików: ~/.local/share/applications. Myślałem, że ograniczę wyszukiwanie do globalnie zainstalowanych aplikacji.
Jacob Vlijm,
Jacob, nie wiem, Python, ale może to będzie przydatna w poprawie skryptu obracając go w zakresie jedności. Rozumiem, że jest to jedyny sposób, aby zmienić funkcjonalność deski rozdzielczej dokładnie tak, jak chce OP
Sergiy Kolodyazhnyy
@Serg Dzięki! to ciekawy post, na pewno dobrze się przyjrzy!
Jacob Vlijm,
0

W przypadku programu uruchamiającego (pionowy panel po lewej stronie ekranu) jest to już zachowanie domyślne, ponieważ jest to interfejs przełączania zadań.

W przypadku myślnika (duży bit, który otwiera się po kliknięciu logo Ubuntu), nie ma sposobu, aby zmienić zachowanie w ten sposób, bez przypuszczalnie znaczącej modyfikacji samego kodu źródłowego.

Niektóre aplikacje mogą już to robić, ponieważ zostały zaprojektowane w taki sposób. Jednak wszystkie aplikacje nie są i nie muszą być implementowane w taki sposób.

Jako kolejna funkcja, jeśli otworzysz rozkładówkę okna za pomocą Super+ Wi zaczniesz wpisywać nazwę aplikacji, okna dla tej aplikacji będą jedynymi pokazanymi.

dobey
źródło
Znalazłem wersję Linuksa tego, czego chciał OP, patrz mój post, sekcja bonusowa. Najwyraźniej ktoś inny wpadł na pomysł wprowadzenia Alfreda na Linuksa
Sergiy Kolodyazhnyy
1
@Erg, wypróbuj Albert; Albert nadal ma „błąd LibreOffice”, „błąd Chromium” i „błąd Chrome”. Nawet „Błąd plików” ... Albert po prostu zawsze otwiera nowe wystąpienie tych aplikacji. LibreOffice po prostu w ogóle nie działa. Ponadto w komentarzach pod linkiem można znaleźć wiele problemów.
Jacob Vlijm,
@Serg Nie, znalazłeś obejście, dodając dodatkowe oprogramowanie działające w tle, które zapewnia zupełnie inne wrażenia użytkownika i wymaga ponownego przeszkolenia w wyszukiwaniu aplikacji. Pytanie dotyczyło tego, jak wykonać tę pracę w desce rozdzielczej Jedności. Możesz być w stanie uzyskać podobną funkcję poza samym Unity, ale jedynym sposobem na zmianę Unity, aby to zrobić, jest zmiana kodu źródłowego.
dobey,
@dobey Cóż, to prawda; zmiana funkcjonalności deski rozdzielczej jest poza naszym zasięgiem, więc. . . muszę zrobić z tym, co mamy do dyspozycji, prawda? Chyba że istnieje programista, który byłby skłonny zakodować zakres jedności za pomocą takiej funkcjonalności. . .
Sergiy Kolodyazhnyy