アクティブウィンドウ一覧アプリ(カテゴリ別ツリービュー)
📖 概要
このアプリは、現在開いているすべてのウィンドウをカテゴリ別に分類し、ツリービュー形式で表示するPyQt5アプリです。各ウィンドウの先頭には、ファイルタイプに応じたアイコン絵文字が表示され、クリックするとそのウィンドウがアクティブ化されます。
🧩 機能
- 📁 フォルダ / 🎞️ 動画 / 🎵 音楽 / 📄 PDF / 📗 Excel / 📒 テキスト / 🏷️ アプリケーション(.exe)
-
.exe
名ごとに分類(例:chrome.exe
,vscode.exe
,explorer.exe
) -
Program Manager
やWindows入力エクスペリエンス
は自動的に除外 - ツリービューをクリック or アクティブにしたときに内容を再取得・更新
- アプリケーションアイコンを
.exe
から自動取得(取得できない場合はなし)
🖥️ 動作環境
- Windows 10 / 11(推奨)
- Python 3.8 以上
- 管理者権限(
pywin32
で一部ウィンドウ情報取得に必要)
📦 インストール
pip install PyQt5 pywin32 psutil
📦 プログラム
import sys
import os
import ctypes
import psutil
import win32gui
import win32process
import win32con
from PyQt5.QtWidgets import (
QApplication, QTreeView, QMainWindow,
QVBoxLayout, QWidget, QAbstractItemView
)
from PyQt5.QtGui import (
QStandardItemModel, QStandardItem, QIcon, QPixmap
)
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtCore import QTimer
class WindowInfo:
def __init__(self, hwnd, title, exe_path, icon, category):
self.hwnd = hwnd
self.title = title
self.exe_path = exe_path
self.icon = icon
self.category = category
class WindowTreeView(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("開いているウィンドウ一覧(ツリービュー)")
self.resize(700, 500)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
layout = QVBoxLayout(self.central_widget)
self.tree_view = QTreeView()
layout.addWidget(self.tree_view)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(['ウィンドウタイトル'])
self.tree_view.setModel(self.model)
self.tree_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.tree_view.viewport().installEventFilter(self)
self.installEventFilter(self)
self.populate_windows()
self.tree_view.clicked.connect(self.on_item_clicked)
# タイマーで定期更新
self.timer = QTimer(self)
self.timer.timeout.connect(self.populate_windows)
self.timer.start(10000) # 10秒ごと
def changeEvent(self, event):
if event.type() == QEvent.FocusIn:
if self.isActiveWindow():
self.populate_windows()
super().changeEvent(event)
#'''
def populate_windows(self):
self.model.removeRows(0, self.model.rowCount())
self.windows = []
self.category_items = {}
win32gui.EnumWindows(self.enum_callback, None)
for win in self.windows:
display_name = f"{self.get_emoji(win.category)} {win.title}"
item = QStandardItem(display_name)
item.setData(win.hwnd, Qt.UserRole)
item.setToolTip(win.exe_path)
if win.icon:
item.setIcon(win.icon)
self.category_items[win.category].appendRow(item)
for i in range(self.model.rowCount()):
index = self.model.index(i, 0)
self.tree_view.expand(index)
def enum_callback(self, hwnd, _):
if not win32gui.IsWindowVisible(hwnd):
return
title = win32gui.GetWindowText(hwnd)
if not title or title in ["Program Manager", "Windows Input Experience","Windows 入力エクスペリエンス"]:
return
_, pid = win32process.GetWindowThreadProcessId(hwnd)
try:
process = psutil.Process(pid)
exe_path = process.exe()
exe_name = os.path.basename(exe_path).lower()
except Exception:
return
if exe_name in ["systemsettings.exe", "applicationframehost.exe"]:
return
category = self.classify_app(exe_name)
icon = self.get_icon(exe_path)
if category not in self.category_items:
cat_item = QStandardItem(f"{self.get_emoji(category)} {category}")
cat_item.setEditable(False)
self.model.appendRow(cat_item)
self.category_items[category] = cat_item
self.windows.append(WindowInfo(hwnd, title, exe_path, icon, category))
def classify_app(self, exe_name):
if "excel" in exe_name:
return "Excel"
elif "chrome" in exe_name:
return "Chrome"
elif "msedge" in exe_name:
return "Msedge"
elif "vscode" in exe_name or "code" in exe_name:
return "VSCode"
elif "explorer" in exe_name:
return "エクスプローラー"
elif exe_name.endswith(".exe"):
return exe_name.replace(".exe", "").capitalize()
else:
return "その他"
def get_emoji(self, category):
emojis = {
"Excel": "📗",
"Chrome": "🌐",
"Msedge":"🌍",
"VSCode": "🖊️",
"エクスプローラー": "🗂️",
"音楽": "🎵",
"動画": "🎞️",
"PDF": "📕",
"その他": "📒"
}
return emojis.get(category, "🏷️")
def get_icon(self, exe_path):
try:
large = (ctypes.c_void_p * 1)()
small = (ctypes.c_void_p * 1)()
if ctypes.windll.shell32.ExtractIconExW(exe_path, 0, large, small, 1) > 0:
hicon = large[0]
pixmap = QPixmap.fromWinHICON(hicon)
ctypes.windll.user32.DestroyIcon(hicon)
return QIcon(pixmap)
except Exception:
pass
return None
def on_item_clicked(self, index):
item = self.model.itemFromIndex(index)
hwnd = item.data(Qt.UserRole)
if hwnd:
try:
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
win32gui.SetForegroundWindow(hwnd)
self.populate_windows()
except Exception as e:
print(f"アクティブ化失敗: {e}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = WindowTreeView()
window.show()
sys.exit(app.exec_())