Read this through and you will learn how to create your own plugin! Basically a step-by-step.
Each plugin is a .py
file stored in the plugins/
folder:
Simple File Explorer/ βββ plugins/ β βββ my_plugin.py
Each plugin must define an on_load(app, tools)
function:
def on_load(app, tools):
def on_start():
tools.show_noti("Plugin loaded!")
tools.on_event("app_start", on_start)
After that, a plugin has virtually any possibilitiesβyou can even build full apps inside it!
Method | Description |
---|---|
on_event(event, handler) | Register a function for a named event |
trigger_event(...) | Manually fire an event |
show_noti(message) | Display a notification near the mouse |
run_error(message) | Show a modal error box |
refreshlist() | Refresh the UI file list |
get_selected_filenames() | Get list of selected files |
delete_path(path) | Delete a file or folder |
events() | List all registered events |
GetCurrentPath() | Return current path open in the file explorer |
OnKeyPress(key, handler) | Detects when a key is pressed. Special characters include:Return , Up , Control-Left , Alt-$ , etc. |
AddContextMenuCommand(label,handler) | Adds a command into the Context Menu |
NewFilePrompt() | Creates a prompt for the user to create a new file. |
RenameFilePrompt() | Creates a prompt for the user to rename the selected file. |
DeleteFilePrompt() | Creates a prompt for the user to delete selected file(s). |
register_undo_protoc() | Register an undo event for use in the undo keybind (CTRL+Z). |
add_undo() | Adds and undo dictionary to the undo_stack.(EXAMPLE: {"action": "move",
"extradata1": data,
"extradata2": data} |
run_in_thread(func, *args, **kwargs) | Runs a function in a thread so it doesn't freeze the interface/UI |
get_module(name) | Gets a module from the Python Library that's not preloaded. |
get_file_properties(filepath) | Returns a dictionary containing metadata about the given file or folder.
Returned dictionary keys:
Returns {'error': ...} if the path does not exist or an error occurs. |
Add a plugin_info
block to declare plugin details:
plugin_info = {
"name": "Reverse Theme",
"author": "jr",
"version": "1.0.0",
"trusted": True
}
This appears in the Plugin Manager if present.
Press Ctrl+P
to open a window listing all installed plugins, their authors, and version numbers.
(Some events are experimental or reserved.)
Event Name | Description |
---|---|
"app_start" | Fired when the app starts |
"app_exit" | Before the app quits |
"plugins_loaded" | After all plugins are loaded |
"directory_changed" | When the current directory changes |
"file_selected" | When user selects files |
"file_deleted" | After a file is deleted |
"file_renamed" | After a file is renamed |
"file_created" | After a new file or folder is created |
"file_moved" | After a file is moved |
"before_file_delete" | Before deleting a file (can cancel) |
"before_directory_change" | Before changing folders (can cancel) |
"key_pressed" | When a key is pressed (e.g. <Control-p> ) |
"opened_context_menu" | When the Context Menu is opened. |
def on_load(app, tools):
def handle_file_select(files):
for f in files:
if f.lower().endswith((".jpg", ".png")):
tools.show_noti("πΈ You selected an image!")
tools.on_event("file_selected", handle_file_select)
Access pre-imported modules via tools.modules["name"]
.
Available: os
, shutil
, tkinter
, vlc
, PIL
, random
def on_load(app, tools):
tk = tools.modules["tkinter"]
def show_window():
win = tk.Toplevel(app)
win.title("Plugin Says Hi")
win.geometry("200x100")
label = tk.Label(win, text="hey", font=("Segoe UI", 12))
label.pack(expand=True, padx=20, pady=20)
win.transient(app)
win.grab_set()
win.focus_force()
tools.on_event("app_start", lambda: show_window())
Detect Button Presses via tools.OnKeyPress("key", command)
.
This will either be a single key: "a"
,"b"
or a combination like : "<Control+a>"
,"<Control+Alt+r>"
def on_load(app, tools):
tk = tools.modules["tkinter"]
def reverse_colors():
fg = tools.context.get("fg", "#fff")
bg = tools.context.get("bg", "#000")
inverted_fg = bg
inverted_bg = fg
def invert_widget_colors(widget):
try:
widget.configure(bg=inverted_bg, fg=inverted_fg)
except tk.TclError:
pass
for child in widget.winfo_children():
invert_widget_colors(child)
invert_widget_colors(app)
tools.show_noti("π Reverse theme activated!")
tools.OnKeyPress("", reverse_colors)
def on_load(app, tools):
def on_start():
tools.AddContextMenuCommand("Create New File", lambda e: tools.NewFilePrompt())
tools.on_event("app_start", on_start)
def on_load(app, tools):
print("π§© [ZIP Compressor] Plugin loaded")
os = tools.modules["os"]
zipfile = tools.modules["zipfile"]
api = tools
def compress_selected2():
selected = api.get_selected_filenames()
if not selected:
api.run_error("No files selected to compress.")
return
current_dir = api.GetCurrentPath()
archive_path = os.path.join(current_dir, "compressed.zip")
try:
with zipfile.ZipFile(archive_path, 'w') as zipf:
for fname in selected:
full_path = os.path.join(current_dir, fname)
if os.path.isfile(full_path):
zipf.write(full_path, arcname=fname)
else:
api.run_error(f"Skipping '{fname}': Not a file.")
api.add_undo({
"action": "compress_file",
"info": {"archive_path": archive_path}
})
api.refreshlist()
api.show_noti("β
Created compressed.zip")
except Exception as e:
api.run_error(f"Compression failed: {e}")
def compress_selected():
tools.run_in_thread(compress_selected2)
def undo_zip_compress(info):
archive_path = info.get("archive_path")
if archive_path and os.path.exists(archive_path):
try:
os.remove(archive_path)
api.refreshlist()
print(f"[UNDO] Deleted: {archive_path}")
except Exception as e:
api.run_error(f"Undo failed: {e}")
else:
api.run_error("Undo failed: ZIP file not found.")
api.register_undo_protoc("compress_file", undo_zip_compress)
api.AddContextMenuCommand("π¦ Compress to ZIP (undoable)", compress_selected)
print()
to log activitytools.events()
to inspect available event hookstools.run_error(...)
to test error dialogstools.refreshlist()
if your plugin changes files