Làm cách nào tôi có thể khóa một ứng dụng (và tất cả các cửa sổ mới của nó) vào một không gian làm việc cụ thể?


11

Tôi chạy một Matlabkịch bản trong workspace 1. Điều này tạo ra một số lô. Trong thời gian đó tôi chuyển sang workspace 2và làm việc ở đó. Vấn đề của tôi là các lô đang xuất hiện workspace 2. Có thể khóa phần mềm vào một không gian làm việc. Vì vậy, trong khi Matlabtạo ra các ô trong workspace 1, tôi có thể làm việc workspace 2mà không bị gián đoạn các ô bật lên?


Unity, Gnome Shell hay cái gì khác?
AB

Tôi thêm các thẻ, Đó là Ubuntu 14.04 với Unity
OHLÁLÁ

Các lớp cửa sổ cốt truyện thuộc về lớp nào? (bạn có thể kiểm tra bằng lệnh xprop WM_CLASSvà sau đó nhấp vào cửa sổ không?) Vui lòng thêm WM_CLASS của Matlab.
Jacob Vlijm

2
Tôi sẽ đăng sau hôm nay, nếu không ai đó đăng một giải pháp tuyệt vời khác trong thời gian đó.
Jacob Vlijm

1
Xin chào OHLÁLÁ, tôi thực sự đã làm cho nó hoạt động khá tốt, tất cả các cửa sổ bổ sung của ứng dụng ngay lập tức được chuyển đến không gian làm việc ban đầu của ứng dụng, nhưng .... thực sự cửa sổ hiện tại trên không gian làm việc hiện tại vẫn mất tiêu điểm. Vẫn đang tìm một giải pháp. Bạn vẫn sẽ thử giải pháp chứ?
Jacob Vlijm

Câu trả lời:


8

EDIT QUAN TRỌNG

Bên dưới một phiên bản viết lại của kịch bản từ câu trả lời đầu tiên (bên dưới). Sự khác biệt:

  • Kịch bản bây giờ cực kỳ ít về tài nguyên (giống như với kịch bản nền). Các hành động hiện được sắp xếp để hành động nếu (và chỉ khi) chúng cần thiết. Vòng lặp thực tế không có gì ngoài việc kiểm tra các cửa sổ mới xuất hiện.
  • Bot WM_CLASSvà không gian làm việc được nhắm mục tiêu hiện là đối số để chạy tập lệnh. Chỉ sử dụng phần đầu tiên hoặc phần thứ hai (xác định) của phần WM_CLASS(xem thêm bên dưới: cách sử dụng)
  • Tập lệnh hiện giữ tập trung vào cửa sổ hiện đang hoạt động (thực sự tập trung lại trong tích tắc)
  • Khi tập lệnh bắt đầu, nó sẽ hiển thị một thông báo (ví dụ gedit):

    nhập mô tả hình ảnh ở đây

Kịch bản

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

Cách sử dụng

  1. Kịch bản cần cả hai wmctrlxdotool:

    sudo apt-get install wmctrl xdotool
    
  2. Sao chép tập lệnh ở trên vào một tệp trống, lưu nó dưới dạng lock_towspace.py

  3. Trong ứng dụng cụ thể của bạn, hãy tìm hiểu WM_CLASS: mở ứng dụng của bạn, chạy trong một thiết bị đầu cuối:

    xprop WM_CLASS and click on the window of the application
    

    Đầu ra sẽ trông như thế (trong trường hợp của bạn):

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

    Sử dụng phần đầu tiên hoặc phần thứ hai trong lệnh để chạy tập lệnh.

  4. Lệnh để chạy tập lệnh sau đó là:

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

    Trong lệnh, phần cuối cùng; 2,2là không gian làm việc nơi bạn muốn khóa ứng dụng vào (không có khoảng trắng: (!) cột, hàng ), ở định dạng "con người"; cột / hàng đầu tiên là1,1

  5. Kiểm tra tập lệnh bằng cách chạy nó. Trong khi chạy, hãy mở ứng dụng của bạn và để nó tạo ra các cửa sổ như bình thường. Tất cả các cửa sổ sẽ xuất hiện trên không gian làm việc được nhắm mục tiêu, như được đặt trong lệnh.

TRẢ LỜI BÊN NGOÀI:

(thứ hai) PHIÊN BẢN KIỂM TRA

Kịch bản bên dưới khóa một ứng dụng cụ thể vào không gian làm việc ban đầu của nó. Nếu tập lệnh được khởi động, nó sẽ xác định không gian làm việc mà ứng dụng cư trú. Tất cả các cửa sổ bổ sung mà ứng dụng tạo ra sẽ được chuyển đến cùng một không gian làm việc trong tích tắc.

Vấn đề trọng tâm được giải quyết bằng cách tự động tập trung vào cửa sổ được tập trung trước khi cửa sổ bổ sung được tạo ra.

Kịch bản

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

Cách sử dụng

  1. Kịch bản cần cả hai wmctrlxdotool

    sudo apt-get install wmctrl xdotool
    
  2. Sao chép tập lệnh vào một tập tin trống, lưu nó dưới dạng keep_workspace.py

  3. xác định `WM_CLASS 'của ứng dụng của bạn bằng cách mở ứng dụng, sau đó mở terminal và chạy lệnh:

    xprop WM_CLASS
    

    Sau đó bấm vào cửa sổ ứng dụng của bạn. Sao chép đầu ra, trông giống như "sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"trong trường hợp của bạn và đặt nó giữa các dấu ngoặc đơn trong phần đầu của tập lệnh, như được chỉ ra.

  4. Chạy kịch bản với lệnh:

    python3 /path/to/keep_workspace.py
    

Nếu nó hoạt động như bạn muốn, tôi sẽ thêm chức năng chuyển đổi. Mặc dù nó đã hoạt động được vài giờ trên hệ thống của tôi, tuy nhiên trước tiên nó có thể cần một số điều chỉnh.

Ghi chú

Mặc dù bạn không nên chú ý đến nó, tập lệnh sẽ thêm một số tải bộ xử lý vào hệ thống. Trên hệ thống người cao tuổi của tôi, tôi nhận thấy mức tăng 3-10%. Nếu bạn thích cách nó hoạt động, tôi có thể sẽ điều chỉnh thêm để giảm tải.

Kịch bản, như nó là, giả sử các cửa sổ bí mật là cùng loại với cửa sổ chính, như bạn đã chỉ ra trong một nhận xét. Tuy nhiên, với một thay đổi đơn giản, các cửa sổ phụ thể thuộc một lớp khác.

Giải trình

Mặc dù có lẽ không thú vị lắm đối với một người đọc trung bình, kịch bản hoạt động bằng cách tính toán trong các vectơ. Khi khởi động, tập lệnh sẽ tính toán:

  • vectơ từ gốc đến không gian làm việc hiện tại với đầu ra của wmctrl -d
  • vectơ cho cửa sổ của ứng dụng, liên quan đến không gian làm việc hiện tại, bởi đầu ra của wmctrl -lG
  • Từ hai cái này, tập lệnh sẽ tính toán vị trí tuyệt đối của cửa sổ của ứng dụng trên màn hình nền (tất cả các không gian làm việc trong một ma trận)

Từ đó trở đi, tập lệnh tìm kiếm các cửa sổ mới của cùng một ứng dụng, với đầu ra là xprop WM_CLASS, tìm kiếm vị trí của chúng theo cùng một cách như trên và di chuyển chúng đến không gian làm việc "ban đầu".

Do cửa sổ mới được tạo "đánh cắp" tiêu điểm từ cửa sổ được sử dụng cuối cùng mà người dùng đang làm việc, tiêu điểm sau đó được đặt thành cửa sổ đã lấy nét trước đó.


Điều này là rất khủng khiếp. Có thể là một ý tưởng tốt để tạo một chỉ báo nơi người dùng có thể khóa ứng dụng khác nhau vào không gian làm việc. Ngay bây giờ tôi đã gặp vấn đề với Matlab, nhưng vấn đề tương tự sẽ xảy ra với matplotlib
OHLÁLÁ

@ OHLÁLÁ như đã đề cập, tôi thấy câu hỏi rất thú vị và sẽ tiếp tục làm việc với nó. Những gì tôi có trong tâm trí là một tập tin mà người dùng có thể thiết lập applicationvà cài đặt workspace. Nếu bạn gặp phải các lỗi có thể xảy ra, hãy đề cập đến nó!
Jacob Vlijm

Điều gì sẽ là hành vi khi hai Matlab được bắt đầu trên các không gian làm việc riêng biệt?
OHLÁLÁ

@ OHLÁLÁ thì cả hai sẽ bị khóa vào không gian làm việc bạn đặt trong lệnh. Vì chúng WM_CLASSgiống hệt nhau, cái thứ hai sẽ được chuyển đến cái bạn đặt trong lệnh.
Jacob Vlijm

Có sự tích cực nào khác để xác định một ứng dụng, ngoài WM_CLASS không?
OHLÁLÁ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.