0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

アクティブウィンドウ一覧アプリ

Posted at

アクティブウィンドウ一覧アプリ(カテゴリ別ツリービュー)

📖 概要

このアプリは、現在開いているすべてのウィンドウをカテゴリ別に分類し、ツリービュー形式で表示するPyQt5アプリです。各ウィンドウの先頭には、ファイルタイプに応じたアイコン絵文字が表示され、クリックするとそのウィンドウがアクティブ化されます。


🧩 機能

  • 📁 フォルダ / 🎞️ 動画 / 🎵 音楽 / 📄 PDF / 📗 Excel / 📒 テキスト / 🏷️ アプリケーション(.exe)
  • .exe名ごとに分類(例: chrome.exe, vscode.exe, explorer.exe
  • Program ManagerWindows入力エクスペリエンス自動的に除外
  • ツリービューをクリック 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_())
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?