Как я могу запустить приложение с предопределенным размером и позицией окна?
Мне интересно, есть ли способ добиться эффекта сочетания клавиш Ctrl-Alt-Keypad в Unity, используя вместо этого команды терминала? Я хочу команду, которая устанавливает окно графического интерфейса на половину размера экрана, либо по левому, либо по правому краю.
В качестве фона я пишу скрипт, который запускается после входа в систему. Он использует Zenity, чтобы спросить, хочу ли я открыть свою среду разработки (GVim и IPython параллельно). Я пытался получить два окна одинакового размера для этих программ с помощью set lines= columns=
в моем .gvimrc
а также c.IPythonWidget.width =
а также c.IPythonWidget.height =
в моем ipython_qtconsole_config.py
, Однако есть проблемы, связанные с этим подходом.
11 ответов
С чем вы столкнетесь
Если вы хотите сначала вызвать приложение, а затем поместить его окно в определенную позицию и размер, время между вызовом приложения и моментом, когда окно действительно появляется, непредсказуемо. Если ваша система занята, она может быть значительно дольше, чем в режиме ожидания.
Вам нужен "умный" способ убедиться, что позиционирование / изменение размера выполняется (сразу) после появления окна.
Скрипт для вызова приложения, дождитесь его появления и разместите на экране
С помощью приведенного ниже сценария вы можете вызвать приложение и установить позицию и размер, в котором оно должно отображаться, с помощью команды:
<script> <application> <x-position> <y-position> <h-size> <v-size>
Несколько примеров:
Звонить
gnome-terminal
и измените размер окна до 50% и поместите его в правую половину:<script> gnome-terminal 840 0 50 100
Звонить
gedit
, поместите его окно слеваи позвонитеgnome-terminal
, поместите его справа (устанавливая егоv-size
46% чтобы дать ему немного места между окнами)<script> gedit 0 0 46 100&&<script> gnome-terminal 860 0 46 100
Чтобы вызвать Inkscape, поместите его окно в левую / верхнюю четверть экрана:
<script> inkscape 0 0 50 50
Сценарий и как его использовать
установить оба
xdotool
а такжеwmctrl
, Я использовал оба, так как изменение размера сwmctrl
может вызвать некоторые особенности (в частности)Unity
,sudo apt-get install wmctrl sudo apt-get install xdotool
- Скопируйте приведенный ниже скрипт в пустой файл и сохраните его как
setwindow
(без расширения) в~/bin
; создайте каталог при необходимости. - Сделайте скрипт исполняемым (!)
- Если вы только что создали
~bin
, бежать:source ~/.profile
Тестовый запуск скрипта с помощью команды (например)
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
Что оно делает
Когда скрипт вызывается, он:
- запускает приложение
- следит за списком окон (используя
wmctrl -lp
) - если появляется новое окно, оно проверяет, принадлежит ли новое окно к вызываемому приложению (используя
ps -ef ww
, сравнивая pid окна с pid приложения) - если это так, он устанавливает размер и положение в соответствии с вашими аргументами. В случае, если приложение не "появляется" в пределах ок. 15 секунд сценарий предполагает, что приложение не будет запущено из-за ошибки. Затем сценарий завершается, чтобы предотвратить бесконечное ожидание нового окна.
Незначительная проблема
В Unity, когда вы (пере) позиционируете и (пере) масштабируете окно с wmctrl
или же xdotool
, окно всегда будет держать небольшой отступ к границам вашего экрана, если вы не установите его на 100%. Вы можете видеть это на изображении (3) выше; в то время как inkscape
окно было помещено на x
положение 0, вы все еще можете увидеть небольшую границу между Unity Launcher и inkscape
окно.
Вы можете сделать это используя xdotool
,
Установить xdotool
Вы можете запустить:
sudo apt-get update && sudo apt-get install xdotool
Затем отправить комбинацию клавиш Ctrl + Alt + X
Окно вы можете запустить:
xdotool key Ctrl+Alt+<keypad_key_value>
*
Чтобы запустить программу с графическим интерфейсом и отправить нажатие клавиши на ее X
окно (которое в этом случае является активным окном во время xdotool
выполнение команды) вы можете запустить:
<command> && window="$(xdotool getactivewindow)" xdotool key --delay <delay> --window "$window" <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
и нажмите клавишу, чтобы вывести его значение внутри терминала.
Я создал приложение под названием Worksets (на github) для Unity, которое позволяет вам сделать это легко через графический интерфейс пользователя - это бесплатно и с открытым исходным кодом.
По сути, это оболочка для решений wmctrl и xdotool, перечисленных здесь как ответы, и предоставляет простой способ быстрого создания и сохранения таких настроек.
Фактическая команда, которую вы хотите, что-то вроде
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
У меня нет представителя, чтобы прокомментировать отличный пост Якоба Влийма, но я изменил скрипт, чтобы разрешить запуск приложения с аргументами (необходимо использовать 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]
Для большинства программ есть очень простая опция "-geometry":
xclock -geometry 150x150+20+20
kate -geometry 400x400+100+100
<appname> -geometry <height>x<width>+<posH>+<posV>
Для простых задач вы можете использовать следующий bash-скрипт.
#!/bin/bash
nemo &
sleep 2
LastWID=$(wmctrl -li | awk '{ print $1}' | sort | tail -n1)
wmctrl -ia $LastWID
wmctrl -i -r $LastWID -b remove,maximized_vert,maximized_horz
wmctrl -i -r $LastWID -e 0,900,300,700,700
Для большей гибкости используйте следующий скрипт, который является ответвлением Руссо . Удален
xdotool
зависимость. Также (на мой взгляд) упростил код.
Поместите этот скрипт в
PATH
и использовать его с помощью
<script-name> <x-position> <y-position> <h-size> <v-size> <application> <application-arguments>
#!/usr/bin/env python3
import subprocess
import time
import sys
commandCore = sys.argv[5]
command= sys.argv[5:];
commandStr = " ".join(command)
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
w_id_pre_value = get("wmctrl -li | awk '{ print $1}' | wc -l").strip()
subprocess.Popen(["/bin/bash", "-c", commandStr],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
while True:
time.sleep(0.1)
if w_id_pre_value < get("wmctrl -li | awk '{ print $1}' |wc -l").strip():
break
w_id = get("wmctrl -li | awk '{ print $1}' | tail -n1").strip()
cmd1 = "wmctrl -is "+w_id
cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert,maximized_horz"
cmd3 = "wmctrl -ir "+w_id+" -e 0,"+sys.argv[1]+","+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]
for cmd in [cmd1, cmd2, cmd3]:
subprocess.call(["/bin/bash", "-c", cmd])
Прелесть этого скрипта Python в том, что вы можете создать файл оболочки, например
#!/bin/bash
setwindow 0 0 960 1078 firefox
setwindow 960 0 960 1078 nemo
setwindow 1920 0 960 1078 codium
и откройте несколько приложений в назначенных им местах.
Обновление 1:
Если у вас есть Python 3.5+, вы также можете использовать следующий код.
#!/usr/bin/env python3
import subprocess
import time
import sys
commandCore = sys.argv[5]
command= sys.argv[5:];
commandStr = " ".join(command)
w_id_pre_value = subprocess.run("wmctrl -li | awk '{ print $1}' | wc -l", shell=True, capture_output=True, text=True).stdout.strip()
subprocess.run(commandStr, shell=True)
while True:
time.sleep(0.1)
if w_id_pre_value < subprocess.run("wmctrl -li | awk '{ print $1}' |wc -l", shell=True, capture_output=True, text=True).stdout.strip():
break
w_id = subprocess.run("wmctrl -li | awk '{ print $1}' | tail -n1", shell=True, capture_output=True, text=True).stdout.strip()
cmd1 = "wmctrl -is "+w_id
cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert,maximized_horz"
cmd3 = "wmctrl -ir "+w_id+" -e 0,"+sys.argv[1]+","+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]
for cmd in [cmd1, cmd2, cmd3]:
subprocess.run(cmd, shell=True)
Однако у него есть некоторые проблемы.
- с использованием
shell=True
имеет много недостатков, и самый худший из них — возможные утечки безопасности - «Открыть несколько приложений в назначенных им местах» не работает. Не уверен, почему.
Я поиграл со сценарием Тердона сверху и добавил несколько новых вещей (возможность выбирать монитор и устанавливать высоту для каждого монитора). Я думаю, что это можно расширить, добавив больше мониторов. Надеюсь, другие найдут это полезным.
Основной синтаксис:
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
Shell Script, который можно превратить в "~/Desktop/Terminals.desktop
"файл со значком ТЕРМИНАЛ на рабочем столе, чтобы запустить его:
/ usr / bin / gnome-terminal --window-with-profile = Красный --geometry = 100x52+200+ 0
/ usr / bin / gnome-terminal --window-with-profile = Бирюзовый --geometry = 100x52+400+ 0
/ usr / bin / gnome-terminal --window-with-profile = Green--geometry = 100x52+600+ 0
/ usr / bin / gnome-terminal --window-with-profile = Blue--geometry = 100x52+800+ 0
/ usr / bin / gnome-terminal --window-with-profile = Cyan--geometry = 100x52+1100+ 0
$ cat /home/$USER/Desktop/Terminals.desktop
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Exec=/home/$USER/.terminals
TryExec=
Icon=/usr/share/pixmaps/blueprint-config-xfree.png
X-GNOME-DocPath=
Terminal=false
Name=Terminal
GenericName=
Comment=
Некоторые приложения поддерживают опцию--geometry
что позволяет запускать их в любом месте с любым размером окна.
Запустить из командной строки~ $ app-cmd-line-name --help
( например~ $ gnome-terminal --help-all
или~ $ xed --help
или~ $ nemo --help
), чтобы увидеть в списке поддерживаемых опций:
-g, --geometry=GEOMETRY Set size and pos. of the window (WIDTHxHEIGHT+X+Y)
Обратите внимание, что при запуске gnome-терминала вам нужно указать COLSxROWS+X+Y , а не WIDTHxHEIGHT+X+Y в пикселях .
Если--help
не работает, вы также можете использовать
man
или~ $
info
. Найдиapp-cmd-line-name
проверив содержимое поля командной строки лаунчера или взглянув на содержимое соответствующего.desktop
файл, используемый для запуска приложения.
Я модифицировал сценарий Джейкоба Влайма, чтобы разрешить добавление дополнительных аргументов / параметров к приложению.
Например, если вы хотите запустить Firefox с двумя вкладками в сценарии bash:
command = "firefox -new-tab -url https://duckduckgo.com -new-tab -url https://www.google.com"
path_to_your_setwindow_script/setwindow 0 0 32 100 $command
Затем модифицированный скрипт Джейкоба Влайма ниже примет первые 4 аргумента для настроек положения и размера, а затем остальные аргументы для запуска приложения.
#!/usr/bin/env python3
import subprocess
import time
import sys
print('inside setwindow')
print(sys.argv)
print(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
commandCore = sys.argv[5]
command= sys.argv[5:];
commandStr = " ".join(command)
print('commandCore:', commandCore, '\ncommandStr:', commandStr)
#print('exiting now...')
#sys.exit()
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", commandStr])
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 commandCore 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[3]+"% "+sys.argv[4]+"%"
cmd4 = "xdotool windowmove "+procs[0][0][1]+" "+sys.argv[1]+" "+sys.argv[2]
for cmd in [cmd1, cmd2, cmd3, cmd4]:
subprocess.call(["/bin/bash", "-c", cmd])
break
time.sleep(0.5)
t = t+1