Inicie la aplicación solo si aún no está abierta

16

Me gustaría imitar el uso de Alfred en Mac OS X, donde si intentas abrir una aplicación después de buscarla, solo abrirá una nueva ventana si el programa aún no se está ejecutando, de lo contrario establecerá el foco en el instancia actualmente en ejecución de esa aplicación. ¿Hay alguna forma de cambiar el comportamiento predeterminado del iniciador para verificar esto antes de abrir una nueva ventana?

Dan Jenson
fuente
También @pidge Hacer esto no sería difícil, pero también afectaría el comportamiento del botón derecho "abrir con". Supongo que es un efecto secundario inaceptable.
Jacob Vlijm
1
Creo que debería poder crear un script que pueda verificar si un proceso específico ya se está ejecutando y decidir si se inicia un nuevo proceso o si se enfoca en la ventana existente. Desafortunadamente, todavía no soy muy bueno en las secuencias de comandos ... Pero @JacobVlijm es conocido como un tipo con secuencias de comandos para cada propósito;) Sin embargo, tendría que reemplazar todos los lanzadores originales por la secuencia de comandos respectiva. No estoy seguro si quieres / puedes hacer eso - Yo no lo haría ...
Byte Commander
2
Si sigue la ruta del script, podría usar este script como punto de partida. Lo escribí para LXDE / Openbox inicialmente, pero también debería funcionar en Unity. Más información sobre el script y su uso aquí .
Glutanimate
1
@ByteCommander exactamente eso era lo que tenía en mente. Incluso podría reemplazar los comandos en los .desktoparchivos. .desktopSin embargo, si reemplaza el comando en el archivo, el botón derecho abrir con opción está roto.
Jacob Vlijm
1
¿Para qué entorno de escritorio?
j0h

Respuestas:

6

Actualización 7 de abril: ¡Se agregó una versión diferente y se encontró a Albert, vea la actualización y el bono a continuación!

Con respecto a la funcionalidad del guión : Usted ha preguntado " ¿Hay alguna forma de cambiar el comportamiento predeterminado del iniciador para verificar esto antes de abrir una nueva ventana ". La respuesta básica es, no, como usuario habitual no tiene forma de agregar ese comportamiento al tablero. Sin embargo, si hubiera un desarrollador de alcance de la unidad que estuviera dispuesto a implementar eso, podría acercarse a ellos o desarrollar uno usted mismo si tiene resolución y está dispuesto a aprender. Mis habilidades de codificación son muy modestas, por lo tanto, uso scripts de shell y el front-end gráfico disponible para los scripts como solución alternativa.

Información relacionada

Publicación original:

He escrito un script que usa el diálogo zenity y wmctrl para lograr lo que pediste. Tenga en cuenta que este es un script gráfico, lo que significa que solo funcionará con Windows, en la GUI, y no funcionará si intenta iniciar algo en tty. Además, por lo que entiendo, Alfred hace exactamente lo mismo. Puede crear un acceso directo de escritorio o un acceso directo de iniciador, como se describe aquí y aquí .

La secuencia de comandos:

#!/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 al margen: en la versión anterior, el script usaba echo $ ?, para probar si las expresiones anteriores salieron con éxito. Según la sugerencia de muru (de la edición), cambié el código a una versión algo más compacta, por lo que le sugiero que eche un vistazo a la versión anterior y la actual.

Además, anteriormente wmctrl -a $MYPROGno funcionaba con las pruebas de google-chrome o chromium-browser; por alguna estúpida razón, algunos programas tienen la propiedad WM_CLASS de la ventana en mayúscula, mientras que el programa que se muestra en dpkg --get-selectionsminúsculas (solo lee man wmctrly ejecuta wmctrl -lx, lo sabrás). Agregar ese -ax debería encargarse de esto. El script muestra la ventana de cromo ya abierta como debería

Otra cosa: wmctlr es algo extraño, ya que a veces necesita un retraso (tenía experiencia con él en otro script), así que tuve que agregar una sleep 1línea. Anteriormente sería un poco intermitente con Firefox, pero ahora funciona de maravilla.

El guión en acción

En la siguiente animación, puede ver que en la primera ejecución del script, hay una instancia de firefox abierta, y el script cambia el foco a esa ventana; en la segunda prueba, abro una nueva instancia de google-chrome, que no se había abierto anteriormente. (Nota al margen: si tiene curiosidad sobre el escritorio, por cierto, eso es openbox con cairo dock)

Por sugerencia en los comentarios, animación incrustada eliminada, solo enlace publicado ¡Informe si está roto por favor! http://i.stack.imgur.com/puuPZ.gif

Actualización, 7 de abril

Mejoré un poco el script para hacer que todos los programas enumerados en el cuadro de entrada desplegable de zenity. Ahora el usuario no tiene que memorizar cada programa, sino que simplemente puede desplazarse por una lista de ellos con las teclas de flecha o simplemente abrir el menú desplegable. Además, esta versión mejorada genera ventanas no por nombre, sino por id de ventana, lo que proporciona un rendimiento mucho mejor. Tenga en cuenta que la forma en que reviso los archivos .desktop es algo redundante, usando el comando de corte dos veces, pero como mi script-fu no es tan bueno hasta ahora, esto es todo lo que puedo hacer. ¡Sugerencias para mejorar son bienvenidas!

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

ingrese la descripción de la imagen aquí

Prima:

De hecho, he encontrado a Albert , que es la versión Linux de Alfred, pero no lo he probado yo mismo. Vale la pena echarle un vistazo. Sin embargo, como ya señaló Jacob, todavía tiene errores.

Hay una aplicación llamada Gnome-Do, que gráficamente se parece a Alfred, sin embargo, no tiene la misma funcionalidad que este script.

ingrese la descripción de la imagen aquí

Avíseme si le gusta este script, si hay algo que necesita ser arreglado, y no olvide votar la respuesta si le resulta útil.

Sergiy Kolodyazhnyy
fuente
Por cierto, observe cómo escribo los nombres de los programas, exactamente como se enumeran en dpkg --get-selectons. El lanzamiento de escritor libreoffice escribiendo "escritor" no funcionará, pero puede hacer un enlace simbólico en su carpeta ~ / bin, / bin o / usr / bin o usar un alias en .bashrc o .profile.
Sergiy Kolodyazhnyy
Tenga en cuenta también que necesitará instalar wmctl, no viene por defecto, pero es bastante útil. También lo he usado para hacer esto
Sergiy Kolodyazhnyy
¿Podría reemplazar la anotación por una imagen que se vincule con la anotación? Mi navegador sigue "cargando" la página, por lo que no puedo actualizarla. (y la anulación no se ejecuta :))
Jacob Vlijm
¡Gracias! El enlace funciona bien en Chrome, no en Firefox.
Jacob Vlijm
@JacobVlijm Ugh, así es. No estoy seguro de por qué Firefox se niega a jugarlo. Es solo un enlace imgur a lo que originalmente cargué
Sergiy Kolodyazhnyy
5

1. Dash the Second

Debajo de un script que se puede usar como una alternativa a Dash, cuando se trata de ejecutar aplicaciones como se describe en su pregunta.

Existe una ventana con la misma funcionalidad que Dash; Si escribe uno o más caracteres de la aplicación, la aplicación aparecerá en la lista. Presione Enterpara iniciar o subir la aplicación, dependiendo de si ya se está ejecutando o no.

Puede llamarlo desde una combinación de teclas de acceso directo, o establecer un icono en el iniciador para usarlo de manera similar a Dash (ver más abajo), o ambos.

ingrese la descripción de la imagen aquí

La secuencia de comandos

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

Cómo utilizar

El script necesita estar wmctrlinstalado:

sudo apt-get install wmctrl

Luego:

  1. Pegue el script anterior en un archivo vacío, guárdelo como dash_alternative.py
  2. Agréguelo a una combinación de teclas de acceso directo: Elija: Configuración del sistema> "Teclado"> "Atajos"> "Atajos personalizados". Haga clic en "+" y agregue el comando:

    python3 /path/to/dash_alternative.py
    

Explicación

Cuando se ejecuta el script, enumera todas las aplicaciones, representadas en /usr/share/applications. Busca en los .dektoparchivos, creando una lista de todos los nombres de las aplicaciones (desde la primera línea "Nombre =") y el comando para ejecutar la aplicación (desde la primera línea "Exec =").

Posteriormente, se crea una lista de Zenity, que presenta todas las aplicaciones ordenadas.

Cada vez que se selecciona una aplicación, el script busca en la lista de procesos en ejecución si la aplicación se está ejecutando. Si es así, se abre la ventana correspondiente. Si no, se abre una nueva instancia.

Notas

  1. Para ejecutar el script en 12.04 (ya que la pregunta original fue etiquetada, 12.04simplemente cambie el shebang #!/usr/bin/env pythony ejecútelo mediante el comando

    python /path/to/dash_alternative.py
    
  2. Hasta donde lo probé, el script funciona bien. Los comandos y sus (no-) nombres de proceso correspondientes (por ejemplo, LibreOffice<> soffice.bin), diferentes tipos de ventanas ( nautilustiene varios tipos de ventanas diferentes, además de ventanas "reales"), múltiples pids por aplicación ( Chromium, Google-chrome) pueden causar excepciones, que arreglé en los ejemplos encima. Si alguien se encuentra con un problema, menciónelo.

2. Adicional: configurarlo como una alternativa al Dash "real" para ejecutar aplicaciones

  1. Copie y guarde el script como se mencionó anteriormente
  2. Guarde el icono a continuación (clic derecho> seguro como) como dash_alternative.png

    ingrese la descripción de la imagen aquí

  3. Copie el siguiente código en un archivo vacío, guárdelo ~/.local/share/applicationscomo dash_thesecond.desktop. Establezca las rutas correctas para /path/to/dash_alternative.py(el script) y /path/to/dash_alternative.png(el icono)

    [Desktop Entry]
    Name=Dash the Second
    Exec=python3 /path/to/dash_alternative.py
    Icon=/path/to/dash_alternative.png
    Type=Application
    Hidden=false
    
  4. Arrastre el .desktoparchivo al lanzador:

Jacob Vlijm
fuente
1
¡Es bueno saber que hay una carpeta completa de archivos .desktop! Me preguntaba cómo se enumeran las aplicaciones nativas e instaladas por apt en lugar de los comandos. Buen trabajo allí!
Sergiy Kolodyazhnyy
@Serg ¡Gracias! Y lo mismo para ti :). También hay un directorio local para .desktoparchivos: ~/.local/share/applications. Pensé que limitaría la búsqueda a aplicaciones instaladas globalmente.
Jacob Vlijm
Jacob, no conozco Python, pero tal vez esto te sea útil para mejorar tu script convirtiéndolo en un ámbito de unidad. Tengo entendido que esta es la única forma de alterar la funcionalidad del tablero exactamente como OP quiere
Sergiy Kolodyazhnyy
@Serg ¡Gracias! esa es una publicación interesante, definitivamente la echaremos un buen vistazo!
Jacob Vlijm
0

Para el iniciador (el panel vertical en el lado izquierdo de la pantalla), este ya es el comportamiento predeterminado, ya que es la interfaz de cambio de tareas.

Para el guión (la parte grande que se abre al hacer clic en el logotipo de Ubuntu), no hay forma de cambiar el comportamiento de esta manera, sin una modificación presumiblemente significativa del código fuente en sí.

Sin embargo, algunas aplicaciones ya pueden hacer esto, ya que están diseñadas para comportarse de esa manera. Sin embargo, todas las aplicaciones no se implementan, y no necesariamente deben implementarse de esa manera.

Sin embargo, como otra característica, si abre la ventana desplegada con Super+ Wy comienza a escribir el nombre de una aplicación, las ventanas para esa aplicación serán las únicas que se muestran.

dobey
fuente
De hecho, he encontrado la versión de Linux de lo que OP quería, vea mi publicación, la sección de bonificación. Al parecer, alguien más tuvo la idea de llevar a Alfred a Linux
Sergiy Kolodyazhnyy
1
@Serg por favor prueba Albert; Albert todavía tiene el "error LibreOffice", el "error Chromium" y el "error Chrome". Incluso el "error de archivos" ... Albert simplemente siempre abre una nueva instancia de esas aplicaciones. LibreOffice simplemente no funciona en absoluto. Además, en los comentarios debajo de su enlace puede encontrar una serie de problemas.
Jacob Vlijm
@Serg No, encontró una solución alternativa al agregar una pieza adicional de software que se ejecuta en segundo plano, lo que proporciona una experiencia de usuario completamente diferente y requiere volver a entrenar cómo encontrar aplicaciones. La pregunta que se hizo fue cómo hacer que esto funcione en el tablero de Unity. Es posible que pueda obtener una característica similar fuera de Unity, pero la única forma de cambiar Unity para hacerlo es cambiar el código fuente.
dobey
@dobey Bueno, eso es cierto; alterar la funcionalidad del tablero está más allá de nuestro alcance, entonces. . . tengo que hacer con lo que tenemos disponible, ¿verdad? A menos que haya un desarrollador que esté dispuesto a codificar un alcance de unidad con dicha funcionalidad. . .
Sergiy Kolodyazhnyy