起動時に最小化されたプログラムを実行するにはどうすればよいですか?


19

Telegramを実行したいだけで、起動アプリに追加しました。ポイントは、最小化する必要があるということです。コマンドはありますか?


Telegramを起動するコマンドと、アプリケーションの起動直後のウィンドウ名は何ですか?
ジェイコブVlijm

使用したコマンドはアプリのパスのみで、ウィンドウ名はTelegram Desktop
Hossein Soltanloo

こんにちはHossien、ウィンドウタイトルの代わりにpidを使用したい場合に備えて、私の回答を編集しました。
ジェイコブVlijm

@JacobVlijmありがとう!非常に効率的で便利です!ただし、最初の方法は、可変ウィンドウ名の場合にシームレスに機能します。よくやった!
ホセインソルタンルー

1
@SumeetDeshmukhあなたは信じられないほど素敵で寛大な人です。本当に!
ジェイコブVlijm

回答:


29

最小化されたアプリケーションの起動

最小限の方法でアプリケーションを起動するには、次の2つのコマンドが必要です。

  • アプリケーションを開始する
  • ウィンドウを最小化する

したがって、コマンドまたはスクリプトは「スマート」である必要があります。2番目のコマンドは、アプリケーションウィンドウが実際に表示されるまで待機する必要があります。

最小化されたアプリケーションを起動する一般的なソリューション

次のスクリプトはそれを行い、最小限の方法でアプリケーションを起動する一般的なソリューションとして使用できます。構文で実行するだけです:

<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

使い方

このスクリプトは、両方を必要とするwmctrlxdotool

sudo apt-get install wmctrl xdotool

次に:

  1. スクリプトを空のファイルにコピーして、名前を付けて保存します startup_minimizd.py
  2. テスト-(たとえば)geditコマンドでスクリプトを実行します:

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. すべてが正常に機能する場合、コマンド(アプリケーション用)を追加します Startup Applications

説明

  • スクリプトはアプリケーションを起動し、最初の引数として指定したコマンドを実行します
  • 次に、スクリプトは、wmctrl2番目の引数にちなんで命名されたウィンドウのウィンドウリストを(の助けを借りて)チェックします。
  • ウィンドウが表示された場合、xdotool 何らかの理由でウィンドウが表示されない場合に無限ループを防ぐために、スクリプトはすぐに最小化されます。スクリプトはウィンドウが表示されるまで30秒の制限時間を設けています。

注意

スクリプトの外部で引数を使用してスクリプトを実行するため、スクリプトを一度に複数のアプリケーションに使用できることを述べる必要はありません。


編集

pidでウィンドウを認識する

ウィンドウのタイトルが不明または可変である場合、またはウィンドウの名前に名前の衝突のリスクがある場合は、を使用する方pidがより信頼性の高い方法です。

以下のスクリプトは、wmctrl -lpとの両方の出力のように、アプリケーションのpidの使用に基づいています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)

2番目のスクリプトに関する注意

一般に、2番目のバージョンはより信頼性が高いはずですが、アプリケーションがラッパースクリプトによって起動される場合、コマンドのpidは最終的に呼び出されるアプリケーションとは異なります。

このような場合、最初のスクリプトを使用することをお勧めします。



EDIT2 Steam用のスクリプトの特定のバージョン

STEAMの起動用に特別に作成されたバージョンの下のコメントで要求されているように最小化。

Steamに特定のバージョンが必要な理由

Steam「通常の」アプリケーションとはまったく異なる動作をすることがわかります。

  • 1つの PID Steamは実行されませんが、(私のテストでは)8つも実行されます。
  • Steam起動時に少なくとも 2つのウィンドウ(1つのスプラッシュのようなウィンドウ)で実行されますが、追加のメッセージウィンドウが表示される場合があります。
  • SteamのWindowsにpid 0は、スクリプトの問題があります。
  • メインウィンドウが作成された後、ウィンドウは1秒ほどで2回目に浮き上がります。そのため、1回の最小化ではできません。

この例外的な動作は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を返すだけではキャッチしません。おそらく、失敗したものを確認できるように、失敗させてください。そうしないと、あらゆる種類の異なる原因で破損する可能性があり、広告されずに渡されます。
fedorqui

1
@fedorqui良いもの、2つの例外が発生する可能性があります:(subprocess.CalledProcesError バグの結果としてwmctrl)およびIndexError(通常の例外)すぐに編集します:)。言及していただきありがとうございます
ジェイコブヴリム

@HosseinSoltanlooスクリプトを実行するコマンドとは正確には何ですか?
ジェイコブVlijm

@JacobVlijmスクリプトは正常に機能しますが、修正できる別の問題があります。未読メッセージがあり、アプリを開くたびに、ウィンドウ名が「Telegram(2)」のようなものに変わります。これは、未読メッセージが2つあるためです。
ホセインソルタンルー

2
@JDHolland修正できると確信しています。数日中にどこかで調査します:)
ジェイコブヴリム

3

問題に対する一般的な解決策として、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では、これらのアプリケーションはランチャーのアイコンとしてではなく、トップメニューバーのアイコンとして最小化されて起動しますが、アプリケーションの使用を開始すると通常の起動アイコンが表示されます。他のアプリケーションの動作は異なる場合があります。


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)

主な違いは次のとおりです。

  • プログラムは、プロセスのグループID(GID)を設定します。したがって、すべての子プロセスとそのウィンドウを簡単に見つけることができます
  • whileループの代わりにxdotool --syncオプションが使用されます
  • スクリプトは、プログラムに引数を渡すことができます

WAIT_TIMEは、プログラムが子プロセスを分岐できるように十分に大きく設定する必要があります。私のコンピューターでは、steamのような大きなプログラムには十分です。必要に応じて増やします。

添加

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秒の遅延を使用します。別のコマンドを使用してアプリを最小化するため、2番目のスクリプトを試すこともできます。
セルゲイ

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
xdotoolWaylandでのインストールでは正常に動作しないことに注意してください。
Videonauth

0

私はただサーフィンしていて、この質問に出くわしたので、あなたのオペレーティングシステムは何だと思いましたか?私については、UBUNTU BUDGIE 18.04 LTSを使用しているため、このオペレーティングシステムでは非常に簡単です。

メニューに移動するだけです

メニューからBudgie Desktop Settingsに移動します

そして

デスクトップ設定から自動起動に移動します

「+」から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ウィンドウ、システムトレイアイコン、またはその他の「隠された」ウィンドウがあります。

解決策は、常に1つの一意のウィンドウのみを返すxdotoolコマンドを作成することです。を使用して行われた私の両方の例では--name、複数のセレクターを組み合わせることができます--all (例:指定されたクラス名+クラス名+名前正規表現に一致)通常、良い--name正規表現がトリックを行います。

search条件を作成したら、パラメータと条件を指定したxdotoolのインスタンス(シェルから切り離された)を生成し--sync、その後にを続けますwindowclose。その後、アプリを実行します。

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

xdotool search --help希望する正確なウィンドウをターゲットにできるように調整できる組み合わせのすべての可能性を確認してください。時にはそれがトリッキーになり、いくつかの条件を組み合わせる必要がありますが、一度終了すると、失敗することはほとんどありません(もちろん、更新によってアプリケーションが変更され、実装が中断されない限り)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.