Apakah ada skrip (atau perangkat lunak) untuk membuka jendela aplikasi pada viewport dan posisi tertentu?

8

Jadi, saya punya 8 Desktop virtual di Unity (dengan Compiz) karena saya punya banyak proyek yang sedang saya kerjakan secara bersamaan.

Masalahnya adalah, setiap kali saya perlu me-reboot atau menutup Chrome secara tidak sengaja (yang merupakan bagian besar dari windows yang saya perlukan untuk bekerja), saya harus membuka jendela itu secara manual lagi dan kemudian mengaturnya (membuka file, buka yang benar url dll.).

Bagaimana Anda akan menulis skrip yang akan melakukan semua itu untuk saya? Yaitu: 1) Buka windows 2) Masukkan mereka ke dalam koordinat yang benar di layar virtual yang benar

(1) jelas, untuk Google Chrome Anda cukup menjalankan 'google-chrome'. Tapi bagaimana Anda menempatkannya di tempat yang tepat? (2)

Atau ada skrip / perangkat lunak yang sudah ada yang akan melakukannya untuk saya?

snitko
sumber
Saya dapat mencoba membuat skrip untuk menempatkan jendela apa pun yang mungkin Anda perlukan pada desktop yang sesuai saat startup, mungkin memerlukan waktu beberapa hari, karena saya akan menyelesaikan final minggu depan. Ini akan melibatkan wmctrl, yang seperti perangkat lunak untuk mengendalikan windows melalui skrip atau terminal. Tetapi untuk memulai kembali sebuah jendela, itu mungkin sesuatu yang sedikit lebih menantang
Sergiy Kolodyazhnyy
Meskipun Anda secara khusus bertanya tentang Unity, perlu dicatat bahwa KDE memiliki program (kebanyakan tidak berdokumen) bernama kstart yang melakukan ini untuk Anda. Ini berfungsi baik dengan program-program KDE, tetapi juga memiliki beberapa keberhasilan dengan program-program lain.
Joe

Jawaban:

14

Ini bisa dilakukan dengan sangat baik, tetapi Anda perlu pemahaman tentang Unity / viewports. Saya harap cerita di bawah ini bisa dimengerti, jika tidak, silakan tinggalkan komentar.

Skrip di bawah ini dapat digunakan untuk membuka jendela aplikasi apa pun di salah satu viewports Anda, pada posisi apa pun, jika Anda menjalankannya dengan argumen yang benar. Script adalah versi yang diedit dari yang satu ini , tetapi sekarang siap untuk menempatkan windows pada desktop virtual yang merentang .

1. Memahami viewports dan koordinat jendela

Ruang kerja di Unity

Di Unity, tidak seperti window manager lainnya, Anda sebenarnya hanya memiliki satu ruang kerja spanning, yang dibagi menjadi viewports. Dalam kasus Anda, ruang kerja Anda dibagi menjadi delapan viewports.

Bagaimana posisi jendela didefinisikan

Posisi jendela, sebagai output dari perintah:

wmctrl -lG
(you need to have wmctrl installed to run the command)

digambarkan sebagai posisi, relatif terhadap sudut kiri atas viewport saat ini :


Jadi jika Anda berada di viewport 1:
sebuah jendela di viewport 2 dapat diposisikan pada mis. 1700 (x-bijaksana) x 500 (y-bijaksana)
(layar saya 1680x1050)

masukkan deskripsi gambar di sini


Namun, jika Anda berada di viewport 6:
jendela yang sama akan diposisikan pada 20 (x), -550 (y) masukkan deskripsi gambar di sini


Menggunakan koordinat ini dengan benar adalah penting untuk menjalankan skrip dengan argumen yang benar, seperti dijelaskan di bawah ini:

2. Cara menggunakan skrip

Script di bawah ini dapat digunakan untuk menempatkan jendela aplikasi baru di ruang kerja virtual Anda (spanning).

  1. Pastikan wmctrlsudah terpasang:

    sudo apt-get install wmctrl
    
  2. Salin skrip di bawah ini ke file kosong, simpan sebagai setwindow(tanpa ekstensi) di ~/bin. Buat direktori jika belum ada. Jadikan skrip dapat dieksekusi .

  3. Jika Anda baru saja dibuat ~/bin, jalankan perintah source ~/.profileatau keluar / masuk untuk membuat direktori tersedia di $PATH.
  4. Uji jalankan perintah:

    setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size>
    

    misalnya

    setwindow gedit 100 100 200 200
    

    Jendela gedit akan muncul di viewport saat ini.

Catatan:

  • Ingatlah bahwa tidak semua aplikasi memungkinkan ukuran jendela di bawah lebar atau tinggi tertentu. Lebar minimum geditjendela pada sistem saya adalah mis. Appr. 470 px.
  • Script hanya berfungsi dengan baik jika seluruh jendela pas di viewport yang ditargetkan, pilih koordinat / ukuran Anda sesuai. Juga perhatikan bahwa Unity Launcher dan panel menggunakan beberapa ruang (!) Yang dapat memengaruhi posisi jendela.
  • Gunakan negatif <x_position>untuk menempatkan jendela di sebelah kiri viewport saat ini
  • Gunakan negatif <y_position>untuk menempatkan jendela di atas viewport saat ini
  • Untuk membuka jendela baru pada viewports yang berbeda sekaligus, Anda dapat dengan mudah mengaitkan perintah. Melihat pengaturan viewport dalam contoh "Panjang cerita", Jika saya di viewport 1, saya dapat membuka jendela gedit di viewport 1, 2, 3 dan 4 dengan perintah:

    setwindow gedit 100 100 200 200&&setwindow gedit 1780 100 200 200&&setwindow gedit 3460 100 200 200&&setwindow gedit 5140 100 200 200
    

Naskah

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

app = sys.argv[1]

get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", app])
# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else app
while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        cmd3 = "wmctrl -ir "+procs[0][0][1]+" -e 0,"+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]+","+sys.argv[5]
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1



EDIT: versi malas

Jika Anda lebih suka memasukkan koordinat dan ukuran, seperti seolah-olah Anda akan membuka jendela pada viewport saat ini, dan memberikan viewport yang ditargetkan sebagai argumen (tanpa harus menghitung apa pun), maka gunakan versi di bawah ini ...

Jika Anda mengaturnya seperti versi pertama skrip, Anda dapat menjalankannya dengan perintah:

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport>

Contoh: untuk membuka Google-Chromejendela yang diposisikan pada 20, 20, ukuran 300x300, pada viewport 5:

setwindow google-chrome 20 20 300 300 5

Penyiapannya hampir sama dengan skrip versi pertama.
Perhatikan bahwa skrip ini juga hanya berfungsi dengan benar jika jendela yang ditentukan (posisi / ukuran) cocok sepenuhnya dalam viewport yang ditargetkan.

Naskah:

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

app = sys.argv[1]
target_vp = int(sys.argv[6])

def get_res():
    # get resolution
    xr = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    pos = xr.index("current")
    return [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]

res = get_res()

def current(set_vp):
    # get the current viewport
    vp_data = subprocess.check_output(
        ["wmctrl", "-d"]
        ).decode("utf-8").split()
    dt = [int(n) for n in vp_data[3].split("x")]
    cols = int(dt[0]/res[0])
    rows = int(dt[1]/res[1])    
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    curr_col = int(curr_vpdata[0]/res[0])
    curr_row = int(curr_vpdata[1]/res[1])
    curr_vp = curr_col+curr_row*cols+1
    # calculate the vector to the origin from the current viewport (in resolution units)
    vec_curr = vector(curr_vp, cols)
    # calculate the vector to the origin from the targeted viewport
    vec_set = vector(set_vp, cols)
    # calculate the vector between current and targeted viewport
    vec_relative = [vec_set[0] - vec_curr[0],
                    vec_set[1] - vec_curr[1]]
    # calculate needed correction (absolute)
    relative = [vec_relative[0]*res[0],
                vec_relative[1]*res[1]]
    return relative

def vector(vp, cols):
    rem = vp%cols
    vec_x = rem-1 if rem != 0 else cols-1
    vec_y = int((vp-1)/cols)
    return [vec_x, vec_y]

res = get_res() # nieuw
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
# check for additional arguments to run the application
try:
    subprocess.Popen(["/bin/bash", "-c", app+" "+sys.argv[7]])  
except IndexError:
    subprocess.Popen(["/bin/bash", "-c", app])

# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else app
while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        # calculate the correction, related to the current workspace, marge for launcher and panel
        pos_x = int(sys.argv[2]); pos_y = int(sys.argv[3]); x_marge = 65; y_marge = 35
        pos_x = pos_x if pos_x > x_marge else x_marge; pos_y = pos_y if pos_y > y_marge else y_marge
        x_relative = pos_x+current(target_vp)[0]
        y_relative = pos_y+current(target_vp)[1]
        # correct possible inaccurately set width / height
        x_size = res[0]; y_size = res[1]
        set_width = int(sys.argv[4]); set_height = int(sys.argv[5])
        width = set_width if set_width+x_marge+pos_x < x_size else x_size - pos_x - x_marge
        height = set_height if set_height+y_marge+pos_y < y_size else y_size - pos_y - y_marge
        cmd3 = "wmctrl -ir "+w_id+" -e 0,"+str(x_relative)+","+str(y_relative)+","+str(width)+","+str(height)
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1


Membuka jendela aplikasi dengan argumen

Untuk menyelesaikan pekerjaan, jawab pertanyaan Anda sepenuhnya:

Jika Anda menjalankan skrip seperti misalnya:

setwindow google-chrome 20 20 300 300 5

itu akan membuka jendela default pada desktop yang ditargetkan.
Namun, dengan skrip versi terbaru, Anda dapat menambahkan argumen tambahan untuk membuka jendela aplikasi, misalnya url:

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport> <(optional)_argument>

misalnya:

setwindow google-chrome 0 0 600 600 3 "--new-window http://askubuntu.com"

Jika argumen (ekstra) berisi spasi, gunakan tanda kutip. Contoh di atas akan membuka google-chromejendela pada viewport 3, membuka jendela url http://askubuntu.com.

Anda dapat mengaitkan perintah untuk membuka banyak jendela / url pada ruang kerja yang berbeda dalam satu perintah, misalnya:

setwindow google-chrome 0 0 600 600 8 "--new-window http://askubuntu.com"&&setwindow google-chrome 0 0 600 600 7 "--new-window www.google.com"
Yakub Vlijm
sumber
@snitko Terima kasih atas pertanyaan yang menyenangkan, itu adalah tantangan yang bagus untuk menyelesaikannya :)
Jacob Vlijm
Saya perhatikan ada sedikit offset untuk jendela ketika saya menggunakan skrip. Jadi, misalnya, jika saya buka pada koordinat 0 0, sebenarnya dibuka sedikit lebih ke kanan dan ke bawah (a ~ 10px offset). Tidak apa-apa, tetapi masalahnya sebenarnya dengan skrip kedua: offset dalam skrip kedua anehnya lebih besar pada sumbu horizontal. Saya pikir ini tentang ~ 50px ke kiri. Bisakah Anda melihat mengapa begitu? Mengatur koordinat negatif tidak membantu dalam kasus itu.
snitko
Pertanyaan lain: bagaimana cara membuka jendela layar penuh?
snitko
Pembaruan: offset dalam kasus skrip kedua tampaknya sama dengan lebar dermaga penyatuan di sebelah kiri (meskipun tersembunyi).
snitko
Bagi yang berminat, saya telah mengimplementasikan Desktopen: github.com/snitko/desktopen
snitko
1

Ini diperluas pada jawaban hebat @Jacob Vlijim di atas dengan setwindowskrip yang sedikit dimodifikasi :

#!/usr/bin/env python

import time
import argparse
import subprocess

DEFAULT_WIDTH = '1920'
DEFAULT_HEIGHT = '1080'


def get_window_list():
    window_list = subprocess.check_output(['/bin/bash', '-c', 'wmctrl -l'])
    parsed_list = []
    for line in window_list.splitlines():
        window_info = line.split()
        if window_info[1] != '-1':
            parsed_list.append(window_info[0])
    return parsed_list


def main(params):
    old_list = get_window_list()
    subprocess.Popen(['/bin/bash', '-c', params.command])

    def get_diff(old):
        new_list = get_window_list()
        return list(set(new_list) - set(old))

    diff = get_diff(old_list)
    x = 0
    while not diff:
        if x == 10:
            print 'window not found'
            return
        x += 1
        diff = get_diff(old_list)
        time.sleep(1)
    if len(diff) > 1:
        raise Exception(diff)
    window_id = diff[0]
    command_list = []
    command_list.append('wmctrl -ir %s -t %s' % (window_id, params.desktop))
    command_list.append('wmctrl -ir %s -b remove,maximized_horz,maximized_vert'
        % window_id)
    command_list.append('wmctrl -ir %s -e 0,%s,%s,%s,%s' %
        (window_id, params.x_pos, params.y_pos, params.width, params.height))
    for command in command_list:
        subprocess.call(['/bin/bash', '-c', command])

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('command', type=str)
    parser.add_argument('-d', '--desktop', default='0', type=str)
    parser.add_argument('-x', '--x-pos', default='0', type=str)
    parser.add_argument('-y', '--y-pos', default='0', type=str)
    parser.add_argument('-w', '--width', default=DEFAULT_WIDTH, type=str)
    parser.add_argument('-t', '--height', default=DEFAULT_HEIGHT, type=str)
    args = parser.parse_args()
    main(args)

Deskripsi perubahan:

  1. python3ke python(hanya preferensi pribadi)
  2. sys.argvke argparseuntuk antarmuka baris perintah yang lebih baik
  3. penguraian jendela id ketat (dan bukan id proses)
    • beberapa program menggunakan id proses tunggal untuk banyak windows
  4. while loop 0,5 detik hingga 1 detik penuh waktu tidur
  5. lebih banyak nama variabel verbose / dapat dibaca dan kepatuhan pep8
  6. variabel konstan global untuk ukuran layar alih-alih xrandrketergantungan

CATATAN: Ini adalah versi yang sedikit lebih baik yang saya tulis untuk penggunaan pribadi di Debian Jessie LXDE. Hasil Anda dapat bervariasi.

lscstu22
sumber
0

Bagi yang berminat, saya telah mengimplementasikan Desktopen: github.com/snitko/desktopen

Ini memungkinkan Anda untuk menulis skrip untuk membuka jendela pada berbagai viewports dan tampilan dengan cara yang sangat ramah.

snitko
sumber