ฉันจะล็อคแอปพลิเคชัน (และหน้าต่างใหม่ทั้งหมด) ลงในพื้นที่ทำงานเฉพาะได้อย่างไร


11

ผมทำงานสคริปต์ในMatlab workspace 1สิ่งนี้สร้างหลายแปลง ในช่วงเวลาที่ฉันเปลี่ยนไปworkspace 2และทำงานที่นั่น ปัญหาของฉันที่แปลง popping workspace 2ขึ้นใน เป็นไปได้หรือไม่ที่จะล็อคซอฟต์แวร์ไว้ในพื้นที่ทำงาน ดังนั้นในขณะที่Matlabสร้างแปลงในworkspace 1ฉันสามารถทำงานworkspace 2โดยไม่หยุดชะงักของแปลงที่ปรากฏขึ้นหรือไม่


Unity, GNOME Shell หรืออะไรอย่างอื่น?
AB

ฉันเพิ่มแท็กเป็น Ubuntu 14.04 พร้อม Unity
OHLÁLÁ

พล็อต windows เป็นของคลาสใด (คุณสามารถตรวจสอบด้วยคำสั่งxprop WM_CLASSแล้วคลิกที่หน้าต่างได้หรือไม่) โปรดเพิ่ม WM_CLASS ของ Matlab
Jacob Vlijm

2
ฉันจะโพสต์ในภายหลังวันนี้ถ้าไม่มีใครโพสต์ทางออกที่ยอดเยี่ยมอีกในเวลาเดียวกัน
Jacob Vlijm

1
สวัสดีOHLÁ, จริง ๆ แล้วฉันทำให้มันทำงานได้ค่อนข้างดี, หน้าต่างเพิ่มเติมทั้งหมดของแอปพลิเคชันจะถูกย้ายไปยังพื้นที่ทำงานเริ่มต้นของแอปทันที, แต่ .... แน่นอนหน้าต่างปัจจุบันของพื้นที่ทำงานปัจจุบันสูญเสียการโฟกัส ยังคงหาวิธีการแก้ปัญหา คุณจะลองวิธีแก้ปัญหาเหรอ?
Jacob Vlijm

คำตอบ:


8

การแก้ไขที่สำคัญ

ด้านล่างเป็นเวอร์ชันที่เขียนใหม่ของสคริปต์จากคำตอบแรก (ด้านล่าง) ความแตกต่าง:

  • สคริปต์ในตอนนี้มีทรัพยากรเหลือน้อยมาก (อย่างที่ควรจะเป็นกับสคริปต์พื้นหลัง) ตอนนี้การกระทำจะได้รับการจัดทำขึ้นเพื่อดำเนินการถ้า (และเฉพาะในกรณี) สิ่งเหล่านั้นจำเป็น การวนซ้ำไม่ทำอะไรเลยนอกจากตรวจสอบว่ามีหน้าต่างใหม่ปรากฏขึ้นหรือไม่
  • บอทWM_CLASSและเวิร์กสเปซเป้าหมายตอนนี้มีอาร์กิวเมนต์เพื่อเรียกใช้สคริปต์ ใช้เฉพาะส่วนแรกหรือส่วนที่สอง (การระบุ) ของส่วนWM_CLASS(ดูเพิ่มเติมด้านล่าง: วิธีใช้)
  • ตอนนี้สคริปต์โฟกัสไปที่หน้าต่างที่ใช้งานอยู่ในปัจจุบัน (จริง ๆ แล้วโฟกัสในเสี้ยววินาที)
  • เมื่อสคริปต์เริ่มทำงานมันจะแสดงการแจ้งเตือน (ตัวอย่างgedit):

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

สคริปต์

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

วิธีใช้

  1. สคริปต์ต้องการทั้งwmctrlและxdotool:

    sudo apt-get install wmctrl xdotool
    
  2. คัดลอกสคริปต์ด้านบนลงในไฟล์เปล่าแล้วบันทึกเป็น lock_towspace.py

  3. จากแอปพลิเคชันเฉพาะของคุณให้ค้นหาWM_CLASS: เปิดแอปพลิเคชันของคุณรันในเทอร์มินัล:

    xprop WM_CLASS and click on the window of the application
    

    ผลลัพธ์จะมีลักษณะ (ในกรณีของคุณ):

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

    ใช้ส่วนแรกหรือส่วนที่สองในคำสั่งเพื่อเรียกใช้สคริปต์

  4. คำสั่งเพื่อรันสคริปต์นั้นคือ:

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

    ในคำสั่งส่วนสุดท้าย; 2,2เป็นเวิร์กสเปซที่คุณต้องการล็อคแอปพลิเคชันให้ (ไม่ต้องเว้นวรรค: (!) คอลัมน์แถว ) ในรูปแบบ "คน" คอลัมน์ / แถวแรกคือ1,1

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

คำตอบที่ล้าสมัย:

(วินาที) รุ่นทดสอบ

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

ปัญหาการโฟกัสได้รับการแก้ไขโดยการปรับโฟกัสบนหน้าต่างที่โฟกัสโดยอัตโนมัติก่อนที่จะสร้างหน้าต่างเพิ่มเติม

สคริปต์

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

วิธีใช้

  1. สคริปต์ต้องการทั้งwmctrlและxdotool

    sudo apt-get install wmctrl xdotool
    
  2. คัดลอกสคริปต์ลงในไฟล์เปล่าบันทึกเป็น keep_workspace.py

  3. กำหนด `WM_CLASS 'แอปพลิเคชันของคุณโดยการเปิดแอปพลิเคชันจากนั้นเปิดเทอร์มินัลแล้วเรียกใช้คำสั่ง:

    xprop WM_CLASS
    

    จากนั้นคลิกที่หน้าต่างแอปพลิเคชันของคุณ คัดลอกเอาต์พุตที่ดูเหมือน"sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"ในกรณีของคุณและวางไว้ระหว่างเครื่องหมายคำพูดเดี่ยวในส่วนหัวของสคริปต์ตามที่ระบุ

  4. รันสคริปต์ด้วยคำสั่ง:

    python3 /path/to/keep_workspace.py
    

ถ้ามันทำงานได้ตามที่ต้องการฉันจะเพิ่มฟังก์ชั่นสลับ แม้ว่ามันจะใช้งานได้ในระบบของฉันไปแล้วสองสามชั่วโมง แต่ก็อาจต้องปรับแต่งก่อน

หมายเหตุ

แม้ว่าคุณไม่ควรสังเกตเห็น แต่สคริปต์จะเพิ่มตัวประมวลผลบางตัวลงในระบบ ในระบบผู้สูงอายุของฉันฉันสังเกตเห็นการเพิ่มขึ้น 3-10% หากคุณชอบวิธีการทำงานฉันอาจจะปรับแต่งเพิ่มเติมเพื่อลดภาระ

สคริปต์ดังกล่าวถือว่าหน้าต่างความปลอดภัยเป็นคลาสเดียวกันกับหน้าต่างหลักเหมือนที่คุณระบุไว้ในความคิดเห็น ด้วยการเปลี่ยนแปลงที่ง่าย (มาก) หน้าต่างที่สองสามารถเป็นคลาสอื่นได้

คำอธิบาย

แม้ว่าอาจจะไม่น่าสนใจสำหรับผู้อ่านโดยเฉลี่ย แต่สคริปต์ทำงานโดยการคำนวณในเวกเตอร์ เมื่อเริ่มต้นสคริปต์จะคำนวณ:

  • เวกเตอร์จากจุดเริ่มต้นไปยังพื้นที่ทำงานปัจจุบันด้วยผลลัพธ์ของ wmctrl -d
  • เวกเตอร์ไปยังหน้าต่างของแอปพลิเคชันซึ่งสัมพันธ์กับพื้นที่ทำงานปัจจุบันโดยผลลัพธ์ของ wmctrl -lG
  • จากทั้งสองสคริปต์จะคำนวณตำแหน่งที่แน่นอนของหน้าต่างแอปพลิเคชันบนเดสก์ท็อปที่ขยาย (พื้นที่ทำงานทั้งหมดในเมทริกซ์เดียว)

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

เนื่องจากหน้าต่างที่สร้างขึ้นใหม่ "stole" โฟกัสจากหน้าต่างที่ใช้ล่าสุดที่ผู้ใช้ทำงานอยู่โฟกัสจะถูกตั้งค่าเป็นหน้าต่างที่มีโฟกัสก่อนหน้าในภายหลัง


นี่มันยอดเยี่ยมมาก มันอาจเป็นความคิดที่ดีที่จะสร้างตัวบ่งชี้ที่ผู้ใช้สามารถล็อคแอปพลิเคชันที่แตกต่างกันในพื้นที่ทำงาน ตอนนี้ผมมีปัญหากับ Matlab แต่ปัญหาเดียวกันจะเกิดขึ้นกับ matplotlib
OHLALA

@ OHLÁLÁตามที่กล่าวมาฉันพบว่าคำถามน่าสนใจมากและจะทำงานต่อไป สิ่งที่ฉันมีอยู่ในใจคือไฟล์ที่ผู้ใช้สามารถตั้งค่าapplicationและworkspace-sets หากคุณพบข้อบกพร่องที่เป็นไปได้โปรดพูดถึงมัน!
Jacob Vlijm

จะเกิดอะไรขึ้นเมื่อ Matlab สองตัวเริ่มทำงานในพื้นที่ทำงานแยกกัน
OHLÁLÁ

@ OHLÁLÁจากนั้นทั้งคู่จะถูกล็อคไปยังพื้นที่ทำงานที่คุณตั้งไว้ในคำสั่ง เนื่องจากWM_CLASSสิ่งที่เหมือนกันรายการที่สองจะถูกย้ายไปยังรายการที่คุณกำหนดไว้ในคำสั่ง
Jacob Vlijm

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