Запускать приложение, только если оно еще не открыто

16

Я хотел бы имитировать использование Alfred в Mac OS X, где, если вы попытаетесь открыть приложение после его поиска, оно откроет новое окно только в том случае, если программа еще не запущена, в противном случае она установит фокус на в данный момент запущен экземпляр этого приложения. Есть ли способ изменить поведение модуля запуска по умолчанию, чтобы проверить это перед открытием нового окна?

Дэн Дженсон
источник
Кроме того, @pidge. Сделать это будет несложно, но это также повлияет на поведение при открытии правой кнопкой мыши. Я думаю, что это недопустимый побочный эффект.
Джейкоб Влейм
1
Я думаю, что вы должны быть в состоянии создать сценарий, который может проверить, запущен ли уже определенный процесс, и принять решение о запуске нового процесса или выделении фокуса для существующего окна. К сожалению, я пока не очень хорош в написании сценариев ... Но @JacobVlijm известен как парень со сценариями для любых целей;) Хотя вам придется заменить все оригинальные средства запуска соответствующим сценарием. Не уверен, если вы хотите / можете сделать это - я бы не стал ...
Byte Commander
2
Если вы идете по маршруту сценария, вы можете использовать этот сценарий в качестве отправной точки. Сначала я написал его для LXDE / Openbox, но он должен работать и в Unity. Больше информации о скрипте и его использовании здесь .
Glutanimate
1
@ByteCommander именно это я и имел в виду. Вы могли бы даже сценарий - заменить команды в .desktopфайлах. Если вы замените команду в .desktopфайле, однако, щелчок правой кнопкой мыши с опцией open будет прерван.
Джейкоб Влейм
1
Для какой среды рабочего стола?
j0h

Ответы:

6

Обновление 7 апреля: добавлена ​​другая версия и найден Альберт, см. Обновление и Бонус ниже!

Относительно функциональности тире : Вы спросили: « Есть ли возможность изменить поведение модуля запуска по умолчанию, чтобы проверить это перед открытием нового окна ». Основной ответ: нет, как обычный пользователь, у вас нет возможности добавить это поведение в dash. Однако, если бы был разработчик единой области, который хотел бы реализовать это, вы могли бы обратиться к ним или разработать его самостоятельно, если у вас есть решимость и желание учиться. Мои навыки кодирования очень скромны, поэтому я использую сценарии оболочки и доступный графический интерфейс для сценариев в качестве обходного пути.

Связанная информация

Исходное сообщение:

Я написал скрипт, который использует диалог zenity и wmctrl для достижения того, что вы просили. Обратите внимание, что это графический скрипт, то есть он будет работать только с окнами в графическом интерфейсе и не будет работать, если вы попытаетесь запустить что-то в tty. Кроме того, насколько я понимаю, Альфред делает то же самое. Вы можете создать ярлык на рабочем столе или ярлык для запуска, как описано здесь и здесь .

Сценарий:

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

Примечания: в предыдущей версии скрипт использовал echo $? Для проверки успешного завершения предыдущих выражений. Согласно предложению Муру (из редактирования), я изменил код на несколько более компактную версию, поэтому я предлагаю вам взглянуть на предыдущую версию и текущую.

Кроме того, ранее wmctrl -a $MYPROGне работали с тестированием Google-Chrome или Chromium-браузера; по какой-то глупой причине некоторые программы имеют свойство WM_CLASS заглавной буквы окна, в то время как перечисленная программа написана dpkg --get-selectionsстрочными буквами (вы просто будете читать man wmctrlи запускать wmctrl -lx, вы будете знать). Добавление -ax должно позаботиться об этом. Скрипт вызывает уже открытое окно хрома, как и должно

Другое дело - wmctlr несколько странно, потому что иногда ему нужна задержка (у меня был опыт работы с ним в другом скрипте), поэтому мне пришлось добавить sleep 1строку. Раньше с Firefox он включался и выключался, но теперь работает плавно.

Сценарий в действии

В приведенной ниже анимации вы можете видеть, что при первом запуске скрипта открывается один экземпляр firefox, и скрипт переключает фокус на это окно; во втором тесте я открываю новый экземпляр google-chrome, который ранее не был открыт. (Примечание: если вы не любите рабочий стол, кстати, это openbox с док-станцией cairo)

Согласно предложению в комментариях, встроенная анимация удалена, размещена только ссылка. Сообщите, если он сломан, пожалуйста! http://i.stack.imgur.com/puuPZ.gif

Обновление, 7 апреля

Я несколько улучшил сценарий, чтобы все программы, перечисленные в выпадающем списке zenity, были введены. Теперь пользователю не нужно запоминать каждую программу, а можно просто прокрутить их список с помощью клавиш со стрелками или просто открыть раскрывающееся меню. Кроме того, эта улучшенная версия поднимает окна не по имени, а по идентификатору окна, что дает гораздо лучшую производительность. Обратите внимание, что способ, которым я просматриваю файлы .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 /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

введите описание изображения здесь

Бонус:

Я действительно нашел Альберта , который является версией Альфреда для Linux, но сам не пробовал. Стоит проверить, хотя. Однако, как уже заметил Джейкоб, он все еще глючит.

Есть приложение под названием Gnome-Do, которое графически похоже на Alfred, однако оно не обладает такой же функциональностью, как этот скрипт.

введите описание изображения здесь

Дайте мне знать, если вам нравится этот сценарий, если что-то нужно исправить, и не забудьте высказать ответ, если вы найдете его полезным

Сергей Колодяжный
источник
Кстати, обратите внимание, как я набираю названия программ - именно так, как указано в dpkg --get-selectons. Запуск libreoffice writer, введя «writer», не сработает, но вы можете сделать символическую ссылку на него в папке ~ / bin, / bin или / usr / bin или использовать псевдоним в .bashrc или .profile.
Сергей Колодяжный
Также обратите внимание, что вам понадобится установить wmctl, он не поставляется по умолчанию, но довольно удобен. Я также использовал это, чтобы сделать это
Сергей Колодяжный
Не могли бы вы заменить аннотацию на изображение, связанное с аннотацией? Мой браузер продолжает «загружать» страницу, поэтому я не могу ее обновить. (и анимация не запускается :))
Джейкоб Влейм
Благодарность! Ссылка отлично работает в Chrome, а не в Firefox.
Джейкоб Влейм
@JacobVlijm Тьфу, так оно и есть. Не уверен, почему Firefox отказывается играть в нее. Это просто imgur ссылка на то, что я изначально загрузил
Сергей Колодяжный
5

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имеет несколько разных типов окон, кроме «настоящих» окон), несколько пидов на приложение ( Chromium,Google-chrome ) могут вызывать исключения, которые я исправил в примерах над. Если у кого-то возникнут проблемы, пожалуйста, укажите это.

2. Дополнительно: установка его в качестве альтернативы «настоящему» Dash для запуска приложений

  1. Скопируйте и сохраните скрипт, как указано выше
  2. Сохраните значок ниже (щелкните правой кнопкой мыши> Safe as) как 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файл на панель запуска:

Якоб Влейм
источник
1
Приятно знать, что есть целая папка .desktop файлов! Мне было интересно, как родные и apt-установленные приложения отображаются по имени вместо команд. Хорошая работа там!
Сергей Колодяжный
@ Серж Спасибо! и вам того же :). Существует также локальный каталог для .desktopфайлов: ~/.local/share/applications. Я думал, что ограничу поиск глобально установленными приложениями.
Джейкоб Влейм
Джейкоб, я не знаю python, но, возможно, это поможет тебе улучшить свой сценарий, превратив его в область единства. Насколько я понимаю, это единственный способ изменить функциональность тире именно так, как хочет ОП
Сергей Колодяжный,
@ Серж Спасибо! это интересный пост, обязательно на него внимательно посмотрю!
Джейкоб Влейм
0

Для модуля запуска (вертикальная панель в левой части экрана) это уже поведение по умолчанию, так как это интерфейс переключения задач.

Для тире (большой бит, который открывается, когда вы щелкаете по логотипу Ubuntu), нет способа изменить поведение таким образом без предположительно существенной модификации самого исходного кода.

Однако некоторые приложения уже могут это делать, поскольку они предназначены для такого поведения. Все приложения, однако, не должны и не должны быть реализованы таким образом.

Однако, как еще одна функция, если вы откроете окно с Super+ Wи начнете вводить имя приложения, будут показаны только окна для этого приложения.

Добей
источник
Я на самом деле нашел версию Linux того, что хотел ОП, см. Мой пост, раздел бонусов. Видимо, у кого-то была идея перенести Альфреда в Linux
Сергей Колодяжный
1
@ Серг, пожалуйста, попробуй Альберта; У Альберта все еще есть «ошибка LibreOffice», «ошибка Chromium» и «ошибка Chrome». Даже «Ошибка файлов» ... Альберт просто всегда открывает новый экземпляр этих приложений. LibreOffice просто не работает вообще. Также в комментариях под вашей ссылкой вы можете найти ряд вопросов.
Джейкоб Влейм
@ Serg Нет, вы нашли обходной путь, добавив дополнительное программное обеспечение, работающее в фоновом режиме, которое обеспечивает совершенно другой пользовательский интерфейс и требует переподготовки для поиска приложений. Вопрос, который задавался, состоял в том, как сделать эту работу в приборной панели Unity. Возможно, вам удастся получить аналогичную функцию вне самого Unity, но единственный способ изменить Unity - это изменить исходный код.
Добей
@ Dobey Ну, это правда; изменение функциональности тире за пределами нашей досягаемости, поэтому. , . делать то, что у нас есть, не так ли? Если нет разработчика, который был бы готов закодировать область единства с такой функциональностью. , ,
Сергей Колодяжный