我如何将要升起的窗口分组为一个?

我如何将要升起的窗口分组为一个?

我有两个窗口,A 和 B。是否可以以某种方式将两个窗口链接在一起,使得切换到 A 时也会引发 B,或者切换到 B 时也会引发 A?

我知道使用多个工作区是一种替代选择,但想知道这是否也可行?

答案1

介绍

以下脚本允许选择两个窗口,当两个窗口都打开时,当用户聚焦其中一个窗口时,它将同时提升两个窗口。例如,如果一个链接窗口 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 要点

#!/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()

笔记:

答案2

将任意数量的窗口作为一个窗口

以下解决方案将让您选择任何使用键盘快捷键将两个、三个或多个窗口组合并作为一个窗口显示。

该脚本通过三个参数完成其工作:

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

笔记

  • 该脚本也将在最小化的窗口上运行,它将取消最小化可能最小化的窗口
  • 如果窗口集位于另一个视口上,则脚本将切换到相应的视口
  • 该集合是灵活的,您可以随时将其他窗口添加到当前集合中。

更加灵活?

如上所述,上面的脚本允许随时将窗口添加到分组窗口中。下面的版本还允许移除分组列表中的任何窗口(随时):

#!/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为快捷方式。

相关内容