Як я можу заблокувати додаток (та всі його нові вікна) у конкретній робочій області?


11

Я запускаю Matlabсценарій в workspace 1. Це генерує кілька сюжетів. Тим часом я переходжу на workspace 2та працюю там. Моя проблема полягає в тому, що сюжети спливають workspace 2. Чи можна заблокувати програмне забезпечення в робочу область. Тож, Matlabстворюючи сюжети workspace 1, я можу працювати workspace 2без зривів з'являються сюжетів?


Єдність, GNOME Shell чи щось інше?
AB

Я додаю теги, це Ubuntu 14.04 з Unity
OHLÁLÁ

До якого класу належать вікна сюжету? (ви можете скористатися командою xprop WM_CLASS, а потім натиснути на вікно?) Будь ласка, додайте також WM_CLASS Matlab.
Яків Влійм

2
Я відправлю пізніше сьогодні, якщо не хтось опублікує ще одне геніальне рішення.
Яків Влійм

1
Привіт OHLÁLÁ, я насправді працював досить добре, усі додаткові вікна програми миттєво переміщуються до початкової робочої області додатка, але .... дійсно поточне вікно на поточній робочій області все ж втрачає фокус. Ще розбираюсь над рішенням. Ви б все ж спробували рішення?
Яків Влійм

Відповіді:


8

ВАЖЛИВА РЕДАКЦІЯ

Нижче переписана версія сценарію з першої відповіді (нижче). Відмінності:

  • Зараз у сценарію надзвичайно мало ресурсів (як це має бути із фоновими сценаріями). Тепер дії організовані для того, щоб діяти, якщо вони потрібні (і лише якщо вони потрібні). Цикл практично нічого не робить, окрім перевірки появи нових вікон.
  • WM_CLASSТепер аргумент для запуску сценарію є робочим простором " Bot" та "Націлене" . Використовуйте лише першу або другу (ідентифікаційну) частину 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. Перевірте сценарій, запустивши його. Під час роботи відкрийте додаток і дайте йому створювати вікна, як зазвичай. Усі вікна повинні з’являтися на цільовій робочій області, як встановлено в команді.

ВИРІШЕНИЙ ВІДПОВІДЬ:

(друга) ТЕСТОВА ВЕРСІЯ

Сценарій нижче блокує певну програму до її початкової робочої області. Якщо сценарій запущений, він визначає, в якій робочій області знаходиться програма. Усі додаткові вікна, які виробляє додаток, будуть переміщені до тієї ж робочої області за секунду.

Проблема фокусування вирішується автоматичним повторним фокусуванням на вікні, на яке було сфокусовано до створення додаткового вікна.

Сценарій

#!/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, шукає їх положення так само, як вище, і переміщує їх у "оригінальну" робочу область.

Оскільки новостворене вікно "вкрало" фокус з останнього використовуваного вікна, над яким працював користувач, згодом фокус встановлюється у вікно, на яке було раніше фокусування.


Це дуже дивовижно. Можливо, буде гарною ідеєю створити індикатор, де користувач може заблокувати різні програми у робочих просторах. Зараз у мене була проблема з Matlab, але така ж проблема буде з matplotlib
OHLÁLÁ

@ OHLÁLÁ, як згадувалося, я вважаю це питання дуже цікавим і продовжуватиму працювати над ним. Що я маю на увазі - це файл, в якому користувач може встановлювати applicationі workspace-набір. Якщо ви зіткнулися з можливими помилками, будь ласка, згадайте про це!
Яків Влійм

Якою буде поведінка, коли два Matlab запускаються на окремих робочих просторах?
OHLÁLÁ

@ OHLÁLÁ, вони обидва будуть заблоковані в робочій області, яку ви встановили в команді. Оскільки їхні дані WM_CLASSоднакові, другий буде переміщений до того, який ви встановили в команді.
Яків Влійм

Чи є інші можливості для ідентифікації програми, крім WM_CLASS?
OHLÁLÁ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.