Python ファイルエクスプローラー(VSCode風)
このアプリは PyQt5 を用いて構築された、シンプルで直感的な ファイルエクスプローラー を作ってみました。
VSCode のサイドバーのような使いやすさを目指して設計しました。
1. 機能一覧
🔍 基本操作
ディレクトリのツリー表示
ファイルをダブルクリックで開く(OSの標準アプリで)
フォルダをクリックで展開
「⬅ 上に戻る」ボタンで1階層上へ移動
表示中のパスを上部に表示
右クリックでディレクトリ操作が可能
🧷 お気に入り機能
「ファイル > お気に入りに追加」で任意のフォルダを登録可能
メニュー「お気に入り」からワンクリックで開く
「❌ 削除」でお気に入りから除去可能
.favorites.txt に永続保存(アプリ起動時に読み込まれます)
🧰 ツール機能
文字サイズの変更
ウィンドウを常に最前面に固定/解除
2. 🧰 必要なライブラリ
- Python 3.x
- PyQt5
pip install PyQt5
実行方法
tool_view.py
3. 対応ファイル
テキスト デフォルトのエディタで開く
画像 (.jpg, .png) ビューワーで開く
動画 (.mp4) メディアプレイヤーで開く
Excel, Word, PDFなど OS既定アプリで開く
4.🔧 カスタマイズ例
.setRootPath() で起動時の初期フォルダ変更
5.その他
ファイルフィルタ追加(特定の拡張子だけ表示)
コンテキストメニュー(右クリックで削除や名前変更など)
6.画像
7.ライセンス
ほぼ生成AIによって作成、自由にカスタマイズ・商用利用可能
8.プログラム
import sys
import os
import shutil
import subprocess
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QMenuBar,
QAction, QFileDialog, QInputDialog, QPushButton, QHBoxLayout,
QMessageBox, QTreeView, QFileSystemModel, QMenu
)
from PyQt5.QtCore import Qt, QDir, QPoint
FAVORITES_FILE = ".favorites.txt"
class FileExplorer(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Python File")
self.setGeometry(100, 100, 900, 600)
self.current_path = QDir.homePath()
self.favorites = []
self.always_on_top = False
# Main widget and layout
central = QWidget()
self.setCentralWidget(central)
self.layout = QVBoxLayout(central)
# Top bar with back button and path label
top_bar = QHBoxLayout()
self.back_button = QPushButton("⬅ 上に戻る")
self.back_button.setFixedWidth(120)
self.back_button.clicked.connect(self.go_up)
self.path_label = QLabel()
top_bar.addWidget(self.back_button)
top_bar.addWidget(self.path_label)
self.layout.addLayout(top_bar)
# File tree view
self.model = QFileSystemModel()
self.model.setRootPath(self.current_path)
self.tree = QTreeView()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(self.current_path))
self.tree.setColumnWidth(0, 300)
self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree.customContextMenuRequested.connect(self.show_context_menu)
self.tree.clicked.connect(self.on_tree_clicked)
self.tree.doubleClicked.connect(self.on_tree_double_clicked)
self.layout.addWidget(self.tree)
self.menu_bar = QMenuBar()
self.setMenuBar(self.menu_bar)
self.setup_menus()
self.ensure_favorites_file()
self.load_favorites()
self.update_path_label()
def setup_menus(self):
file_menu = self.menu_bar.addMenu("ファイル")
open_dir = QAction("別のディレクトリを開く", self)
open_dir.triggered.connect(self.select_directory)
file_menu.addAction(open_dir)
add_fav = QAction("お気に入りに追加", self)
add_fav.triggered.connect(self.add_to_favorites)
file_menu.addAction(add_fav)
exit_action = QAction("終了", self)
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
self.favorite_menu = self.menu_bar.addMenu("お気に入り")
tool_menu = self.menu_bar.addMenu("ツール")
font_size_action = QAction("文字サイズを変更", self)
font_size_action.triggered.connect(self.change_font_size)
tool_menu.addAction(font_size_action)
topmost_action = QAction("常に前面に表示", self, checkable=True)
topmost_action.triggered.connect(self.toggle_always_on_top)
tool_menu.addAction(topmost_action)
def update_path_label(self):
self.path_label.setText(f"📁 現在のパス: {self.current_path}")
def go_up(self):
parent = os.path.dirname(self.current_path)
if os.path.exists(parent):
self.current_path = parent
self.tree.setRootIndex(self.model.index(self.current_path))
self.update_path_label()
def on_tree_clicked(self, index):
self.tree.expand(index)
def on_tree_double_clicked(self, index):
path = self.model.filePath(index)
if os.path.isdir(path):
self.current_path = path
self.tree.setRootIndex(self.model.index(path))
self.update_path_label()
else:
self.open_with_default_app(path)
def open_with_default_app(self, path):
try:
if sys.platform == 'win32':
os.startfile(path)
elif sys.platform == 'darwin':
subprocess.Popen(['open', path])
else:
subprocess.Popen(['xdg-open', path])
except Exception as e:
QMessageBox.warning(self, "エラー", f"ファイルを開けませんでした:\n{e}")
def select_directory(self):
dir_path = QFileDialog.getExistingDirectory(self, "ディレクトリを選択", self.current_path)
if dir_path:
self.current_path = dir_path
self.tree.setRootIndex(self.model.index(self.current_path))
self.update_path_label()
def change_font_size(self):
size, ok = QInputDialog.getInt(self, "フォントサイズ変更", "新しいフォントサイズ:", 10, 6, 40)
if ok:
font = self.tree.font()
font.setPointSize(size)
self.tree.setFont(font)
self.path_label.setFont(font)
def toggle_always_on_top(self, checked):
self.always_on_top = checked
flags = self.windowFlags()
if self.always_on_top:
self.setWindowFlags(flags | Qt.WindowStaysOnTopHint)
else:
self.setWindowFlags(flags & ~Qt.WindowStaysOnTopHint)
self.show()
def ensure_favorites_file(self):
if not os.path.exists(FAVORITES_FILE):
with open(FAVORITES_FILE, 'w', encoding='utf-8') as f:
pass
def add_to_favorites(self):
folder = QFileDialog.getExistingDirectory(self, "お気に入りに追加するフォルダを選択", self.current_path)
if folder and folder not in self.favorites:
self.favorites.append(folder)
self.save_favorites()
self.refresh_favorite_menu()
def load_favorites(self):
self.favorites.clear()
try:
with open(FAVORITES_FILE, 'r', encoding='utf-8') as f:
for line in f:
folder = line.strip()
if os.path.isdir(folder):
self.favorites.append(folder)
except Exception:
pass
self.refresh_favorite_menu()
def save_favorites(self):
with open(FAVORITES_FILE, 'w', encoding='utf-8') as f:
for path in self.favorites:
f.write(path + '\n')
def refresh_favorite_menu(self):
self.favorite_menu.clear()
for folder in self.favorites:
open_action = QAction(folder, self)
open_action.triggered.connect(lambda checked=False, path=folder: self.open_favorite(path))
self.favorite_menu.addAction(open_action)
del_action = QAction(f"❌ {folder} を削除", self)
del_action.triggered.connect(lambda checked=False, path=folder: self.remove_favorite(path))
self.favorite_menu.addAction(del_action)
def show_context_menu(self, pos: QPoint):
index = self.tree.indexAt(pos)
if not index.isValid():
return
file_path = self.model.filePath(index)
menu = QMenu()
copy_action = menu.addAction("📋 コピー")
cut_action = menu.addAction("✂️ 切り取り")
paste_action = menu.addAction("📄 貼り付け")
delete_action = menu.addAction("🗑 削除")
rename_action = menu.addAction("📝 名前の変更")
new_folder_action = menu.addAction("📁 新しいフォルダを作成")
action = menu.exec_(self.tree.viewport().mapToGlobal(pos))
if action == copy_action:
self.clipboard_path = file_path
self.clipboard_cut = False
elif action == cut_action:
self.clipboard_path = file_path
self.clipboard_cut = True
elif action == paste_action:
if self.clipboard_path:
dst = os.path.join(file_path, os.path.basename(self.clipboard_path)) if os.path.isdir(file_path) else os.path.join(os.path.dirname(file_path), os.path.basename(self.clipboard_path))
try:
if self.clipboard_cut:
shutil.move(self.clipboard_path, dst)
else:
if os.path.isdir(self.clipboard_path):
shutil.copytree(self.clipboard_path, dst)
else:
shutil.copy2(self.clipboard_path, dst)
except Exception as e:
QMessageBox.warning(self, "貼り付けエラー", str(e))
elif action == delete_action:
reply = QMessageBox.question(self, "確認", f"{file_path} を削除しますか?", QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
try:
if os.path.isdir(file_path):
shutil.rmtree(file_path)
else:
os.remove(file_path)
except Exception as e:
QMessageBox.warning(self, "削除エラー", str(e))
elif action == rename_action:
new_name, ok = QInputDialog.getText(self, "名前の変更", "新しい名前:", text=os.path.basename(file_path))
if ok:
new_path = os.path.join(os.path.dirname(file_path), new_name)
try:
os.rename(file_path, new_path)
except Exception as e:
QMessageBox.warning(self, "名前変更エラー", str(e))
elif action == new_folder_action:
new_name, ok = QInputDialog.getText(self, "新しいフォルダ", "フォルダ名:")
if ok:
try:
os.mkdir(os.path.join(file_path if os.path.isdir(file_path) else os.path.dirname(file_path), new_name))
except Exception as e:
QMessageBox.warning(self, "作成エラー", str(e))
def open_favorite(self, path):
if os.path.isdir(path):
self.current_path = path
self.tree.setRootIndex(self.model.index(path))
self.update_path_label()
def remove_favorite(self, path):
confirm = QMessageBox.question(self, "削除確認", f"{path} をお気に入りから削除しますか?",
QMessageBox.Yes | QMessageBox.No)
if confirm == QMessageBox.Yes:
if path in self.favorites:
self.favorites.remove(path)
self.save_favorites()
self.refresh_favorite_menu()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = FileExplorer()
window.show()
sys.exit(app.exec_())
最後に
少しでもツリーが見やすくなればと思ってます。
なお、このプログラムによってPCが壊れても補償しかねます。ご了承ください。