アプリケーション(およびそのすべての新しいウィンドウ)を特定のワークスペースにロックするにはどうすればよいですか?


11

Matlabスクリプトを実行しますworkspace 1。これにより、複数のプロットが生成されます。それまでの間、私はそこで切り替えworkspace 2てそこで働いています。私の問題は、プロットがにポップアップしていることworkspace 2です。ソフトウェアをワークスペースにロックすることは可能ですか?ではMatlab、プロットを生成している間、ポップアッププロットを中断workspace 1するworkspace 2ことなく作業できますか?


Unity、GNOME Shell、または他の何か?
AB

私はそれはUnityとのUbuntu 14.04で、タグを追加
OHLÁLÁ

プロットウィンドウはどのクラスに属しますか?(コマンドxprop WM_CLASSで確認してからウィンドウをクリックしますか?)MatlabのWM_CLASSも追加してください。
ジェイコブVlijm

2
誰かが別の素晴らしいソリューションをその間に投稿しない限り、私は今日後で投稿します。
ジェイコブVlijm

1
こんにちはOHLÁLÁ、私は実際に非常にうまく機能しました。アプリケーションのすべての追加ウィンドウはすぐにアプリケーションの初期ワークスペースに移動されますが、実際に現在のワークスペースの現在のウィンドウはフォーカスを失います。まだ解決策を考えています。それでも解決策を試しますか?
ジェイコブVlijm

回答:


8

重要な編集

以下の最初の回答から書き直されたバージョンのスクリプトの下。違い:

  • スクリプトのリソースが非常に少なくなりました(バックグラウンドスクリプトの場合と同様)。アクションは、必要な場合にのみ(そして必要な場合にのみ)動作するようになりました。ループは実際には何もしませんが、新しいウィンドウが表示されるかどうかを確認します。
  • ボットWM_CLASSとターゲットワークスペースは、スクリプトを実行するための引数になりました。の最初または2番目の(識別)部分のみを使用します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. このスクリプトは、両方を必要とするwmctrlxdotool

    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"
    

    コマンドの最初または2番目の部分を使用して、スクリプトを実行します。

  4. スクリプトを実行するコマンドは次のとおりです。

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

    コマンドの最後のセクション。2,2アプリケーションをロックするワークスペース(スペースなし:(!)列、行)、「人間」形式。最初の列/行は1,1

  5. スクリプトを実行してテストします。実行中にアプリケーションを開き、通常どおりウィンドウを生成します。コマンドで設定されているように、すべてのウィンドウがターゲットワークスペースに表示されます。

時代遅れの回答:

(2番目)テストバージョン

以下のスクリプトは、特定のアプリケーションを初期ワークスペースにロックます。スクリプトが開始されると、アプリケーションが存在するワークスペースを決定します。アプリケーションが生成するすべての追加ウィンドウは、一瞬で同じワークスペースに移動されます。

フォーカスの問題は、追加ウィンドウが作成される前にフォーカスされていたウィンドウに自動的に再フォーカスすることで解決されます。

スクリプト

#!/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. スクリプトのニーズの両方wmctrlxdotool

    sudo apt-get install wmctrl xdotool
    
  2. スクリプトを空のファイルにコピーして、名前を付けて保存します keep_workspace.py

  3. アプリケーションを開いてアプリケーションの「WM_CLASS」を特定し、ターミナルを開いてコマンドを実行します。

    xprop WM_CLASS
    

    次に、アプリケーションのウィンドウをクリックします。"sun-awt-X11-XFramePeer", "MATLAB R2015a - academic use"ケースのように出力をコピーし、示されているように、スクリプトのheadセクションの単一引用符の間に配置します。

  4. 次のコマンドを使用してスクリプトを実行します。

    python3 /path/to/keep_workspace.py
    

希望どおりに機能する場合は、トグル機能を追加します。私のシステムでは既に数時間は動作しますが、最初に微調整する必要があるかもしれません。

ノート

気付かないはずですが、スクリプトシステムにいくらかのプロセッサ負荷を追加します。高齢者のシステムでは、3〜10%の増加に気付きました。それがどのように機能するかが気に入ったら、おそらくそれをさらに調整して負荷を減らします。

このスクリプトでは、コメントで示したように、セカンダリウィンドウがメインウィンドウと同じクラスであると想定しています。ただし、(非常に)簡単な変更により、セカンダリウィンドウを別のクラスにすることができます。

説明

平均的な読者にはおそらくあまり面白くないかもしれませんが、スクリプトはベクトルで計算することで機能します。起動時に、スクリプトは次を計算します。

  • 原点から現在のワークスペースへのベクトルの出力 wmctrl -d
  • 現在のワークスペースを基準とした、アプリケーションのウィンドウへのベクトルの出力 wmctrl -lG
  • これらの2つから、スクリプトはスパニングデスクトップ(1つのマトリックス内のすべてのワークスペース)上のアプリケーションのウィンドウの絶対位置を計算します

それ以降、スクリプトは、同じアプリケーションの新しいウィンドウを探し、その出力をxprop WM_CLASS上記と同じ方法で探し、「元の」ワークスペースに移動します。

新しく作成されたウィンドウは、ユーザーが最後に使用していたウィンドウからフォーカスを「盗んだ」ため、フォーカスはその後、以前フォーカスがあったウィンドウに設定されます。


これは非常に素晴らしいです。ユーザーが別のアプリケーションをワークスペースにロックできるインジケーターを作成することをお勧めします。今、私はMatlabのに問題があったが、同じ問題がmatplotlibのに発生します
OHLÁLÁ

@OHLÁLÁ言及されたように、私はこの質問を非常に興味深いと思っており、引き続き作業を続けます。私が念頭に置いているのは、ユーザーが設定applicationおよび設定できるファイルですworkspace。可能性のあるバグに遭遇した場合は、言及してください!
ジェイコブVlijm

2つのMatlabを別々のワークスペースで起動したときの動作はどうなりますか?
オハララ

@OHLÁLÁを選択すると、コマンドで設定したワークスペースに両方がロックされます。これらWM_CLASSは同一であるため、2番目のものはコマンドで設定したものに移動します。
ジェイコブVlijm

WM_CLASS以外に、アプリケーションを識別する他の可能性はありますか?
オハララ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.