以最小化的方式启动应用程序需要两个命令:
因此,命令或脚本需要“智能”;第二个命令应该等待应用程序窗口实际出现。
下面的脚本可以实现这一点,并可用作启动应用程序最小化的通用解决方案。只需按以下语法运行即可:
<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
然后:
startup_minimizd.py
使用(例如)gedit
命令来测试运行脚本:
python3 /path/to/startup_minimizd.py gedit gedit
启动应用程序
中wmctrl
帮助检查窗口列表,以查找与您的第二个参数同名的窗口。xdotool
将其最小化。
为了防止无限循环,如果由于某种原因窗口可能不会出现,脚本对窗口出现的时间设定了30秒的限制。无需提及您可以同时在多个应用程序上使用该脚本,因为您在脚本外部使用参数来运行它。
如果窗口标题不确定或可变,或者窗口名称存在命名冲突的风险,使用进程ID(pid)是一种更可靠的方法。
下面的脚本基于应用程序的进程ID,即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)
虽然通常情况下第二个版本应该更可靠,但在应用程序由包装脚本启动时,命令的进程ID将与最终调用的应用程序不同。
在这种情况下,我建议使用第一个脚本。
根据评论的要求,以下是一个专门为启动Steam最小化而制作的版本。
事实证明,Steam
与“普通”应用程序有很大的不同:
Steam
不仅运行一个进程ID,而是至少(在我的测试中)八个!Steam
在启动时会出现至少两个窗口(一个类似闪屏的窗口),但有时还会出现额外的消息窗口。pid 0
,这在原始脚本中是个问题。#!/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。最好让它失败,这样你就能看到出了什么问题;否则,它可能因为各种不同原因而崩溃,并且没有提示地通过。 - fedorquisubprocess.CalledProcessError
(由于有问题的 wmctrl
导致)和 IndexError
(正常的异常),一分钟后会进行编辑 :)。谢谢提醒。 - Jacob Vlijm拥有由用户72216和Sergey提供的脚本作为问题的一般解决方案是很好的,但有时您希望启动的应用程序已经有了一个可以实现您想要的功能的开关。
以下是一些示例及其相应的启动程序命令字符串:
-startintray
选项:<path-to-Telegram>/Telegram -startintray
-silent
选项:/usr/bin/steam %U -silent
--minimized
选项:/usr/bin/transmission-gtk --minimized
在Unity中,这些应用程序会作为图标以最小化状态启动在顶部菜单栏上,而不是作为launcher上的图标,尽管一旦开始使用应用程序,正常启动图标仍将出现。其他应用程序可能行为有所不同。
--use-tray-icon
和--start-in-tray
选项。 - Márciosudo apt-get install wmctrl xdotool
startup_closed.py
,赋予执行权限,然后执行python3 ./startup_closed.py -c <打开程序的命令>
-splash
或-hide
。例如:python3 ./startup_closed.py -hide -c teamviewer
或python3 ./startup_closed.py -splash -c slack
./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")
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
<脚本路径> <要启动的程序>
xdotool
可能无法正常工作。 - Videonauthwmctrl
(它对我来说甚至都不起作用)。我使用了以下命令:xdotool search --onlyvisible --pid $pid --sync | xargs xdotool windowminimize
- AlonLxdotool
,对于那些没有"启动最小化"参数的应用程序(如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
(例如:匹配给定的 classname + 一个 class name + 一个名称正则表达式)。通常一个好的 --name
正则表达式就行了。
制作好您的 search
条件后,只需使用 --sync
参数和您的条件生成一个 xdotool 实例 (从 shell 中分离),然后跟随 windowclose
。之后再运行您的应用程序:
xdotool search --sync [... myapp-match-conditions] windowclose &
my-app
xdotool search --help
以了解您可以组合的所有可能性,以便能够定位到您想要的确切窗口。有时候会变得棘手,您需要结合几个条件,但一旦完成,它很少会失败(除非更新更改了应用程序并破坏了您的实现,当然)。#!/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)
xdotool
的windowunmap
选项可能与某些应用程序和托盘程序(例如Linux Mint的托盘)一起工作不正常,因此这里提供了一个针对这些异常情况的替代版本的脚本。#!/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
。日历打开并继续运行吗? - Khurshid AlamWAIT_TIME
。对于性能较差的电脑,我使用40秒的延迟。另外,你也可以尝试第二个脚本,因为它使用了不同的命令来最小化应用程序。 - Sergey然后从菜单中选择Budgie桌面设置
它会给你两个选项,通过"+"添加从桌面设置中选择自动启动
1. 添加应用程序
2. 添加命令
通过选择添加应用程序,所有应用程序都将列出,选择任何你想要的应用程序,它将在你启动电脑时自动启动,并且也会最小化显示。&
可以完成任务
<运行应用程序的命令> &
-u
。Slack
,请使用以下命令:/usr/bin/slack %U -u
wmctrl -c <WIN>
通过将字符串与窗口标题匹配来自然关闭窗口。例如,下面我关闭了Autokey、Telegram、Pidgin和Whatsapp这些程序:/bin/bash -c "sleep 25 && wmctrl -c 'Autokey' && wmctrl -c 'Telegram' && wmctrl -c 'Buddy' && wmctrl -c 'Whatsapp'"