Як я можу запустити програму при запуску, мінімізовану?


20

Я просто хочу, щоб Telegram запустився, і я додав його до запуску програм. Справа в тому, що мені потрібно, щоб воно було мінімізоване. Будь-які команди?


Яка команда запустити Telegram і як називається вікно відразу після запуску програми?
Яків Влійм

Команда, яку я використав, - це лише шлях програми, а назва вікна - Telegram Desktop
Hossein Soltanloo

Привіт Хоссьєне, на всякий випадок, якщо ви хочете скористатися pid замість заголовка вікна, ви редагували мою відповідь.
Яків Влійм

@JacobVlijm Дякую! Це надзвичайно ефективно та корисно! Однак перший метод працює безперебійно у випадках з назвою змінних вікон. Хороша робота!
Hossein Soltanloo

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 секунд для появи вікна.

Примітка

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


EDIT

розпізнаючи вікно за його підом

Якщо назва вікна невірна або змінна, або існує ризик зіткнення імен у назві вікна, використання цього 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)

Примітка до другого сценарію

Хоча в цілому друга версія повинна бути більш надійною, у випадках, коли додаток запускається сценарієм обгортки, під команди буде відрізнятися від програми, яка нарешті викликається.

У таких випадках рекомендую використовувати перший сценарій.



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:щоб повернути жодного. Напевно, краще нехай це провалюється, щоб ви побачили, що не вдалося; в іншому випадку він може зламатися з будь-яких видів різних причин і пройде без реклами.
fedorqui

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

@HosseinSoltanloo З якою саме командою ви запускаєте сценарій?
Яків Влійм

@JacobVlijm Сценарій працює добре, але є ще одна проблема, яку ви можете виправити. Щоразу, коли у мене є непрочитані повідомлення, і я відкриваю додаток, ім'я вікна змінюється на щось на зразок "Telegram (2)", оскільки є два непрочитаних повідомлення, і таким чином сценарій не буде працювати, оскільки змінити ім'я.
Hossein Soltanloo

2
@JDHolland Я впевнений, що це можна виправити.
Розглянемо

4

Добре, щоб сценарії, подані користувачем72216 та Сергієм, були загальними рішеннями проблеми, але іноді програма, яку ви хочете запустити, зведена до мінімуму, вже має комутатор, який буде робити те, що ви хочете.

Ось кілька прикладів із відповідними командними рядками програми запуску:

  • 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
  • Сценарій дозволяє передавати аргументи програмі

WAIT_TIME має бути встановлено достатньо великим, щоб програма могла розщедрити дочірні процеси. На моєму комп’ютері цього достатньо для великих програм, таких як пар. Збільште її, якщо потрібно.

Доповнення

xdotoolОпція windowunmapможе спрацьовувати з деякими програмами та трей-програмами (наприклад, лоток монетного двору Linux), тому ось альтернативна версія сценарію для цих винятків.

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

0

Я просто займався серфінгом і натрапив на це питання, тому мені просто цікаво, що це за ваша операційна система? Що стосується мене, я використовую UBUNTU BUDGIE 18.04 LTS, тому в цій операційній системі це дуже просто.

Просто перейдіть до меню

З меню перейдіть у Налаштування робочого столу Budgie

і

У налаштуваннях робочого столу перейдіть до автоматичного запуску

Це дасть вам 2 варіанти, від "+" додати

1. Додати додаток

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

Вибравши Додати додаток, всі програми будуть перераховані, виберіть будь-яку програму, яку ви хочете, і вона запуститься при запуску комп'ютера, і вона також буде мінімізована.


0

Мені потрібні були програми, закриті для лотка, не мінімізовані, і я спробував усі розміщені тут сценарії, ті, що працювали, працювали лише для деяких програм, а не для інших. Тож я зашифрував той, який працює набагато краще (ви майже не бачите вікна, яке відображається, лише значок лотка, він виглядає рідним) і працює для всіх програм, які я пробував. Він заснований на такому Якова. За допомогою цього сценарію вам може знадобитися додати аргумент залежно від програми (див. Нижче), але завжди працював для мене з великою кількістю програм, він також повинен працювати з парою.

Використання:

  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.