- Local time
- 10:19 PM
- Posts
- 3,179
- OS
- Windows 11 Pro
I'm working on recreating the Classic Alt Tab via Python. So far it is semi-limited, but am posting the source code for anyone that wants to play with it while i finish it up... unless someone better at python beats me to it.
1) as of now, it is CTRL+ALT+TAB - meaning it stays open until a selection is made. it does not replace Alt+Tab (i have it pinned to my taskbar for now).
2) you can cycle through the icons via Tab / Left Arrow / Up Arrow or counter via Shift+Tab / Right Arrow / Down Arrow
3) you can select via Single Left Click / Enter / Spacebar
4) holds 7 icons per row. height will dynamically expand per number of rows

original classic alt tab:

Code:
1) as of now, it is CTRL+ALT+TAB - meaning it stays open until a selection is made. it does not replace Alt+Tab (i have it pinned to my taskbar for now).
2) you can cycle through the icons via Tab / Left Arrow / Up Arrow or counter via Shift+Tab / Right Arrow / Down Arrow
3) you can select via Single Left Click / Enter / Spacebar
4) holds 7 icons per row. height will dynamically expand per number of rows

original classic alt tab:

Code:
Code:
import tkinter as tk
import win32gui
import win32ui
import win32con
import win32api
from PIL import Image, ImageTk
import ctypes
from ctypes import wintypes
class AltTabReplacement:
def __init__(self, root):
self.root = root
self.root.title("Alt+Tab Replacement")
self.root.configure(bg="#F0F0F0")
self.root.overrideredirect(True)
self.windows = self.get_open_windows()
self.adjust_window_size()
self.current_index = 0
self.icon_frames = []
self.label_display = None
self.create_interface()
self.refresh_interface()
root.bind("<Tab>", self.next_icon)
root.bind("<Shift-Tab>", self.previous_icon)
root.bind("<Left>", self.previous_icon)
root.bind("<Right>", self.next_icon)
root.bind("<Up>", self.previous_icon)
root.bind("<Down>", self.next_icon)
root.bind("<space>", lambda event: self.execute_highlighted(self.current_index))
root.bind("<Return>", lambda event: self.execute_highlighted(self.current_index))
def get_cursor_monitor_center(self):
cursor_pos = ctypes.windll.user32.GetCursorPos
point = wintypes.POINT()
cursor_pos(ctypes.byref(point))
monitor = win32api.MonitorFromPoint((point.x, point.y))
monitor_info = win32api.GetMonitorInfo(monitor)
monitor_rect = monitor_info["Monitor"]
screen_width = monitor_rect[2] - monitor_rect[0]
screen_height = monitor_rect[3] - monitor_rect[1]
return monitor_rect[0], monitor_rect[1], screen_width, screen_height
def adjust_window_size(self):
icon_size = 50
max_columns = 7
total_icons = len(self.windows)
columns = min(total_icons, max_columns)
rows = (total_icons // max_columns) + (1 if total_icons % max_columns > 0 else 0)
window_width = columns * icon_size + 40
window_height = rows * icon_size + 100
x, y, screen_width, screen_height = self.get_cursor_monitor_center()
x = x + (screen_width // 2) - (window_width // 2)
y = y + (screen_height // 2) - (window_height // 2)
self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")
def refresh_interface(self):
new_windows = self.get_open_windows()
if len(new_windows) != len(self.windows):
self.windows = new_windows
for widget in self.icon_grid.winfo_children():
widget.destroy()
self.icon_frames.clear()
self.create_interface()
self.root.after(2000, self.refresh_interface)
def get_open_windows(self):
windows = []
def callback(hwnd, _):
window_title = win32gui.GetWindowText(hwnd)
if win32gui.IsWindowVisible(hwnd) and window_title and "Windows Input Experience" not in window_title:
style = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
if not (style & win32con.WS_EX_TOOLWINDOW) and (win32gui.GetParent(hwnd) == 0 or style & win32con.WS_EX_APPWINDOW):
windows.append((hwnd, window_title))
win32gui.EnumWindows(callback, None)
return windows
def get_window_icon(self, hwnd):
try:
hicon = win32gui.SendMessage(hwnd, win32con.WM_GETICON, win32con.ICON_SMALL, 0)
if hicon == 0:
hicon = win32gui.SendMessage(hwnd, win32con.WM_GETICON, win32con.ICON_BIG, 0)
if hicon == 0:
hicon = win32gui.GetClassLong(hwnd, win32con.GCL_HICON)
if hicon == 0:
return None
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hdc_mem = hdc.CreateCompatibleDC()
hbitmap = win32ui.CreateBitmap()
hbitmap.CreateCompatibleBitmap(hdc, 32, 32)
hdc_mem.SelectObject(hbitmap)
ctypes.windll.user32.DrawIconEx(hdc_mem.GetHandleAttrib(), 0, 0, hicon, 32, 32, 0, 0, 0x0003)
bmpinfo = hbitmap.GetInfo()
bmpstr = hbitmap.GetBitmapBits(True)
icon_image = Image.frombuffer("RGBA", (bmpinfo["bmWidth"], bmpinfo["bmHeight"]), bmpstr, "raw", "BGRA", 0, 1)
hdc.DeleteDC()
hdc_mem.DeleteDC()
win32gui.DeleteObject(hbitmap.GetHandle())
return icon_image.resize((28, 28), Image.LANCZOS)
except Exception as e:
print(f"Error fetching icon: {e}")
return Image.new("RGBA", (28, 28), color=(255, 255, 255, 0))
def create_interface(self):
self.icon_grid = tk.Frame(self.root, bg="#F0F0F0")
self.icon_grid.pack(fill=tk.BOTH, expand=True)
icon_size = 50
max_columns = 7
for i, (hwnd, title) in enumerate(self.windows):
icon_image = self.get_window_icon(hwnd)
if icon_image is None:
icon_image = Image.new("RGBA", (24, 24), color=(255, 255, 255, 0))
tk_icon = ImageTk.PhotoImage(icon_image)
frame = tk.Frame(self.icon_grid, bg="#F0F0F0", width=icon_size, height=icon_size, relief="flat", borderwidth=1)
frame.grid(row=i // max_columns, column=i % max_columns, padx=10, pady=10)
self.icon_frames.append((frame, hwnd, title))
icon_label = tk.Label(frame, image=tk_icon, bg="#F0F0F0")
icon_label.image = tk_icon
icon_label.pack(side=tk.TOP)
frame.bind("<Button-1>", lambda event, idx=i: self.execute_highlighted(idx))
icon_label.bind("<Button-1>", lambda event, idx=i: self.execute_highlighted(idx))
label_width = max_columns * icon_size + 40
self.label_display = tk.Label(
self.root, text="", fg="black", bg="#E0E0E0",
font=("Consolas", 12), relief="sunken", bd=3,
anchor="w", width=label_width // 10
)
self.label_display.pack(pady=10, fill=tk.X, padx=10)
if self.icon_frames:
self.highlight_icon(0)
def highlight_icon(self, index):
for frame, _, _ in self.icon_frames:
frame.configure(bg="#F0F0F0", relief="flat", borderwidth=1)
self.icon_frames[index][0].configure(bg="blue", relief="ridge", borderwidth=3)
self.label_display.configure(text=self.icon_frames[index][2][:30]) # Truncate label text if too long
self.current_index = index
def next_icon(self, event=None):
self.current_index = (self.current_index + 1) % len(self.icon_frames)
self.highlight_icon(self.current_index)
def previous_icon(self, event=None):
self.current_index = (self.current_index - 1) % len(self.icon_frames)
self.highlight_icon(self.current_index)
def execute_highlighted(self, index):
hwnd = self.icon_frames[index][1]
window_placement = win32gui.GetWindowPlacement(hwnd)
is_maximized = window_placement[1] == win32con.SW_SHOWMAXIMIZED
if not is_maximized:
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
win32gui.SetForegroundWindow(hwnd)
self.root.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = AltTabReplacement(root)
root.mainloop()
My Computer
System One
-
- OS
- Windows 11 Pro