Как я могу запустить программу при запуске, свернуто?


20

Я просто хочу, чтобы Telegram запускался, и я добавил его в автозагрузку приложений. Дело в том, что мне нужно, чтобы это было минимизировано. Любые команды?


Что такое команда для запуска Telegram и как называется окно сразу после запуска приложения?
Джейкоб Влейм

Команда, которую я использовал, - это просто путь к приложению, а имя окна - Telegram Desktop
Хоссейн Солтанлу

Привет, Хоссиен, на всякий случай, если вы предпочитаете использовать pid вместо заголовка окна, отредактировал мой ответ.
Джейкоб Влейм

@JacobVlijm Спасибо! Это очень эффективно и полезно! Однако первый метод работает без проблем в переменных именах окон. Молодец!
Хоссейн Солтанлу

1
@SumeetDeshmukh ты невероятно хороший и щедрый человек. В самом деле!
Якоб Влейм

Ответы:


29

Запуск приложения свернут

Минимизированный запуск приложения требует двух команд:

  • запуск приложения
  • свернуть свое окно

Следовательно, команда или скрипт должны быть «умными»; вторая команда должна дождаться фактического появления окна приложения.

Общее решение для запуска приложения сведено к минимуму

Сценарий ниже делает это и может использоваться в качестве общего решения для запуска приложения в минимизированном виде. Просто запустите его в синтаксисе:

<script> <command_to_run_the_application> <window_name>

Сценарий

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

Как пользоваться

Скрипту нужны оба wmctrlи xdotool:

sudo apt-get install wmctrl xdotool

Потом:

  1. Скопируйте скрипт в пустой файл, сохраните его как startup_minimizd.py
  2. Протестируйте скрипт с помощью (например) geditкоманды:

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. Если все работает нормально, добавьте команду (для вашего приложения) в Startup Applications

объяснение

  • Сценарий запускает приложение, выполняя команду, которую вы дали в качестве первого аргумента.
  • Затем скрипт проверяет список окон (с помощью wmctrl) для окон, названных в честь вашего второго аргумента.
  • Если окно появляется, оно сразу минимизируется с помощью xdotool Чтобы предотвратить бесконечный цикл, если окно может не появиться по какой-либо причине, сценарий установил ограничение времени 30 секунд для появления окна.

Заметка

Не нужно упоминать, что вы можете использовать скрипт для нескольких приложений одновременно, так как вы запускаете его с аргументами вне скрипта.


РЕДАКТИРОВАТЬ

узнав окно по пиду

Если заголовок окна является неопределенным или переменным, или существует риск столкновения имен в имени окна, использование pidболее надежного метода для использования.

Приведенный ниже скрипт основан на использовании pid приложения, как при выводе обоих wmctrl -lpи ps -ef.

Настройка почти такая же, но заголовок окна в этой версии не нужен, поэтому команда для его запуска:

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

Так же , как первый сценарий, он должен как wmctrlиxdotool

Сценарий

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

Обратите внимание на второй сценарий

Хотя в общем случае вторая версия должна быть более надежной, в случаях, когда приложение запускается сценарием-оболочкой, pid команды будет отличаться от приложения, которое окончательно вызывается.

В таких случаях я рекомендую использовать первый скрипт.



EDIT2 - конкретная версия скрипта для Steam

Как указано в комментарии, ниже версия, специально сделанная для запуска STEAM, свернута.

Почему конкретная версия для Steam?

Оказывается, Steamведет себя совсем не так, как «нормальное» приложение:

  • Оказывается, Steamзапускается не один пид, но не менее (в моем тесте) восемь!
  • Steamзапускается при запуске как минимум с двумя окнами (одно окно, похожее на заставку), но иногда появляется дополнительное окно сообщений.
  • Windows из Steam есть pid 0, что является проблемой в скрипте как было.
  • После создания главного окна, окно поднимается во второй раз после второго или около того , поэтому единственная минимизации не будет делать.

Это исключительное поведение Steamтребует специальной версии скрипта, которая добавлена ​​ниже. Сценарий запускается Steamи в течение 12 секунд отслеживает все новые окна соответствующих окон WM_CLASS, проверяя, свернуты ли они. Если нет, сценарий гарантирует, что они будут.

Как и оригинальный скрипт, этот нужно wmctrlи xdotoolнужно установить.

Сценарий

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

Использовать его

  • Просто скопируйте его в пустой файл, сохраните как runsteam_minimized.py
  • Запустите его командой:

    python3 /path/to/runsteam_minimized.py
    

вау, классно! Я бы не поймал except:только, чтобы вернуть None. Вероятно, лучше позволить этому потерпеть неудачу, чтобы вы увидели, что не удалось; в противном случае, он может сломаться для любого рода различных причин и пройдет незаметно.
Федорки

1
@fedorqui Хорошо, вероятно, могут возникнуть два исключения: subprocess.CalledProcesError (в результате ошибки wmctrl) и IndexError(обычное исключение) будут редактироваться через минуту :). Спасибо за упоминание
Джейкоб Влейм

@HosseinSoltanloo Какой именно командой вы запускаете скрипт?
Джейкоб Влейм

@JacobVlijm Скрипт работает хорошо, но есть другая проблема, которую вы можете исправить. Всякий раз, когда у меня есть непрочитанные сообщения и я открываю приложение, имя окна меняется на что-то вроде «Telegram (2)», так как есть два непрочитанных сообщения, и таким образом скрипт не будет работать, потому что изменение имени.
Хоссейн Солтанлу

2
@JDHolland Я уверен, что это можно исправить.
Рассмотрим

4

Хорошо иметь сценарии, предоставленные user72216 и Sergey, в качестве общих решений проблемы, но иногда приложение, которое вы хотите минимизировать при запуске, уже имеет переключатель, который будет делать то, что вы хотите.

Вот несколько примеров с соответствующими строками команд запуска программы:

  • Telegram (начиная с версии 0.7.10) имеет -startintrayопцию:<path-to-Telegram>/Telegram -startintray
  • В Steam есть -silentопция:/usr/bin/steam %U -silent
  • Коробка передач имеет --minimizedопцию:/usr/bin/transmission-gtk --minimized

В Unity эти приложения начинают сворачиваться в виде значков в верхней строке меню, а не значков на панели запуска, хотя значок обычного запуска все равно будет отображаться после запуска приложения. Другие приложения могут вести себя по-другому.


Сигнал имеет --use-tray-iconи --start-in-tray.
Марсио

1

Я взял сценарии Джейкоба и немного изменил их, чтобы сделать более универсальным.

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

Основные отличия:

  • Программа устанавливает идентификатор группы (GID) для процесса. Таким образом, все дочерние процессы и их окна могут быть легко найдены
  • Параметр xdotool --sync используется вместо цикла while
  • Скрипт позволяет передавать аргументы программе

Значение WAIT_TIME должно быть достаточно большим, чтобы программа могла форкнуть свои дочерние процессы. На моем компьютере достаточно больших программ, таких как steam. Увеличьте его, если необходимо.

прибавление

xdotoolЭта опция windowunmapможет работать в стиле фанк с некоторыми приложениями и программами в трее (например, в linux mint's tray), поэтому вот альтернативная версия скрипта для этих исключений.

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

Я попробовал твой первый сценарий. Это не сработало или не свелось к минимуму достаточно быстро. Я сохранил это как startminimized. Затем я побежал startminimized gnome-calendar. Календарь открыт и работает?
Хуршид Алам

1
Вы можете попытаться увеличить переменную WAIT_TIME. Я использую 40 секундную задержку для слабых компьютеров. Также вы можете попробовать второй скрипт, так как он использует другую команду, чтобы свернуть приложение.
Сергей

1

Если программа закрывается в трей, можно на самом деле захотеть закрыть окно программы при запуске, а не сворачивать его. Одним из примеров такой программы является Viber. В этом случае можно использовать следующий скрипт 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

Использование: <path-to-script> <program-to-start>


1
Вы можете заметить, что он не xdotoolбудет работать должным образом на установках с Wayland.
Видеонавт

0

Я просто занимался серфингом и столкнулся с этим вопросом, так что мне было просто интересно, какая у вас операционная система? Что касается меня, я использую UBUNTU BUDGIE 18.04 LTS, поэтому в этой операционной системе это очень просто.

Просто зайдите в меню

Из меню перейдите в настройки рабочего стола Budgie.

и

От настройки рабочего стола перейдите к автозапуску

Это даст вам 2 варианта, от "+" добавить

1. Добавить приложение

2. Добавить команду

При выборе « Добавить приложение» будут перечислены все приложения, выберите любое приложение, которое вы хотите, и оно запустится при запуске компьютера, а также будет свернуто.


0

Мне нужно было, чтобы программы были закрыты в трей, а не свернуты, и я перепробовал все скрипты, которые выложены здесь, те, которые работали, работали только для одних программ, а не для других. Итак, я написал код, который работает намного лучше (вы почти не видите появления окна, только значок в трее, он выглядит нативно) и работает для всех программ, которые я пробовал. Он основан на Джейкобе. С этим сценарием вам может потребоваться добавить аргумент в зависимости от программы (см. Ниже), но он всегда работал для меня со многими программами, он также должен работать с Steam.

Использование:

  1. sudo apt-get install wmctrl xdotool
  2. Сохраните сценарий, startup_closed.pyпредоставив ему права на выполнение, а затем выполнитеpython3 ./startup_closed.py -c <command to open program>
  3. Если значок на панели задач не отображается или окно не отображается, необходимо добавить один из следующих аргументов: -splashлибо -hideметодом проб и ошибок. Например: python3 ./startup_closed.py -hide -c teamviewerилиpython3 ./startup_closed.py -splash -c slack
  4. Есть больше аргументов, но вы, вероятно, не нуждаетесь в них. Также есть полная информация о том, когда и зачем нужны аргументы в справке:./startup_closed.py --help

Автор сценария:

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

Я пришел с довольно элегантным решением, которое опирается исключительно на него xdotool, и оно весьма полезно для приложений, у которых нет аргумента «минимизировать начало» , таких как Telegram.

Единственным недостатком является то, что решение должно быть вручную создано для каждого приложения, но при условии, что это не проблема (например: если вы хотите автоматически запустить определенное приложение, не позволяя ему загрязнять экран после входа в систему) , это намного проще и понятнее. ,

Актуальные примеры

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

Решение

На первый взгляд, вы можете подумать, что лучше использовать PID или класс процесса для сопоставления, однако это на самом деле контрпродуктивно, поскольку вы часто получаете несколько результатов для одного и того же PID. Примерами являются окно 0x0, которое на самом деле ожидает уведомления, значок системного окна или любое другое «скрытое» окно.

Решением является создание команды xdotool, которая всегда возвращает только одно уникальное окно . В обоих моих примерах это было сделано с использованием --name, однако вы можете комбинировать несколько селекторов с --all (например: сопоставить заданное имя класса + имя класса + регулярное выражение имени) . Обычно хорошее --nameрегулярное выражение делает свое дело.

После создания ваших searchусловий просто создайте экземпляр xdotool (отсоединенный от оболочки) с --syncпараметром и вашими условиями, а затем windowclose. Запустите ваше приложение потом:

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

Проверьте xdotool search --helpвсе возможности комбинаций, которые вы можете организовать, чтобы иметь возможность выбрать точное окно, которое вы хотите. Иногда это становится сложным, и вам приходится комбинировать несколько условий, но, как только вы закончите, он редко будет терпеть неудачу (если, конечно, обновление не изменит приложение и не нарушит вашу реализацию, конечно).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.