Iniciar o aplicativo apenas se ainda não estiver aberto

16

Gostaria de imitar o uso do Alfred no Mac OS X, onde, se você tentar abrir um aplicativo depois de procurá-lo, ele abrirá apenas uma nova janela se o programa ainda não estiver em execução, caso contrário, o foco será o instância atualmente em execução desse aplicativo. Existe alguma maneira de alterar o comportamento padrão do iniciador para verificar isso antes de abrir uma nova janela?

Dan Jenson
fonte
Também @pidge Para fazer isso, não seria difícil, mas também afetaria o comportamento do botão direito do mouse em "abrir com". Eu acho que é um efeito colateral inaceitável.
Jacob Vlijm
11
Eu acho que você deve ser capaz de criar um script que possa verificar se um processo específico já está em execução e decidir iniciar um novo processo ou dar foco à janela existente. Infelizmente, ainda não sou muito bom em scripts ... Mas @JacobVlijm é conhecido como um cara com scripts para todos os fins;) Você teria que substituir todos os lançadores originais pelo respectivo script. Não tenho certeza se você quer / pode fazer isso - eu não ...
Byte Commander
2
Se você seguir a rota do script, poderá usá- lo como ponto de partida. Eu escrevi para o LXDE / Openbox inicialmente, mas deve funcionar no Unity também. Mais informações sobre o script e seu uso aqui .
precisa saber é o seguinte
11
@ByteCommander exatamente era o que eu tinha em mente. Você pode até substituir por script os comandos nos .desktoparquivos. Se você substituir o comando no .desktoparquivo, no entanto, o botão direito do mouse em abrir com a opção será quebrado.
Jacob Vlijm
11
Para qual ambiente de área de trabalho?
J0h 06/04

Respostas:

6

Atualização em 7 de abril: Uma versão diferente foi adicionada e encontrou Albert, veja a atualização e o bônus abaixo !!!

Sobre a funcionalidade do traço : Você perguntou " Existe alguma maneira de alterar o comportamento padrão do iniciador para verificar isso antes de abrir uma nova janela ". A resposta básica é: não, como usuário comum, você não tem como adicionar esse comportamento ao traço. No entanto, se houver um desenvolvedor de escopo de unidade que esteja disposto a implementá-lo, você poderá abordá-lo ou desenvolver você mesmo se tiver determinação e vontade de aprender. Minhas habilidades de codificação são muito modestas, portanto, uso scripts de shell e o front-end gráfico disponível para os scripts como solução alternativa.

Informação relacionada

Post original:

Eu escrevi um script que usa o zenity dialog e o wmctrl para alcançar o que você pediu. Observe que este é um script gráfico, o que significa que funcionará apenas com o Windows, na GUI, e não funcionará se você tentar iniciar algo no tty. Além disso, pelo que entendi, Alfred faz exatamente a mesma coisa. Você pode criar um atalho na área de trabalho ou um atalho do iniciador, conforme descrito aqui e aqui .

O script:

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

Notas laterais: na versão anterior, o script usava echo $ ?, para testar se as expressões anteriores foram encerradas com êxito. Conforme a sugestão de muru (da edição), mudei o código para uma versão um pouco mais compacta, então sugiro que você dê uma olhada na versão anterior e na atual.

Além disso, anteriormente wmctrl -a $MYPROGnão funcionava com o teste do google-chrome ou chromium-browser; por algum motivo estúpido, alguns programas têm a propriedade WM_CLASS da janela em maiúscula, enquanto o programa listado por dpkg --get-selectionsé minúsculo (basta ler man wmctrle executarwmctrl -lx , você saberá). Adicionando -ax deve cuidar disso. O script exibe a janela do cromo já aberta, como deveria

Outra coisa - o wmctlr é um pouco estranho, pois às vezes precisa de um atraso (tinha experiência com ele em outro script), então tive que adicionar uma sleep 1linha. Anteriormente, seria meio que ligado e desligado com o Firefox, mas agora funciona muito bem.

O script em ação

Na animação abaixo, você pode ver que, na primeira execução do script, há uma instância do firefox open, e o script muda o foco para essa janela; no segundo teste, abro uma nova instância do google-chrome, que não estava aberta anteriormente. (Observação: se você é cuidadoso com a área de trabalho, a propósito, isso é openbox com cairo dock)

Por sugestão nos comentários, animação incorporada removida, apenas link postado. Relate se está quebrado, por favor! http://i.stack.imgur.com/puuPZ.gif

Atualização, 7 de abril

Aprimorei um pouco o script para criar todos os programas listados na caixa de entrada suspensa do zenity. Agora, o usuário não precisa memorizar cada programa, mas pode simplesmente rolar uma lista deles usando as teclas de seta ou simplesmente abrir o menu suspenso. Além disso, esta versão aprimorada aumenta as janelas não pelo nome, mas pelo ID da janela, o que proporciona um desempenho muito melhor. Observe que a maneira como leio os arquivos .desktop é meio redundante, usando o comando cut duas vezes, mas como meu script-fu não é tão bom até agora, isso é tudo o que posso fazer. Sugestões de melhorias são bem-vindas!

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

insira a descrição da imagem aqui

Bônus:

Na verdade, eu encontrei o Albert , que é a versão Linux do Alfred, mas ainda não tentei. Vale a pena conferir embora. No entanto, como Jacob já observou, ainda é um buggy.

Existe um aplicativo chamado Gnome-Do, que se parece graficamente com Alfred, mas não tem a mesma funcionalidade que este script.

insira a descrição da imagem aqui

Deixe-me saber se você gosta deste script, se houver algo que precise ser corrigido e não se esqueça de votar na resposta se achar útil

Sergiy Kolodyazhnyy
fonte
A propósito, observe como digito os nomes dos programas - exatamente como listado em dpkg --get-selectons. Iniciar o escritor do libreoffice digitando "writer" não funcionará, mas você poderá fazer um link simbólico para ele na sua pasta ~ / bin, / bin ou / usr / bin ou usar o alias em .bashrc ou .profile.
Sergiy Kolodyazhnyy
Observe também que você precisará do wmctl instalado, ele não vem por padrão, mas é bastante útil. Eu também usei para fazer isso
Sergiy Kolodyazhnyy
Você poderia substituir a animação por uma imagem, vinculando à animação? Meu navegador continua "carregando" a página, então não posso atualizá-la. (eo annimation não corre :))
Jacob Vlijm
Obrigado! O link funciona bem no Chrome, não no Firefox.
Jacob Vlijm
@JacobVlijm Ugh, é o que faz. Não sei por que o firefox se recusa a reproduzi-lo. É apenas um link imgur para o que eu
carreguei
5

1. Dê o segundo

Abaixo de um script que pode ser usado como uma alternativa ao Dash, quando se trata de executar aplicativos, conforme descrito em sua pergunta.

Existe uma janela com a mesma funcionalidade que o Dash; se digitar um ou mais caracteres do aplicativo, o aplicativo aparecerá na lista. Pressione Enterpara iniciar ou aumentar o aplicativo, dependendo se ele já estiver em execução ou não.

Você pode chamá-lo a partir de uma combinação de teclas de atalho ou definir um ícone no iniciador para usá-lo de maneira semelhante ao Dash (veja mais abaixo), ou ambos.

insira a descrição da imagem aqui

O script

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

Como usar

O script precisa estar wmctrlinstalado:

sudo apt-get install wmctrl

Então:

  1. Cole o script acima em um arquivo vazio, salve-o como dash_alternative.py
  2. Adicione-o a uma combinação de teclas de atalho: Escolha: Configurações do sistema> "Teclado"> "Atalhos"> "Atalhos personalizados". Clique no "+" e adicione o comando:

    python3 /path/to/dash_alternative.py
    

Explicação

Quando o script é executado, ele lista todos os aplicativos, representados em /usr/share/applications. Ele pesquisa os .dektoparquivos, criando uma lista de todos os nomes de aplicativos (da primeira linha "Name =") e o comando para executar o aplicativo (da primeira linha "Exec =").

Posteriormente, é criada uma lista do Zenity, apresentando todos os aplicativos de maneira ordenada.

Sempre que um aplicativo é selecionado, o script procura na lista de processos em execução se o aplicativo estiver em execução. Nesse caso, a janela correspondente é elevada. Caso contrário, uma nova instância é aberta.

Notas

  1. Para executar o script em 12.04 (desde que a pergunta original foi marcada, 12.04altere o shebang para #!/usr/bin/env pythone execute-o pelo comando

    python /path/to/dash_alternative.py
    
  2. Tanto quanto eu testei, o script funciona bem. Comandos e seus nomes de processos (não-) correspondentes (por exemplo, LibreOffice<> soffice.bin), diferentes tipos de janelas ( nautiluscom vários tipos de janelas diferentes, além de janelas "reais"), vários pids por aplicativo ( Chromium, Google-chrome) podem causar exceções que eu consertei nos exemplos acima. Se alguém encontrar um problema, mencione-o.

2. Adicional: configurando-o como uma alternativa ao Dash "real" para aplicativos em execução

  1. Copie e proteja o script como mencionado acima
  2. Salve o ícone abaixo (clique com o botão direito do mouse> seguro como) como dash_alternative.png

    insira a descrição da imagem aqui

  3. Copie o código abaixo em um arquivo vazio, salve-o ~/.local/share/applicationscomo dash_thesecond.desktop. Defina os caminhos corretos para /path/to/dash_alternative.py(o script) e /path/to/dash_alternative.png(o ícone)

    [Desktop Entry]
    Name=Dash the Second
    Exec=python3 /path/to/dash_alternative.py
    Icon=/path/to/dash_alternative.png
    Type=Application
    Hidden=false
    
  4. Arraste o .desktoparquivo para o iniciador:

Jacob Vlijm
fonte
11
É bom saber que há uma pasta inteira de arquivos .desktop! Eu queria saber como os aplicativos nativos e instalados pelo apt são listados por nome, em vez de comandos. Bom trabalho lá!
Sergiy Kolodyazhnyy
@ Thanks Obrigado! e o mesmo para você :). Há também um diretório local de .desktoparquivos: ~/.local/share/applications. Pensei em limitar a pesquisa a aplicativos instalados globalmente.
Jacob Vlijm
Jacob, eu não conheço python, mas talvez isso seja útil para você melhorar seu script, transformando-o em um escopo de unidade. Meu entendimento é que essa é a única maneira de alterar a funcionalidade do traço exatamente como o OP deseja
Sergiy Kolodyazhnyy
@ Thanks Obrigado! que é um post interessante, definitivamente dará uma boa olhada nele!
Jacob Vlijm
0

Para o iniciador (o painel vertical no lado esquerdo da tela), esse já é o comportamento padrão, pois é a interface de alternância de tarefas.

Para o traço (o grande pedaço que se abre quando você clica no logotipo do Ubuntu), não há como mudar o comportamento dessa maneira, sem uma modificação presumivelmente significativa no próprio código-fonte.

No entanto, alguns aplicativos já podem fazer isso, pois foram projetados para se comportar dessa maneira. Todos os aplicativos não são, e não devem necessariamente ser, implementados dessa maneira.

Como outro recurso, porém, se você abrir a janela, espalhe com Super + We começar a digitar o nome de um aplicativo, as janelas desse aplicativo serão as únicas exibidas.

dobey
fonte
Na verdade, eu encontrei a versão Linux do que o OP queria, veja meu post, a seção de bônus. Aparentemente, alguém teve uma ideia para trazer Alfred para Linux
Sergiy Kolodyazhnyy
11
@ Berg, por favor, tente Albert; Albert ainda tem o "bug do LibreOffice", o "bug do Chromium" e o "bug do Chrome". Até o "erro de arquivos" ... Albert simplesmente sempre abre uma nova instância desses aplicativos. O LibreOffice simplesmente não funciona. Além disso, nos comentários abaixo do seu link, você pode encontrar vários problemas.
Jacob Vlijm
@Serg Não, você encontrou uma solução alternativa, adicionando um software adicional em execução em segundo plano, que fornece uma experiência ao usuário totalmente diferente e requer reciclagem de como encontrar aplicativos. O que a pergunta foi feita foi como ter esse trabalho no traço do Unity. Você pode conseguir um recurso semelhante fora do próprio Unity, mas a única maneira de alterar o Unity para isso é alterar o código-fonte.
Dobey
@ Dobey Bem, isso é verdade; alterar a funcionalidade do traço está além do nosso alcance, então. . .tem que fazer com o que temos disponível, certo? A menos que haja um desenvolvedor disposto a codificar um escopo de unidade com essa funcionalidade. . .
Sergiy Kolodyazhnyy