Как показать (поднять) все окна приложения?

17

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

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

Когда я нажимаю значок приложения, я получаю полноэкранный обзор всех окон. Я должен выбрать каждое окно вручную и несколько раз передвигать мышь на половину экрана.

Мое лучшее решение до сих пор сводит к минимуму все окна ( Ctrl + Super + D ), а затем показывает окна моего приложения, используя колесо прокрутки.

Есть ли лучшее решение?

    
задан peq 11.04.2014 в 17:43
источник

3 ответа

20

EDIT - новый ответ -

Ответ (ответы) ниже / все еще полностью действителен, поэтому предлагаемые варианты. Однако постоянное понимание позволило мне добавить этот вариант, чтобы использовать индикатор ниже, что, вероятно, является самым элегантным решением.

Таким образом, он должен, вероятно, заменить вариант 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" 
    

OLD ANSWER:

О вопросе

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

Ниже пяти вариантов, чтобы позаботиться об этом, чтобы показать, как это сделать . Все опции готовы к использованию. Последний вариант, однако, является своего рода экспериментальным; он отлично работает, но имеет несколько незначительных косметических недостатков, как объясняется в описании варианта. Я добавил его, тем не менее, в качестве концепции .

Распространение окон автоматически неперекрывающимся образом, как это предлагается в комментарии, кажется мне нецелесообразным; если вы работаете в настройке сгруппированных окон (по-разному), сценарий, возможно, будет нежелательно переупорядочивать окна.

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

Для всех параметров вам необходимо:

  • установить wmctrl , если он еще не включен в вашу систему:

    sudo apt-get install wmctrl
    
  • создать, если он еще не существует, каталог:

    ~/bin
    

    (объяснение: каталог ~/bin находится в $ PATH, поэтому вы можете запускать исполняемые файлы по их имени)

  • Скопируйте сценарий, соответствующий параметру, вставьте его в пустой файл, сохраните его как raise_app (без расширения) в ~/bin и сделайте его выполнимым

В отдельных опциях будут объяснены возможные дополнительные шаги.

Вариант 1: выберите приложение, введя один или несколько символов

  • Нажмите комбинацию клавиш, появится окно zenity
  • Введите один или несколько символов имени приложения в поле ввода
  • Нажмите enter

Это приведет к тому, что все окна соответствующего приложения (в окне current ) перейдут на передний план.

поднять все gnome-terminal окна в текущем видовом экране:

Как использовать:

  • Сделайте настройку, как описано в разделе «Как использовать»
  • Протестируйте его командой:

    raise_app
    
  • Если все работает нормально, добавьте его к сочетанию клавиш быстрого доступа по вашему выбору: Выберите: Системные настройки & gt; «Клавиатура» & gt; «Ярлыки» & gt; «Пользовательские ярлыки». Нажмите «+» и добавьте команду

Сценарий:

#!/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 . У меня открыто несколько окон:

  • светлячок
  • гном-терминал
  • Наутилус

Текущее состояние:

Я нажимаю один раз Alt + 1 , все nautilus окна подняты:

Я снова нажимаю Alt + 1 , все firefox окна подняты:

Я снова нажимаю Alt + 1 , все gnome-terminal окна снова появляются, цикл начинается:

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

  • Сделайте настройку, как описано в разделе «Как использовать»
  • Добавьте его в комбинацию сочетаний клавиш по вашему выбору: Выберите: Системные настройки & gt; «Клавиатура» & gt; «Ярлыки» & gt; «Пользовательские ярлыки». Нажмите «+» и добавьте команду

    raise_app
    

Затем циклически выполняйте свои приложения с помощью сгруппированных окон приложений с помощью комбинации клавиш.

Сценарий:

#!/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: нажмите комбинацию клавиш + щелкните по значку программы запуска -or - , чтобы поднять все окна в текущем окне просмотра

Вероятно, это вариант, который ближе всего к тому, что описано в вопросе / комментарии.

Предположим, у меня грязный рабочий стол с тремя nautilus окнами, зарытыми под другими окнами.

Чтобы поднять все окна nautilus (пример ярлыка: Alt + 1 ):

  • Нажмите Alt + 1 , отпустите (!)
  • В течение 3 секунд либо:

    щелкните значок приложения в панели запуска

    или

    нажмите на одно из окон приложения

    результат:


Как использовать:

  • Сделайте настройку, как описано в разделе «Как использовать»
  • Протестируйте его командой:

    raise_app
    
  • Если все работает нормально, добавьте его к сочетанию клавиш быстрого доступа по вашему выбору: Выберите: Системные настройки & gt; «Клавиатура» & gt; «Ярлыки» & gt; «Пользовательские ярлыки». Нажмите «+» и добавьте команду

Тогда:

  • Нажмите комбинацию клавиш и в течение 3 секунд, либо:

    • щелкните значок приложения в панели запуска
    • нажмите на одно из окон приложения

Скрипт

#!/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
    
  • Если все работает нормально, добавьте его к сочетанию клавиш быстрого доступа по вашему выбору: Выберите: Системные настройки & gt; «Клавиатура» & gt; «Ярлыки» & gt; «Пользовательские ярлыки». Нажмите «+» и добавьте команду

Скрипт

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

  1. Подготовьте настройку, как в «Как использовать», сохраните первый (основной) сценарий как raise_app в ~/bin
  2. Сохраните значок ниже (щелкните правой кнопкой мыши, сохраните как) в raise.png

  3. Скопируйте файл .desktop в пустой файл, отредактируйте строку

        Icon=/path/to/raise.png
    

    к реальному пути к значку (пути с пробелами между кавычками)
     Сохраните его как raise.desktop в ~/.local/share/applications

  4. Перетащите файл .desktop в панель запуска, чтобы добавить его

  5. скопируйте второй скрипт, вставьте его в пустой файл, сохраните его как update_apps в ~/bin , сделайте его выполнимым.
  6. Добавьте в свои приложения для запуска следующие команды (Dash & gt; Startup Applications & gt; Add):

    update_apps
    
  7. Выйдите из системы и вернитесь, чтобы он работал.

Первый скрипт

#!/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-й столбец: идентификатор окна (который мы можем использовать для его повышения)
  • Третий столбец: pid, которому принадлежит окно.
  • 4-й / 5-й столбец: геометрия окна x-y (которую мы используем, чтобы увидеть, находится ли окно в текущем окне просмотра, i.c.w xrandr )

pid просматривается в выводе ps -u <username> , чтобы получить «понятную пользователю» идентификацию (имя) приложения.
Таким образом, мы можем выделять окна приложениям. Впоследствии мы можем поднять окна данного приложения в цикле for с командой wmctrl -ia .

В опции 3
сценарий запускает 3-секундный цикл ожидания, используя команду xprop -root , чтобы увидеть, есть ли какие-либо изменения в том, что является самым передним окном; это произойдет, если пользователь либо нажмет на значок запуска, чтобы поднять окно приложения, либо щелкнет по окну напрямую. Если это так, while-loop разбивается и просматривает «новое» приложение frontmost, а затем поднимает все остальные окна этого приложения.

    
ответ дан Jacob Vlijm 06.01.2015 в 10:59
источник
1

Существует ярлык Super + W , который будет показывать экспонирование всех открытых в настоящее время окон, хотя это будет и другие приложения. Это происходит по умолчанию и не требует каких-либо изменений, поэтому, возможно, это самый простой вариант.

Кроме всего прочего, вы можете позиционировать окна в правой и левой половине экрана с помощью Ctrl + Super + Влево / Right и переключаться между ними с помощью Alt + ~ (тильда, рядом с номером 1).     

ответ дан Sergiy Kolodyazhnyy 07.01.2015 в 00:41
1

Если вы нажмете Alt + Tab, чтобы циклически запускать приложения, и вы переходите к одному с несколькими окнами, просто держите нажатой клавишу alt и примерно через полтора секунды значок будет заменен видом всех окон для это приложение.

Это может быть или не быть тем, что вы ищете, но это работает для меня, и это намного проще, поэтому я решил, что поделился бы этим предложением!

    
ответ дан Sean Colombo 11.09.2015 в 02:48