Как я могу заблокировать приложение (и все его новые окна) в определенном рабочем пространстве?

Я запускаю скрипт Matlab в workspace 1. Это создает несколько графиков. А пока я переключаюсь на workspace 2 и работаю там. Моя проблема в том, что сюжеты появляются в workspace 2. Можно ли заблокировать программное обеспечение в рабочей области. Таким образом, хотя Matlab генерирует графики в workspace 1, я могу работать в workspace 2 без нарушения всплывающих графиков?

11
задан 30 August 2015 в 18:15

1 ответ

ВАЖНОЕ РЕДАКТИРОВАНИЕ

Ниже переписанной версии сценария из первого ответа (ниже). Различия:

  • Сценарий теперь является чрезвычайно низким на ресурсах (как он, должен быть с фоновыми сценариями). Действия теперь расположены, чтобы действовать, если (и только если) они необходимы. Цикл действительно практически только проверяет на новые окна для появления.
  • Бот WM_CLASS и целенаправленная рабочая область является теперь аргументами для запущения скрипта. Только используйте или первое или второе (идентификация) часть WM_CLASS (см. далее ниже: как использовать),
  • Сценарий теперь сосредотачивает внимание на в настоящее время активном окне (на самом деле пере - фокусируется в долю секунды),
  • Когда сценарий запускается, он показывает уведомление (пример gedit):

    enter image description here

Сценарий

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

Если это будет работать как Вам угодно, то я добавлю функцию переключателя. Хотя это уже работает в течение нескольких часов в моей системе, bu это, возможно, нуждался бы в некоторой тонкой настройке сначала как бы то ни было.

Примечания

Хотя Вы не должны замечать его, сценарий действительно добавляет, что некоторый процессор загружается к системе. В моей пожилой системе я заметил увеличение 3-10%. Если Вам нравится, как это работает, я, вероятно, далее настрою его для сокращения загрузки.

Сценарий, как это, предполагает, что secundary окна имеют тот же класс как главное окно, как Вы обозначены в комментарии. С (очень) простым изменением вторичные окна могут иметь другой класс как бы то ни было.

Объяснение

Хотя, вероятно, не очень интересный для среднего читателя, сценарий работает путем вычисления в векторах. На запуске вычисляет сценарий:

  • вектор от источника до текущей рабочей области с выводом wmctrl -d
  • вектор к окну приложения, относительно текущей рабочей области, выводом wmctrl -lG
  • От этих двух сценарий вычисляет абсолютное положение окна приложения на рабочем столе охвата (все рабочие области в одной матрице)

С тех пор сценарий ищет новые окна того же приложения с выводом xprop WM_CLASS, ищет их положение таким же образом как выше и перемещает их в "исходную" рабочую область.

Так как недавно созданное окно "украло" фокус из последнего используемого окна, пользователь продолжал работать, фокус впоследствии установлен на окно, которое сфокусировалось прежде.

8
ответ дан 31 August 2015 в 04:15
  • 1
    Спасибо, $ LESS_TERMCAP_md= экспорта (tput полужирный; tput setaf 7), то, что я хочу – user1641854 18 November 2016 в 22:56

Другие вопросы по тегам:

Похожие вопросы: