Как я могу запустить приложение с предопределенным размером и позицией окна?

22

Мне интересно, есть ли способ добиться эффекта сочетания клавиш Ctrl-Alt-Keypad в Unity, используя вместо этого команды терминала? Я хочу команду, которая устанавливает окно графического интерфейса на половину размера экрана, либо по левому, либо по правому краю.

В качестве фона я пишу скрипт, который запускается после входа в систему. Он использует Zenity, чтобы спросить, хочу ли я открыть свою среду разработки (GVim и IPython параллельно). Я пытался добиться двух окон одинакового размера для этих программ, используя set lines= columns=в моих .gvimrcи c.IPythonWidget.width =и c.IPythonWidget.height =в моем ipython_qtconsole_config.py. Однако есть проблемы, связанные с этим подходом.

стим
источник
Я обновил свой ответ, потому что он недостаточно подробно объяснил тему, вы можете проверить обновление
kos

Ответы:

17

С чем вы столкнетесь

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

Вам нужен «умный» способ убедиться , что размещение / изменение размеров делается (сразу) после того , как окно появляется .

Скрипт для вызова приложения, дождитесь его появления и разместите на экране

С помощью приведенного ниже сценария вы можете вызвать приложение и установить позицию и размер, в котором оно должно отображаться, с помощью команды:

<script> <application> <x-position> <y-position> <h-size> <v-size>

Несколько примеров:

  • Чтобы вызвать gnome-terminalи изменить размер окна до 50%, поместите его в правую часть:

    <script> gnome-terminal 840 0 50 100

    введите описание изображения здесь

  • Чтобы позвонить gedit, поместите его окно слева и позвоните gnome-terminal, разместите его справа (установив его v-size46%, чтобы оставить немного пространства между окнами):

    <script> gedit 0 0 46 100&&<script> gnome-terminal 860 0 46 100

    введите описание изображения здесь

  • Чтобы вызвать Inkscape, поместите его окно в левую / верхнюю четверть экрана:

    <script> inkscape 0 0 50 50

    введите описание изображения здесь

Сценарий и как его использовать

  1. установить оба xdotoolи wmctrl. Я использовал оба, так как изменение размера с wmctrlможет вызвать некоторые особенности (в частности) Unity.

    sudo apt-get install wmctrl
    sudo apt-get install xdotool
  2. Скопируйте приведенный ниже скрипт в пустой файл, сохраните его как setwindow(без расширения) в ~/bin; создайте каталог при необходимости.
  3. Сделайте скрипт исполняемым (!)
  4. Если вы только что создали ~bin, запустите:source ~/.profile
  5. Тестовый запуск скрипта с помощью команды (например)

    setwindow gnome-terminal 0 0 50 100

    Другими словами:

    setwindow <application> <horizontal-position> <vertical-position> <horizontal-size (%)> <vertical-size (%)>

Если все работает нормально, используйте команду везде, где вам это нужно.

Сценарий

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

app = sys.argv[1]

get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", app])

while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        cmd3 = "xdotool windowsize --sync "+procs[0][0][1]+" "+sys.argv[4]+"% "+sys.argv[5]+"%"
        cmd4 = "xdotool windowmove "+procs[0][0][1]+" "+sys.argv[2]+" "+sys.argv[3]
        for cmd in [cmd1, cmd2, cmd3, cmd4]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1

Что оно делает

Когда скрипт вызывается, он:

  1. запускает приложение
  2. следит за списком окон (используя wmctrl -lp)
  3. если появляется новое окно, оно проверяет, принадлежит ли новое окно к вызываемому приложению (используя ps -ef ww, сравнивая pid окна с pid приложения)
  4. если это так, он устанавливает размер и положение в соответствии с вашими аргументами. В случае, если приложение не «появляется» в пределах ок. 15 секунд сценарий предполагает, что приложение не будет запущено из-за ошибки. Затем сценарий завершается, чтобы предотвратить бесконечное ожидание нового окна.

Незначительная проблема

В Unity, когда вы (пере) перемещаете и (изменяете) размер окна с помощью wmctrlили xdotool, окно всегда будет держать небольшой отступ к границам экрана, если вы не установите его на 100%. Вы можете видеть это на изображении (3) выше; в то время как inkscapeокно было расположено в xпозиции 0, вы все еще можете видеть незначительный разрыв между Unity Launcher и inkscapeокном.

Якоб Влейм
источник
Дорогой Джейкоб, это фантастический сценарий! Один вопрос: Как я могу найти размер открытого окна, чтобы впоследствии использовать его вместе со сценарием?
Орширо
Привет @orschiro Я должен бежать на встречу ... вернусь через несколько часов :)
Джейкоб Vlijm
1
Привет @orschiro, если вы уже wmctrlустановили, команда wmctrl -lG | grep <windowname>покажет вам выход , как: 0x03600e4f 0 723 197 1114 563 jacob-System-Product-Name dimkeyboard.sh (~/Bureaublad) - gedit. В этих выходных данных с 3-го по 6-й столбец показаны размеры: x, y, ширина, ширина, высота.
Джейкоб Влейм
Очень ценится Джейкоб! Я думаю, что я сделал установку правильно, но соответствующее окно действительно открывается в полноэкранном режиме. Любые идеи?
Orschiro
1
Мне понравился этот сценарий, но мне пришлось внести следующие изменения в первый блок, чтобы он работал для меня, потому что я добавил параметры в приложение: appAndParams = sys.argv[1] app = appAndParams[0].split(' ', 1)[0] get = lambda x: subprocess.check_output(["/bin/bash", "-c",x]).decode("utf-8") ws1 = get("wmctrl -lp"); t = 0 subprocess.Popen(["/bin/bash", "-c", appAndParams])</ pre> <edit> оно отказывается форматировать этот код. </ Edit>
Рон Томпсон
3

Фактическая команда, которую вы хотите это что-то вроде

wmctrl -r :ACTIVE: -b add,maximized_vert && 
wmctrl -r :ACTIVE: -e 0,0,0,$HALF,-1

Это заставит текущее окно занимать половину экрана (изменить $HALF размеры экрана) и привязываться к левой стороне. Для привязки вправо используйте

wmctrl -r :ACTIVE: -b add,maximized_vert && 
wmctrl -r :ACTIVE: -e 0,$HALF,0,$HALF,-1 

Вы также можете поиграть, wmctrlчтобы получить идентификатор интересующего вас окна вместо того, чтобы использовать :ACTIVE:. Я не могу помочь, хотя, так как это зависит от рассматриваемых окон. Посмотрите на man wmctrlбольше.


Я написал сценарий для этого. Я не использую Unity, поэтому я не могу гарантировать, что он будет работать с ним, но я не вижу причин, почему бы и нет. Необходимо wmctrl, xdpyinfoи disperдолжны быть установлены:

sudo apt-get install wmctrl x11-utils disper

Затем сохраните приведенный ниже скрипт как ~/bin/snap_windows.sh, сделайте его исполняемым, chmod a+x ~/bin/snap_windows.shи вы сможете запустить

snap_windows.sh r

Для привязки к правой стороне. Используйте lдля левой стороны и без аргументов, чтобы развернуть окно. Обратите внимание, что оно запускается в текущем окне, поэтому вам нужно назначить ему ярлык, если вы хотите, чтобы он работал на любом другом устройстве, кроме терминала.

Сценарий немного сложнее, чем вы просите, потому что я написал его для работы как с одним, так и с двумя мониторами.

#!/usr/bin/env bash

## If no side has been given, maximize the current window and exit
if [ ! $1 ]
then
    wmctrl -r :ACTIVE: -b toggle,maximized_vert,maximized_horz
    exit
fi

## If a side has been given, continue
side=$1;
## How many screens are there?
screens=`disper -l | grep -c display`
## Get screen dimensions
WIDTH=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x'`;
HALF=$(($WIDTH/2));

## If we are running on one screen, snap to edge of screen
if [ $screens == '1' ]
then
    ## Snap to the left hand side
    if [ $side == 'l' ]
    then
        ## wmctrl format: gravity,posx,posy,width,height
    wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,0,0,$HALF,-1
    ## Snap to the right hand side
    else
    wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,$HALF,0,$HALF,-1 
    fi
## If we are running on two screens, snap to edge of right hand screen
## I use 1600 because I know it is the size of my laptop display
## and that it is not the same as that of my 2nd monitor.
else
    LAPTOP=1600; ## Change this as approrpiate for your setup.
    let "WIDTH-=LAPTOP";
    SCREEN=$LAPTOP;
    HALF=$(($WIDTH/2));
    if [ $side == 'l' ]
    then
        wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,$LAPTOP,0,$HALF,-1
    else
    let "SCREEN += HALF+2";
    wmctrl -r :ACTIVE: -b add,maximized_vert && wmctrl -r :ACTIVE: -e 0,$SCREEN,0,$HALF,-1;
    fi
fi
terdon
источник
3

Вы можете сделать это используя xdotool .

Для установки xdotoolвы можете запустить:

sudo apt-get update && sudo apt-get install xdotool

Затем, чтобы отправить Ctrl+ Alt+ <keypad_key>нажатие в Xокно терминала, вы можете запустить:

xdotool key Ctrl+Alt+<keypad_key_value>

* <keypad_key_value> = значение клавиши клавиатуры в списке ниже

Чтобы запустить программу с графическим интерфейсом и отправить нажатие клавиши в ее Xокно (которое в данном случае является активным окном во время выполнения xdotoolкоманды), вы можете запустить:

<command> && window="$(xdotool getactivewindow)" xdotool key --delay <delay> --window "$window" <keypad_key_value>

* <команда> = команда, которая открывает окно, в которое вы хотите отправить нажатие клавиши; <delay> = время ожидания в миллисекундах перед отправкой нажатия клавиши; <keypad_key_value> = значение клавиши клавиатуры в списке ниже

Обратите внимание, что в большинстве случаев вам нужно будет запустить команду, которую вы выдаете, как отдельный процесс (например, запустив nohup <command> &вместо того, что <command>в примере выше), иначе xdotoolне будет выполняться до<command> , пока не завершится выполнение.

Также вам нужно будет установить некоторую задержку, в противном случае нажатие клавиши будет отправлено до того, как целевое окно будет полностью загружено X(задержка 500msдолжна быть).

Возможные значения для <keypad_key_value>:

  • 0: 90
  • 1: 87
  • 2: 88
  • 3: 89
  • 4: 83
  • 5: 84
  • 6: 85
  • 7: 79
  • 8: 80
  • 9: 81

Как правило большого пальца, чтобы узнать значение любой клавиши на клавиатуре в Xсреде, можно запустить xevи нажать клавишу, чтобы вывести ее значение внутри терминала.

кос
источник
Что-то не так с нашими правками?
Тим
@Tim Нет, по крайней мере, не удаление последней строки, которая, я согласен, довольно бесполезна, но то, что может варьироваться в команде, по моему мнению, лучше отформатировать, если заключить в угловые скобки, что является стандартной нотацией в теории языка для обозначения к синтаксической категории (ссылаясь на ваше редактирование), и я думаю, что имя команды лучше отформатировать, если оно заключено в обратные кавычки, чтобы его можно было сразу же распознать (ссылаясь на редактирование AB); обратите внимание, что не все это не в моей голове, у меня раньше были сомнения, и я спросил об этом на meta
kos
Да, все хорошо, как сейчас :) <> это стандарт, и я иду `` вокруг любой команды :)
Тим
3

Я создал приложение под названием Worksets (на github ) для Unity, которое позволяет вам сделать это легко через графический интерфейс пользователя - это бесплатно и с открытым исходным кодом.

меню tTray

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

буйство
источник
1

У меня нет представителя, чтобы напрямую комментировать превосходный пост Якоба Влиима, но я изменил скрипт, чтобы разрешить запуск приложения с аргументами (необходимо использовать setwindowс gedit --new-window). Изменить:

if app in p and w[2] in p] for w in ws2]

чтобы:

if app.split()[0] in p and w[2] in p] for w in ws2]
Д. Хэнкок
источник
0

Я поиграл со сценарием Тердона сверху и добавил несколько новых вещей (возможность выбирать монитор и устанавливать высоту для каждого монитора). Я думаю, что это можно расширить, добавив больше мониторов. Надеюсь, другие найдут это полезным.

Основной синтаксис:

prog_name monitor l/r/m window_name (optional)

Где prog_name - это то, что вы сохранили этот код; монитор - это номер монитора, например, 1 или 2; л / р / м слева или справа или макс; а window_name - это имя (или часть его имени) целевого окна.

Например:

setwindow 1 m chrome

Bash скрипт

#!/usr/bin/env bash
set -e
#######################-    Early Definitions    -#######################

snap () {
    wmctrl -r ${WIN} -b toggle,add,maximized_vert && wmctrl -r ${WIN} -e 0,${WINX},0,${WINWIDTH},${WINHEIGHT}
    }

DISPLAYWIDTH=`xdpyinfo | grep 'dimensions:' | cut -f 2 -d ':' | cut -f 1 -d 'x'`;       ## Get screen dimensions
LEFTSCREENWIDTH=1024    ## user set
LEFTSCREENHEIGHT=720    ## user set
RIGHTSCREENHEIGHT=960   ## user set
let "RIGHTSCREENWIDTH=(DISPLAYWIDTH-LEFTSCREENWIDTH)"

#############################-    Logic    -#############################

if [ ! ${3} ]; then
    WIN=":ACTIVE:"
else
    WIN=${3}
fi
case ${1} in
    1)  # monitor one
        LEFT=0
        WINHEIGHT=${LEFTSCREENHEIGHT}
        let "WINWIDTH=LEFTSCREENWIDTH/2"
    ;;
    2)  # monitor two
        let "LEFT=LEFTSCREENWIDTH"
        WINHEIGHT=${RIGHTSCREENHEIGHT}
        let "WINWIDTH=RIGHTSCREENWIDTH/2"
    ;;
    "") # error please select a monitor
        echo "please select a monitor (1 or 2)"
        exit 0
    ;;
esac
case ${2} in
    l|L)
        WINX=${LEFT}
        snap
    ;;
    r|R)
        let "WINX=LEFT+WINWIDTH"
        snap
    ;;
    ""|m|M)
        WINX=${LEFT}
        let "WINWIDTH=WINWIDTH*2"
        snap
    ;;
esac

exit 0
Философ Рекс
источник