複数のウィンドウを使用するアプリケーションがあります。そのアプリケーションのすべてのウィンドウをすばやく前面に表示するにはどうすればよいですか?
スクロールホイールでアプリケーションをスクロールすると、1つのウィンドウしか表示されません。次のウィンドウに移動すると、最後のウィンドウが再びバックグラウンドになります。
アプリケーションアイコンをクリックすると、すべてのウィンドウの概要が全画面表示されます。各ウィンドウを手動で選択し、マウスを画面の半分に数回移動する必要があります。
これまでの私の最善の解決策は、すべてのウィンドウ(Ctrl+Super+D)スクロールホイールを使用して、アプリケーションのウィンドウを表示します。
より良い解決策はありますか?
以下の答えはまだ完全に有効であるため、推奨されるオプションです。しかし、継続的な洞察により、このオプションを追加して以下のインジケーターを使用することができました。これはおそらく最もエレガントなソリューションです。
そのため、おそらくオプション5を置き換える必要があります(.desktopファイルを使用)。
リストからアプリケーションを選択するだけで、対応するアプリケーション(現在のビューポートに存在する)のすべてのウィンドウが表示されます:
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インディケーターと同じディレクトリ.
次に、次のコマンドで実行します。
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
(拡張子なし)として保存し、実行可能にします
別のオプションでは、可能な追加の手順が説明されます。
zenity
ウィンドウが表示されますこれにより、一致するアプリケーションのすべてのウィンドウ(currentビューポート)が前面に表示されます。
現在のビューポート上のすべての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
キーの組み合わせの下に以下のスクリプトがあるとしましょう Alt+1。私はいくつかのウィンドウを開いています:
現在の状態:
一度押す Alt+1、すべてのnautilus
ウィンドウが発生します:
もう一度押す Alt+1、すべてのfirefox
ウィンドウが発生します:
もう一度押す Alt+1、すべてのgnome-terminal
ウィンドウが再び発生し、サイクルが最初から始まります。
使用方法
選択したショートカットキーの組み合わせに追加します。[システム設定]> [キーボード]> [ショートカット]> [カスタムショートカット]を選択します。 「+」をクリックして、コマンドを追加します
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つのnautilus
ウィンドウが埋め込まれた乱雑なデスクトップがあるとします。
すべてのnautilusウィンドウを上げるには(ショートカットの例: Alt+1):
3秒以内に、次のいずれか:
ランチャーでアプリケーションのアイコンをクリックします
または:
アプリケーションのウィンドウのいずれかをクリックします
結果:
使用方法:
次のコマンドでテスト実行します。
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
これは、私が想定したよりも便利であることが判明しました。
(もう一度例)キーの組み合わせを押す 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"')
このオプションにはランチャーアイコンがあり、現在実行中のアプリケーションがクイックリストに表示されます。いずれかを選択すると、アプリケーションのすべてのウィンドウが表示されます。
(現在のビューポートで)実行中のアプリケーションのリストが変更されると、ランチャーが自動的に更新されます。クイックリストには、他のアプリケーションのウィンドウが開かれている他のビューポートの別のリストが表示されます(適応するには1〜2秒かかります)。
前述のように、このオプションは完全に機能しますが、概念の概念を意味します。いくつかのマイナーな化粧品の欠点がそのままあります。最も重要な:
さらに、セットアップは少し複雑です(ただし、以下で詳しく説明します)。
使用方法
以下にあります:
2つのスクリプト/アイコン/ .desktop
ファイル
raise_app
に~/bin
として保存します下のアイコンをraise.png
として保存(右クリック、名前を付けて保存)
.desktop
ファイルを空のファイルにコピーし、行を編集します
Icon=/path/to/raise.png
アイコンへの実際のパス(引用符の間にスペースがあるパス)raise.desktop
に~/.local/share/applications
として保存します
.desktop
ファイルをランチャーにドラッグして追加します
update_apps
として~/bin
として保存し、実行可能にします。次のコマンドをスタートアップアプリケーションに追加します([ダッシュ]> [スタートアップアプリケーション]> [追加])。
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
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
これらの行は次のとおりです。
xrandr
)Pidはps -u <username>
の出力で検索され、アプリケーションの「ユーザーが読み取り可能な」識別(名前)を取得します。
したがって、アプリケーションにウィンドウを割り当てることができます。その後、コマンドwmctrl -ia
を使用して、for
ループで特定のアプリケーションのウィンドウを上げることができます。
オプション3
スクリプトは3秒間の「待機」ループを開始し、xprop -root
コマンドを繰り返し使用して、最前面のウィンドウに変更があるかどうかを確認します。これは、ユーザーがランチャーアイコンをクリックしてアプリケーションのウィンドウを開くか、ウィンドウを直接クリックすると発生します。その場合、whileループは中断して「新しい」最前面のアプリケーションを検索し、その後そのアプリケーションの他のすべてのウィンドウを表示します。
がある Super+W 現在開いているすべてのウィンドウのエキスポを表示するショートカット。ただし、他のアプリケーションも含まれます。これはデフォルトで提供されており、変更を必要としないため、おそらく最も簡単なオプションです。
とりわけ、画面の右半分と左半分にウィンドウを配置できます Ctrl+Super+Left/Right ボタン、Alt +〜(チルダ、ナンバーワンキーの隣のボタン)で切り替えます。
Altキーを押しながらTabキーを押してアプリケーションを切り替え、複数のウィンドウが表示されている場合は、altキーを押し続けると、約1秒後にアイコンがそのアプリケーションのすべてのウィンドウのビューに置き換えられます。
それはあなたが探しているものかもしれないし、そうでないかもしれないが、それは私のために機能し、非常に簡単ですので、私はオプションを共有すると思いました!
@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()