มีสคริปต์ (หรือซอฟต์แวร์) เพื่อเปิดหน้าต่างแอปพลิเคชันในวิวพอร์ตและตำแหน่งที่ระบุหรือไม่


8

ดังนั้นฉันมีเดสก์ท็อปเสมือน 8 รายการใน Unity (พร้อม Compiz) เพราะฉันมีหลายโครงการที่ฉันกำลังทำงานพร้อมกัน

ปัญหาคือทุกครั้งที่ฉันต้องรีบูตหรือปิด Chrome โดยไม่ตั้งใจ (ซึ่งถือว่าเป็นส่วนใหญ่ของหน้าต่างที่ฉันต้องการใช้งาน) ฉันต้องเปิดหน้าต่างเหล่านั้นด้วยตนเองอีกครั้งแล้วตั้งค่า (เปิดไฟล์ไปที่ถูกต้อง URL เป็นต้น)

คุณจะเขียนบทที่จะทำทุกอย่างให้ฉันได้อย่างไร นั่นคือ: 1) เปิด windows 2) ใส่พิกัดที่ถูกต้องที่หน้าจอเสมือนที่ถูกต้อง

(1) ชัดเจนสำหรับ Google Chrome ที่คุณเรียกใช้ 'google-chrome' แต่คุณจะวางไว้ในที่ที่เหมาะสมได้อย่างไร? (2)

หรือมีสคริปต์ / ซอฟต์แวร์ที่มีอยู่แล้วที่จะทำเพื่อฉัน


ฉันสามารถสร้างสคริปต์เพื่อวางหน้าต่างอะไรก็ได้ที่คุณต้องการบนเดสก์ท็อปที่เหมาะสมเมื่อเริ่มต้นอาจใช้เวลาสักสองสามวันเพราะฉันมีรอบชิงชนะเลิศมาในสัปดาห์หน้า มันจะเกี่ยวข้องwmctrlซึ่งเป็นเหมือนซอฟต์แวร์สำหรับควบคุมหน้าต่างผ่านสคริปต์หรือเทอร์มินัล แต่สำหรับการรีสตาร์ทหน้าต่างนั่นอาจเป็นเรื่องท้าทายอีกเล็กน้อย
Sergiy Kolodyazhnyy

แม้ว่าคุณจะถามถึง Unity โดยเฉพาะ แต่ก็น่าสังเกตว่า KDE มีโปรแกรม (ส่วนใหญ่ที่ไม่มีเอกสาร) เรียกว่า kstart ซึ่งทำสิ่งนี้ให้คุณ มันใช้งานได้ดีที่สุดกับโปรแกรม KDE แต่ก็ประสบความสำเร็จกับโปรแกรมอื่น
Joe

คำตอบ:


14

สามารถทำได้ดีมาก แต่คุณต้องมีความเข้าใจเกี่ยวกับ Unity / วิวพอร์ต ฉันหวังว่าเรื่องราวด้านล่างนี้เป็นที่เข้าใจได้ถ้าไม่ใช่โปรดแสดงความคิดเห็น

สคริปต์ด้านล่างนี้สามารถใช้เพื่อเปิดหน้าต่างของแอปพลิเคชันใด ๆ ในวิวพอร์ตใด ๆ ของคุณในทุกตำแหน่งหากคุณเรียกใช้ด้วยอาร์กิวเมนต์ที่ถูกต้อง สคริปต์นี้เป็นเวอร์ชันที่ถูกแก้ไขของอันนี้แต่ตอนนี้ได้เตรียมที่จะวาง windows บนเดสก์ท็อปเสมือนที่ขยายออก

1. การทำความเข้าใจวิวพอร์ตและพิกัดหน้าต่าง

พื้นที่ทำงานใน Unity

ใน Unity ซึ่งแตกต่างจากตัวจัดการหน้าต่างอื่น ๆ จริง ๆ แล้วคุณมีพื้นที่ทำงานเพียงแห่งเดียวซึ่งแบ่งออกเป็นวิวพอร์ต ในกรณีของคุณเวิร์กสเปซของคุณแบ่งออกเป็นแปดวิวพอร์ต

วิธีการกำหนดตำแหน่งของหน้าต่าง

ตำแหน่งหน้าต่างเป็นผลลัพธ์ของคำสั่ง:

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

ถูกอธิบายว่าเป็นตำแหน่งซึ่งสัมพันธ์กับมุมซ้ายบนของวิวพอร์ตปัจจุบัน :


ดังนั้นหากคุณอยู่ในวิวพอร์ต1:
หน้าต่างในวิวพอร์ต 2 ที่สามารถวางตำแหน่งบนเช่น 1700 (x-wise) x 500 (y-wise)
(หน้าจอของฉันคือ 1680x1050)

ป้อนคำอธิบายรูปภาพที่นี่


อย่างไรก็ตามหากคุณอยู่ในวิวพอร์ต 6:
หน้าต่างเดียวกันจะอยู่ในตำแหน่งที่ 20 (x), -550 (y) ป้อนคำอธิบายรูปภาพที่นี่


การใช้พิกัดเหล่านี้อย่างถูกต้องเป็นสิ่งสำคัญในการเรียกใช้สคริปต์ด้วยอาร์กิวเมนต์ที่ถูกต้องดังที่อธิบายไว้ด้านล่าง:

2. วิธีใช้งานสคริปต์

สคริปต์ด้านล่างนี้สามารถใช้เพื่อวางหน้าต่างใหม่ของแอปพลิเคชันบนเวิร์กสเปซเสมือน (การขยาย) ของคุณ

  1. ตรวจสอบให้แน่ใจว่าwmctrlได้ติดตั้งแล้ว:

    sudo apt-get install wmctrl
    
  2. คัดลอกสคริปต์ด้านล่างลงในไฟล์ที่ว่างเปล่าบันทึกเป็นsetwindow(ไม่มีนามสกุล) ~/binใน สร้างไดเรกทอรีหากยังไม่มีอยู่ ทำให้สคริปต์เรียกใช้งานได้

  3. ถ้าคุณสร้างเพียง~/binอย่างใดอย่างหนึ่งเรียกใช้คำสั่งsource ~/.profileหรือออกจากระบบ / $PATHในเพื่อให้สามารถใช้ได้ในไดเรกทอรี
  4. ทดสอบเรียกใช้คำสั่ง:

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

    เช่น

    setwindow gedit 100 100 200 200
    

    หน้าต่าง gedit ควรปรากฏในวิวพอร์ตปัจจุบัน

หมายเหตุ:

  • โปรดทราบว่าบางแอปพลิเคชันไม่อนุญาตให้มีขนาดหน้าต่างด้านล่างความกว้างหรือความสูง ความกว้างต่ำสุดของgeditหน้าต่างในระบบของฉันคือ appr 470 px
  • สคริปต์จะทำงานได้ดีถ้าทั้งหน้าต่างพอดีกับวิวพอร์ตเป้าหมายให้เลือกพิกัด / ขนาดของคุณตามนั้น นอกจากนี้โปรดทราบว่าตัวเรียกใช้งาน Unity และแผงควบคุมนั้นใช้พื้นที่ (!) ซึ่งสามารถมีอิทธิพลต่อตำแหน่งของหน้าต่าง
  • ใช้ค่าลบ<x_position>เพื่อวางหน้าต่างทางด้านซ้ายของวิวพอร์ตปัจจุบัน
  • ใช้ค่าลบ<y_position>เพื่อวางหน้าต่างเหนือวิวพอร์ตปัจจุบัน
  • หากต้องการเปิดหน้าต่างใหม่ในวิวพอร์ตที่แตกต่างกันในครั้งเดียวคุณสามารถเชนคำสั่งได้ ดูที่การตั้งค่าวิวพอร์ตในตัวอย่าง "เรื่องยาว" ถ้าฉันเป็นวิวพอร์ต 1 ฉันสามารถเปิดหน้าต่าง gedit บนวิวพอร์ต 1, 2, 3 และ 4 ด้วยคำสั่ง:

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

สคริปต์

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



แก้ไข: รุ่นขี้เกียจ

ในกรณีที่คุณต้องการป้อนพิกัดและขนาดเพียงแค่เปิดหน้าต่างในวิวพอร์ตปัจจุบันและกำหนดวิวพอร์ตเป้าหมายเป็นอาร์กิวเมนต์ (โดยไม่ต้องคำนวณอะไร) จากนั้นใช้เวอร์ชันด้านล่าง ...

หากคุณตั้งค่าเหมือนสคริปต์เวอร์ชันแรกคุณสามารถเรียกใช้ได้ด้วยคำสั่ง:

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

ตัวอย่าง: เมื่อต้องการเปิดGoogle-Chromeหน้าต่างในตำแหน่ง20, 20, ขนาด300x300, บนวิวพอร์ต5:

setwindow google-chrome 20 20 300 300 5

การตั้งค่าค่อนข้างเหมือนกับเวอร์ชั่นแรกของสคริปต์
โปรดทราบว่าสคริปต์นี้ยังทำงานได้อย่างถูกต้องหากหน้าต่างที่กำหนด (ตำแหน่ง / ขนาด) เหมาะสมอย่างสมบูรณ์ภายในวิวพอร์ตเป้าหมาย

สคริปต์:

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


การเปิดหน้าต่างแอปพลิเคชันพร้อมอาร์กิวเมนต์

เพื่อให้งานสำเร็จให้ตอบคำถามของคุณโดยสมบูรณ์:

หากคุณเรียกใช้สคริปต์เช่น:

setwindow google-chrome 20 20 300 300 5

มันจะเปิดหน้าต่างเริ่มต้นบนเดสก์ท็อปเป้าหมาย
อย่างไรก็ตามด้วยเวอร์ชันล่าสุดของสคริปต์คุณสามารถเพิ่มอาร์กิวเมนต์เพิ่มเติมเพื่อเปิดหน้าต่างแอปพลิเคชันตัวอย่างเช่นurl:

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

เช่น:

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

หากอาร์กิวเมนต์ (พิเศษ) มีช่องว่างให้ใช้เครื่องหมายอัญประกาศ ตัวอย่างข้างต้นจะเปิดgoogle-chromeหน้าต่างวิวพอร์ต 3 url http://askubuntu.comเปิด

คุณสามารถโยงคำสั่งเพื่อเปิดหลาย windows / url บนพื้นที่ทำงานที่แตกต่างกันในคำสั่งเดียวเช่น:

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"

@snitko ขอขอบคุณสำหรับคำถามที่ดีมันเป็นความท้าทายที่ดีที่จะได้รับมันกระทำ :)
จาค็อบ Vlijm

ฉันสังเกตเห็นว่ามีการชดเชยเล็กน้อยสำหรับหน้าต่างเมื่อฉันใช้สคริปต์ ตัวอย่างเช่นถ้าฉันเปิดที่พิกัด 0 0 จริง ๆ แล้วมันจะเปิดทางด้านขวาและด้านล่างเล็กน้อย (ออฟเซ็ต ~ 10px) ไม่เป็นไร แต่ปัญหาที่เกิดขึ้นจริงก็คือกับสคริปต์ที่สอง: การชดเชยในสคริปต์ที่สองนั้นมีขนาดใหญ่กว่าในแกนนอน ฉันคิดว่าเหลือประมาณ ~ 50px ทางซ้าย คุณเห็นหรือไม่ว่าทำไม การตั้งค่าพิกัดเชิงลบไม่ได้ช่วยในกรณีนั้น
snitko

คำถามอื่น: ฉันจะเปิดหน้าต่างเต็มหน้าจอได้อย่างไร
snitko

การอัปเดต: ออฟเซ็ตในกรณีของสคริปต์ที่สองดูเหมือนจะเหมือนกับความกว้างของ unity dock ทางด้านซ้าย (แม้ว่าจะซ่อนอยู่)
snitko

สำหรับผู้ที่สนใจฉันได้ติดตั้ง Desktopen: github.com/snitko/desktopen
snitko

1

นี่เป็นการขยายคำตอบที่ยอดเยี่ยมของ @Jacob Vlijim ด้านบนด้วยsetwindowสคริปต์ที่ปรับเปลี่ยนเล็กน้อย:

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

คำอธิบายของการเปลี่ยนแปลง:

  1. python3ถึงpython(เพียงการตั้งค่าส่วนตัว)
  2. sys.argvเพื่อargparseสำหรับอินเตอร์เฟสบรรทัดคำสั่งที่ดีกว่า
  3. id หน้าต่างที่เข้มงวด (และไม่ใช่การประมวลผล id) การแยกวิเคราะห์หน้าต่าง
    • บางโปรแกรมใช้ id กระบวนการเดียวสำหรับหลาย windows
  4. while วนรอบ 0.5 วินาทีถึง 1 เวลาพักเครื่องเต็มวินาที
  5. ชื่อตัวแปร verbose / อ่านได้มากขึ้นและการยึดมั่น pep8
  6. ตัวแปรคงที่ทั่วโลกสำหรับขนาดหน้าจอแทนการxrandrพึ่งพา

หมายเหตุ: นี่เป็นเพียงรุ่นปรับปรุงเล็กน้อยที่ฉันเขียนเพื่อการใช้งานส่วนตัวบน Debian Jessie LXDE ผลลัพธ์ของคุณอาจแตกต่างกันไป


0

สำหรับผู้ที่สนใจฉันได้ติดตั้ง Desktopen: github.com/snitko/desktopen

อนุญาตให้คุณเขียนสคริปต์สำหรับเปิดหน้าต่างในวิวพอร์ตและจอแสดงผลที่แตกต่างกันในลักษณะที่เป็นมิตรมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.