Как я могу сгруппировать окна, которые будут подняты как одно?

10

У меня есть два окна, A и B. Возможно ли как-то связать два окна вместе, так что переключение на A также поднимает B, или переключение на B также поднимает A?

Я понимаю, что использование нескольких рабочих областей является альтернативным вариантом, но мне было интересно, возможно ли это?

Саймон Тонг
источник
Z-порядок не супер важен, но если возможно, это было бы здорово
Саймон Тонг
Я думаю, что несколько рабочих мест, безусловно, самое простое решение. Вы знаете комбинации клавиш для переключения между ними?
Томасруттер
1
Вы быстро принимаете :) Тем не менее, был бы признателен, если бы вы прокомментировали мой ответ.
Джейкоб Влейм
5
Возможный дубликат окна «Группировка»?

Ответы:

9

Вступление

Следующий скрипт позволяет выбрать два окна, и, пока оба окна открыты, он поднимает оба окна, когда пользователь фокусирует одно из них. Например, если кто-то связывает вдов А и В, то ведьма к А или В заставит обоих подняться выше других вдов.

Чтобы остановить скрипт, вы можете использовать его killall link_windows.pyв терминале или закрыть и снова открыть одно из окон. Вы также можете отменить выполнение, нажав кнопку закрытия Xв любом из всплывающих диалоговых окон выбора окна.

Потенциальные настройки:

  • несколько экземпляров скрипта могут быть использованы для группировки пар из двух окон. Например, если у нас есть окна A, B, C и D, мы можем связать A и B вместе и связать C и D вместе.
  • несколько окон могут быть сгруппированы под одним окном. Например, если я связываю окно B с A, C с A и D с A, это означает, что если я всегда переключаюсь на A, я могу поднять все 4 окна одновременно.

использование

Запустите скрипт как:

python link_windows.py

Скрипт совместим с Python 3, поэтому он также может работать как

python3 link_windows.py

Есть две опции командной строки:

  • --quietили -q, позволяет отключить окна GUI. С помощью этой опции вы можете просто щелкнуть мышью на любых двух окнах, и скрипт начнет их связывать.
  • --helpили -hпечатает информацию об использовании и описании.

-hОпция производит следующую информацию:

$ python3 link_windows.py  -h                                                                                            
usage: link_windows.py [-h] [--quiet]

Linker for two X11 windows.Allows raising two user selected windows together

optional arguments:
  -h, --help  show this help message and exit
  -q, --quiet  Blocks GUI dialogs.

Дополнительную техническую информацию можно просмотреть через pydoc ./link_windows.py, где ./означает, что вы должны находиться в том же каталоге, что и скрипт.

Простой процесс использования для двух окон:

  1. Появится всплывающее окно с просьбой выбрать окно № 1, нажмите OKили нажмите Enter. Указатель мыши превратится в крест. Нажмите на одно из окон, которое вы хотите связать.

  2. Появится второе всплывающее окно с просьбой выбрать окно № 2, нажать OKили нажать Enter. Опять же, указатель мыши превратится в крестик. Нажмите на другое окно, которое вы хотите связать. После этого начнется казнь.

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

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

Источник скрипта

Также доступно как GitHub Gist

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Author: Sergiy Kolodyazhnyy
Date:  August 2nd, 2016
Written for: https://askubuntu.com/q/805515/295286
Tested on Ubuntu 16.04 LTS
"""
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, Gtk
import time
import subprocess
import sys
import argparse


def run_cmd(cmdlist):
    """ Reusable function for running shell commands"""
    try:
        stdout = subprocess.check_output(cmdlist)
    except subprocess.CalledProcessError:
        sys.exit(1)
    else:
        if stdout:
            return stdout


def focus_windows_in_order(first, second, scr):
    """Raise two user-defined windows above others.
       Takes two XID integers and screen object.
       Window with first XID will have the focus"""

    first_obj = None
    second_obj = None

    for window in scr.get_window_stack():
        if window.get_xid() == first:
            first_obj = window
        if window.get_xid() == second:
            second_obj = window

    # When this  function is called first_obj is alread
    # raised. Therefore we must raise second one, and switch
    # back to first
    second_obj.focus(int(time.time()))
    second_obj.get_update_area()
    # time.sleep(0.25)
    first_obj.focus(int(time.time()))
    first_obj.get_update_area()


def get_user_window():
    """Select two windows via mouse. Returns integer value of window's id"""
    window_id = None
    while not window_id:
        for line in run_cmd(['xwininfo', '-int']).decode().split('\n'):
            if 'Window id:' in line:
                window_id = line.split()[3]
    return int(window_id)


def main():
    """ Main function. This is where polling for window stack is done"""

    # Parse command line arguments
    arg_parser = argparse.ArgumentParser(
        description="""Linker for two X11 windows.Allows raising """ +
                    """two user selected windows together""")
    arg_parser.add_argument(
                '-q','--quiet', action='store_true',
                help='Blocks GUI dialogs.',
                required=False)
    args = arg_parser.parse_args()

    # Obtain list of two user windows
    user_windows = [None, None]
    if not args.quiet:
        run_cmd(['zenity', '--info', '--text="select first window"'])
    user_windows[0] = get_user_window()
    if not args.quiet:
        run_cmd(['zenity', '--info', '--text="select second window"'])
    user_windows[1] = get_user_window()

    if user_windows[0] == user_windows[1]:
        run_cmd(
            ['zenity', '--error', '--text="Same window selected. Exiting"'])
        sys.exit(1)

    screen = Gdk.Screen.get_default()
    flag = False

    # begin watching for changes in window stack
    while True:

        window_stack = [window.get_xid()
                        for window in screen.get_window_stack()]

        if user_windows[0] in window_stack and user_windows[1] in window_stack:

            active_xid = screen.get_active_window().get_xid()
            if active_xid not in user_windows:
                flag = True

            if flag and active_xid == user_windows[0]:
                focus_windows_in_order(
                    user_windows[0], user_windows[1], screen)
                flag = False

            elif flag and active_xid == user_windows[1]:
                focus_windows_in_order(
                    user_windows[1], user_windows[0], screen)
                flag = False

        else:
            break

        time.sleep(0.15)


if __name__ == "__main__":
    main()

Заметки:

Сергей Колодяжный
источник
Ура, я действительно впечатлен. time.sleepБит между переключениями, я в состоянии положить , что к нулю? есть ли причина для задержки?
Саймон Тонг
1
@simontong вы можете попробовать закомментировать эту строку, как, # time.sleep(0.25)и она не будет выполняться. Причина этого заключается в том, чтобы убедиться, что каждое окно правильно поднято. По моему опыту в прошлом, мне нужно было иметь задержки для работы на Windows. Я думаю, что задержка в четверть секунды не так уж и велика. На самом деле, позвольте мне добавить еще одну строку в сценарий, это может ускорить его. ОК ?
Сергей Колодяжный
@simontong ОК, я обновил скрипт. Попробуй это сейчас. Должно быть более быстрое переключение
Сергей Колодяжный
@simontong Я буду обновлять скрипт с небольшими дополнениями, чтобы добавить несколько дополнительных функций. Я дам вам знать, как только он будет готов, пожалуйста, дайте мне знать, что вы думаете
Сергей Колодяжный
@simontong добавил дополнительные опции в скрипт, пожалуйста, просмотрите
Сергей Колодяжный
6

Поднимите произвольное количество окон как одно

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

Скрипт работает с тремя аргументами:

add

добавить активное окно в группу

raise

поднять заданную группу

clear

очистить группу, готов определить новую группу

Сценарий

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

wlist = os.path.join(os.environ["HOME"], ".windowlist")

arg = sys.argv[1]

if arg == "add":
    active = subprocess.check_output([
        "xdotool", "getactivewindow"
        ]).decode("utf-8").strip()
    try:
        currlist = open(wlist).read()
    except FileNotFoundError:
        currlist = []
    if not active in currlist:
        open(wlist, "a").write(active + "\n")
elif arg == "raise":
    group = [w.strip() for w in open(wlist).readlines()]
    [subprocess.call(["wmctrl", "-ia", w]) for w in group]
elif arg == "clear":
    os.remove(wlist)

Как пользоваться

  1. Скрипт нуждается wmctrlи xdotool:

    sudo apt-get install wmctrl xdotool
  2. Скопируйте приведенный выше скрипт в пустой файл, сохраните его как groupwindows.py
  3. Тест - запустите скрипт: откройте два окна терминала, выполните команду:

    python3 /absolute/path/to/groupwindows.py add

    в них обоих. Накройте их другими окнами (или сверните их). Откройте третье окно терминала, выполните команду:

    python3 /absolute/path/to/groupwindows.py raise

    Первые два окна будут подняты как одно.

  4. Если все работает нормально, создайте три пользовательских сочетания клавиш: Выберите: «Системные настройки»> «Клавиатура»> «Ярлыки»> «Пользовательские сочетания клавиш». Нажмите «+» и добавьте команды ниже к трем отдельным ярлыкам:

    в моей системе я использовал:

    Alt+ A, запустив команду:

    python3 /absolute/path/to/groupwindows.py add

    ... чтобы добавить окно в группу.

    Alt+ R, запустив команду:

    python3 /absolute/path/to/groupwindows.py raise

    ... чтобы поднять группу.

    Alt+ C, запустив команду:

    python3 /absolute/path/to/groupwindows.py clear

    ... очистить группу

объяснение

Скрипт работает довольно просто:

  • При запуске с аргументом addскрипт сохраняет / добавляет идентификатор окна активного окна в скрытый файл~/.windowlist
  • При запуске с аргументом raiseскрипт читает файл, поднимает окна в списке с помощью команды:

    wmctrl -ia <window_id>
  • При запуске с аргументом clearскрипт удаляет скрытый файл ~/.windowlist.

Заметки

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

Больше гибкости?

Как уже упоминалось, приведенный выше скрипт позволяет в любое время добавлять окна в сгруппированные окна. Версия ниже также позволяет удалить любое из окон (в любое время) из сгруппированного списка:

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

wlist = os.path.join(os.environ["HOME"], ".windowlist")
arg = sys.argv[1]
# add windows to the group
if arg == "add":
    active = subprocess.check_output([
        "xdotool", "getactivewindow"
        ]).decode("utf-8").strip()
    try:
        currlist = open(wlist).read()
    except FileNotFoundError:
        currlist = []
    if not active in currlist:
        open(wlist, "a").write(active + "\n")
# delete window from the group
if arg == "delete":
    try:
        currlist = [w.strip() for w in open(wlist).readlines()]
    except FileNotFoundError:
        pass
    else:
        currlist.remove(subprocess.check_output([
            "xdotool", "getactivewindow"]).decode("utf-8").strip())      
        open(wlist, "w").write("\n".join(currlist)+"\n")
# raise the grouped windows
elif arg == "raise":
    group = [w.strip() for w in open(wlist).readlines()]
    [subprocess.call(["wmctrl", "-ia", w]) for w in group]
# clear the grouped window list
elif arg == "clear":
    os.remove(wlist)

Дополнительный аргумент для запуска скрипта deleteтаков:

python3 /absolute/path/to/groupwindows.py delete

удаляет активное окно из сгруппированных окон. Чтобы запустить эту команду, в моей системе я установил Alt+ Dкак ярлык.

Якоб Влейм
источник