Introducción
El siguiente script permite seleccionar dos ventanas, y aunque ambas ventanas están abiertas, las abrirá cuando el usuario enfoque cualquiera de ellas. Por ejemplo, si uno vincula a las viudas A y B, las brujas con A o B harán que ambas se eleven por encima de otras viudas.
Para detener el script, puede usarlo killall link_windows.py
en la terminal, o cerrar y volver a abrir una de las ventanas. También puede cancelar la ejecución presionando el botón Cerrar Xen cualquiera de los cuadros de diálogo emergentes de selección de ventana.
Ajustes potenciales:
- Se pueden usar varias instancias del script para agrupar pares de dos ventanas. Por ejemplo, si tenemos ventanas A, B, C y D, podemos vincular A y B, y vincular C y D.
- Se pueden agrupar varias ventanas en una sola ventana. Por ejemplo, si enlace la ventana B a A, C a A y D a A, eso significa que si siempre cambio a A, puedo subir las 4 ventanas al mismo tiempo.
Uso
Ejecute el script como:
python link_windows.py
El script es compatible con Python 3, por lo que también puede ejecutarse como
python3 link_windows.py
Hay dos opciones de línea de comando:
--quiet
o -q
, permite silenciar las ventanas de la GUI. Con esta opción, puede hacer clic con el mouse en cualquiera de las dos ventanas y el script comenzará a vincularlas.
--help
o -h
imprime la información de uso y descripción.
La -h
opción produce la siguiente información:
$ 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.
Se puede ver información técnica adicional a través de pydoc ./link_windows.py
, donde ./
significa que debe estar en el mismo directorio que el script.
Proceso de uso simple para dos ventanas:
Aparecerá una ventana emergente pidiéndole que seleccione una ventana # 1, presione OKo presione Enter. El puntero del mouse se convertirá en una cruz. Haga clic en una de las ventanas que desea vincular.
Aparecerá una segunda ventana emergente pidiéndole que seleccione la ventana # 2, presione OKo presione Enter. Nuevamente, el puntero del mouse se convertirá en una cruz. Haga clic en la otra ventana que desea vincular. Después de esa ejecución comenzará.
Cada vez que enfoca cualquiera de las ventanas, el script elevará la otra ventana, pero volverá a enfocarse en la seleccionada originalmente (nota: con un cuarto de segundo de retraso para obtener el mejor rendimiento), creando así la sensación de que las ventanas están unidas.
Si selecciona la misma ventana las dos veces, el script se cerrará. Si en cualquier momento hace clic en el botón Cerrar del cuadro de diálogo emergente, el script se cerrará.
Fuente de script
También disponible como GitHub Gist
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Author: Sergiy Kolodyazhnyy
Date: August 2nd, 2016
Written for: /ubuntu//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()
Notas :
- Cuando se ejecuta desde la línea de comandos, los cuadros de diálogo emergentes generan el siguiente mensaje:
Gtk-Message: GtkDialog mapped without a transient parent. This is discouraged.
Estos pueden ignorarse.
- Consultar ¿Cómo puedo editar / crear nuevos elementos de iniciador en Unity a mano? para crear un iniciador o acceso directo de escritorio para este script, si desea iniciarlo con doble clic
- Para vincular esta secuencia de comandos a un atajo de teclado para un acceso fácil, consulte ¿Cómo agregar atajos de teclado?