Come posso eseguire un programma all'avvio, ridotto a icona?


19

Voglio solo far funzionare Telegram e l'ho aggiunto alle app di avvio. Il punto è che ne ho bisogno per essere minimizzato. Qualche comando?


Qual è il comando per avviare Telegram e qual è il nome della finestra subito dopo l'avvio dell'applicazione?
Jacob Vlijm,

Il comando che ho usato è solo il percorso dell'app e il nome della finestra è Telegram Desktop
Hossein Soltanloo,

Ciao Hossien, nel caso tu preferissi usare il pid invece del titolo della finestra, ho modificato la mia risposta.
Jacob Vlijm,

@JacobVlijm Grazie! È molto efficiente e utile! Tuttavia, il primo metodo funziona perfettamente nei casi di nomi di finestre variabili. Buon lavoro!
Hossein Soltanloo,

1
@SumeetDeshmukh sei una persona incredibilmente gentile e generosa. Veramente!
Jacob Vlijm,

Risposte:


29

Avvio di un'applicazione ridotto al minimo

L'avvio di un'applicazione in modo ridotto richiede due comandi:

  • avviare l'applicazione
  • minimizzare la sua finestra

Pertanto, il comando o lo script deve essere "intelligente"; il secondo comando dovrebbe attendere che appaia effettivamente la finestra dell'applicazione.

Soluzione generale per l'avvio di un'applicazione ridotta al minimo

Lo script seguente lo fa e può essere utilizzato come soluzione generale per avviare un'applicazione in modo ridotto. Basta eseguirlo nella sintassi:

<script> <command_to_run_the_application> <window_name>

Il copione

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

Come usare

Lo script ha bisogno di entrambi wmctrle xdotool:

sudo apt-get install wmctrl xdotool

Poi:

  1. Copia lo script in un file vuoto, salvalo come startup_minimizd.py
  2. Prova- esegui lo script con (es.) geditIl comando:

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. Se tutto funziona correttamente, aggiungi il comando (per la tua applicazione) a Startup Applications

Spiegazione

  • Lo script avvia l'applicazione, eseguendo il comando fornito come primo argomento
  • Quindi lo script controlla l'elenco delle finestre (con l'aiuto di wmctrl) per windows, che prende il nome dal tuo secondo argomento.
  • Se viene visualizzata la finestra, viene immediatamente ridotta a icona con l'aiuto di xdotool Per evitare un ciclo infinito se la finestra potrebbe non apparire per qualche motivo, lo script applica un limite di tempo di 30 secondi per la visualizzazione della finestra.

Nota

Non è necessario menzionare che è possibile utilizzare lo script per più applicazioni contemporaneamente, poiché lo si esegue con argomenti esterni allo script.


MODIFICARE

riconoscendo la finestra dal suo pid

Se il titolo della finestra non è sicuro o variabile, oppure esiste il rischio di scontro di nomi nel nome della finestra, è possibile utilizzare il pidmetodo più affidabile.

Lo script seguente si basa sull'uso del pid dell'applicazione, come nell'output di entrambi wmctrl -lpe ps -ef.

L'installazione è praticamente la stessa, ma il titolo della finestra non è necessario in questa versione, quindi il comando per eseguirlo è:

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

Proprio come il primo copione, ha bisogno di entrambi wmctrlexdotool

Il copione

#!/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 sul secondo script

Sebbene in generale la seconda versione dovrebbe essere più affidabile, nei casi in cui l'applicazione viene avviata da uno script wrapper, il pid del comando sarà diverso dall'applicazione che viene finalmente chiamata.

In questi casi, ti consiglio di usare il primo script.



EDIT2 una versione specifica dello script per Steam

Come richiesto in un commento, sotto una versione, realizzata appositamente per l'avvio di STEAM minimizzata.

Perché una versione specifica per Steam?

Si scopre che Steamsi comporta in modo abbastanza diverso da un'applicazione "normale":

  • Si scopre Steamche non esegue un pid, ma non meno di (nel mio test) otto!
  • Steamviene eseguito all'avvio con almeno due finestre (una finestra simile a uno splash), ma a volte viene visualizzata una finestra di messaggio aggiuntiva.
  • Windows di Steam ha pid 0, che è un problema nello script così com'era.
  • Dopo aver creato la finestra principale, la finestra viene sollevata una seconda volta dopo circa un secondo, quindi una singola minimizzazione non funziona.

Questo comportamento eccezionale Steamrichiede una versione speciale dello script, che viene aggiunta di seguito. Lo script si avvia Steame, per 12 secondi, tiene d'occhio tutte le nuove finestre del corrispondente WM_CLASS, controllando se sono minimizzate. In caso contrario, lo script si assicura che lo saranno.

Come lo script originale, questo si ha bisogno wmctrle xdotoolda installare.

Il copione

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

Per usarlo

  • Basta copiarlo in un file vuoto, salvarlo come runsteam_minimized.py
  • Eseguilo con il comando:

    python3 /path/to/runsteam_minimized.py
    

caspita! Non vorrei prendere except:solo per restituire None. Probabilmente è meglio lasciarlo fallire in modo da vedere cosa è fallito; in caso contrario, può rompersi per qualsiasi tipo di cause diverse e passerà senza pubblicità.
fedorqui,

1
@fedorqui Buona, probabilmente possono verificarsi due eccezioni: subprocess.CalledProcesError (a seguito di un bug wmctrl) e IndexError(normale eccezione) verranno modificate in un minuto :). Grazie per la menzione
Jacob Vlijm,

@HosseinSoltanloo Qual è esattamente il comando con cui esegui lo script?
Jacob Vlijm,

@JacobVlijm Lo script funziona bene ma c'è un altro problema che potresti risolvere. Ogni volta che ho messaggi non letti e apro l'app, il nome della finestra cambia in qualcosa come "Telegram (2)" poiché ci sono due messaggi non letti e in questo modo lo script non funzionerà perché cambia il nome.
Hossein Soltanloo,

2
@JDHolland Sono sicuro che può essere risolto. Lo esaminerò da qualche parte nei prossimi giorni :)
Jacob Vlijm,

3

È bene avere gli script forniti da user72216 e Sergey come soluzioni generali al problema, ma a volte l'applicazione che si desidera avviare al minimo ha già un interruttore che farà ciò che si desidera.

Ecco alcuni esempi con le stringhe di comando del programma di avvio corrispondenti:

  • Telegram (dalla versione 0.7.10) ha l' -startintrayopzione:<path-to-Telegram>/Telegram -startintray
  • Steam ha l' -silentopzione:/usr/bin/steam %U -silent
  • La trasmissione ha l' --minimizedopzione:/usr/bin/transmission-gtk --minimized

In Unity queste applicazioni iniziano a essere minimizzate come icone nella barra dei menu in alto anziché come icone nel programma di avvio, anche se l'icona di avvio normale verrà comunque visualizzata una volta che inizi a utilizzare l'applicazione. Altre applicazioni potrebbero comportarsi diversamente.


1

Ho preso gli script di Jacob e li ho modificati un po 'per renderli più universali.

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

Le principali differenze sono:

  • Il programma imposta l'ID gruppo (GID) per il processo. Pertanto, tutti i processi figlio e le loro finestre possono essere facilmente trovati
  • L'opzione xdotool --sync viene utilizzata al posto di un ciclo while
  • Lo script consente di passare argomenti al programma

WAIT_TIME dovrebbe essere impostato abbastanza grande da consentire al programma di fork i suoi processi figlio. Sul mio computer è sufficiente per grandi programmi come Steam. Aumentalo, se necessario.

aggiunta

xdotoolL 'opzione windowunmappotrebbe funzionare in modo strano con alcune applicazioni e programmi di notifica (ad esempio il vassoio di Linux Mint), quindi ecco una versione alternativa dello script per quelle eccezioni.

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

Ho provato la tua prima sceneggiatura. Non ha funzionato o non ha minimizzato abbastanza velocemente. L'ho salvato come startminimized. Poi ho corso startminimized gnome-calendar. Calendario aperto e continua a funzionare?
Khurshid Alam

1
Puoi provare ad aumentare la variabile WAIT_TIME. Uso un ritardo di 40 secondi per i computer deboli. Inoltre, puoi provare il secondo script in quanto utilizza un comando diverso per ridurre a icona l'app.
Sergey,

1

Se il programma viene chiuso nel vassoio, si potrebbe effettivamente voler chiudere la finestra del programma all'avvio invece di minimizzarlo. Un esempio di tale programma è Viber. In questo caso si potrebbe usare il seguente 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>


1
Potresti voler notare che xdotoolnon funzionerà correttamente su installazioni con Wayland.
Videonauth,

0

Stavo solo navigando e mi sono imbattuto in questa domanda, quindi mi chiedevo quale fosse il tuo sistema operativo? Per quanto mi riguarda sto usando UBUNTU BUDGIE 18.04 LTS, quindi in questo sistema operativo è molto semplice.

Vai semplicemente al menu

Dal menu vai a Impostazioni desktop Budgie

e

Da Impostazioni desktop vai a Avvio automatico

Ti darà 2 opzioni, da "+" aggiungi

1. Aggiungi applicazione

2. Aggiungi comando

Selezionando Aggiungi applicazione verranno elencate tutte le applicazioni selezionate qualsiasi applicazione desiderata e verrà avviata all'avvio del computer e verrà anche ridotta a icona.


0

Avevo bisogno dei programmi chiusi nel vassoio, non minimizzati, e ho provato tutti gli script pubblicati qui, quelli che funzionavano, funzionavano solo per alcuni programmi e non per altri. Quindi ho codificato uno che funziona molto meglio (quasi non vedi apparire la finestra, solo l'icona nella barra delle applicazioni, sembra nativa) e funziona per tutti i programmi che ho provato. Si basa su quello di Jacob. Con questo script potresti dover aggiungere un argomento a seconda del programma (vedi sotto) ma ha sempre funzionato per me con molti programmi e dovrebbe funzionare anche con Steam.

Uso:

  1. sudo apt-get install wmctrl xdotool
  2. Salvare lo script come startup_closed.pyconcedergli le autorizzazioni di esecuzione ed eseguirlopython3 ./startup_closed.py -c <command to open program>
  3. Se l'icona della barra delle applicazioni non viene visualizzata o la finestra non viene visualizzata, è necessario aggiungere uno di questi argomenti: -splashoppure -hide, per tentativi ed errori. Ad esempio: python3 ./startup_closed.py -hide -c teamvieweropython3 ./startup_closed.py -splash -c slack
  4. Ci sono più argomenti ma probabilmente non ne hai bisogno. Inoltre, ci sono dettagli completi su quando e perché gli argomenti sono necessari nella guida:./startup_closed.py --help

script:

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

Sono arrivato con una soluzione piuttosto elegante che si basa esclusivamente su xdotool, ed è abbastanza utile per le applicazioni che non hanno un argomento "start minimized" , come Telegram.

L'unico aspetto negativo è che la soluzione deve essere elaborata manualmente per ogni app, ma supponendo che non sia un problema (ad esempio: se si desidera avviare automaticamente una determinata applicazione senza consentirle di inquinare lo schermo dopo aver effettuato l'accesso) , questo è molto più semplice e diretto .

Esempi reali

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

A prima vista, potresti pensare che sia meglio usare il processo 'PID o classe per confrontarsi, tuttavia è effettivamente controproducente poiché spesso otterrai più risultati per lo stesso PID. Esempi sono una finestra 0x0 che sta effettivamente aspettando una notifica, un'icona systray o qualsiasi altra finestra "nascosta".

La soluzione sta creando un comando xdotool che restituisce sempre solo una finestra unica . In entrambi i miei esempi che sono stati fatti usando --name, tuttavia è possibile combinare più selettori con --all (ad esempio: abbina un dato nome classe + un nome classe + un nome regex) . Di solito un buon --nameregex fa il trucco.

Dopo aver creato le tue searchcondizioni, genera semplicemente un'istanza di xdotool (staccato dalla shell) con il --syncparametro e le tue condizioni, seguito da windowclose. Esegui la tua app in seguito:

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

Scopri xdotool search --helptutte le possibilità di combinazioni che puoi organizzare per poter scegliere come target la finestra esatta che desideri. A volte diventa complicato e devi combinare diverse condizioni, ma una volta terminato, raramente fallirà mai (a meno che un aggiornamento non cambi l'applicazione e interrompa l'implementazione, ovviamente).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.