Como mostrar (elevar) todas as janelas de uma aplicação?

19

Eu tenho um aplicativo usando várias janelas. Como posso trazer rapidamente todas as janelas desse aplicativo para o primeiro plano?

Quando eu rola os aplicativos com a roda de rolagem, ele mostra apenas uma janela. Ao ir para a próxima janela, a última janela é novamente colocada em segundo plano.

Quando clico no ícone do aplicativo, recebo uma visão geral em tela cheia de todas as janelas. Eu tenho que selecionar cada janela manualmente e passar o mouse pela metade da tela várias vezes.

Minha melhor solução até agora é minimizar todas as janelas ( Ctrl + Super + D ) e, em seguida, mostrar as janelas do meu aplicativo usando o roda de rolagem.

Existe uma solução melhor?

    
por peq 11.04.2014 / 17:43

3 respostas

20

EDIT - nova resposta -

A (s) resposta (s) abaixo é / são ainda totalmente válidas e, portanto, as opções sugeridas. O insight em curso, no entanto, me fez adicionar essa opção para usar o indicador abaixo, que é provavelmente a solução mais elegante.

Como tal, provavelmente deve substituir a opção 5 (usando um arquivo .desktop).

Basta escolher o aplicativo na lista e todas as janelas do aplicativo correspondente (presentes na viewport atual) serão exibidas:

Como usar

de ppa:

sudo add-apt-repository ppa:vlijm/upfront
sudo apt-get update
sudo apt-get install upfront

... ou manualmente:

#!/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()
  • O indicador precisa de wmctrl

    sudo apt-get wmctrl
    
  • Copie o indicador em um arquivo vazio, salve-o como raise_apps.py

  • Copie a imagem abaixo, salve com o mesmo nome raise.png em um e o mesmo diretório o indicador.

  • Em seguida, basta executá-lo pelo comando:

    python3 /path/to/raise_apps.py

  • Adicione se você deseja iniciar aplicativos:

    /bin/bash -c "sleep 10 && python3 /path/to/raise_apps.py" 
    

RESPOSTA ANTIGA:

Sobre a questão

Com as ferramentas certas, não é muito complicado "apenas" levantar todas as janelas de uma aplicação. É um pouco mais complicado garantir que somente as janelas da viewport atual sejam geradas. O desafio real, no entanto, é encontrar uma maneira conveniente de disponibilizar a ação para o usuário.

Abaixo de cinco opções para cuidar disso, para mostrar como pode ser feito. Todas as opções estão prontas para serem usadas. A última opção, no entanto, é meio experimental; funciona bem, mas tem algumas desvantagens cosméticas menores, conforme explicado na descrição da opção. Eu adicionei, no entanto, como um conceito .

Espalhar as janelas automaticamente de maneira não sobreposta, como sugerido em um comentário, não parece uma idéia prática para mim; se você trabalha em uma configuração de janela agrupada (em termos de aplicativo), o script poderia reorganizar o Windows de maneira indesejada.

Como usar

Para todas as opções, você precisa:

  • instale wmctrl se ainda não estiver no seu sistema:

    sudo apt-get install wmctrl
    
  • crie, se ainda não existir, o diretório:

    ~/bin
    

    (explicação: o diretório ~/bin está em $ PATH, então você pode executar executáveis pelo nome)

  • Copie o script correspondente à opção, cole-o em um arquivo vazio, salve-o como raise_app (sem extensão) em ~/bin e torne-o executável

Nas opções separadas, possíveis etapas adicionais serão explicadas.

Opção 1: escolha o aplicativo inserindo um ou mais caracteres

  • Pressione uma combinação de teclas, uma janela zenity aparecerá
  • Insira um ou mais caracteres do nome do aplicativo na caixa de entrada
  • Pressione Enter

Isso fará com que todas as janelas do aplicativo correspondente (na viewport atual ) venham para frente.

aumenta todas as gnome-terminal janelas na janela de visualização atual:

Como usar:

  • Faça a configuração conforme descrito em "Como usar"
  • Teste-o pelo comando:

    raise_app
    
  • Se tudo funcionar bem, adicione-o a uma combinação de teclas de atalho de sua escolha: Escolha: Configurações do sistema & gt; "Teclado" & gt; "Atalhos" & gt; "Atalhos personalizados". Clique no botão "+" e adicione o comando

O script:

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


Opção 2: percorra os aplicativos e aumente suas janelas com uma combinação de teclas:

Digamos que eu tenha o script abaixo sob uma combinação de teclas Alt + 1 . Eu tenho várias janelas abertas de:

  • firefox
  • gnome-terminal
  • nautilus

O estado atual:

Pressiono uma vez Alt + 1 , todas as janelas nautilus são geradas:

Pressiono novamente Alt + 1 , todas as janelas firefox são geradas:

Eu pressiono novamente Alt + 1 , todas as janelas gnome-terminal são levantadas novamente, o ciclo começa de novo:

Como usar

  • Faça a configuração conforme descrito em "Como usar"
  • Adicione-o a uma combinação de teclas de atalho de sua escolha: Escolha: Configurações do sistema & gt; "Teclado" & gt; "Atalhos" & gt; "Atalhos personalizados". Clique no botão "+" e adicione o comando

    raise_app
    

Em seguida, percorra seus aplicativos com janelas de aplicativos agrupados com sua combinação de teclas.

O script:

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


Opção 3: pressione a combinação de teclas + clique no ícone do iniciador - ou - janela do aplicativo para levantar todas as janelas na viewport atual

Esta é provavelmente a opção mais próxima do que é descrito na questão / comentário.

Digamos que eu tenha uma área de trabalho confusa com três nautilus janelas enterradas em outras janelas.

Para levantar todas as janelas do nautilus (exemplo: Alt + 1 ):

  • Pressione Alt + 1 , solte (!)
  • Em 3 segundos:

    clique no ícone da aplicação no launcher

    ou:

    clique em uma das janelas do aplicativo

    resultado:


Como usar:

  • Faça a configuração conforme descrito em "Como usar"
  • Teste-o pelo comando:

    raise_app
    
  • Se tudo funcionar bem, adicione-o a uma combinação de teclas de atalho de sua escolha: Escolha: Configurações do sistema & gt; "Teclado" & gt; "Atalhos" & gt; "Atalhos personalizados". Clique no botão "+" e adicione o comando

Então:

  • Pressione a combinação de teclas e, em três segundos:

    • clique no ícone do aplicativo no inicializador
    • clique em uma das janelas do aplicativo

O script

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


Opção 4: uma combinação de teclas chama uma lista de opções, mostrando o número de janelas por aplicativo na viewport atual

Este acabou por ser mais conveniente do que eu assumi:

Pressionando a combinação de teclas (novamente exemplo) Alt + 1 chama a janela zenity , listando todos os aplicativos e o número de suas janelas na viewport atual:

Basta pressionar as setas ou para a opção correta. Pressione Enter e todas as janelas do aplicativo escolhido serão levantadas.

Como usar:

  • Faça a configuração conforme descrito em "Como usar"
  • Teste-o pelo comando:

    raise_app
    
  • Se tudo funcionar bem, adicione-o a uma combinação de teclas de atalho de sua escolha: Escolha: Configurações do sistema & gt; "Teclado" & gt; "Atalhos" & gt; "Atalhos personalizados". Clique no botão "+" e adicione o comando

O script

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


Opção 5: criar janelas de aplicativos em execução a partir de um ícone de inicialização

Esta opção existe de um ícone de iniciador, com os aplicativos atualmente em execução em uma lista rápida. Escolha um, e todas as janelas dos aplicativos serão levantadas.

O ativador é atualizado automaticamente quando a lista de aplicativos em execução (na viewport atual) é alterada. A lista rápida mostra uma lista diferente em outras viewports, onde as janelas de outros aplicativos são abertas (levará de 1 a 2 segundos para se adaptar).

Como mencionado, embora totalmente funcional, essa opção é um um conceito . Tem algumas desvantagens cosméticas menores como é. O mais importante:

  • A "roda" do cursor continua girando por alguns segundos após uma ação. Embora não afecte a funcionalidade, é uma desvantagem cosmética.
  • Leva de 1 a 2 segundos para que a lista de aplicativos no ícone do ativador seja atualizada após a alteração da lista de aplicativos em execução.

Além disso, a configuração é um pouco mais complicada (embora explicada em detalhes abaixo):

Como usar

Abaixo você encontrará:

dois scripts / um ícone / um arquivo .desktop

  1. Prepare a configuração como em "Como usar", salve o primeiro script (principal) como raise_app in ~/bin
  2. Salve o ícone abaixo (clique com o botão direito do mouse, salve como) como raise.png

  3. Copie o arquivo .desktop em um arquivo vazio, edite a linha

        Icon=/path/to/raise.png
    

    para o caminho real para o ícone (caminhos com espaços entre aspas)
     Salve como raise.desktop em ~/.local/share/applications

  4. Arraste o arquivo .desktop para o lançador para adicioná-lo

  5. copie o segundo script, cole-o em um arquivo vazio, salve-o como update_apps em ~/bin , torne-o executável.
  6. Adicione o seguinte comando aos seus aplicativos de inicialização (Dash & gt; Aplicativos de inicialização & gt; Adicionar):

    update_apps
    
  7. Faça logout e volte a trabalhar para que isso funcione.

O primeiro script

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

O segundo script

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

O arquivo .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=


Breve Explicação

Todas as soluções acima usam wmctrl para criar uma lista de janelas, usando o comando wmctrl -lpG .Este comando produz linhas, parecendo:

0x044000b3  0 3429   65   24   1615 1026 jacob-System-Product-Name unity - How to show all windows of an application? - Ask Ubuntu - Mozilla Firefox

Estas linhas incluem:

  • 1ª coluna: o id da janela (que podemos usar para aumentá-la)
  • 3ª coluna: o pid que possui a janela.
  • 4ª / 5ª coluna: a geometria da janela x-y (que usamos para ver se a janela está na viewport atual, i.c.w xrandr )

O pid é procurado na saída de ps -u <username> para obter uma identificação (nome) "legível pelo usuário" do aplicativo.
Assim, podemos alocar janelas para aplicativos. Posteriormente, podemos levantar as janelas de um determinado aplicativo em um loop for com o comando wmctrl -ia .

Na opção 3
o script inicia um ciclo de "espera" de 3 segundos, usando repetidamente o comando xprop -root para ver se há alguma mudança na janela mais frontal; isso acontecerá se o usuário clicar em um ícone do inicializador para levantar a janela de um aplicativo ou clicar diretamente em uma janela. Em caso afirmativo, o loop while interrompe e procura o aplicativo "novo" mais avançado e subseqüentemente gera todas as outras janelas desse aplicativo.

    
por Jacob Vlijm 06.01.2015 / 10:59
1

Existe um atalho Super + W que mostra a expo de todas as janelas abertas no momento, embora isso inclua outras aplicações. Isso vem por padrão e não requer alterações, então talvez seja a opção mais simples disponível.

Entre outras coisas, você poderia posicionar janelas nas metades direita e esquerda da tela com Ctrl + Super + Esquerda / Botões direito , e alterne entre eles com Alt + ~ (til, aquele ao lado da tecla número um).

    
por Sergiy Kolodyazhnyy 07.01.2015 / 00:41
1

Se você pressionar Alt + Tab para percorrer os aplicativos e chegar a um com várias janelas, mantenha pressionada a tecla alt e, após cerca de 1 segundo completo, o ícone será substituído por uma exibição de todas as janelas. essa aplicação.

Isso pode ou não ser o que você está procurando, mas funciona para mim e é muito mais simples, então resolvi compartilhar a opção!

    
por Sean Colombo 11.09.2015 / 02:48