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

11

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

О-ЛЯ-ЛЯ
источник
Unity, GNOME Shell или что-то еще?
AB
Я добавляю теги, это Ubuntu 14.04 с Unity
OHLÁLÁ
К какому классу относятся окна сюжета? (не могли бы вы проверить с помощью команды xprop WM_CLASS, а затем нажмите на окно?) Пожалуйста, также добавьте WM_CLASS Matlab.
Джейкоб Влейм
2
Я опубликую позже сегодня, если не кто-то покажет другое блестящее решение в то же время.
Джейкоб Влейм
1
Привет, ОХЛАЛА, у меня все получилось довольно хорошо, все дополнительные окна приложения мгновенно перемещаются в начальную рабочую область приложения, но ... действительно, текущее окно в текущей рабочей области, тем не менее, теряет фокус. Все еще рассчитываю на решение. Вы все еще попробуете решение?
Джейкоб Влейм

Ответы:

8

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

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

  • В скрипте сейчас крайне мало ресурсов (как и в случае фоновых скриптов). Действия теперь организованы, чтобы действовать, если (и только если) они необходимы. Цикл практически ничего не делает, кроме проверки появления новых окон.
  • Bot 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. Проверьте скрипт, запустив его. Во время работы откройте свое приложение и позвольте ему производить окна как обычно. Все окна должны появиться в целевом рабочем пространстве, как указано в команде.

УСТАРЕВШИЙ ОТВЕТ:

(вторая) ИСПЫТАТЕЛЬНАЯ ВЕРСИЯ

Приведенный ниже скрипт блокирует конкретное приложение в исходной рабочей области. Если скрипт запущен, он определяет, в какой рабочей области находится приложение. Все дополнительные окна, которые создает приложение, будут перемещены в одно и то же рабочее пространство за доли секунды.

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

Сценарий

#!/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-sets. Если вы столкнулись с возможными ошибками, пожалуйста, сообщите об этом!
Якоб Влийм
Каково будет поведение, когда два Matlab запускаются в отдельных рабочих пространствах?
OHLÁLÁ
@ OHLÁLÁ тогда они оба будут заблокированы в рабочем пространстве, которое вы установили в команде. Поскольку они WM_CLASSидентичны, второй будет перемещен в тот, который вы задали в команде.
Джейкоб Влейм
Существуют ли другие возможности для идентификации приложения, кроме WM_CLASS?
OHLÁLÁ