Command Palette Launcher (VS Code Style)
 """Command Palette Launcher (VS Code style)Tech: tkinter, os, keyboard, difflibFeatures:- Ctrl+P to open palette (global using 'keyboard', and also inside Tk window)- Index files from a folder for quick search- Fuzzy search using difflib- Open files (os.startfile on Windows / xdg-open on Linux / open on macOS)- Add custom commands (open app, shell command)- Demo includes uploaded file path: /mnt/data/image.png"""import osimport sysimport threadingimport platformimport subprocessfrom pathlib import Pathimport tkinter as tkfrom tkinter import ttk, filedialog, messageboxfrom difflib import get_close_matches# Optional global hotkey packagetry:  import keyboard # pip install keyboard  KEYBOARD_AVAILABLE = Trueexcept Exception:  KEYBOARD_AVAILABLE = False# Demo uploaded file path (from your session)DEMO_FILE = "/mnt/data/image.png"# ------------------------------# Utility functions# ------------------------------def open_path(path):  """Open a file or folder using the OS default application."""  p = str(path)  if platform.system() == "Windows":    os.startfile(p)  elif platform.system() == "Darwin": # macOS    subprocess.Popen(["open", p])  else: # Linux and others    subprocess.Popen(["xdg-open", p])def is_executable_file(path):  try:    return os.access(path, os.X_OK) and Path(path).is_file()  except Exception:    return False# ------------------------------# Indexer# ------------------------------class FileIndexer:  def __init__(self):    self.items = [] # list of {"title": ..., "path": ..., "type": "file"|"cmd"}    # preload demo item if exists    if Path(DEMO_FILE).exists():      self.add_item(title=Path(DEMO_FILE).name, path=str(DEMO_FILE), typ="file")  def add_item(self, title, path, typ="file"):    rec = {"title": title, "path": path, "type": typ}    self.items.append(rec)  def index_folder(self, folder, max_files=5000):    """Recursively index a folder (stop at max_files)."""    folder = Path(folder)    count = 0    for root, dirs, files in os.walk(folder):      for f in files:        try:          fp = Path(root) / f          self.add_item(title=f, path=str(fp), typ="file")          count += 1          if count >= max_files:            return count        except Exception:          continue    return count  def add_common_apps(self):    """Add some common app commands (platform-specific)."""    sysplat = platform.system()    apps = []    if sysplat == "Windows":      # common Windows apps (paths may vary)      apps = [        ("Notepad", "notepad.exe"),        ("Calculator", "calc.exe"),        ("Paint", "mspaint.exe"),      ]    elif sysplat == "Darwin":      apps = [        ("TextEdit", "open -a TextEdit"),        ("Calculator", "open -a Calculator"),      ]    else: # Linux      apps = [        ("Gedit", "gedit"),        ("Calculator", "gnome-calculator"),      ]    for name, cmd in apps:      self.add_item(title=name, path=cmd, typ="cmd")  def search(self, query, limit=15):    """Simple fuzzy search: look for substrings first, then difflib matches."""    q = query.strip().lower()    if not q:      # return top items      return self.items[:limit]    # substring matches (higher priority)    substr_matches = [it for it in self.items if q in it["title"].lower() or q in it["path"].lower()]    if len(substr_matches) >= limit:      return substr_matches[:limit]    # prepare list of titles for difflib    titles = [it["title"] for it in self.items]    close = get_close_matches(q, titles, n=limit, cutoff=0.4)    # map back to records preserving order (titles may repeat)    close_records = []    for t in close:      for it in self.items:        if it["title"] == t and it not in close_records:          close_records.append(it)          break    # combine substring + close matches, ensure uniqueness    results = []    seen = set()    for it in substr_matches + close_records:      key = (it["title"], it["path"])      if key not in seen:        results.append(it)        seen.add(key)      if len(results) >= limit:        break    return results# ------------------------------# GUI# ------------------------------class CommandPalette(tk.Toplevel):  def __init__(self, master, indexer: FileIndexer):    super().__init__(master)    self.indexer = indexer    self.title("Command Palette")    self.geometry("700x380")    self.transient(master)    self.grab_set() # modal    self.resizable(False, False)    # Styling    self.configure(bg="#2b2b2b")    # Search box    self.search_var = tk.StringVar()    search_entry = ttk.Entry(self, textvariable=self.search_var, font=("Consolas", 14), width=60)    search_entry.pack(padx=12, pady=(12,6))    search_entry.focus_set()    search_entry.bind("<KeyRelease>", self.on_search_key)    search_entry.bind("<Escape>", lambda e: self.close())    search_entry.bind("<Return>", lambda e: self.open_selected())    # Results list    self.tree = ttk.Treeview(self, columns=("title","path","type"), show="headings", height=12)    self.tree.heading("title", text="Title")    self.tree.heading("path", text="Path / Command")    self.tree.heading("type", text="Type")    self.tree.column("title", width=250)    self.tree.column("path", width=350)    self.tree.column("type", width=80, anchor="center")    self.tree.pack(padx=12, pady=6, fill="both", expand=True)    self.tree.bind("<Double-1>", lambda e: self.open_selected())    self.tree.bind("<Return>", lambda e: self.open_selected())    # Bottom buttons    btn_frame = ttk.Frame(self)    btn_frame.pack(fill="x", padx=12, pady=(0,12))    ttk.Button(btn_frame, text="Open Folder to Index", command=self.browse_and_index).pack(side="left")    ttk.Button(btn_frame, text="Add Command", command=self.add_command_dialog).pack(side="left", padx=6)    ttk.Button(btn_frame, text="Close (Esc)", command=self.close).pack(side="right")    # initial populate    self.update_results(self.indexer.items[:50])  def on_search_key(self, event=None):    q = self.search_var.get()    results = self.indexer.search(q, limit=50)    self.update_results(results)    # keep the first row selected    children = self.tree.get_children()    if children:      self.tree.selection_set(children[0])      self.tree.focus(children[0])  def update_results(self, records):    # clear    for r in self.tree.get_children():      self.tree.delete(r)    for rec in records:      self.tree.insert("", "end", values=(rec["title"], rec["path"], rec["type"]))  def open_selected(self):    sel = self.tree.selection()    if not sel:      return    vals = self.tree.item(sel[0])["values"]    title, path, typ = vals    try:      if typ == "file":        open_path(path)      elif typ == "cmd":        # if it's a shell command, run it        # Allow both simple exe names and complex shell commands        if platform.system() == "Windows":          subprocess.Popen(path, shell=True)        else:          subprocess.Popen(path.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)      else:        # fallback attempt        open_path(path)    except Exception as e:      messagebox.showerror("Open failed", f"Could not open {path}\n\n{e}")    finally:      self.close()  def browse_and_index(self):    folder = filedialog.askdirectory()    if not folder:      return    count = self.indexer.index_folder(folder)    messagebox.showinfo("Indexed", f"Indexed approx {count} files from {folder}")    # refresh results    self.on_search_key()  def add_command_dialog(self):    dlg = tk.Toplevel(self)    dlg.title("Add Command / App")    dlg.geometry("500x150")    tk.Label(dlg, text="Title:").pack(anchor="w", padx=8, pady=(8,0))    title_e = ttk.Entry(dlg, width=60)    title_e.pack(padx=8)    tk.Label(dlg, text="Command or Path:").pack(anchor="w", padx=8, pady=(8,0))    path_e = ttk.Entry(dlg, width=60)    path_e.pack(padx=8)    def add():      t = title_e.get().strip() or Path(path_e.get()).name      p = path_e.get().strip()      if not p:        messagebox.showwarning("Input", "Please provide a command or path")        return      typ = "cmd" if (" " in p or os.sep not in p and not Path(p).exists()) else "file"      self.indexer.add_item(title=t, path=p, typ=typ)      dlg.destroy()      self.on_search_key()    ttk.Button(dlg, text="Add", command=add).pack(pady=8)  def close(self):    try:      self.grab_release()    except:      pass    self.destroy()# ------------------------------# Main App Window# ------------------------------class PaletteApp:  def __init__(self, root):    self.root = root    root.title("Command Palette Launcher")    root.geometry("700x120")    self.indexer = FileIndexer()    self.indexer.add_common_apps()    # Add a few demo entries (including uploaded file path)    if Path(DEMO_FILE).exists():      self.indexer.add_item(title=Path(DEMO_FILE).name, path=str(DEMO_FILE), typ="file")    # Top UI    frame = ttk.Frame(root, padding=12)    frame.pack(fill="both", expand=True)    ttk.Label(frame, text="Press Ctrl+P to open command palette", font=("Arial", 12)).pack(anchor="w")    ttk.Button(frame, text="Open Palette (Ctrl+P)", command=self.open_palette).pack(pady=10, anchor="w")    ttk.Button(frame, text="Index Folder", command=self.index_folder).pack(side="left")    ttk.Button(frame, text="Exit", command=root.quit).pack(side="right")    # register global hotkey in a background thread (if available)    if KEYBOARD_AVAILABLE:      t = threading.Thread(target=self.register_global_hotkey, daemon=True)      t.start()    else:      print("keyboard package not available — global hotkey disabled. Use app's Ctrl+P instead.")    # bind Ctrl+P inside the Tk window too    root.bind_all("<Control-p>", lambda e: self.open_palette())  def open_palette(self):    # open modal CommandPalette    cp = CommandPalette(self.root, self.indexer)  def index_folder(self):    folder = filedialog.askdirectory()    if not folder:      return    count = self.indexer.index_folder(folder)    messagebox.showinfo("Indexed", f"Indexed approx {count} files")  def register_global_hotkey(self):    """    Register Ctrl+P as a global hotkey using keyboard module.    When pressed, we must bring the Tk window to front and open palette.    """    try:      # On some systems, keyboard requires admin privileges. If it fails, we catch and disable.      keyboard.add_hotkey("ctrl+p", lambda: self.trigger_from_global())      keyboard.wait() # keep the listener alive    except Exception as e:      print("Global hotkey registration failed:", e)  def trigger_from_global(self):    # Because keyboard runs in another thread, schedule UI work in Tk mainloop    try:      self.root.after(0, self.open_palette)      # Try to bring window to front      try:        self.root.lift()        self.root.attributes("-topmost", True)        self.root.after(500, lambda: self.root.attributes("-topmost", False))      except Exception:        pass    except Exception as e:      print("Error triggering palette:", e)# ------------------------------# Run# ------------------------------def main():  root = tk.Tk()  app = PaletteApp(root)  root.mainloop()if __name__ == "__main__":  main()
Python for Engineers
LE CHIC MODE
Style of Sam
Kimberly Halle
The Atlantic » Politics
Small Wars Journal Blog
Ask a Tech Teacher