¿Cómo puedo ejecutar un programa al inicio, minimizado?

19

Solo quiero que se ejecute Telegram y lo he agregado a las aplicaciones de inicio. El punto es que necesito que se minimice. Cualquier comando?

Hossein Soltanloo
fuente
¿Cuál es el comando para iniciar Telegram y cuál es el nombre de la ventana justo después de que se inició la aplicación?
Jacob Vlijm
El comando que utilicé es solo la ruta de la aplicación y el nombre de la ventana es Telegram Desktop
Hossein Soltanloo
Hola Hossien, en caso de que prefieras usar el pid en lugar del título de la ventana, edité mi respuesta.
Jacob Vlijm
@JacobVlijm ¡Gracias! ¡Es muy eficiente y útil! Sin embargo, el primer método funciona a la perfección en casos de nombre de ventana variable. ¡Buen trabajo!
Hossein Soltanloo
1
@SumeetDeshmukh eres una persona increíblemente amable y generosa. ¡De Verdad!
Jacob Vlijm

Respuestas:

29

Iniciar una aplicación minimizada

Iniciar una aplicación de forma minimizada requiere dos comandos:

  • comenzando la aplicación
  • minimizar su ventana

Por lo tanto, el comando o script debe ser "inteligente"; el segundo comando debería esperar a que aparezca la ventana de la aplicación.

Solución general para iniciar una aplicación minimizada

El siguiente script hace eso y puede usarse como una solución general para iniciar una aplicación de manera minimizada. Simplemente ejecútelo en la sintaxis:

<script> <command_to_run_the_application> <window_name>

La secuencia de comandos

#!/usr/bin/env python3
import subprocess
import sys
import time

subprocess.Popen(["/bin/bash", "-c", sys.argv[1]])
windowname = sys.argv[2]

def read_wlist(w_name):
    try:
        l = subprocess.check_output(["wmctrl", "-l"]).decode("utf-8").splitlines()
        return [w.split()[0] for w in l if w_name in w][0]
    except (IndexError, subprocess.CalledProcessError):
        return None

t = 0
while t < 30:
    window = read_wlist(windowname)
    time.sleep(0.1)
    if window != None:
        subprocess.Popen(["xdotool", "windowminimize", window])
        break
    time.sleep(1)
    t += 1

Cómo utilizar

El script necesita ambos wmctrly xdotool:

sudo apt-get install wmctrl xdotool

Luego:

  1. Copie el script en un archivo vacío, guárdelo como startup_minimizd.py
  2. Prueba: ejecute el script con (por ejemplo) geditel comando:

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. Si todo funciona bien, agregue el comando (para su aplicación) a Startup Applications

Explicación

  • El script inicia la aplicación, ejecutando el comando que le dio como primer argumento
  • Luego, el script verifica la lista de ventanas (con la ayuda de wmctrl) para las ventanas, nombradas después de su segundo argumento.
  • Si aparece la ventana, se minimiza inmediatamente con la ayuda de xdotool Para evitar un bucle sin fin si la ventana puede no aparecer por alguna razón, el script practica un límite de tiempo de 30 segundos para que aparezca la ventana.

Nota

No es necesario mencionar que puede usar el script para múltiples aplicaciones a la vez, ya que lo ejecuta con argumentos fuera del script.


EDITAR

reconociendo la ventana por su pid

Si el título de la ventana es inseguro o variable, o existe un riesgo de conflictos de nombres en el nombre de la ventana, utilizar pides un método más confiable de usar.

El siguiente script se basa en el uso del pid de la aplicación, como en la salida de ambos wmctrl -lpy ps -ef.

La configuración es prácticamente la misma, pero el título de la ventana no es necesario en esta versión, por lo que el comando para ejecutarlo es:

python3 /path/to/startup_minimizd.py <command_to_run_application>

Al igual que el primer guión, necesita ambos wmctrlyxdotool

La secuencia de comandos

#!/usr/bin/env python3
import subprocess
import sys
import time

command = sys.argv[1]
command_check = command.split("/")[-1]

subprocess.Popen(["/bin/bash", "-c", command])

t = 1
while t < 30:
    try:
        w_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8").splitlines()]
        proc = subprocess.check_output(["pgrep", "-f", command_check]).decode("utf-8").strip().split()
        match = sum([[l[0] for l in w_list if p in l] for p in proc], [])
        subprocess.Popen(["xdotool", "windowminimize", match[0]])
        break
    except (IndexError, subprocess.CalledProcessError):
        pass
    t += 1
    time.sleep(1)

Nota sobre el segundo guión

Aunque, en general, la segunda versión debería ser más confiable, en los casos en que la aplicación se inicia mediante un script de contenedor, el pid del comando será diferente de la aplicación que finalmente se llama.

En tales casos, recomiendo usar el primer script.



EDIT2 una versión específica del script para Steam

Como se solicitó en un comentario, debajo de una versión, hecha específicamente para iniciar STEAM minimizada.

¿Por qué una versión específica para Steam?

Resulta que se Steamcomporta bastante diferente de una aplicación "normal":

  • Resulta Steamque no ejecuta un pid, ¡pero no menos que (en mi prueba) ocho!
  • Steamse ejecuta al inicio con al menos dos ventanas (una ventana de tipo splash), pero a veces aparece una ventana de mensaje adicional.
  • Windows de Steam tiene pid 0, lo cual es un problema en el script tal como estaba.
  • Una vez creada la ventana principal, la ventana se eleva por segunda vez después de un segundo o menos, por lo que una sola minimización no lo hará.

Este comportamiento excepcional de Steampide una versión especial del script, que se agrega a continuación. El script se inicia Steamy, durante 12 segundos, vigila todas las ventanas nuevas de la correspondiente WM_CLASS, verificando si están minimizadas. Si no, el script se asegura de que lo sean.

Al igual que el script original, este necesita wmctrly xdotooldebe instalarse.

La secuencia de comandos

#!/usr/bin/env python3
import subprocess
import time

command = "steam"
subprocess.Popen(["/bin/bash", "-c", command])

def get(cmd):
    return subprocess.check_output(cmd).decode("utf-8").strip()

t = 0

while t < 12:
    try:
        w_list = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
        for w in w_list:
            data = get(["xprop", "-id", w])
            if all(["Steam" in data, not "_NET_WM_STATE_HIDDEN" in data]):
                subprocess.Popen(["xdotool", "windowminimize", w])
    except (IndexError, subprocess.CalledProcessError):
        pass

    t += 1
    time.sleep(1)

Para usarlo

  • Simplemente cópielo en un archivo vacío, guárdelo como runsteam_minimized.py
  • Ejecútelo con el comando:

    python3 /path/to/runsteam_minimized.py
    
Jacob Vlijm
fuente
wow, genial! Sin embargo, no atraparía except:solo para devolver Ninguno. Probablemente sea mejor dejar que falle para que veas lo que falló; de lo contrario, puede romperse por cualquier tipo de causas diferentes y pasará sin publicidad.
fedorqui
1
@fedorqui Bueno, es probable que ocurran dos excepciones: subprocess.CalledProcesError (como resultado de un error wmctrl) y IndexError(excepción normal) se editará en un minuto :). Gracias por mencionar
Jacob Vlijm
@HosseinSoltanloo ¿Cuál es exactamente el comando con el que ejecuta el script?
Jacob Vlijm
@JacobVlijm El script funciona bien pero hay otro problema que podría solucionarlo. Cada vez que tengo mensajes no leídos y abro la aplicación, el nombre de la ventana cambia a algo así como "Telegram (2)", ya que hay dos mensajes no leídos y de esta manera el script no funcionará debido a un cambio en el nombre.
Hossein Soltanloo
2
@JDHolland Estoy seguro de que se puede solucionar. Lo
veré
3

Es bueno tener las secuencias de comandos proporcionadas por user72216 y Sergey como soluciones generales al problema, pero a veces la aplicación que desea iniciar minimizada ya tiene un interruptor que hará lo que desee.

Aquí hay algunos ejemplos con las cadenas de comando del programa de inicio correspondientes:

  • Telegram (desde la versión 0.7.10) tiene la -startintrayopción:<path-to-Telegram>/Telegram -startintray
  • Steam tiene la -silentopción:/usr/bin/steam %U -silent
  • La transmisión tiene la --minimizedopción:/usr/bin/transmission-gtk --minimized

En Unity, estas aplicaciones comienzan minimizadas como íconos en la barra de menú superior en lugar de como íconos en el iniciador, aunque el ícono de inicio normal aún aparecerá una vez que comience a usar la aplicación. Otras aplicaciones pueden comportarse de manera diferente.

Francis Chin
fuente
1

Tomé los guiones de Jacob y los modifiqué un poco para que fueran más universales.

#!/usr/bin/python

import os
import subprocess
import sys
import time
import signal

WAIT_TIME = 10


def check_exist(name):
    return subprocess.Popen("which "+name,
                            shell=True,
                            stdout=subprocess.PIPE
                            ).stdout.read().rstrip("-n")


def killpid(pidlist):
    for pid in pidlist:
        args = ["xdotool",
                "search",
                "--any",
                "--pid",
                pid,
                "--name",
                "notarealprogramname",
                "windowunmap",
                "--sync",
                "%@"]
        subprocess.Popen(args)


def killname(name):
    args = ["xdotool",
            "search",
            "--any",
            "--name",
            "--class",
            "--classname",
            name,
            "windowunmap",
            "--sync",
            "%@"]
    subprocess.Popen(args)


sys.argv.pop(0)

if check_exist(sys.argv[0]) == "":
    sys.exit(1)
if check_exist("xdotool") == "":
    sys.stderr.write("xdotool is not installed\n")
    sys.exit(1)
if check_exist("wmctrl") == "":
    sys.stderr.write("wmctrl is not installed\n")
    sys.exit(1)

try:
    prog = subprocess.Popen(sys.argv, preexec_fn=os.setsid)
except OSError, e:
    sys.exit(1)

time.sleep(WAIT_TIME)
idlist = subprocess.Popen("pgrep -g " + str(prog.pid),
                          shell=True,
                          stdout=subprocess.PIPE
                          ).stdout.read().splitlines()

ps1 = os.fork()
if ps1 > 0:
    ps2 = os.fork()

if ps1 == 0:  # Child 1
    os.setpgid(os.getpid(), os.getpid())
    killpid(idlist)
    sys.exit(0)
elif ps2 == 0:  # Child 2
    killname(os.path.basename(sys.argv[0]))
    sys.exit(0)
elif ps1 > 0 and ps2 > 0:  # Parent
    time.sleep(WAIT_TIME)
    os.killpg(os.getpgid(int(ps1)), signal.SIGTERM)
    os.kill(ps2, signal.SIGTERM)
    os.waitpid(ps1, 0)
    os.waitpid(ps2, 0)
    sys.exit(0)
else:
    exit(1)

Las principales diferencias son:

  • El programa establece la ID de grupo (GID) para el proceso. Por lo tanto, todos los procesos secundarios y sus ventanas se pueden encontrar fácilmente
  • Se usa la opción xdotool --sync en lugar de un ciclo while
  • El script permite pasar argumentos al programa

WAIT_TIME debe establecerse lo suficientemente grande como para permitir que el programa bifurque sus procesos secundarios. En mi computadora es suficiente para grandes programas como steam. Aumentarlo, si es necesario.

Adición

xdotoolLa opción windowunmappuede funcionar con algunas aplicaciones y programas de bandeja (bandeja de Linux mint, por ejemplo), así que aquí hay una versión alternativa del script para esas excepciones.

#!/usr/bin/python

import os
import subprocess
import sys
import time
import signal

WAIT_TIME = 10


def check_exist(name):
    return subprocess.Popen("which "+name,
                            shell=True,
                            stdout=subprocess.PIPE
                            ).stdout.read().rstrip("-n")


def killpid(pidlist):
    for pid in pidlist:
        args = ["xdotool",
                "search",
                "--sync",
                "--pid",
                pid]
        for i in subprocess.Popen(args,
                                  stdout=subprocess.PIPE).\
                stdout.read().splitlines():
            if i != "":
                subprocess.Popen("wmctrl -i -c " +
                                 hex(int(i)), shell=True)


def killname(name):
    args = ["xdotool",
            "search",
            "--sync",
            "--any",
            "--name",
            "--class",
            "--classname",
            name]
    for i in subprocess.Popen(args,
                              preexec_fn=os.setsid,
                              stdout=subprocess.PIPE)\
            .stdout.read().splitlines():
        if i != "":
            subprocess.Popen("wmctrl -i -c " + hex(int(i)),
                             shell=True)


sys.argv.pop(0)

if check_exist(sys.argv[0]) == "":
    sys.exit(1)
if check_exist("xdotool") == "":
    sys.stderr.write("xdotool is not installed\n")
    sys.exit(1)
if check_exist("wmctrl") == "":
    sys.stderr.write("wmctrl is not installed\n")
    sys.exit(1)


try:
    prog = subprocess.Popen(sys.argv, preexec_fn=os.setsid)
except OSError, e:
    sys.exit(1)

time.sleep(WAIT_TIME)
idlist = subprocess.Popen("pgrep -g " + str(prog.pid),
                          shell=True,
                          stdout=subprocess.PIPE
                          ).stdout.read().splitlines()

ps1 = os.fork()
if ps1 > 0:
    ps2 = os.fork()

if ps1 == 0:  # Child 1
    os.setpgid(os.getpid(), os.getpid())
    killpid(idlist)
    sys.exit(0)
elif ps2 == 0:  # Child 2
    killname(os.path.basename(sys.argv[0]))
    sys.exit(0)
elif ps1 > 0 and ps2 > 0:  # Parent
    time.sleep(WAIT_TIME)
    os.killpg(os.getpgid(int(ps1)), signal.SIGTERM)
    os.kill(ps2, signal.SIGTERM)
    os.waitpid(ps1, 0)
    os.waitpid(ps2, 0)
    sys.exit(0)
else:
    exit(1)
Sergey
fuente
Intenté tu primer guión. No funcionó o no se minimizó lo suficientemente rápido. Lo guardé como startminimized. Entonces corrí startminimized gnome-calendar. Calendario abierto y seguir funcionando?
Khurshid Alam
1
Puedes intentar aumentar la variable WAIT_TIME. Uso 40 segundos de retraso para computadoras débiles. También puede probar el segundo script, ya que utiliza un comando diferente para minimizar la aplicación.
Sergey
1

Si el programa se está cerrando en la bandeja, en realidad es posible que desee cerrar la ventana del programa al inicio en lugar de minimizarlo. Un ejemplo de dicho programa es Viber. En este caso, se podría usar el siguiente script start_closed.sh:

#!/bin/bash

# Check that there is only one input argument
if [[ $# -gt 1 ]]; then
echo "Usage: $0 <program-to-start>"
exit 1
fi

$1 &                               # Start program passed in first argument
pid=$!                             # Get PID of last started program
xdotool search --sync --pid $pid | # Wait for window with PID to appear...
xargs wmctrl -i -c                 # ...and close it

Uso: <path-to-script> <program-to-start>

Mykola Novik
fuente
1
Es posible que desee tener en cuenta que xdotoolno funcionará correctamente en instalaciones con Wayland.
Videonauth
0

Estaba navegando y me encontré con esta pregunta, así que me preguntaba cuál es su sistema operativo. En cuanto a mí, estoy usando UBUNTU BUDGIE 18.04 LTS, por lo que en este sistema operativo es muy simple.

Simplemente ve al menú

Desde el menú, vaya a Configuración de escritorio Budgie

y

Desde la configuración del escritorio, vaya a Inicio automático

Te dará 2 opciones, desde "+" agregar

1. Agregar aplicación

2. Agregar comando

Al seleccionar Agregar aplicación, se enumerarán todas las aplicaciones, seleccione la aplicación que desee y se iniciará cuando inicie su computadora y también se minimizará.

Muntaha Liaqat
fuente
0

Necesitaba los programas cerrados a la bandeja, no minimizados, y he probado todos los scripts publicados aquí, los que funcionaron, funcionaron solo para algunos programas y no para otros. Así que he codificado uno que funciona mucho mejor (casi no ves aparecer la ventana, solo el icono de la bandeja, parece nativo) y funciona para todos los programas que he probado. Está basado en el de Jacob. Con este script, es posible que deba agregar un argumento según el programa (consulte a continuación), pero siempre funcionó para mí con muchos programas, también debería funcionar con steam.

Uso:

  1. sudo apt-get install wmctrl xdotool
  2. Guarde el script para startup_closed.pydarle permisos de ejecución y luego ejecutepython3 ./startup_closed.py -c <command to open program>
  3. Si el icono de la bandeja del programa no se muestra o la ventana no se muestra, entonces debe agregar uno de estos argumentos: -splasho -hidepor prueba y error. Por ejemplo: python3 ./startup_closed.py -hide -c teamvieweropython3 ./startup_closed.py -splash -c slack
  4. Hay más argumentos pero probablemente no los necesites. También hay detalles completos de cuándo y por qué se necesitan los argumentos en la ayuda:./startup_closed.py --help

Guión:

#!/usr/bin/env python3
import subprocess
import sys
import time
import argparse
import random

parser = argparse.ArgumentParser(description='This script executes a command you specify and closes or hides the window/s that opens from it, leaving only the tray icon. Useful to "open closed to tray" a program. If the program does not have a tray icon then it just gets closed. There is no magic solution to achieve this that works for all the programs, so you may need to tweek a couple of arguments to make it work for your program, a couple of trial and error may be required with the arguments -splash and -hide, you probably will not need the others.')

parser.add_argument("-c", type=str, help="The command to open your program. This parameter is required.", required=True)
parser.add_argument("-splash", help="Does not close the first screen detected. Closes the second window detected. Use in programs that opens an independent splash screen. Otherwise the splash screen gets closed and the program cannot start.", action='store_true', default=False)
parser.add_argument("-hide", help="Hides instead of closing, for you is the same but some programs needs this for the tray icon to appear.", action='store_true', default=False)
parser.add_argument("-skip", type=int, default=0, help='Skips the ammount of windows specified. For example if you set -skip 2 then the first 2 windows that appear from the program will not be affected, use it in programs that opens multiple screens and not all must be closed. The -splash argument just increments by 1 this argument.', required=False)
parser.add_argument("-repeat", type=int, default=1, help='The amount of times the window will be closed or hidden. Default = 1. Use it for programs that opens multiple windows to be closed or hidden.', required=False)
parser.add_argument("-delay", type=float, default=10, help="Delay in seconds to wait before running the application, useful at boot to not choke the computer. Default = 10", required=False)
parser.add_argument("-speed", type=float, default=0.02, help="Delay in seconds to wait between closing attempts, multiple frequent attempts are required because the application may be still loading Default = 0.02", required=False)

args = parser.parse_args()

if args.delay > 0:
    finalWaitTime = random.randint(args.delay, args.delay * 2);
    print(str(args.delay) + " seconds of delay configured, will wait for: " + str(finalWaitTime))
    time.sleep(finalWaitTime)
    print("waiting finished, running the application command...")

command_check = args.c.split("/")[-1]
subprocess.Popen(["/bin/bash", "-c", args.c])

hasIndependentSplashScreen = args.splash
onlyHide = args.hide
skip = args.skip
repeatAmmount = args.repeat
speed = args.speed

actionsPerformed = 0
lastWindowId = 0

if hasIndependentSplashScreen:
    skip += 1

while True:
    try:
        w_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8").splitlines()]
        proc = subprocess.check_output(["pgrep", "-f", command_check]).decode("utf-8").strip().split()
        match = sum([[l[0] for l in w_list if p in l] for p in proc], [])
        if len(match) > 0:
            windowId = match[0]
            if windowId != lastWindowId:
                if skip > 0:
                    skip -= 1
                    print("skipped window: " + windowId)
                    lastWindowId = windowId
                else:
                    print("new window detected: " + windowId)
                    if onlyHide:
                        subprocess.Popen(["xdotool", "windowunmap", windowId])
                        print("window was hidden: " + windowId)
                    else:
                        subprocess.Popen(["xdotool", "key", windowId, "alt+F4"])
                        print("window was closed: " + windowId)

                    actionsPerformed += 1
                    lastWindowId = windowId

            if actionsPerformed == repeatAmmount:
                break

    except (IndexError, subprocess.CalledProcessError):
        break

    time.sleep(speed)

print("finished")
fermmm
fuente
0

Llegué con una solución bastante elegante que se basa exclusivamente en xdotool, y es bastante útil para aplicaciones que no tienen un argumento de "inicio minimizado" , como Telegram.

El único inconveniente es que la solución debe diseñarse manualmente para cada aplicación, pero suponiendo que no sea un problema (por ejemplo: si desea iniciar automáticamente una determinada aplicación sin permitir que contamine su pantalla después de iniciar sesión) , esto es mucho más simple y directo .

Ejemplos reales

## Starts Telegram and immediately closes it
xdotool search --sync --onlyvisible --name '^Telegram$' windowclose &
telegram-desktop &
## Starts WhatsApp and immediately closes it
xdotool search --sync --onlyvisible --name '(\([0-9]*\) ){0,1}(WhatsApp$|WhatsApp Web$)' windowclose &
whatsapp-nativefier &

La solución

A primera vista, puede pensar que es mejor usar el proceso 'PID o clase para comparar, sin embargo, eso es realmente contraproducente ya que con frecuencia obtendrá múltiples resultados para el mismo PID. Los ejemplos son una ventana 0x0 que realmente está esperando una notificación, un icono de bandeja del sistema o cualquier otra ventana "oculta".

La solución es crear un comando xdotool que siempre devuelva solo una ventana única . En mis dos ejemplos que se hicieron usando --name, sin embargo, puede combinar múltiples selectores con --all (por ejemplo: hacer coincidir un nombre de clase dado + un nombre de clase + una expresión regular de nombre) . Por lo general, una buena --nameexpresión regular hace el truco.

Después de crear sus searchcondiciones, simplemente genere una instancia de xdotool (separada del caparazón) con el --syncparámetro y sus condiciones, seguido de windowclose. Ejecute su aplicación después:

xdotool search --sync [... myapp-match-conditions] windowclose &
my-app

Consulte xdotool search --helptodas las posibilidades de combinaciones que puede organizar para poder apuntar a la ventana exacta que desea. A veces se vuelve complicado y tiene que combinar varias condiciones, pero una vez que haya terminado, rara vez fallará (a menos que una actualización cambie la aplicación y rompa su implementación, por supuesto).

emi
fuente