web-dev-qa-db-ja.com

ウィンドウの「グループ化」?

ウィンドウを「グループ化」する方法はありますか?つまり、2つ以上のウィンドウの端を結合して、一方が移動するともう一方も一緒に移動して、1つの大きなウィンドウとして機能するようにする方法はありますか?または、少なくとも1つのウィンドウを移動すると、もう1つのウィンドウも同じように移動するという点で似ていますか? Ubuntu GNOME 15.10とGNOME 3.18を実行しています。

3
user364819

未完の答え;入力を探しています

一見、wmctrlを使用すると非常にうまくいくように見えますが、いつものように、現実は理論よりも(はるかに)複雑です。

これは回答として投稿することをheしています。これは実験的な概念的な回答であり、いくつかのバグのためにすぐに使えるソリューションではありません(まだ)。それにもかかわらず、現在のバージョンの問題を解決するための情報を得るために投稿しています。この質問は、スムーズなソリューションを作成できるかどうかを確認するためのさらなる開発(IMO)にとって十分興味深いものです。

言語:なし

スクリプトはPythonで作成しましたが、この言語は私が実行している問題とは無関係です。主にwmctrlの使用の特性に関連しています。

Icouldはウィンドウを配置するためにxdotoolを使用できますが、OPは一連のウィンドウを別のワークスペースに移動することに言及しているため、wmctrlにはいくつかの利点があります。 Gnomeを使用します。ワークスペースはUnityとは異なります。

ウィンドウのグループを移動する現在の動作例

enter image description here

ウィンドウをグループとして移動する

上記の画面キャストの例はUnityで作成されました。ただし、Gnomeでも同様に機能するはずです(逸脱は別として、スクリプトについては以下を参照してください)。

  • 現在の設定では、次のスクリプトを引数aで呼び出して、最前面のウィンドウをグループに追加することにより、ウィンドウのグループを作成できます。スクリーンキャストでは、コマンドを(Unity)ランチャーにGnomeに追加しました。これはショートカットキーで実行できます。
  • 続いて、グループ化されたウィンドウの1つが移動された後に引数rを使用してスクリプトが呼び出されると、スクリプトはすべてのウィンドウを同様にグループから移動し、ウィンドウを相互に復元します。

方法

  • オプションaを指定して実行すると、以下のスクリプトは現在アクティブなウィンドウ、その位置とサイズ(wmctrl -lGの出力の対応する行のように)をファイルwgroup_data.txt~内)に追加します。
  • オプションrを指定して実行すると、スクリプトはファイルを読み取り、位置を変更したウィンドウを探し、古い位置と新しい位置の間のベクトルを計算し、それに応じて他のウィンドウを移動します。
  • ウィンドウが閉じられている場合、スクリプトによってグループリストから自動的に削除されます。

これまでのところ問題ありません。

課題

ただし、、ウィンドウの1つが現在のワークスペースの境界線に完全に収まらない場合、スクリプトは失敗します。実際、wmctrlコマンドは失敗します。これは、スクリプトの「外部」でも失敗し、単一のコマンドとして実行されるためです。

スクリプト

#!/usr/bin/env python3
import subprocess
import os
import sys

arg = sys.argv[1]

# vertical deviation for Unity (use 0 for Gnome)
deviation = 28

fdata = os.environ["HOME"]+"/wgroup_data.txt"

def get_wmctrl():
    # try because of buggy wmctrl...
    try:
        return subprocess.check_output(["wmctrl", "-lG"]).decode("utf-8")
    except subprocess.CalledProcessError:
        pass

def remove_window(window):
    data = open(fdata).readlines()
    [data.remove(l) for l in data if l.startswith(window)]
    open(fdata, "wt").write(("").join(data))

def addwindow():
    relevant = get_wmctrl()
    frontmost = hex(int((subprocess.check_output(["xdotool", "getactivewindow"]).decode("utf-8").strip())))
    frontmost = frontmost[:2]+str((10-len(frontmost))*"0")+frontmost[2:]
    open(fdata, "+a").write([l+("\n") for l in get_wmctrl().splitlines() if frontmost in l][0])

    print(frontmost)

def rearrange():
    wlist = get_wmctrl()
    if wlist != None:
        group = [(l.strip(), l.split()) for l in open(fdata).read().splitlines() if not l in ("", "\n")]
        try: 
            changed = [w for w in group if (w[0] in wlist, w[1][0] in wlist) == (False, True)][0] #
            # only proceed if one of the grouped windows moved (give priority to a light loop if not):
            follow = []
            for w in group:
                if not w == changed:
                    test = (w[0] in wlist, w[1][0] in wlist)
                    if test == (True, True):
                        follow.append(w)
                    Elif test == (False, False):
                        # remove closed window from list
                        remove_window(w[1][0])
            # only proceed if there are windows to move:
            if follow:
                # find match of the moved window (new coords)
                wlines = wlist.splitlines()
                match = [l.split() for l in wlines if changed[1][0] in l][0]
                # calculate the move vector
                x_move = int(match[2])-(int(changed[1][2])); y_move = int(match[3])-(int(changed[1][3]))
                for w in follow:
                    # should be changed to try?
                    w[1][2] = str(int(w[1][2]) + x_move); w[1][3] = str(int(w[1][3]) + y_move - deviation)
                    subprocess.Popen([
                        "wmctrl", "-ir", w[1][0], "-e",
                        (",").join([w[1][1], w[1][2], w[1][3], w[1][4], w[1][5]])
                        ])
                # update grouplist
                while True:
                    try:
                        newlines = sum([[l for l in get_wmctrl().splitlines() if w in l] for w in [match[0]]+[item[1][0] for item in follow]], [])
                        open(fdata, "wt").write(("\n").join(newlines))                 
                        break
                    except AttributeError:
                        pass
        except IndexError:
            print("nothing changed")

if arg == "a":
    addwindow()
Elif arg == "r":
    rearrange()

使い方

  1. スクリプトにはwmctrlxdotoolの両方が必要です

    Sudo apt-get install xdotool wmctrl
    
  2. スクリプトを空のファイルにコピーし、group_windows.pyとして保存します

  3. Gnomeを使用している場合:
    スクリプトのヘッドセクションで、次の行を変更します。

    deviation = 28
    

    deviation = 0 
    
  4. 異なるショートカットに2つのコマンドを追加します。

    python3 /path/to/group_windows.py a
    

    グループにウィンドウを追加する

    python3 /path/to/group_windows.py r
    

    スクリーンキャストに示すように、ウィンドウを再配置します

  5. スクリーンキャストに示すように、グループにいくつかのウィンドウを追加してスクリプトをテストし、それらを移動して相対位置を復元します。

さらなる開発

この問題は、いずれかのウィンドウが現在のワークスペースから出ることが予想される場合に、ウィンドウの移動を拒否することで、コードごとに解決できます。その場合、移動したばかりのウィンドウでも初期位置に戻して、相対位置を維持する必要があります。

ただし、これには大規模な計算が必要になり(コンピューターには何も必要ありませんが、コーディングが複雑になります)、現在のワークスペース外での部分的な配置を可能にするとよりエレガントになります。ウィンドウが「エッジの上または上」に手動で配置されても問題ありません。
この問題を解決するための提案は大歓迎です。

1
Jacob Vlijm