Як я можу згрупувати вікна, які піднімаються як єдині?


10

У мене два вікна, A і B. Чи можна якось зв’язати два вікна разом, так що перемикання на A також піднімає B, або перемикання на B також піднімає A?

Я розумію, що використання декількох робочих просторів - це альтернативний варіант, але мені було цікаво, чи це теж можливо?


z-порядок не надто важливий, але, якщо можливо, це було б чудово
Саймон Тонг

Я думаю, що кілька робочих місць - це найпростіше рішення. Ви знаєте комбінації клавіш для перемикання між ними?
thomasrutter

1
Ви швидкий акцептор :) Тим не менше, вдячний, якби ви дали коментар до моєї відповіді.
Яків Влійм

5
Можливий дублікат віконної "групування"?

Відповіді:


9

Вступ

Наведений нижче сценарій дозволяє вибрати два вікна, і поки обидва вікна відкриті, він підніме обидва вікна, коли користувач фокусує будь-яке. Наприклад, якщо одна пов'язує вдови A і B, відьма або до A або B зробить обидві підняття над іншими вдовами.

Щоб зупинити скрипт, який ви можете використовувати 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сценарій зберігає / додає активний вікно-id активного вікна в прихований файл~/.windowlist
  • При запуску з аргументом raiseсценарій зчитує файл, піднімає вікна у списку командою:

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

Примітки

  • Сценарій також буде працювати на мінімізованих вікнах, він скасуватиме мінімізацію можливих мінімізованих вікон
  • Якщо набір вікон знаходиться в іншому вікні перегляду, скрипт переключиться на відповідний вікно перегляду
  • Набір є flexibel, ви завжди можете додати інші вікна до поточного набору.

Більше гнучкості?

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

#!/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як ярлик.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.