web-dev-qa-db-ja.com

コマンドラインを使用してウィンドウを特定の画面に移動する

これは キーボードのみを使用してウィンドウを別の画面にすばやく配置する に似ていますが、コマンドラインを使用できるようにしたいので(コマンドラインをリコールするだけです) bash履歴)。

たとえば、送信

  • eDP1へのすべてのgnome端末ウィンドウ、
  • すべてのEmacsウィンドウをVGA1に、および
  • すべてのChromeウィンドウからHDMI1

(そして、移動後に最大化する-しかし、クレイジーではない F11 方法、通常のウィンドウマネージャースタイルの最大化)。

実行可能ファイル名でウィンドウを指定したいと思います。

5
sds

特定のウィンドウクラスのすべてのウィンドウを(スクリーン)名で特定の画面に移動する

以下のスクリプトは、特定のWM_CLASS(アプリケーション)に属するウィンドウを特定の画面に、画面のnameで送信します。それがどのように行われるかは、スクリプトで説明されています。

このスクリプトは、画面が水平に配置され、多少上下に揃えられることを想定しています(差は<100 PX)。

スクリプト

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

# just a helper function, to reduce the amount of code
get = lambda cmd: subprocess.check_output(cmd).decode("utf-8")

# get the data on all currently connected screens, their x-resolution
screendata = [l.split() for l in get(["xrandr"]).splitlines() if " connected" in l]
screendata = sum([[(w[0], s.split("+")[-2]) for s in w if s.count("+") == 2] for w in screendata], [])

def get_class(classname):
    # function to get all windows that belong to a specific window class (application)
    w_list = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
    return [w for w in w_list if classname in get(["xprop", "-id", w])]

scr = sys.argv[2]

try:
    # determine the left position of the targeted screen (x)
    pos = [sc for sc in screendata if sc[0] == scr][0]
except IndexError:
    # warning if the screen's name is incorrect (does not exist)
    print(scr, "does not exist. Check the screen name")
else:
    for w in get_class(sys.argv[1]):
        # first move and resize the window, to make sure it fits completely inside the targeted screen
        # else the next command will fail...
        subprocess.Popen(["wmctrl", "-ir", w, "-e", "0,"+str(int(pos[1])+100)+",100,300,300"])
        # maximize the window on its new screen
        subprocess.Popen(["xdotool", "windowsize", "-sync", w, "100%", "100%"])

使い方

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

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

  3. 次のコマンドで実行します:

    python3 /path/to/move_wclass.py <WM_CLASS> <targeted_screen>
    

    例えば:

    python3 /path/to/move_wclass.py gnome-terminal VGA-1
    

WM_CLASSには、例のようにWM_CLASSpartを使用できます。画面の名前は、exactおよび完全な名前である必要があります。

方法(概念)

説明は主にコンセプトに関するものであり、コーディングに関するものではありません。

Xrandrの出力には、接続されているすべての画面について、次のような文字列/行があります。

VGA-1 connected 1280x1024+1680+0

この行は、画面のpositionとそのnameに関する情報を提供します。説明 ここ

スクリプトは、すべての画面の情報をリストします。画面とウィンドウクラスを引数としてスクリプトを実行すると、画面の(x-)位置を検索し、特定のクラスのすべてのウィンドウ(-idの)を検索します(wmctrl -lの助けを借りて)およびxprop -id <window_id>の出力。

その後、スクリプトはすべてのウィンドウを1つずつターゲット画面上の位置に移動し(wmctrl -ir <window_id> -e 0,<x>,<y>,<width>,<height>を使用)、最大化します(xdotool windowsize 100% 100%を使用)。

注意

スクリプトは、実行したテストで正常に機能しました。 Unityでwmctrl、さらにはxdotoolを使用すると、頑固な特性を持つことがありますが、推論ではなく実験によって解決する必要がある場合があります。例外が発生する可能性がある場合は、言及してください。

6
Jacob Vlijm

@jacobs pythonコードを単純なbashに書き直して動作させました(ubuntu 16シナモンでこれをテストしました)。

ウィンドウが動かないままremove,maximized_vert, remove,maximized_horzを追加する必要がありました。

#!/bin/bash

if [ ! -z "$1" ] || [ -z "$2" ]; then
    command=$(wmctrl -l | grep $1 | cut -d" " -f1)

    if [ ! -z "$command" ]; then
        position=$(xrandr | grep "^$2" | cut -d"+" -f2)

        if [ ! -z "$position" ]; then
            for window in $command; do 
               wmctrl -ir $window -b remove,maximized_vert
               wmctrl -ir $window -b remove,maximized_horz 
               wmctrl -ir $window -e 0,$position,0,1920,1080
               wmctrl -ir $window -b add,maximized_vert
               wmctrl -ir $window -b add,maximized_horz 
            done
        else
            echo -e "not found monitor with given name"
        fi
    else
        echo -e "not found windows with given name"
    fi
else
    echo -e "specify window and monitor name;\nmove.sh window-name monitor-name"
fi
  1. Sudo apt-get install xdotool wmctrl
  2. /path/to/script.sh "window-name" "monitor-name"
1

記録のために、この質問と 複数のモニター設定を復元する の組み合わせに使用するものを以下に示します。

# configure multiplr displays and
# move the windows to their appropriate displays

import subprocess
import os
import wmctrl
import re

mydisplays = [("VGA1",0,"left"),
              ("eDP1",1080,"normal"),
              ("HDMI1",3000,"left")]

# https://askubuntu.com/questions/702002/restore-multiple-monitor-settings
def set_displays ():
    subprocess.check_call(" && ".join([
        "xrandr --output %s --pos %dx0  --rotate %s" % d for d in mydisplays]),
                          Shell=True)

# https://askubuntu.com/questions/702071/move-windows-to-specific-screens-using-the-command-line
mywindows = [("/emacs$","VGA1"),
             ("/chrome$","HDMI1"),
             ("gnome-terminal","eDP1")]
def max_windows ():
    didi = dict([(d,x) for d,x,_ in mydisplays])
    for w in wmctrl.Window.list():
        try:
            exe = os.readlink("/proc/%d/exe" % (w.pid))
            for (r,d) in mywindows:
                if re.search(r,exe):
                    x = didi[d]
                    print "%s(%s) --> %s (%d)" % (r,exe,d,x)
                    w.set_properties(("remove","maximized_vert","maximized_horz"))
                    w.resize_and_move(x,0,w.w,w.h)
                    w.set_properties(("add","maximized_vert","maximized_horz"))
                    break
        except OSError:
            continue

def cmdlines (cmd):
    return subprocess.check_output(cmd).splitlines()

def show_displays ():
    for l in cmdlines(["xrandr"]):
        if " connected " in l:
            print l

if __== '__main__':
    show_displays()
    set_displays()
    show_displays()
    max_windows()

wmctrl バージョン0.3以降を使用する必要があります(私の プルリクエスト のため)。

1
sds