Como posso executar um programa na inicialização, minimizado?


19

Eu só quero que o Telegram seja executado e o adicionei aos aplicativos de inicialização. O ponto é que eu preciso que seja minimizado. Algum comando?


Qual é o comando para iniciar o Telegram e qual é o nome da janela logo após o início do aplicativo?
Jacob Vlijm

O comando que eu usei é apenas o caminho do aplicativo e o nome da janela é Telegram desktop
Hossein Soltanloo

Oi Hossien, apenas no caso de você preferir usar o pid em vez do título da janela, editei minha resposta.
Jacob Vlijm

@JacobVlijm Thanks! É muito eficiente e útil! No entanto, o primeiro método funciona perfeitamente em casos de nome de janela variável. Bom trabalho!
Hossein Soltanloo

11
@SumeetDeshmukh você é uma pessoa incrivelmente agradável e generosa. Realmente!
Jacob Vlijm

Respostas:


29

Iniciando um aplicativo minimizado

A inicialização de um aplicativo de maneira minimizada requer dois comandos:

  • iniciando o aplicativo
  • minimizar sua janela

Portanto, o comando ou script precisa ser "inteligente"; o segundo comando deve esperar que a janela do aplicativo seja exibida.

Solução geral para iniciar um aplicativo minimizado

O script abaixo faz isso e pode ser usado como uma solução geral para inicializar um aplicativo de maneira minimizada. Basta executá-lo na sintaxe:

<script> <command_to_run_the_application> <window_name>

O script

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

Como usar

O script precisa de ambos wmctrle xdotool:

sudo apt-get install wmctrl xdotool

Então:

  1. Copie o script em um arquivo vazio, salve-o como startup_minimizd.py
  2. Teste - execute o script com (por exemplo) gedito comando:

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. Se tudo funcionar bem, adicione o comando (para seu aplicativo) a Startup Applications

Explicação

  • O script inicia o aplicativo, executando o comando que você deu como primeiro argumento
  • Em seguida, o script verifica a lista de janelas (com a ajuda de wmctrl) para janelas, com o nome do seu segundo argumento.
  • Se a janela aparecer, ela será imediatamente minimizada com a ajuda de xdotool Para evitar um loop sem fim, se a janela não aparecer por algum motivo, o script pratica um limite de tempo de 30 segundos para a janela aparecer.

Nota

Não é necessário mencionar que você pode usar o script para vários aplicativos ao mesmo tempo, desde que você o execute com argumentos fora do script.


EDITAR

reconhecendo a janela pelo seu pid

Se o título da janela for incerto ou variável, ou se houver um risco de conflito de nome no nome da janela, usar o pidé um método mais confiável de usar.

O script abaixo é baseado no uso do pid do aplicativo, como na saída de ambos wmctrl -lpe ps -ef.

A configuração é praticamente a mesma, mas o título da janela não é necessário nesta versão; portanto, o comando para executá-la é:

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

Assim como o primeiro script, ele precisa de ambos wmctrlexdotool

O script

#!/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 o segundo script

Embora, em geral, a segunda versão deva ser mais confiável, nos casos em que o aplicativo é iniciado por um script de wrapper, o pid do comando será diferente do aplicativo finalmente chamado.

Nesses casos, eu recomendo usar o primeiro script.



EDIT2 uma versão específica do script para Steam

Conforme solicitado em um comentário, abaixo de uma versão, feita especificamente para iniciar o STEAM minimizado.

Por que uma versão específica para o Steam?

Acontece que se Steamcomporta bem diferente de um aplicativo "normal":

  • Acontece Steamque não executa um pid, mas não menos que (no meu teste) oito!
  • Steamé executado na inicialização com pelo menos duas janelas (uma janela tipo splash), mas às vezes uma janela de mensagem adicional aparece.
  • O Windows do Steam possui pid 0, o que é um problema no script como era.
  • Depois que a janela principal é criada, a janela é levantada uma segunda vez depois de mais ou menos um segundo, para que uma única minimização não funcione.

Esse comportamento excepcional de Steamsolicita uma versão especial do script, que é adicionada abaixo. O script é iniciado Steame, durante 12 segundos, fica de olho em todas as novas janelas correspondentes WM_CLASS, verificando se elas são minimizadas. Caso contrário, o script garante que eles serão.

Como o script original, este precisa wmctrle xdotooldeve ser instalado.

O script

#!/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 usá-lo

  • Simplesmente copie-o para um arquivo vazio, salve-o como runsteam_minimized.py
  • Execute-o pelo comando:

    python3 /path/to/runsteam_minimized.py
    

uau, ótimo! Eu não pegaria except:apenas para retornar Nenhum. Provavelmente é melhor deixá-lo falhar para que você veja o que falhou; caso contrário, poderá ocorrer por qualquer tipo de causa diferente e passar sem ser anunciado.
Fedorqui 19/08/2015

11
@fedorqui Bom, duas exceções provavelmente podem ocorrer: subprocess.CalledProcesError (como resultado de um buggy wmctrl) e IndexError(exceção normal) serão editadas em um minuto :). Obrigado por mencionar
Jacob Vlijm

@HosseinSoltanloo Qual é exatamente o comando com o qual você executa o script?
Jacob Vlijm

@JacobVlijm O script funciona bem, mas há outro problema que você pode corrigi-lo. Sempre que tenho mensagens não lidas e abro o aplicativo, o nome da janela muda para algo como "Telegram (2)", pois há duas mensagens não lidas e, dessa forma, o script não funciona, pois as alterações no nome.
Hossein Soltanloo

2
@JDHolland Tenho certeza de que pode ser corrigido. Vou olhar para ele em algum lugar nos próximos dias :)
Jacob Vlijm

3

É bom ter os scripts fornecidos por user72216 e Sergey como soluções gerais para o problema, mas às vezes o aplicativo que você deseja inicializar minimizado já possui uma opção que fará o que você deseja.

Aqui estão alguns exemplos com as cadeias de comando correspondentes do programa de inicialização:

  • O Telegram (desde a versão 0.7.10) tem a -startintrayopção:<path-to-Telegram>/Telegram -startintray
  • O Steam tem a -silentopção:/usr/bin/steam %U -silent
  • A transmissão tem a --minimizedopção:/usr/bin/transmission-gtk --minimized

No Unity, esses aplicativos são minimizados como ícones na barra de menus superior, e não como ícones no iniciador, embora o ícone de inicialização normal ainda apareça quando você começar a usar o aplicativo. Outras aplicações podem se comportar de maneira diferente.


1

Peguei os roteiros de Jacob e os modifiquei um pouco para criar um mais universal.

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

As principais diferenças são:

  • O programa define o ID do grupo (GID) para o processo. Assim, todos os processos filhos e suas janelas podem ser facilmente encontrados
  • A opção xdotool --sync é usada em vez de um loop while
  • Script permite passar argumentos para o programa

WAIT_TIME deve ser definido como grande o suficiente para permitir que o programa bifurque seus processos filhos. No meu computador, é suficiente para grandes programas como o steam. Aumente, se necessário.

Adição

xdotoolA opção de windowunmappode funcionar com alguns aplicativos e programas de bandeja (bandeja do linux mint, por exemplo), então aqui está uma versão alternativa do script para essas exceções.

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

Eu tentei seu primeiro script. Não funcionou ou não minimizou rápido o suficiente. Eu salvei como startminimized. Então eu corri startminimized gnome-calendar. O calendário abre e continua funcionando?
Khurshid Alam

11
Você pode tentar aumentar a variável WAIT_TIME. Eu uso um atraso de 40 segundos para computadores fracos. Além disso, você pode tentar o segundo script, pois ele usa um comando diferente para minimizar o aplicativo.
Sergey

1

Se o programa estiver sendo fechado na bandeja, é possível fechar a janela do programa na inicialização, em vez de minimizá-la. Um exemplo desse programa é o Viber. Nesse caso, pode-se usar o seguinte 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>


11
Você pode observar que xdotoolnão funcionará corretamente em instalações com Wayland.
Videonauth

0

Eu estava apenas navegando e me deparei com essa pergunta, então estava me perguntando qual é o seu sistema operacional? Quanto a mim, estou usando o UBUNTU BUDGIE 18.04 LTS, portanto, neste sistema operacional, é muito simples.

Basta ir ao menu

No menu, vá para Configurações da área de trabalho do Budgie

e

Em Configurações da área de trabalho, vá para Início automático

Isso lhe dará duas opções, de "+" add

1. Adicionar aplicativo

2. Adicionar comando

Ao selecionar Adicionar aplicativo, todos os aplicativos serão listados, selecione qualquer aplicativo desejado e ele será iniciado quando você iniciar o computador e também será minimizado.


0

Eu precisava dos programas fechados na bandeja, não minimizados, e tentei todos os scripts postados aqui, os que funcionavam, funcionavam apenas para alguns programas e não para outros. Então, eu codifiquei um que funciona muito melhor (você quase não vê a janela aparecendo, apenas o ícone da bandeja, ela parece nativa) e funciona para todos os programas que experimentei. É baseado no de Jacob. Com esse script, você pode precisar adicionar um argumento, dependendo do programa (veja abaixo), mas sempre trabalhou para mim em muitos programas, mas também deve funcionar com o steam.

Uso:

  1. sudo apt-get install wmctrl xdotool
  2. Salve o script como startup_closed.pyconceda permissões de execução e executepython3 ./startup_closed.py -c <command to open program>
  3. Se o ícone da bandeja do programa não aparecer ou a janela não aparecer, é necessário adicionar um destes argumentos: -splashou -hide, por tentativa e erro. Por exemplo: python3 ./startup_closed.py -hide -c teamvieweroupython3 ./startup_closed.py -splash -c slack
  4. Existem mais argumentos, mas você provavelmente não precisa deles. Também há detalhes completos sobre exatamente quando e por que os argumentos são necessários na ajuda:./startup_closed.py --help

Roteiro:

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

0

Eu vim com uma solução bastante elegante, que depende exclusivamente xdotool, e é bastante útil para aplicativos que não possuem um argumento "iniciar minimizado" , como o Telegram.

A única desvantagem é que a solução precisa ser criada manualmente para cada aplicativo, mas supondo que isso não seja um problema (por exemplo: se você deseja iniciar automaticamente um determinado aplicativo sem permitir que ele polua sua tela após o login) , isso é muito mais simples e direto. .

Exemplos reais

## 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 &

A solução

À primeira vista, você pode pensar que é melhor usar o PID ou a classe do processo para comparar, no entanto, isso é realmente contraproducente, pois você frequentemente obtém vários resultados para o mesmo PID. Exemplos são uma janela 0x0 que está aguardando uma notificação, um ícone de bandeja ou qualquer outra janela "oculta".

A solução é criar um comando xdotool que sempre retorna apenas uma janela exclusiva . Nos dois exemplos que fiz --name, no entanto, você pode combinar vários seletores com --all (por exemplo: corresponder a um determinado nome de classe + um nome de classe + um nome regex) . Normalmente, um bom --nameregex faz o truque.

Após criar suas searchcondições, basta gerar uma instância do xdotool (desanexada do shell) com o --syncparâmetro e suas condições, seguidos por windowclose. Execute seu aplicativo depois:

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

Confira xdotool search --helptodas as possibilidades de combinações que você pode organizar para poder segmentar exatamente a janela desejada. Às vezes, fica complicado e você precisa combinar várias condições, mas, uma vez terminado, raramente falha (a menos que uma atualização altere o aplicativo e interrompa sua implementação, é claro).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.