如何在启动时最小化运行程序?

如何在启动时最小化运行程序?

我只想运行 Telegram,我已将其添加到启动应用中。关键是我需要将其最小化。有什么命令吗?

答案1

启动最小化应用程序

以最小化方式启动应用程序需要两个命令:

  • 启动应用程序
  • 最小化窗口

因此,命令或脚本需要“智能”;第二个命令应该等待应用程序窗口实际出现。

启动应用程序最小化的通用解决方案

下面的脚本可以实现这一点,并且可以用作以最小化方式启动应用程序的通用解决方案。只需按照以下语法运行它:

<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

解释

  • 该脚本启动应用程序,运行您作为第一个参数提供的命令
  • 然后脚本检查窗口列表(在 的帮助下wmctrl)以第二个参数命名的窗口。
  • 如果窗口出现,它会立即最小化,为了xdotool 防止由于某种原因窗口可能不出现而出现无限循环,脚本对窗口出现的时间限制为 30 秒。

笔记

无需提及,您可以同时将该脚本用于多个应用程序,因为您使用脚本外部的参数运行它。


编辑

通过 pid 识别窗口

如果窗口标题不确定或可变,或者窗口名称存在名称冲突的风险,则使用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并不运行pid,但不小于(在我的测试中)八!
  • Steam启动时运行至少两个窗口(一个类似启动画面的窗口),但有时会出现一个附加消息窗口。
  • Steam 的 Windows 版本有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
    

答案2

最好将 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 中,这些应用程序启动时会最小化为顶部菜单栏中的图标,而不是启动器上的图标,不过一旦您开始使用该应用程序,正常的启动图标仍会出现。其他应用程序的行为可能有所不同。

答案3

我需要将程序关闭到托盘,而不是最小化,我尝试了这里发布的所有脚本,那些有效的脚本只对某些程序有效,对其他程序无效。所以我编写了一个效果更好的脚本(您几乎看不到窗口出现,只有托盘图标,它看起来像原生的),并且适用于我尝试过的所有程序。它基于 Jacob 的脚本。使用此脚本,您可能需要根据程序添加参数(见下文),但它总是对我的许多程序有效,它也应该适用于 steam。

用法:

  1. sudo apt-get install wmctrl xdotool
  2. 保存脚本为startup_closed.py赋予其执行权限然后执行python3 ./startup_closed.py -c <command to open program>
  3. 如果程序托盘图标不显示或窗口不显示,则需要通过反复试验添加以下参数之一:-splash或。例如:或-hidepython3 ./startup_closed.py -hide -c teamviewerpython3 ./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")

答案4

我想到了一个相当优雅的解决方案,它完全依赖于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所有可以安排的组合可能性,以便能够精确定位您想要的窗口。有时它会变得很棘手,您必须组合多个条件,但一旦完成,它几乎不会失败(当然,除非更新更改了应用程序并破坏了您的实现)。

相关内容