РЕДАКТИРОВАТЬ-новый ответ-
Ответ (ы) ниже / все еще полностью действительны, и поэтому предложенные варианты. Тем не менее, постоянное понимание заставило меня добавить эту опцию, чтобы использовать индикатор ниже, что, вероятно, является наиболее элегантным решением.
Таким образом, он, вероятно, должен заменить опцию 5 (используя файл .desktop).
Просто выберите приложение из списка, и все окна соответствующего приложения (представленные в текущем окне просмотра) появятся:
Как пользоваться
из ppa:
sudo add-apt-repository ppa:vlijm/upfront
sudo apt-get update
sudo apt-get install upfront
... или вручную:
#!/usr/bin/env python3
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject
import time
from threading import Thread
import os
import subprocess
import getpass
currpath = os.path.dirname(os.path.realpath(__file__))
class Indicator():
def __init__(self):
self.app = 'raise_apps'
iconpath = os.path.join(currpath, "raise.png")
self.indicator = AppIndicator3.Indicator.new(
self.app, iconpath,
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
# the thread:
self.update = Thread(target=self.check_recent)
# daemonize the thread to make the indicator stopable
self.update.setDaemon(True)
self.update.start()
def create_menu(self):
# creates the (initial) menu
self.menu = Gtk.Menu()
# separator
initial = Gtk.MenuItem("Fetching list...")
menu_sep = Gtk.SeparatorMenuItem()
self.menu.append(initial)
self.menu.append(menu_sep)
# item_quit.show()
self.menu.show_all()
return self.menu
def raise_wins(self, *args):
index = self.menu.get_children().index(self.menu.get_active())
selection = self.menu_items2[index][1]
for w in selection:
execute(["wmctrl", "-ia", w])
def set_new(self):
# update the list, appearing in the menu
for i in self.menu.get_children():
self.menu.remove(i)
for app in self.menu_items2:
sub = Gtk.MenuItem(app[0])
self.menu.append(sub)
sub.connect('activate', self.raise_wins)
# separator
menu_sep = Gtk.SeparatorMenuItem()
self.menu.append(menu_sep)
# quit
item_quit = Gtk.MenuItem('Quit')
item_quit.connect('activate', self.stop)
self.menu.append(item_quit)
self.menu.show_all()
def get_apps(self):
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# creating window list on current viewport / id's / application names
w_data = [l.split() for l in get(["wmctrl", "-lpG"]).splitlines()]
# windows on current viewport
relevant = [w for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]]
# pids
pids = [l.split() for l in get(["ps", "-u", getpass.getuser()]).splitlines()]
matches = [[p[-1], [w[0] for w in relevant if w[2] == p[0]]] for p in pids]
return [m for m in matches if m[1]]
def check_recent(self):
self.menu_items1 = []
while True:
time.sleep(4)
self.menu_items2 = self.get_apps()
for app in self.menu_items2:
app[0] = "gnome-terminal" if "gnome-terminal" in app[0] else app[0]
if self.menu_items2 != self.menu_items1:
GObject.idle_add(
self.set_new,
priority=GObject.PRIORITY_DEFAULT
)
self.menu_items1 = self.menu_items2
def stop(self, source):
Gtk.main_quit()
def get(command):
return subprocess.check_output(command).decode("utf-8")
def execute(command):
subprocess.Popen(command)
Indicator()
GObject.threads_init()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
Индикатор нуждается wmctrl
sudo apt-get wmctrl
Скопируйте индикатор в пустой файл, сохраните его как raise_apps.py
Скопируйте изображение ниже, сохраните его с точным именем raise.png
в том же каталоге, что и индикатор.
Затем просто запустите его командой:
python3 /path/to/raise_apps.py
Добавьте, если вы хотите запускать приложения:
/bin/bash -c "sleep 10 && python3 /path/to/raise_apps.py"
СТАРЫЙ ОТВЕТ:
О вопросе
С правильными инструментами не очень сложно «просто» поднять все окна приложения. Немного сложнее убедиться, что подняты только окна текущего окна просмотра. Однако реальная задача - найти удобный способ сделать действие доступным для пользователя.
Ниже пять вариантов, чтобы позаботиться об этом, чтобы показать, как это можно сделать. Все варианты готовы к использованию. Последний вариант, однако, является экспериментальным; он работает нормально, но имеет несколько мелких косметических недостатков, как описано в описании опции. Я добавил это как концепцию .
Как показано в комментарии, автоматическое разбрасывание окон без наложения друг на друга кажется мне не практической идеей; если вы работаете в (с точки зрения приложения) групповой настройке окна, сценарий может нежелательно переставить окна.
Как пользоваться
Для всех вариантов вам необходимо:
установить, wmctrl
если он еще не установлен в вашей системе:
sudo apt-get install wmctrl
создайте, если он еще не существует, каталог:
~/bin
(объяснение: каталог ~/bin
находится в $ PATH, поэтому вы можете запускать исполняемые файлы по их именам)
Скопируйте скрипт, соответствующий опции, вставьте его в пустой файл, сохраните как raise_app
(без расширения) ~/bin
и сделайте его исполняемым
В отдельных опциях будут описаны возможные дополнительные шаги.
Вариант 1: выберите приложение, введя один или несколько символов
- Нажмите комбинацию клавиш, появится
zenity
окно
- Введите один или несколько символов названия приложения в поле ввода
- Нажмите Ввод
Это заставит все окна соответствующего приложения (в текущем окне просмотра) выйти вперед.
поднять все gnome-terminal
окна в текущем окне просмотра:
Как использовать:
- Выполните настройку, как описано в разделе «Как использовать»
Протестируйте его командой:
raise_app
Если все работает нормально, добавьте его в комбинацию клавиш по вашему выбору: Выберите: «Системные настройки»> «Клавиатура»> «Ярлыки»> «Пользовательские сочетания клавиш». Нажмите «+» и добавьте команду
Сценарий:
#!/usr/bin/env python3
import subprocess
import getpass
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def execute(command):
subprocess.Popen(["/bin/bash", "-c", command])
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# creating window list on current viewport / id's / application names
w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()]
windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]]
for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]]
# ask user for first characters
try:
arg = get('zenity --entry --text "first characters" --title "application"').strip()
except subprocess.CalledProcessError:
pass
# raise matching windows
try:
[execute("wmctrl -ia "+item[1]) for item in windows if item[0].startswith(arg)]
except (subprocess.CalledProcessError, NameError):
pass
Вариант 2: прокручивать приложения и поднимать их окна с помощью комбинации клавиш:
Допустим, у меня есть сценарий ниже под комбинацией клавиш Alt+ 1. У меня есть несколько открытых окон:
- Fire Fox
- гном-терминал
- кораблик
Текущее состояние:
Жму один раз Alt+ 1, все nautilus
окна подняты:
Жму снова Alt+ 1, все firefox
окна подняты:
Я нажимаю снова Alt+ 1, все gnome-terminal
окна снова поднимаются, цикл начинается заново:
Как пользоваться
Затем прокрутите свои приложения с помощью сгруппированных окон приложений с помощью комбинации клавиш.
Сценарий:
#!/usr/bin/env python3
import subprocess
import getpass
include_single = True # set to False if you only want to cycle through apps with multiple windows
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def execute(command):
subprocess.Popen(["/bin/bash", "-c", command])
def get_frontmost():
cmd = "xprop -root"
frontmost = [l for l in get(cmd).splitlines() if\
"ACTIVE_WINDOW(WINDOW)" in l][0].split()[-1]
return frontmost[:2]+"0"+frontmost[2:]
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# creating window list on current viewport / id's / application names
w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()]
windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]]
for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]]
# create application list to cycle through
if include_single == False:
pre = [it[0] for it in windows]
apps = sorted(list(set([it for it in pre if pre.count(it) > 1])))
else:
apps = sorted(list(set([it[0] for it in windows])))
if len(apps) == 0:
pass
else:
# get the frontmost window as a last itm in the cycle
front = get_frontmost()
front_pid = [l.split()[2] for l in get("wmctrl -lp").splitlines() if front in l][0]
last_infront = get("ps -u "+getpass.getuser()+" | grep "+front_pid).split()[-1]
# determine next apllication to raise
if not last_infront in apps or last_infront == apps[-1]:
arg = apps[0]
print(arg)
else:
arg = apps[apps.index(last_infront)+1]
# raise matching windows
try:
[execute("wmctrl -ia "+item[1]) for item in windows if item[0] == arg]
except (subprocess.CalledProcessError, NameError):
pass
Вариант 3: нажмите комбинацию клавиш + щелкните значок запуска -или- окно приложения, чтобы поднять все окна в текущем окне просмотра
Вероятно, этот вариант наиболее близок к описанному в вопросе / комментарии.
Допустим, у меня грязный рабочий стол с тремя nautilus
окнами под другими окнами.
Чтобы поднять все окна nautilus (пример ярлыка: Alt+ 1):
Как использовать:
- Выполните настройку, как описано в разделе «Как использовать»
Протестируйте его командой:
raise_app
Если все работает нормально, добавьте его в комбинацию клавиш по вашему выбору: Выберите: «Системные настройки»> «Клавиатура»> «Ярлыки»> «Пользовательские сочетания клавиш». Нажмите «+» и добавьте команду
Затем:
Сценарий
#!/usr/bin/env python3
import subprocess
import getpass
import time
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def execute(command):
subprocess.Popen(["/bin/bash", "-c", command])
def get_frontmost():
cmd = "xprop -root"
frontmost = [l for l in get(cmd).splitlines() if\
"ACTIVE_WINDOW(WINDOW)" in l][0].split()[-1]
return frontmost[:2]+"0"+frontmost[2:]
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# get window data for various purposes
w_data = get("wmctrl -lpG").splitlines()
non_windows = sum([[l.split()[0] for l in w_data if it in l]\
for it in ("unity-launcher", "unity-panel", "unity-dash", "Hud")], [])
# get id of current window
curr_window = get_frontmost()
# user gets 3 seconds to pick an application window (or launcher icon)
t = 0
while t < 4:
w_id1 = get_frontmost()
time.sleep(1)
w_id2 = get_frontmost()
if w_id1 == w_id2 or w_id2 in non_windows+[curr_window]:
t = t+1
else:
new_frontmost = w_id2
break
# raise
try:
pid = [l.split()[2] for l in w_data if new_frontmost in l]
wl_data = [l.split() for l in w_data]
raise_windows = [l[0] for l in wl_data if pid[0] == l[2] and\
0 < int(l[3]) < res[0] and 0 < int(l[4]) < res[1]]
[execute("wmctrl -ia "+item) for item in raise_windows]
except NameError:
pass
Вариант 4: комбинация клавиш вызывает список опций, показывающий количество окон на приложение в текущем окне просмотра
Этот оказался более удобным, чем я предполагал:
Нажатие (снова пример-) комбинации клавиш Alt+ 1вызывает zenity
окно, в котором перечислены все приложения и количество их окон в текущем окне просмотра:
Простое нажатие стрелок ▴или ▾приведет вас к правильному варианту. Нажмите Enterи все окна выбранного приложения будут подняты.
Как использовать:
- Выполните настройку, как описано в разделе «Как использовать»
Протестируйте его командой:
raise_app
Если все работает нормально, добавьте его в комбинацию клавиш по вашему выбору: Выберите: «Системные настройки»> «Клавиатура»> «Ярлыки»> «Пользовательские сочетания клавиш». Нажмите «+» и добавьте команду
Сценарий
#!/usr/bin/env python3
import subprocess
import getpass
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def execute(command):
subprocess.Popen(["/bin/bash", "-c", command])
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# creating window list on current viewport / id's / application names
w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()]
windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]]
for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]]
# preparing zenity optionlist
apps = [item[0] for item in windows]
# prevent multiple zenity windows
if apps.count("zenity") > 1:
pass
elif apps.count("zenity") > 0:
execute('zenity --info --text "Another Zenity window is open already"')
# preventing empty windowlist
elif len(apps) > 0:
applist = [[app, str(apps.count(app))] for app in set(apps)]
applist.sort(key=lambda x: x[1])
# calling zenity window
try:
arg = get('zenity --list --text "Choose an application" '+\
'--title "Current windows" '+\
'--column "application" '+\
'--column "windows" '+\
'--height 250 '+\
'--width 250 '+\
(" ").join(sum(applist, [])))
except subprocess.CalledProcessError:
pass
# raise matching windows
try:
[execute("wmctrl -ia "+item[1]) \
for item in windows if arg.startswith(item[0])]
except (subprocess.CalledProcessError, NameError):
pass
else:
execute('zenity --info --text "No windows to list"')
Вариант 5: поднять окна запущенных приложений с помощью значка запуска
Эта опция существует в виде значка запуска, с запущенными в данный момент приложениями в быстром списке. Выберите один, и все окна приложений будут подняты.
Панель запуска автоматически обновляется при изменении списка запущенных приложений (в текущем окне просмотра). Быстрый список показывает другой список в других окнах просмотра, где открываются окна других приложений (адаптация займет 1-2 секунды).
Как уже упоминалось, хотя и полностью функциональный, этот вариант является концепцией . У него есть несколько незначительных косметических недостатков, как есть. Самое важное:
- Курсор «Колесо» продолжает вращаться в течение нескольких секунд после действия. Хотя это не влияет на функциональность, это косметический недостаток.
- Обновление списка приложений на значке панели запуска после изменения списка запущенных приложений занимает 1-2 секунды.
Кроме того, настройка немного сложнее (хотя подробно объясняется ниже):
Как пользоваться
Ниже вы найдете:
два скрипта / иконка / .desktop
файл
- Подготовьте настройку как в «Как использовать», сохраните первый (основной) скрипт как
raise_app
в~/bin
Сохраните значок ниже (щелкните правой кнопкой мыши, сохраните как) как raise.png
Скопируйте .desktop
файл в пустой файл, отредактируйте строку
Icon=/path/to/raise.png
к реальному пути к значку (пути с пробелами между кавычками)
Сохраните его как raise.desktop
в~/.local/share/applications
Перетащите .desktop
файл в панель запуска, чтобы добавить его
- скопировать второй сценарий, вставьте его в пустой файл, сохраните его как
update_apps
в ~/bin
, сделать его исполняемым.
Добавьте следующую команду в свои автозагрузки (Dash> Startup Applications> Add):
update_apps
- Выйдите из системы и вернитесь, чтобы она заработала.
Первый скрипт
#!/usr/bin/env python3
import subprocess
import getpass
import sys
arg = sys.argv[1]
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def execute(command):
subprocess.Popen(["/bin/bash", "-c", command])
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# creating window list on current viewport / id's / application names
w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()]
windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]]
for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]]
try:
[execute("wmctrl -ia "+item[1]) for item in windows if item[0].startswith(arg)]
except (subprocess.CalledProcessError, NameError):
pass
Второй сценарий
#!/usr/bin/env python3
import subprocess
import getpass
import time
import os
dtfile = os.environ["HOME"]+"/.local/share/applications/raise.desktop"
def get(command):
return subprocess.check_output(["/bin/bash", "-c", command]).decode("utf-8")
def execute(command):
subprocess.Popen(["/bin/bash", "-c", command])
# calculate screen resolution
res_output = get("xrandr").split(); idf = res_output.index("current")
res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
# creating window list on current viewport / id's / application names
def applist():
try:
w_data = [l.split()[0:7] for l in get("wmctrl -lpG").splitlines()]
windows = [[get("ps -u "+getpass.getuser()+" | grep "+w[2]).split()[-1], w[0]]
for w in w_data if 0 < int(w[3]) < res[0] and 0 < int(w[4]) < res[1]]
except subprocess.CalledProcessError:
return []
else:
return set([app[0] for app in windows])
def update_dtfile(applications, text):
actionline = "Actions="+(";").join(applications)+";\n"
with open(dtfile) as src:
lines = src.readlines()
lines = lines[:[i for i in range(len(lines)) \
if lines[i].startswith("Actions=")][0]]+[actionline]
for item in text:
for it in item:
lines.append(it)
with open(dtfile, "wt") as out:
for line in lines:
out.write(line)
while True:
apps1 = applist()
time.sleep(1)
apps2 = applist()
if apps1 != apps2:
text = [["[Desktop Action "+it+"]\n", "Name="+it+"\n",
"Exec=raise_app "+it+"\n", "OnlyShowIn=Unity;\n\n",
]for it in apps2]
update_dtfile(apps2, text)
Файл .desktop
[Desktop Entry]
Name=Raise application windows
Comment=Raise groups of windows
Icon=/path/to/raise.png
Terminal=false
Type=Application
Version=1.0
Actions=
Краткое объяснение
Все вышеперечисленные решения используют wmctrl
для создания списка окон, используя wmctrl -lpG
команду. Эта команда производит строки, похожие на:
0x044000b3 0 3429 65 24 1615 1026 jacob-System-Product-Name unity - How to show all windows of an application? - Ask Ubuntu - Mozilla Firefox
Эти строки включают в себя:
- 1-й столбец: идентификатор окна (который мы можем использовать, чтобы поднять его)
- 3-й столбец: pid, которому принадлежит окно.
- 4-й / 5-й столбец: геометрия окна xy (которую мы используем, чтобы увидеть, находится ли окно в текущем окне просмотра, icw
xrandr
)
Идентификатор pid ищется в выходных данных, ps -u <username>
чтобы получить «читабельную» идентификацию (имя) приложения.
Таким образом, мы можем выделить окна для приложений. Впоследствии мы можем поднять окна данного приложения в for
цикле с помощью команды wmctrl -ia
.
В варианте 3
сценарий запускает 3-секундный цикл ожидания, используя xprop -root
команду несколько раз, чтобы увидеть, есть ли какие-либо изменения в том, что находится в переднем окне; это произойдет, если пользователь щелкнет значок запуска, чтобы открыть окно приложения, или непосредственно щелкнет окно. Если это так, цикл while прерывает и ищет «новое» переднее приложение, а затем открывает все остальные окна этого приложения.