web-dev-qa-db-ja.com

アプリケーションのすべてのウィンドウを表示(表示)する方法は?

複数のウィンドウを使用するアプリケーションがあります。そのアプリケーションのすべてのウィンドウをすばやく前面に表示するにはどうすればよいですか?

スクロールホイールでアプリケーションをスクロールすると、1つのウィンドウしか表示されません。次のウィンドウに移動すると、最後のウィンドウが再びバックグラウンドになります。

アプリケーションアイコンをクリックすると、すべてのウィンドウの概要が全画面表示されます。各ウィンドウを手動で選択し、マウスを画面の半分に数回移動する必要があります。

これまでの私の最善の解決策は、すべてのウィンドウ(Ctrl+Super+D)スクロールホイールを使用して、アプリケーションのウィンドウを表示します。

より良い解決策はありますか?

20
peq

編集-新しい回答-

以下の答えはまだ完全に有効であるため、推奨されるオプションです。しかし、継続的な洞察により、このオプションを追加して以下のインジケーターを使用することができました。これはおそらく最もエレガントなソリューションです。

そのため、おそらくオプション5を置き換える必要があります(.desktopファイルを使用)。

リストからアプリケーションを選択するだけで、対応するアプリケーション(現在のビューポートに存在する)のすべてのウィンドウが表示されます:

enter image description here

使い方

from 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として保存します

  • 以下の画像をコピーして保存しますexactly namedraise.png inインディケーターと同じディレクトリ.

    enter image description here

  • 次に、次のコマンドで実行します。

    python3 /path/to/raise_apps.py

  • スタートアップアプリケーションに追加する場合:

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

古い回答:

質問について

適切なツールを使用すると、アプリケーションのすべてのウィンドウを「ジャスト」に上げることはそれほど複雑ではありません。現在のビューポートのウィンドウがonlyだけ浮かび上がるようにするのはもう少し複雑です。ただし、実際の課題は、ユーザーがアクションを利用できるようにする便利な方法を見つけることです。

それを処理する5つのオプションを以下に示し、どのようにを実行できるかを示します。すべてのオプションを使用する準備ができました。ただし、最後のオプションは実験的なものです。正常に機能しますが、オプションの説明で説明されているように、いくつかの小さな見た目の欠点があります。それにもかかわらず、conceptとして追加しました。

コメントで示唆されているように、重複しない方法でウィンドウを自動的に広げることは、私にとって実用的な考えではありません。 (アプリケーションごとに)グループ化されたウィンドウ設定で作業する場合、スクリプトはウィンドウを不必要に再配置する可能性があります。

使い方

すべてのオプションについて、次のことが必要です。

  • システムにまだない場合はwmctrlをインストールします。

    Sudo apt-get install wmctrl
    
  • ディレクトリがまだ存在しない場合は作成します:

    ~/bin
    

    (説明:ディレクトリ~/binは$ PATHにあるため、実行可能ファイルを名前で実行できます)

  • オプションに対応するスクリプトをコピーし、空のファイルに貼り付け、raise_app~/bin(拡張子なし)として保存し、実行可能にします

別のオプションでは、可能な追加の手順が説明されます。

オプション1:1つ以上の文字を入力してアプリケーションを選択します

  • キーの組み合わせを押すと、zenityウィンドウが表示されます
  • 入力ボックスにアプリケーション名の1つ以上の文字を入力します
  • Enterを押す

これにより、一致するアプリケーションのすべてのウィンドウ(currentビューポート)が前面に表示されます。

現在のビューポート上のすべてのgnome-terminalウィンドウを上げる:

enter image description here

enter image description here

使用方法:

  • 「使用方法」の説明に従ってセットアップを実行します
  • 次のコマンドでテスト実行します。

    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。私はいくつかのウィンドウを開いています:

  • fox
  • gnome-terminal
  • ノーチラス

現在の状態:

enter image description here

一度押す Alt+1、すべてのnautilusウィンドウが発生します:

<image>

もう一度押す Alt+1、すべてのfirefoxウィンドウが発生します:

<image>

もう一度押す Alt+1、すべてのgnome-terminalウィンドウが再び発生し、サイクルが最初から始まります。

<image>

使用方法

  • 「使用方法」の説明に従ってセットアップを実行します
  • 選択したショートカットキーの組み合わせに追加します。[システム設定]> [キーボード]> [ショートカット]> [カスタムショートカット]を選択します。 「+」をクリックして、コマンドを追加します

    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:キーの組み合わせを押して、ランチャーアイコン-または-アプリケーションウィンドウをクリックして、現在のビューポート上のすべてのウィンドウを上げる

これはおそらく、質問/コメントで説明されている内容に最も近いオプションです。

他のウィンドウの下に3つのnautilusウィンドウが埋め込まれた乱雑なデスクトップがあるとします。

<image>

すべてのnautilusウィンドウを上げるには(ショートカットの例: Alt+1):

  • 押す Alt+1、リリース(!)
  • 3秒以内に、次のいずれか:

    ランチャーでアプリケーションのアイコンをクリックします

    <image>

    または:

    アプリケーションのウィンドウのいずれかをクリックします

    <image>

    結果:

    <image>


使用方法:

  • 「使用方法」の説明に従ってセットアップを実行します
  • 次のコマンドでテスト実行します。

    raise_app
    
  • すべて正常に機能する場合は、選択したショートカットキーの組み合わせに追加します。[システム設定]> [キーボード]> [ショートカット]> [カスタムショートカット]を選択します。 「+」をクリックして、コマンドを追加します

次に:

  • キーの組み合わせを押して、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 image description here

単に押す  または  矢印を使用すると、適切なオプションが表示されます。押す 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:ランチャーアイコンから実行中のアプリケーションのウィンドウを上げる

このオプションにはランチャーアイコンがあり、現在実行中のアプリケーションがクイックリストに表示されます。いずれかを選択すると、アプリケーションのすべてのウィンドウが表示されます。

enter image description here

(現在のビューポートで)実行中のアプリケーションのリストが変更されると、ランチャーが自動的に更新されます。クイックリストには、他のアプリケーションのウィンドウが開かれている他のビューポートの別のリストが表示されます(適応するには1〜2秒かかります)。

前述のように、このオプションは完全に機能しますが、概念の概念を意味します。いくつかのマイナーな化粧品の欠点がそのままあります。最も重要な:

  • カーソルの「ホイール」は、アクション後数秒間回転し続けます。機能には影響しませんが、見た目の欠点です。
  • 実行中のアプリケーションのリストが変更された後、ランチャーアイコンのapplicationlistが更新されるまでに1〜2秒かかります。

さらに、セットアップは少し複雑です(ただし、以下で詳しく説明します)。

使用方法

以下にあります:

2つのスクリプト/アイコン/ .desktopファイル

  1. 「使用方法」のようにセットアップを準備し、最初の(メイン)スクリプトをraise_app~/binとして保存します
  2. 下のアイコンをraise.pngとして保存(右クリック、名前を付けて保存)

    <icon>

  3. .desktopファイルを空のファイルにコピーし、行を編集します

        Icon=/path/to/raise.png
    

    アイコンへの実際のパス(引用符の間にスペースがあるパス)
    raise.desktop~/.local/share/applicationsとして保存します

  4. .desktopファイルをランチャーにドラッグして追加します

  5. 2番目のスクリプトをコピーして、空のファイルに貼り付け、update_appsとして~/binとして保存し、実行可能にします。
  6. 次のコマンドをスタートアップアプリケーションに追加します([ダッシュ]> [スタートアップアプリケーション]> [追加])。

    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

2番目のスクリプト

#!/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 -lpGコマンドを使用して、wmctrlを使用してウィンドウリストを作成します。このコマンドは、次のような行を生成します。

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列目:ウィンドウのID(それを上げるために使用できます)
  • 3列目:ウィンドウを所有するPID。
  • 4番目/ 5番目の列:ウィンドウのジオメトリx-y(ウィンドウが現在のビューポート上にあるかどうかを確認するために使用します。i.c.wxrandr

Pidはps -u <username>の出力で検索され、アプリケーションの「ユーザーが読み取り可能な」識別(名前)を取得します。
したがって、アプリケーションにウィンドウを割り当てることができます。その後、コマンドwmctrl -iaを使用して、forループで特定のアプリケーションのウィンドウを上げることができます。

オプション3
スクリプトは3秒間の「待機」ループを開始し、xprop -rootコマンドを繰り返し使用して、最前面のウィンドウに変更があるかどうかを確認します。これは、ユーザーがランチャーアイコンをクリックしてアプリケーションのウィンドウを開くか、ウィンドウを直接クリックすると発生します。その場合、whileループは中断して「新しい」最前面のアプリケーションを検索し、その後そのアプリケーションの他のすべてのウィンドウを表示します。

21
Jacob Vlijm

がある Super+W 現在開いているすべてのウィンドウのエキスポを表示するショートカット。ただし、他のアプリケーションも含まれます。これはデフォルトで提供されており、変更を必要としないため、おそらく最も簡単なオプションです。

とりわけ、画面の右半分と左半分にウィンドウを配置できます Ctrl+Super+Left/Right ボタン、Alt +〜(チルダ、ナンバーワンキーの隣のボタン)で切り替えます。

1

Altキーを押しながらTabキーを押してアプリケーションを切り替え、複数のウィンドウが表示されている場合は、altキーを押し続けると、約1秒後にアイコンがそのアプリケーションのすべてのウィンドウのビューに置き換えられます。

それはあなたが探しているものかもしれないし、そうでないかもしれないが、それは私のために機能し、非常に簡単ですので、私はオプションを共有すると思いました!

1
Sean Colombo

@JacobVlijmのraise_apps.pyスクリプトを使用して、より堅牢にするなど、いくつかの機能強化を行いました。

具体的には、1、2日後に@JacobVlijmのスクリプトが機能しなくなることを発見し、スクリプトを手動で再起動して、再び機能させる必要がありました。振り返ってみると、xrandrへの多数の呼び出しが最終的に問題を引き起こすというのが私の最良の推測です。

とにかく、私は彼のコードを修正し、ポーリング頻度を5秒から1秒ごとに増やしました。とにかくCPUをあまり使用しないので、より堅牢になりました。通常、問題なく何日/何週間も実行できます。

1つの注意点は、画面解像度の寸法を取得するために、起動時にxrandrを1回だけ呼び出すことです。したがって、画面の解像度を変更する場合(たとえば、1920x1080から他の解像度に変更する場合)、おそらく新しい解像度を取得するためにraise-apps.pyを手動で再起動する必要があります。個人的に、画面の解像度を変更することはありませんので、これは私にとっては問題ではありません。さらに、xrandrへの呼び出しが多すぎると、@ JacobVlijmのバージョンのスクリプトが1〜2日後に動作しなくなると信じる強い理由があります。

ところで、raise.pngイメージを/ usr/local/icons /ディレクトリに配置する必要があります。または、raise.pngを別のディレクトリに配置する場合は、スクリプトが画像ファイルを見つけられるように、スクリプトに適切な変更を加えます。

Ubuntuがこのタイプの「すべてのウィンドウを上げる」機能をシステムに統合することは、非常に役立つことを願っています。

#!/usr/bin/python2
#
# Note to self:
# You need to add raise.png to /usr/local/icons/ directory.
#
# This script was taken from: https://askubuntu.com/questions/446521/how-to-show-raise-all-windows-of-an-application, 
# (@JacobVlijm's answer), and then improved to fix some
# issues, that were causing it to stop working after a day or two.
#
#
from __future__ import print_function

from sys import stderr, exit
import signal
import gi

gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject, GLib

import logging
import logging.handlers

import time
import os
import subprocess
import getpass

logger = logging.getLogger('MyLogger')
logger.setLevel(logging.DEBUG)

log_handler = logging.handlers.SysLogHandler(address='/dev/log')

logger.addHandler(log_handler)


currpath = os.path.dirname(os.path.realpath(__file__))

class Indicator():
    def __init__(self):
        self.app = 'raise-apps'
        iconpath = '/usr/local/icons/raise.png'
        self.indicator = AppIndicator3.Indicator.new(
            self.app, iconpath,
            AppIndicator3.IndicatorCategory.OTHER)
        self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)

        self.prev_menu_item_names = []
        self.menu_items = []

        res_output = get("xrandr").split()
        if (len(res_output) == 0):
            logger.error("raise-apps.py: invocation of xrandr failed! Unable to continue..")
            exit(-1)

        idf = res_output.index("current")
        res = (int(res_output[idf+1]), int(res_output[idf+3].replace(",", "")))
        (self.screen_width, self.screen_height) = res
        logger.info("raise-apps.py: screen resolution is %s x %s" % (self.screen_width, self.screen_height))

        self.indicator.set_menu(self.create_menu())

        GLib.timeout_add_seconds(1.0, self.check_recent)

    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)

        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_items[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_items:

            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):
        # 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]) < self.screen_width and 0 < int(w[4]) < self.screen_height]
        # 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):
        # print("in check_recent()", file=stderr)
        self.menu_items = self.get_apps()
        for app in self.menu_items:
            app[0] = "gnome-terminal" if "gnome-terminal" in app[0] else app[0]
        # check if menu items have changed:
        has_changed = len(self.menu_items) != len(self.prev_menu_item_names)
        if (not has_changed):
            for i in range(len(self.menu_items)):
                if self.prev_menu_item_names[i] != self.menu_items[i][0]:
                    has_changed = True
                    break

        if has_changed:
            GObject.idle_add(
                self.set_new,
                priority=GObject.PRIORITY_DEFAULT)

            self.prev_menu_item_names = []
            for item in self.menu_items:
                self.prev_menu_item_names.append(item[0])

        GLib.timeout_add_seconds(1.0, self.check_recent)


    def stop(self, source):
        Gtk.main_quit()


    def recreate_menu(self, *args):
        logger.info("in recreate_menu()")
        self.prev_menu_item_names = []
        self.menu_items = []

        self.menu_items = self.get_apps()
        for app in self.menu_items:
            app[0] = "gnome-terminal" if "gnome-terminal" in app[0] else app[0]

        GObject.idle_add(
            self.set_new,
            priority=GObject.PRIORITY_DEFAULT)

        self.prev_menu_item_names = []
        for item in self.menu_items:
            self.prev_menu_item_names.append(item[0])


def get(command):
    # enable to get a feel for what this app is doing..
    # print("get", command, file=stderr)
    try:
        return subprocess.check_output(command).decode("utf-8")

    except subprocess.CalledProcessError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
        return ""

    except OSError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
        return ""

def execute(command):
    # enable to get a feel for what this app is doing..
    # print("exec", command, file=stderr)
    try:
        subprocess.call(command)

    except subprocess.CalledProcessError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
        return ""
    except OSError as e:
        logger.error("raise-apps.py error: cmd=%s, error=%s" % (command, e))
    return ""


logger.info("(raise-apps.py is starting up..)")
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
1
Gino