Bagaimana saya bisa menjalankan program saat startup, diminimalkan?

19

Saya hanya ingin Telegram dijalankan dan saya telah menambahkannya ke aplikasi startup. Intinya adalah bahwa saya perlu diminimalkan. Ada perintah?

Hossein Soltanloo
sumber
Apa perintah untuk memulai Telegram dan apa nama jendela setelah aplikasi dimulai?
Jacob Vlijm
Perintah yang saya gunakan hanyalah jalur aplikasi dan nama jendelanya adalah Telegram Desktop
Hossein Soltanloo
Hai Hossien, kalau-kalau Anda lebih suka menggunakan pid daripada judul jendela, edit jawaban saya.
Jacob Vlijm
@JacobVlijm Terima kasih! Ini sangat efisien dan bermanfaat! Namun metode pertama bekerja dengan mulus dalam kasus nama jendela variabel. Kerja bagus!
Hossein Soltanloo
1
@SumeetDeshmukh Anda orang yang sangat baik dan murah hati. Betulkah!
Jacob Vlijm

Jawaban:

29

Memulai aplikasi diminimalkan

Memulai aplikasi dengan cara yang diminimalkan membutuhkan dua perintah:

  • memulai aplikasi
  • meminimalkan jendelanya

Oleh karena itu, perintah atau skrip harus "pintar"; perintah kedua harus menunggu jendela aplikasi muncul.

Solusi umum untuk memulai aplikasi diminimalkan

Script di bawah ini melakukan itu dan dapat digunakan sebagai solusi umum untuk memulai aplikasi dengan cara yang diminimalkan. Jalankan saja di sintaks:

<script> <command_to_run_the_application> <window_name>

Naskah

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

Cara Penggunaan

Script membutuhkan keduanya wmctrldan xdotool:

sudo apt-get install wmctrl xdotool

Kemudian:

  1. Salin skrip ke file kosong, simpan sebagai startup_minimizd.py
  2. Tes-jalankan skrip dengan (misalnya) geditperintah:

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. Jika semua berfungsi dengan baik, tambahkan perintah (untuk aplikasi Anda) ke Startup Applications

Penjelasan

  • Script menjalankan aplikasi, menjalankan perintah yang Anda berikan sebagai argumen pertama
  • Kemudian skrip memeriksa daftar jendela (dengan bantuan wmctrl) untuk windows, dinamai argumen kedua Anda.
  • Jika jendela muncul, segera diminimalkan dengan bantuan xdotool Untuk mencegah perulangan tanpa akhir jika jendela mungkin tidak muncul karena suatu alasan, skrip mempraktikkan batas waktu 30 detik agar jendela muncul.

Catatan

Tidak perlu menyebutkan bahwa Anda dapat menggunakan skrip untuk beberapa aplikasi sekaligus, karena Anda menjalankannya dengan argumen di luar skrip.


EDIT

mengenali jendela dengan pidnya

Jika judul jendela tidak pasti atau variabel, atau ada risiko bentrokan nama dalam nama jendela, menggunakan pidadalah metode yang lebih dapat diandalkan untuk digunakan.

Script di bawah ini didasarkan pada penggunaan pid aplikasi, seperti pada output keduanya wmctrl -lpdanps -ef .

Pengaturannya hampir sama, tetapi judul jendela tidak diperlukan dalam versi ini, jadi perintah untuk menjalankannya adalah:

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

Sama seperti skrip pertama, ia membutuhkan keduanya wmctrldanxdotool

Naskah

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

Catat pada skrip kedua

Meskipun secara umum versi kedua harus lebih dapat diandalkan, dalam kasus ketika aplikasi dimulai oleh skrip wrapper, pid dari perintah akan berbeda dari aplikasi yang akhirnya dipanggil.

Dalam kasus seperti itu, saya sarankan menggunakan skrip pertama.



EDIT2 versi khusus skrip untuk Steam

Seperti yang diminta dalam komentar, di bawah versi, khusus dibuat untuk memulai STEAM diminimalkan.

Mengapa versi spesifik untuk Steam?

Ternyata Steamberperilaku sangat berbeda dari aplikasi "normal":

  • Ternyata Steamtidak menjalankan satu pid, tetapi tidak kurang dari (dalam pengujian saya) delapan!
  • Steamberjalan saat memulai dengan setidaknya dua jendela (satu jendela seperti splash), tetapi terkadang jendela pesan tambahan muncul.
  • Memiliki Windows Steam pid 0, yang merupakan masalah dalam skrip seperti itu.
  • Setelah jendela utama dibuat, jendela dinaikkan untuk kedua kalinya setelah sekitar satu detik, jadi minimalisasi tunggal tidak akan berhasil.

Perilaku luar biasa ini Steammeminta versi khusus dari skrip, yang ditambahkan di bawah ini. Script dimulai Steam, dan selama 12 detik, itu mengawasi semua jendela baru yang sesuai WM_CLASS, memeriksa apakah mereka diminimalkan. Jika tidak, skrip akan memastikannya.

Seperti skrip asli, yang ini perlu wmctrldan xdotoolharus diinstal.

Naskah

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

Untuk menggunakannya

  • Cukup salin ke file kosong, simpan sebagai runsteam_minimized.py
  • Jalankan dengan perintah:

    python3 /path/to/runsteam_minimized.py
    
Yakub Vlijm
sumber
wow, bagus! Saya tidak akan menangkap except:hanya untuk mengembalikan Tidak ada. Mungkin lebih baik membiarkannya gagal sehingga Anda melihat apa yang gagal; jika tidak, itu dapat rusak untuk segala jenis penyebab yang berbeda dan akan berlalu tanpa iklan.
fedorqui
1
@ fedorqui Bagus, dua pengecualian kemungkinan dapat terjadi: subprocess.CalledProcesError (sebagai hasil dari kereta wmctrl) dan IndexError(pengecualian normal) akan diedit dalam satu menit :). Terima kasih telah menyebutkan
Jacob Vlijm
@ HusseinSoltanloo Apa sebenarnya perintah Anda menjalankan skrip?
Jacob Vlijm
@ JacobVlijm Skrip berfungsi dengan baik tetapi ada masalah lain yang mungkin Anda perbaiki. Setiap kali saya memiliki pesan yang belum dibaca dan saya membuka aplikasi, nama jendela berubah menjadi sesuatu seperti "Telegram (2)" karena ada dua pesan yang belum dibaca dan dengan cara ini skrip tidak akan berfungsi karena perubahan nama.
Hossein Soltanloo
2
@ JDHolland Saya yakin ini bisa diperbaiki. Akan melihatnya di suatu tempat dalam beberapa hari ke depan :)
Jacob Vlijm
3

Ada baiknya memiliki skrip yang diberikan oleh user72216 dan Sergey sebagai solusi umum untuk masalah ini, tetapi kadang-kadang aplikasi yang Anda ingin mulai diminimalkan sudah memiliki sakelar yang akan melakukan apa yang Anda inginkan.

Berikut adalah beberapa contoh dengan string perintah program startup yang sesuai:

  • Telegram (sejak versi 0.7.10) memiliki -startintrayopsi:<path-to-Telegram>/Telegram -startintray
  • Steam memiliki -silentopsi:/usr/bin/steam %U -silent
  • Transmisi memiliki --minimizedopsi:/usr/bin/transmission-gtk --minimized

Di Unity aplikasi ini mulai diminimalkan sebagai ikon di bilah menu atas daripada sebagai ikon pada peluncur, meskipun ikon peluncuran normal akan tetap muncul setelah Anda mulai menggunakan aplikasi. Aplikasi lain mungkin berperilaku berbeda.

Francis Chin
sumber
1

Saya mengambil skrip Jacob dan memodifikasinya sedikit untuk membuatnya lebih universal.

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

Perbedaan utama adalah:

  • Program menetapkan ID grup (GID) untuk proses tersebut. Dengan demikian, semua proses anak dan jendela mereka dapat dengan mudah ditemukan
  • Opsi xdotool --sync digunakan sebagai ganti perulangan while
  • Script memungkinkan lewat argumen ke program

WAIT_TIME harus disetel cukup besar untuk memungkinkan program memotong proses anaknya. Di komputer saya itu sudah cukup untuk program besar seperti steam. Tingkatkan itu, jika perlu.

Tambahan

xdotoolOpsi windowunmapdapat berfungsi funky dengan beberapa aplikasi dan program baki (misalnya baki linux mint), jadi inilah versi alternatif dari skrip untuk pengecualian tersebut.

#!/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)
Sergey
sumber
Saya mencoba skrip pertama Anda. Itu tidak bekerja atau tidak meminimalkan cukup cepat. Saya menyimpannya sebagai startminimized. Lalu aku berlari startminimized gnome-calendar. Kalender terbuka dan terus berjalan?
Khurshid Alam
1
Anda dapat mencoba meningkatkan variabel WAIT_TIME. Saya menggunakan penundaan 40 detik untuk komputer yang lemah. Anda juga dapat mencoba skrip kedua karena menggunakan perintah yang berbeda untuk meminimalkan aplikasi.
Sergey
1

Jika program sedang ditutup ke baki, orang mungkin benar-benar ingin menutup jendela program saat startup daripada meminimalkannya. Salah satu contoh program tersebut adalah Viber. Dalam hal ini orang dapat menggunakan skrip berikut 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

Pemakaian: <path-to-script> <program-to-start>

Mykola Novik
sumber
1
Anda mungkin ingin mencatat bahwa xdotooltidak akan berfungsi dengan baik pada instalasi dengan Wayland.
Videonauth
0

Saya baru saja menjelajahi dan menemukan pertanyaan ini, jadi saya hanya ingin tahu apa sistem operasi Anda? Sedangkan saya, saya menggunakan UBUNTU BUDGIE 18.04 LTS sehingga dalam sistem operasi ini sangat sederhana.

Cukup buka menu

Dari Menu, buka Pengaturan Desktop Budgie

dan

Dari Pengaturan Desktop pergi ke Auto Start

Ini akan memberi Anda 2 opsi, dari "+" tambah

1. Tambahkan Aplikasi

2. Tambahkan Perintah

Dengan memilih Tambahkan Aplikasi, semua aplikasi akan terdaftar, pilih aplikasi apa pun yang Anda inginkan dan itu akan mulai ketika Anda memulai komputer Anda dan itu juga akan diminimalkan.

Muntaha Liaqat
sumber
0

Saya membutuhkan program yang tertutup untuk nampan, tidak diperkecil, dan saya sudah mencoba semua skrip yang diposting di sini, yang bekerja, hanya bekerja untuk beberapa program dan tidak untuk yang lain. Jadi saya telah membuat kode yang berfungsi lebih baik (Anda hampir tidak melihat jendela yang muncul, hanya ikon baki, yang terlihat asli) dan berfungsi untuk semua program yang saya coba. Ini didasarkan pada milik Yakub. Dengan skrip ini Anda mungkin perlu menambahkan argumen tergantung pada programnya (lihat di bawah) tetapi selalu bekerja untuk saya dengan banyak program itu juga harus bekerja dengan uap.

Pemakaian:

  1. sudo apt-get install wmctrl xdotool
  2. Simpan skrip sebagai startup_closed.pymemberikan izin eksekusi dan kemudian jalankanpython3 ./startup_closed.py -c <command to open program>
  3. Jika ikon baki program tidak menunjukkan atau jendela tidak muncul maka Anda perlu menambahkan salah satu argumen ini: -splashatau -hide, dengan coba-coba. Misalnya: python3 ./startup_closed.py -hide -c teamviewerataupython3 ./startup_closed.py -splash -c slack
  4. Ada lebih banyak argumen tetapi Anda mungkin tidak membutuhkannya. Juga ada detail lengkap kapan dan mengapa argumen diperlukan dalam bantuan:./startup_closed.py --help

Naskah:

#!/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")
fermmm
sumber
0

Saya datang dengan solusi yang agak elegan yang hanya mengandalkan xdotool, dan ini cukup berguna untuk aplikasi yang tidak memiliki argumen "mulai diminimalkan" , seperti Telegram.

Satu-satunya downside adalah bahwa solusi harus dibuat secara manual untuk setiap aplikasi, tetapi dengan asumsi itu bukan masalah (misalnya: jika Anda ingin melakukan autostart aplikasi tertentu tanpa membiarkannya mencemari layar Anda setelah masuk) , ini jauh lebih sederhana dan mudah. .

Contoh Aktual

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

Solusinya

Pada pandangan pertama, Anda mungkin berpikir lebih baik menggunakan proses 'PID atau kelas untuk mencocokkan, namun itu sebenarnya kontraproduktif karena Anda sering akan mendapatkan beberapa hasil untuk PID yang sama. Contohnya adalah jendela 0x0 yang sebenarnya menunggu pemberitahuan, ikon systray, atau jendela "tersembunyi" lainnya.

Solusinya adalah membuat perintah xdotool yang selalu mengembalikan hanya satu jendela unik . Dalam kedua contoh saya yang dilakukan menggunakan --name, namun Anda dapat menggabungkan beberapa penyeleksi dengan --all (misalnya: cocok dengan nama kelas yang diberikan + nama kelas + nama regex) . Biasanya --nameregex yang baik berhasil .

Setelah menyusun searchkondisi Anda , cukup buat instance xdotool (terlepas dari shell) dengan --syncparameter dan kondisi Anda, diikuti oleh windowclose. Jalankan aplikasi Anda setelahnya:

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

Periksa xdotool search --helpsemua kemungkinan kombinasi yang dapat Anda atur untuk dapat menargetkan jendela yang tepat yang Anda inginkan. Kadang-kadang itu menjadi rumit dan Anda harus menggabungkan beberapa kondisi, tetapi begitu Anda selesai, jarang akan gagal (kecuali pembaruan mengubah aplikasi dan menghancurkan implementasi Anda, tentu saja).

emi
sumber