Bagaimana saya bisa mengunci aplikasi (dan semua windows baru) ke ruang kerja tertentu?

11

Saya menjalankan Matlabskrip di workspace 1. Ini menghasilkan beberapa plot. Sementara itu saya beralih ke workspace 2dan bekerja di sana. Masalah saya adalah bahwa plot muncul workspace 2. Apakah mungkin untuk mengunci perangkat lunak ke ruang kerja. Jadi saat Matlabmembuat plot workspace 1, saya dapat bekerja workspace 2tanpa gangguan pada plot yang muncul?

OHLÁLÁ
sumber
Persatuan, GNOME Shell atau yang lainnya?
AB
Saya menambahkan tag, Ini adalah Ubuntu 14,04 dengan Unity
OHLÁLÁ
Di kelas apa jendela plot itu berada? (bisa Anda periksa dengan perintah xprop WM_CLASS, dan kemudian klik pada jendela?) Silakan juga tambahkan WM_CLASS dari Matlab.
Jacob Vlijm
2
Saya akan memposting hari ini, jika tidak ada orang yang memposting solusi brilian lain sementara itu.
Jacob Vlijm
1
Hai OHLÁLÁ, saya benar-benar membuatnya bekerja dengan baik, semua jendela tambahan aplikasi langsung dipindahkan ke ruang kerja awal aplikasi, tapi .... memang jendela saat ini di ruang kerja saat ini kehilangan fokus. Masih mencari solusi. Apakah Anda masih mencoba solusinya?
Jacob Vlijm

Jawaban:

8

EDIT PENTING

Di bawah versi naskah yang ditulis ulang dari jawaban pertama (di bawah). Perbedaan:

  • Skrip sekarang memiliki sumber daya yang sangat rendah (seperti seharusnya dengan skrip latar belakang). Tindakan sekarang diatur untuk bertindak jika (dan hanya jika) mereka diperlukan. Secara praktis, loop tidak melakukan apa pun selain memeriksa jendela baru yang muncul.
  • Bot WM_CLASSdan ruang kerja yang ditargetkan sekarang menjadi argumen untuk menjalankan skrip. Gunakan hanya bagian pertama atau kedua (pengidentifikasi) dari WM_CLASS(lihat lebih lanjut di bawah ini: cara menggunakan)
  • Script sekarang tetap fokus pada jendela yang sedang aktif (sebenarnya fokus kembali dalam sepersekian detik)
  • Ketika skrip dimulai, itu menunjukkan pemberitahuan (contoh gedit):

    masukkan deskripsi gambar di sini

Naskah

#!/usr/bin/env python3
import subprocess
import sys
import time
import math

app_class = sys.argv[1]
ws_lock = [int(n)-1 for n in sys.argv[2].split(",")]

def check_wlist():
    # get the current list of windows
    try:
        raw_list = [
            l.split() for l in subprocess.check_output(
                ["wmctrl", "-lG"]
                ).decode("utf-8").splitlines()
            ]
        ids = [l[0] for l in raw_list]
        return (raw_list, ids)
    except subprocess.CalledProcessError:
        pass

def get_wssize():
    # get workspace size
    resdata = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    i = resdata.index("current")
    return [int(n) for n in [resdata[i+1], resdata[i+3].replace(",", "")]]

def get_current(ws_size):
    # vector of the current workspace to origin of the spanning desktop
    dt_data = subprocess.check_output(
        ["wmctrl", "-d"]
        ).decode("utf-8").split()
    curr = [int(n) for n in dt_data[5].split(",")]
    return (int(curr[0]/ws_size[0]), int(curr[1]/ws_size[1]))

def get_relativewinpos(ws_size, w_data):
    # vector to the application window, relative to the current workspace
    xpos = int(w_data[2]); ypos = int(w_data[3])
    xw = ws_size[0]; yw = ws_size[1]
    return (math.ceil((xpos-xw)/xw), math.ceil((ypos-yw)/yw))

def get_abswindowpos(ws_size, w_data):
    # vector from the origin to the current window's workspace (flipped y-axis)
    curr_pos = get_current(ws_size)
    w_pos = get_relativewinpos(ws_size, w_data)
    return (curr_pos[0]+w_pos[0], curr_pos[1]+w_pos[1])

def wm_class(w_id):
    # get the WM_CLASS of new windows
    return subprocess.check_output(
        ["xprop", "-id", w_id.strip(), "WM_CLASS"]
        ).decode("utf-8").split("=")[-1].strip()

ws_size = get_wssize()
wlist1 = []
subprocess.Popen(["notify-send", 'workspace lock is running for '+app_class])

while True:
    # check focussed window ('except' for errors during "wild" workspace change)
    try:
        focus = subprocess.check_output(
            ["xdotool", "getwindowfocus"]
            ).decode("utf-8")
    except subprocess.CalledProcessError:
        pass
    time.sleep(1)
    wdata = check_wlist() 
    if wdata !=  None:
        # compare existing window- ids, checking for new ones
        wlist2 = wdata[1]
        if wlist2 != wlist1:
            # if so, check the new window's class
            newlist = [[w, wm_class(w)] for w in wlist2 if not w in wlist1]
            valids = sum([[l for l in wdata[0] if l[0] == w[0]] \
                          for w in newlist if app_class in w[1]], [])
            # for matching windows, check if they need to be moved (check workspace)
            for w in valids:
                abspos = list(get_abswindowpos(ws_size, w))
                if not abspos == ws_lock:
                    current = get_current(ws_size)
                    move = (
                        (ws_lock[0]-current[0])*ws_size[0],
                            (ws_lock[1]-current[1])*ws_size[1]-56
                        )
                    new_w = "wmctrl -ir "+w[0]+" -e "+(",").join(
                        ["0", str(int(w[2])+move[0]),
                         str(int(w[2])+move[1]), w[4], w[5]]
                        )
                    subprocess.call(["/bin/bash", "-c", new_w])
                    # re- focus on the window that was focussed
                    if not app_class in wm_class(focus):
                        subprocess.Popen(["wmctrl", "-ia", focus])
        wlist1 = wlist2

Cara Penggunaan

  1. Script membutuhkan keduanya wmctrldan xdotool:

    sudo apt-get install wmctrl xdotool
    
  2. Salin skrip di atas ke dalam file kosong, simpan sebagai lock_towspace.py

  3. Dari aplikasi spesifik Anda, cari tahu WM_CLASS: buka aplikasi Anda, jalankan di terminal:

    xprop WM_CLASS and click on the window of the application
    

    Outputnya akan terlihat seperti (dalam kasus Anda):

    WM_CLASS: WM_CLASS(STRING) = "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"
    

    Gunakan bagian pertama atau kedua dalam perintah untuk menjalankan skrip.

  4. Perintah untuk menjalankan skrip adalah:

    python3 /path/to/lock_towspace.py "sun-awt-X11-XFramePeer" 2,2
    

    Dalam perintah, bagian terakhir; 2,2adalah ruang kerja tempat Anda ingin mengunci aplikasi ke (tanpa spasi: (!) kolom, baris ), dalam format "manusia"; kolom / baris pertama adalah1,1

  5. Uji skrip dengan menjalankannya. Saat berjalan, buka aplikasi Anda dan biarkan menghasilkan windows seperti biasa. Semua jendela akan muncul di ruang kerja yang ditargetkan, sebagaimana diatur dalam perintah.

JAWABAN KAKI:

(kedua) UJI VERSI

Skrip di bawah ini mengunci aplikasi tertentu ke ruang kerja awalnya. Jika skrip dimulai, ia menentukan ruang kerja tempat aplikasi berada. Semua jendela tambahan yang dihasilkan aplikasi akan dipindahkan ke ruang kerja yang sama dalam sepersekian detik.

Masalah fokus diselesaikan dengan secara otomatis memfokuskan kembali pada jendela yang difokuskan sebelum jendela tambahan diproduksi.

Naskah

#!/usr/bin/env python3
import subprocess
import time
import math

app_class = '"gedit", "Gedit"'

def get_wssize():
    # get workspace size
    resdata = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    i = resdata.index("current")
    return [int(n) for n in [resdata[i+1], resdata[i+3].replace(",", "")]]

def get_current(ws_size):
    # get vector of the current workspace to the origin of the spanning desktop (flipped y-axis)
    dt_data = subprocess.check_output(["wmctrl", "-d"]).decode("utf-8").split(); curr = [int(n) for n in dt_data[5].split(",")]
    return (int(curr[0]/ws_size[0]), int(curr[1]/ws_size[1]))

def get_relativewinpos(ws_size, w_data):
    # vector to the application window, relative to the current workspace
    xw = ws_size[0]; yw = ws_size[1]
    return (math.ceil((w_data[1]-xw)/xw), math.ceil((w_data[2]-yw)/yw))

def get_abswindowpos(ws_size, w_data):
    curr_pos = get_current(ws_size)
    w_pos = get_relativewinpos(ws_size, w_data)
    return (curr_pos[0]+w_pos[0], curr_pos[1]+w_pos[1])

def wm_class(w_id):
    return subprocess.check_output(["xprop", "-id", w_id, "WM_CLASS"]).decode("utf-8").split("=")[-1].strip()

def filter_windows(app_class):
    # find windows (id, x_pos, y_pos) of app_class
    try:
        raw_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8").splitlines()]
        return [(l[0], int(l[2]), int(l[3]), l[4], l[5]) for l in raw_list if wm_class(l[0]) == app_class]
    except subprocess.CalledProcessError:
        pass

ws_size = get_wssize()
init_window = get_abswindowpos(ws_size, filter_windows(app_class)[0])
valid_windows1 = filter_windows(app_class)

while True:
    focus = subprocess.check_output(["xdotool", "getwindowfocus"]).decode("utf-8")
    time.sleep(1)
    valid_windows2 = filter_windows(app_class)
    if all([valid_windows2 != None, valid_windows2 != valid_windows1]):
        for t in [t for t in valid_windows2 if not t[0] in [w[0] for w in valid_windows1]]:
            absolute = get_abswindowpos(ws_size, t)
            if not absolute == init_window:
                current = get_current(ws_size)
                move = ((init_window[0]-current[0])*ws_size[0], (init_window[1]-current[1])*ws_size[1]-56)
                new_w = "wmctrl -ir "+t[0]+" -e "+(",").join(["0", str(t[1]+move[0]), str(t[2]+move[1]), t[3], t[4]])
                subprocess.call(["/bin/bash", "-c", new_w])
            focus = str(hex(int(focus)))
            z = 10-len(focus); focus = focus[:2]+z*"0"+focus[2:]
            if not wm_class(focus) == app_class:
                subprocess.Popen(["wmctrl", "-ia", focus])
        valid_windows1 = valid_windows2

Cara Penggunaan

  1. Script membutuhkan keduanya wmctrldanxdotool

    sudo apt-get install wmctrl xdotool
    
  2. Salin skrip ke file kosong, simpan sebagai keep_workspace.py

  3. tentukan `WM_CLASS 'aplikasi Anda dengan membuka aplikasi, lalu buka terminal dan jalankan perintah:

    xprop WM_CLASS
    

    Kemudian klik pada jendela aplikasi Anda. Salin hasilnya, terlihat seperti "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"dalam kasus Anda, dan letakkan di antara tanda kutip tunggal di bagian kepala skrip, seperti yang ditunjukkan.

  4. Jalankan skrip dengan perintah:

    python3 /path/to/keep_workspace.py
    

Jika berfungsi sesuka Anda, saya akan menambahkan fungsi sakelar. Meskipun sudah berfungsi selama beberapa jam di sistem saya, tetapi mungkin perlu beberapa penyesuaian terlebih dahulu.

Catatan

Meskipun Anda tidak akan melihat itu, script tidak menambahkan beberapa beban prosesor ke sistem. Pada sistem lanjut usia saya, saya perhatikan peningkatan 3-10%. Jika Anda suka cara kerjanya, saya mungkin akan men-tweak lebih lanjut untuk mengurangi beban.

Script, seperti itu, mengasumsikan jendela sekunder dari kelas yang sama dengan jendela utama, seperti yang Anda tunjukkan dalam komentar. Dengan perubahan (sangat) sederhana, jendela sekunder dapat dari kelas lain.

Penjelasan

Meskipun mungkin tidak terlalu menarik untuk pembaca rata-rata, skrip bekerja dengan menghitung dalam vektor. Saat memulai, skrip menghitung:

  • vektor dari asal ke ruang kerja saat ini dengan output dari wmctrl -d
  • vektor ke jendela aplikasi, relatif ke ruang kerja saat ini, oleh output dari wmctrl -lG
  • Dari keduanya, skrip menghitung posisi absolut dari jendela aplikasi pada desktop spanning (semua ruang kerja dalam satu matriks)

Sejak saat itu, skrip mencari jendela baru dari aplikasi yang sama, dengan output dari xprop WM_CLASS, mencari posisi mereka dengan cara yang sama seperti di atas dan memindahkannya ke ruang kerja "asli".

Karena jendela yang baru dibuat "mencuri" fokus dari jendela yang terakhir digunakan pengguna, fokus kemudian diatur ke jendela yang sebelumnya fokus.

Yakub Vlijm
sumber
Ini sangat menakjubkan. Mungkin merupakan ide bagus untuk membuat indikator di mana pengguna dapat mengunci aplikasi yang berbeda di ruang kerja. Saat ini saya punya masalah dengan Matlab, tetapi masalah yang sama akan terjadi dengan matplotlib
OHLÁLÁ
@ OHLÁLÁ seperti yang disebutkan, saya menemukan pertanyaan yang sangat menarik dan akan terus mengerjakannya. Apa yang ada dalam pikiran saya adalah file di mana pengguna dapat mengatur applicationdan workspace-set. Jika Anda menemukan kemungkinan bug, sebutkan itu!
Jacob Vlijm
Apa yang akan menjadi perilaku ketika dua Matlab dimulai pada ruang kerja yang terpisah?
OHLÁLÁ
@ OHLÁLÁ maka keduanya akan dikunci ke ruang kerja yang Anda atur di perintah. Karena mereka WM_CLASSidentik, yang kedua akan dipindahkan ke yang Anda atur di perintah.
Jacob Vlijm
Apakah ada kemungkinan lain untuk mengidentifikasi aplikasi, selain WM_CLASS?
OHLÁLÁ