実現したいこと
- 検索結果に出た親フォルダをダブルクリックしてエクスプローラーを開きたい
前提
PythonにてWin インデックス検索のシステムを作っています。
検索結果に出た親フォルダをダブルクリックしてエクスプローラーを開きたいのですがどうしても出来ません。
発生している問題・エラーメッセージ
検索結果の親フォルダをダブルクリックすると「フォルダが存在しません」とのメッセージが出ます。
該当のソースコード
Python
1import os 2import threading 3import tkinter as tk 4from tkinter import ttk, messagebox, filedialog 5import pythoncom 6import win32com.client 7from urllib.parse import urlparse, unquote 8from pathlib import Path 9import subprocess 10 11class WinIndexSearchApp: 12 def __init__(self, root): 13 self.root = root 14 self.root.title("Windowsインデックス検索ツール(親フォルダを開く対応)") 15 self.root.geometry("1000x600") 16 17 self.cancel_flag = False 18 19 frame_top = tk.Frame(root) 20 frame_top.pack(fill=tk.X, padx=10, pady=5) 21 22 tk.Label(frame_top, text="検索フォルダ(省略可):").pack(side=tk.LEFT) 23 self.folder_var = tk.StringVar() 24 tk.Entry(frame_top, textvariable=self.folder_var, width=50).pack(side=tk.LEFT, padx=5) 25 tk.Button(frame_top, text="参照", command=self.browse_folder).pack(side=tk.LEFT, padx=5) 26 27 tk.Label(frame_top, text="キーワード:").pack(side=tk.LEFT, padx=(10,5)) 28 self.keyword_entry = tk.Entry(frame_top, width=30) 29 self.keyword_entry.pack(side=tk.LEFT, padx=5) 30 self.keyword_entry.bind("<Return>", self.start_search_event) 31 tk.Button(frame_top, text="検索", command=self.search_thread).pack(side=tk.LEFT, padx=5) 32 tk.Button(frame_top, text="キャンセル", command=self.cancel_search).pack(side=tk.LEFT, padx=5) 33 34 self.progress_var = tk.DoubleVar() 35 self.progress_bar = ttk.Progressbar(root, variable=self.progress_var, maximum=100) 36 self.progress_bar.pack(fill=tk.X, padx=10, pady=5) 37 38 frame_table = tk.Frame(root) 39 frame_table.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) 40 41 columns = ("#", "FileName", "Folder") 42 self.tree = ttk.Treeview(frame_table, columns=columns, show="headings") 43 self.tree.heading("#", text="#") 44 self.tree.heading("FileName", text="ファイル名") 45 self.tree.heading("Folder", text="親フォルダ") 46 self.tree.column("#", width=50, anchor="center") 47 self.tree.column("FileName", width=300, anchor="w") 48 self.tree.column("Folder", width=600, anchor="w") 49 50 vsb = ttk.Scrollbar(frame_table, orient="vertical", command=self.tree.yview) 51 self.tree.configure(yscroll=vsb.set) 52 self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) 53 vsb.pack(side=tk.RIGHT, fill=tk.Y) 54 55 self.tree.bind("<Double-1>", self.open_folder) 56 57 def browse_folder(self): 58 folder = filedialog.askdirectory() 59 if folder: 60 self.folder_var.set(folder) 61 62 def start_search_event(self, event): 63 self.search_thread() 64 65 def cancel_search(self): 66 self.cancel_flag = True 67 68 def search_thread(self): 69 threading.Thread(target=self.search, daemon=True).start() 70 71 @staticmethod 72 def get_parent_folder(raw_path): 73 if not raw_path: 74 return None 75 parsed = urlparse(raw_path) 76 if parsed.scheme == 'file': 77 path = unquote(parsed.path) 78 if path.startswith('/') and path[2] == ':': 79 path = path[1:] 80 else: 81 path = raw_path 82 path = os.path.abspath(path) 83 return os.path.dirname(path) 84 85 def search(self): 86 self.cancel_flag = False 87 keyword = self.keyword_entry.get().strip() 88 if not keyword: 89 messagebox.showerror("エラー", "検索キーワードを入力してください") 90 return 91 92 folder_scope = self.folder_var.get().strip() or "C:\\" 93 folder_scope = os.path.abspath(folder_scope) 94 95 pythoncom.CoInitialize() 96 try: 97 conn = win32com.client.Dispatch("ADODB.Connection") 98 conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';") 99 cmd = win32com.client.Dispatch("ADODB.Command") 100 cmd.ActiveConnection = conn 101 cmd.CommandText = f""" 102 SELECT System.ItemPathDisplay, System.FileName 103 FROM SYSTEMINDEX 104 WHERE scope='file:{folder_scope}' 105 AND System.FileName LIKE '%{keyword}%' 106 """ 107 rs = cmd.Execute()[0] 108 109 self.tree.delete(*self.tree.get_children()) 110 idx = 1 111 while not rs.EOF: 112 if self.cancel_flag: 113 break 114 raw_path = rs.Fields("System.ItemPathDisplay").Value 115 name = rs.Fields("System.FileName").Value 116 folder_path = self.get_parent_folder(raw_path) 117 self.tree.insert("", tk.END, values=(idx, name, folder_path)) 118 idx += 1 119 rs.MoveNext() 120 self.progress_var.set(idx % 100) 121 self.progress_bar.update() 122 self.progress_var.set(0) 123 except Exception as e: 124 messagebox.showerror("エラー", str(e)) 125 finally: 126 pythoncom.CoUninitialize() 127 128 def open_folder(self, event): 129 selected = self.tree.selection() 130 if not selected: 131 return 132 folder_path = self.tree.item(selected[0], "values")[2] 133 134 try: 135 resolved_path = str(Path(folder_path).resolve()) 136 print(f"[DEBUG] 開くパス: {resolved_path}") 137 if os.path.exists(resolved_path): 138 subprocess.Popen(["explorer", resolved_path], shell=True) 139 else: 140 messagebox.showinfo("情報", f"フォルダが存在しません: {resolved_path}") 141 except Exception as e: 142 messagebox.showerror("エラー", f"フォルダを開けません: {e}") 143 144if __name__ == "__main__": 145 root = tk.Tk() 146 app = WinIndexSearchApp(root) 147 root.mainloop() 148
試したこと
AIを使って何度か修正をしました。
補足情報(FW/ツールのバージョンなど)
Win11
Python 3.13.7
PowerShell
このプログラムの意図としては、フォルダーとキーワードを指定したら、そのフォルダー配下でキーワードを含むファイル名をリストに出す、でよいですか?
Windows 11、Python 3.13.5、pywin32 311の環境では、検索ボタンを押下しても何も出ないのでデバッグできません。
このプログラムの意図としては、フォルダーとキーワードを指定したら、そのフォルダー配下でキーワードを含むファイル名をリストに出す、でよいですか?
はい、そのとおりです。
当方の環境は Win11Pro Python 3.13.7 pywin32 311 です。
検索はこちらでは出来ております。よろしくお願いいたします。
> Windows 11、Python 3.13.5、pywin32 311の環境では、検索ボタンを押下しても何も出ないのでデバッグできません。
...
> 当方の環境は Win11Pro Python 3.13.7 pywin32 311 です。検索はこちらでは出来ております
もしかすると、Windows自体、各個人の設定でWindows Searchのサービスを停止や無効にしていたり、検索したいフォルダーやファイルがWindows Searchのインデックス作成の対象になっていない、とかでしょうかね。そうであると、まったくヒットしない可能性もあります。
[一部修正済み]
なるほど、dodox86さんのおっしゃる通りに、C:\ユーザー 配下にテストフォルダーを作成したら、リストに出ました。
確かに再現しますね。これからデバッグします。
素直にglob使えばいいのに、とか思ってしまいます。
切り分けしているとどうも日本語のフォルダ名が怪しくて、下記のように無理やり追加して対応しました。
もっとスマートな方法あれば良いんですが…
def normalize_path(path):
translations = {
"ユーザー": "Users",
"ドキュメント": "Documents",
"デスクトップ": "Desktop",
"ダウンロード": "Downloads",
"ピクチャ": "Pictures",
"ビデオ": "Videos",
"ミュージック": "Music"
}
for jp, en in translations.items():
if f"\\{jp}" in path:
path = path.replace(f"\\{jp}", f"\\{en}")
return path
その通りです。C:\ユーザーをC:\Usersに変換しないとダメです。
ctypesでWindows APIを呼び出したら変換できますが、ググるかAIに聞けば出てきます。
素直にglob(略)
System.ItemPathDisplay ではなく System.ItemUrl を使う方法もあるようです。
(python ではないですが、ご参考 https://misohena.jp/blog/2023-04-16-windows-search-from-powershell.html )
ItemPath"Display" は表示用にローカライズされるようです。一方 ItemUrl は URL なのでパスへの変更処理がいると思いますので、どっちがいいかは場合に寄りそうですね。

回答1件
あなたの回答
tips
プレビュー