Существует ли скрипт (или программное обеспечение) для открытия окна приложения в определенной области просмотра и позиции?

8

Итак, у меня есть 8 виртуальных рабочих столов в Unity (с Compiz), потому что у меня есть много проектов, над которыми я работаю одновременно.

Проблема в том, что каждый раз, когда мне нужно перезагрузить или случайно закрыть Chrome (который составляет большую часть окон, которые мне нужны для работы), я должен вручную открыть эти окна, а затем настроить их (открыть файлы, перейти к нужному URL и т. д.).

Как бы вы написали сценарий, который сделает все это для меня? То есть: 1) Откройте окна 2) Поместите их в правильные координаты на правильных виртуальных экранах

(1) очевидно, для Google Chrome вы просто запускаете «google-chrome». Но тогда как вы положите его в нужном месте? (2)

Или есть сценарий / программное обеспечение, которое уже существует для меня?

Снитко
источник
Я могу попытаться придумать сценарий для размещения любых окон, которые могут вам понадобиться, на соответствующих рабочих столах при запуске, хотя это может занять несколько дней, так как у меня финал на следующей неделе. Это будет включать в себя wmctrl, как программное обеспечение для управления окнами через скрипт или терминал. Но что касается перезапуска окна, это может быть чем-то более сложным
Сергей Колодяжный
Хотя вы специально спрашивали об Unity, стоит отметить, что в KDE есть (в основном недокументированная) программа kstart, которая делает это за вас. Лучше всего работает с программами KDE, но также имеет успех с другими программами.
Джо

Ответы:

14

Это можно сделать очень хорошо, но вам нужно немного разбираться в Unity / viewports. Я надеюсь, что история ниже понятна, если нет, пожалуйста, оставьте комментарий.

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

1. Понимание области просмотра и координат окна

Рабочие пространства в Unity

В Unity, в отличие от других оконных менеджеров, у вас есть только одно рабочее пространство, которое разделено на области просмотра. В вашем случае ваше рабочее пространство разделено на восемь видов.

Как определяется положение окон

Положение окна, как вывод команды:

wmctrl -lG
(you need to have wmctrl installed to run the command)

описывается как позиция относительно левого верхнего угла текущего окна просмотра :


Поэтому, если вы находитесь в окне просмотра 1:
окно в окне просмотра 2 может быть расположено, например, 1700 (по оси x) x 500 (по оси y)
(мой экран 1680x1050)

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


Однако, если вы находитесь в окне просмотра 6:
то же самое окно будет расположено на 20 (x), -550 (y) введите описание изображения здесь


Правильное использование этих координат важно для запуска сценария с правильными аргументами, как описано ниже:

2. Как использовать скрипт

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

  1. Убедитесь, что wmctrlустановлено:

    sudo apt-get install wmctrl
    
  2. Скопируйте приведенный ниже скрипт в пустой файл, сохраните его как setwindow(без расширения) в ~/bin. Создайте каталог, если он еще не существует. Сделайте скрипт исполняемым .

  3. Если вы только что создали ~/bin, либо выполните команду, source ~/.profileлибо выйдите из системы, чтобы сделать каталог доступным в $PATH.
  4. Тестовый запуск команды:

    setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size>
    

    например

    setwindow gedit 100 100 200 200
    

    Окно Gedit должно отображаться в текущем окне просмотра.

Ноты:

  • Имейте в виду, что не все приложения допускают размеры окон ниже определенной ширины или высоты. Минимальная ширина geditокна в моей системе, например, ок. 470 пикселей
  • Сценарий работает нормально только в том случае, если все окно помещается на целевой области просмотра, соответственно выберите ваши координаты / размеры. Также помните, что Unity Launcher и панель используют некоторое пространство (!), Которое может влиять на положение окна.
  • Используйте негатив <x_position>для размещения окон слева от текущего видового экрана (ов)
  • Используйте минус, <y_position>чтобы разместить окна над текущим окном просмотра
  • Чтобы одновременно открывать новые окна в разных окнах просмотра, вы можете просто объединять команды. Если посмотреть на настройки окна просмотра в примере «Длинная история», если я нахожусь в окне просмотра 1, я могу открыть окна gedit в окнах просмотра 1, 2, 3 и 4 с помощью команды:

    setwindow gedit 100 100 200 200&&setwindow gedit 1780 100 200 200&&setwindow gedit 3460 100 200 200&&setwindow gedit 5140 100 200 200
    

Сценарий

#!/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])
# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else 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 = "wmctrl -ir "+procs[0][0][1]+" -e 0,"+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]+","+sys.argv[5]
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1



РЕДАКТИРОВАТЬ: ленивая версия

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

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

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport>

Пример: открыть Google-Chromeокно, расположенное, по 20, 20размеру 300x300, в окне просмотра 5:

setwindow google-chrome 20 20 300 300 5

Настройка почти такая же, как и в первой версии скрипта.
Обратите внимание, что этот скрипт также работает правильно, только если определенное окно (позиция / размер) полностью помещается в целевой области просмотра.

Сценарий:

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

app = sys.argv[1]
target_vp = int(sys.argv[6])

def get_res():
    # get resolution
    xr = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    pos = xr.index("current")
    return [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]

res = get_res()

def current(set_vp):
    # get the current viewport
    vp_data = subprocess.check_output(
        ["wmctrl", "-d"]
        ).decode("utf-8").split()
    dt = [int(n) for n in vp_data[3].split("x")]
    cols = int(dt[0]/res[0])
    rows = int(dt[1]/res[1])    
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    curr_col = int(curr_vpdata[0]/res[0])
    curr_row = int(curr_vpdata[1]/res[1])
    curr_vp = curr_col+curr_row*cols+1
    # calculate the vector to the origin from the current viewport (in resolution units)
    vec_curr = vector(curr_vp, cols)
    # calculate the vector to the origin from the targeted viewport
    vec_set = vector(set_vp, cols)
    # calculate the vector between current and targeted viewport
    vec_relative = [vec_set[0] - vec_curr[0],
                    vec_set[1] - vec_curr[1]]
    # calculate needed correction (absolute)
    relative = [vec_relative[0]*res[0],
                vec_relative[1]*res[1]]
    return relative

def vector(vp, cols):
    rem = vp%cols
    vec_x = rem-1 if rem != 0 else cols-1
    vec_y = int((vp-1)/cols)
    return [vec_x, vec_y]

res = get_res() # nieuw
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
# check for additional arguments to run the application
try:
    subprocess.Popen(["/bin/bash", "-c", app+" "+sys.argv[7]])  
except IndexError:
    subprocess.Popen(["/bin/bash", "-c", app])

# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else 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"
        # calculate the correction, related to the current workspace, marge for launcher and panel
        pos_x = int(sys.argv[2]); pos_y = int(sys.argv[3]); x_marge = 65; y_marge = 35
        pos_x = pos_x if pos_x > x_marge else x_marge; pos_y = pos_y if pos_y > y_marge else y_marge
        x_relative = pos_x+current(target_vp)[0]
        y_relative = pos_y+current(target_vp)[1]
        # correct possible inaccurately set width / height
        x_size = res[0]; y_size = res[1]
        set_width = int(sys.argv[4]); set_height = int(sys.argv[5])
        width = set_width if set_width+x_marge+pos_x < x_size else x_size - pos_x - x_marge
        height = set_height if set_height+y_marge+pos_y < y_size else y_size - pos_y - y_marge
        cmd3 = "wmctrl -ir "+w_id+" -e 0,"+str(x_relative)+","+str(y_relative)+","+str(width)+","+str(height)
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1


Открытие окна приложения с аргументами

Чтобы закончить работу, полностью отвечая на ваш вопрос:

Если вы запустите скрипт как, например:

setwindow google-chrome 20 20 300 300 5

это откроет окно по умолчанию на целевом рабочем столе (ах).
Однако в последней версии скрипта вы можете добавить дополнительный аргумент для открытия окна приложения, например url:

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport> <(optional)_argument>

например:

setwindow google-chrome 0 0 600 600 3 "--new-window http://askubuntu.com"

Если (дополнительный) аргумент содержит пробелы, используйте кавычки. Приведенный выше пример откроет google-chromeокно в окне просмотра 3, открывая url http://askubuntu.com.

Вы можете объединить команды, чтобы открыть несколько окон / URL-адресов в разных рабочих пространствах в одной команде, например:

setwindow google-chrome 0 0 600 600 8 "--new-window http://askubuntu.com"&&setwindow google-chrome 0 0 600 600 7 "--new-window www.google.com"
Якоб Влейм
источник
@snitko Спасибо за хороший вопрос, это был хороший вызов, чтобы сделать это :)
Джейкоб Влейм
Я заметил небольшое смещение для окна, когда я использую скрипт. Так, например, если я открываю с координатами 0 0, он фактически открывает его чуть правее и снизу (смещение ~ 10px). Это нормально, но проблема на самом деле со вторым сценарием: смещение во втором сценарии странно больше на горизонтальной оси. Я думаю, что это примерно ~ 50 пикселей влево. Вы видите, почему это так? Установка отрицательных координат не помогает в этом случае.
Snitko
Другой вопрос: как открыть окно в полноэкранном режиме?
Snitko
Обновление: смещение в случае второго скрипта кажется таким же, как ширина стыковочного узла слева (хотя он скрыт).
Snitko
Для тех, кто заинтересован, я реализовал Desktopen: github.com/snitko/desktopen
snitko
1

Это расширяет великолепный ответ @Jacob Vlijim, приведенный выше, с немного измененным setwindowсценарием:

#!/usr/bin/env python

import time
import argparse
import subprocess

DEFAULT_WIDTH = '1920'
DEFAULT_HEIGHT = '1080'


def get_window_list():
    window_list = subprocess.check_output(['/bin/bash', '-c', 'wmctrl -l'])
    parsed_list = []
    for line in window_list.splitlines():
        window_info = line.split()
        if window_info[1] != '-1':
            parsed_list.append(window_info[0])
    return parsed_list


def main(params):
    old_list = get_window_list()
    subprocess.Popen(['/bin/bash', '-c', params.command])

    def get_diff(old):
        new_list = get_window_list()
        return list(set(new_list) - set(old))

    diff = get_diff(old_list)
    x = 0
    while not diff:
        if x == 10:
            print 'window not found'
            return
        x += 1
        diff = get_diff(old_list)
        time.sleep(1)
    if len(diff) > 1:
        raise Exception(diff)
    window_id = diff[0]
    command_list = []
    command_list.append('wmctrl -ir %s -t %s' % (window_id, params.desktop))
    command_list.append('wmctrl -ir %s -b remove,maximized_horz,maximized_vert'
        % window_id)
    command_list.append('wmctrl -ir %s -e 0,%s,%s,%s,%s' %
        (window_id, params.x_pos, params.y_pos, params.width, params.height))
    for command in command_list:
        subprocess.call(['/bin/bash', '-c', command])

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('command', type=str)
    parser.add_argument('-d', '--desktop', default='0', type=str)
    parser.add_argument('-x', '--x-pos', default='0', type=str)
    parser.add_argument('-y', '--y-pos', default='0', type=str)
    parser.add_argument('-w', '--width', default=DEFAULT_WIDTH, type=str)
    parser.add_argument('-t', '--height', default=DEFAULT_HEIGHT, type=str)
    args = parser.parse_args()
    main(args)

Описание изменений:

  1. python3к python(просто личных предпочтений)
  2. sys.argvчтобы argparseдля интерфейса командной строки лучше
  3. строгий синтаксический анализ окна (а не идентификатора процесса)
    • некоторые программы используют один идентификатор процесса для нескольких окон
  4. while цикл от 0,5 до 1 секунды
  5. более подробные и читаемые имена переменных и соблюдение pep8
  6. глобальные постоянные переменные для размера экрана вместо xrandrдоверия

ПРИМЕЧАНИЕ. Это слегка улучшенная версия, которую я написал для личного использования в Debian Jessie LXDE. Ваши результаты могут отличаться.

lscstu22
источник
0

Для тех, кто заинтересован, я реализовал Desktopen: github.com/snitko/desktopen

Это позволяет вам написать скрипт для открытия окон в разных окнах просмотра и отображать в очень дружественной манере.

Снитко
источник