Examples¶
Complete Examples¶
Timer Application¶
A menu bar app that shows elapsed time with a rich layout:
import stackit
import time
class TimerApp:
def __init__(self):
self.app = stackit.StackApp("Timer", "⏱")
self.start_time = time.time()
self.setup_ui()
# Update every second
self.timer = stackit.every(1.0, self.update_display)
def setup_ui(self):
# Create timer display item (without layout initially)
self.item = stackit.MenuItem()
self.update_display(None)
self.app.add(self.item)
# Add reset button
reset_layout = stackit.hstack([
stackit.button("🔄 Reset Timer", target=self, action="reset_timer:")
])
reset_item = stackit.MenuItem(layout=reset_layout)
self.app.add(reset_item)
def update_display(self, timer):
elapsed = int(time.time() - self.start_time)
minutes = elapsed // 60
seconds = elapsed % 60
# Update layout dynamically
layout = stackit.hstack([
stackit.label("Time:", bold=True),
stackit.spacer(),
stackit.label(f"{minutes:02d}:{seconds:02d}", font_size=14)
], spacing=8)
self.item.set_layout(layout)
# Force menu to redraw
self.app.update()
def reset_timer_(self, sender):
self.start_time = time.time()
def run(self):
self.app.run()
if __name__ == "__main__":
TimerApp().run()
Network Status Monitor¶
Monitor network connectivity with visual indicators:
import stackit
import subprocess
class NetworkMonitor:
def __init__(self):
self.app = stackit.StackApp("Net")
self.connected = True
self.setup_ui()
# Check every 30 seconds
self.timer = stackit.every(30.0, self.check_network)
self.check_network(None)
def setup_ui(self):
# Status display (dynamic updates)
self.status_item = stackit.MenuItem()
self.app.add(self.status_item)
self.app.add_separator()
# Manual check button
check_layout = stackit.hstack([
stackit.button("🔄 Check Now", target=self, action="manual_check:")
])
check_item = stackit.MenuItem(layout=check_layout)
self.app.add(check_item)
def check_network(self, timer):
try:
subprocess.check_call(
["ping", "-c", "1", "8.8.8.8"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=5
)
self.connected = True
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
self.connected = False
self.update_display()
def update_display(self):
if self.connected:
icon = stackit.image(
stackit.SFSymbol("wifi", point_size=16, color="green"),
width=16, height=16
)
status_text = "Connected"
color = "green"
else:
icon = stackit.image(
stackit.SFSymbol("wifi.slash", point_size=16, color="red"),
width=16, height=16
)
status_text = "Disconnected"
color = "red"
# Update layout
layout = stackit.hstack([
icon,
stackit.label(status_text, color=color)
], spacing=8)
self.status_item.set_layout(layout)
# Update app icon
app_icon = stackit.SFSymbol(
"wifi" if self.connected else "wifi.slash",
point_size=16
)
self.app.set_icon(app_icon)
# Force menu to redraw
self.app.update()
def manual_check_(self, sender):
self.check_network(None)
stackit.notification(
"Network Status",
"Check Complete",
f"Status: {'Connected' if self.connected else 'Disconnected'}"
)
def run(self):
self.app.run()
if __name__ == "__main__":
NetworkMonitor().run()
Todo List Manager¶
A feature-rich todo list with checkboxes:
import stackit
class TodoApp:
def __init__(self):
self.app = stackit.StackApp("📝 Todos")
self.todos = []
self.setup_ui()
def setup_ui(self):
# Add todo button
add_item = stackit.StackMenuItem("Add")
layout = add_item.hstack()
layout.append(stackit.button("➕ Add Todo", target=self, action="add_todo:"))
add_item.set_root_stack(layout)
self.app.add_item("add", add_item)
self.app.add_separator()
def add_todo_(self, sender):
# Use alert as input dialog
result = stackit.alert(
"New Todo",
"Enter your todo item:",
ok="Add",
cancel="Cancel"
)
if result == 1: # OK clicked
# In real app, you'd get text from a proper input dialog
todo_text = f"Todo Item {len(self.todos) + 1}"
self.add_todo_item(todo_text)
def add_todo_item(self, text):
todo_id = f"todo_{len(self.todos)}"
self.todos.append({"id": todo_id, "text": text, "done": False})
# Create todo item with checkbox
item = stackit.StackMenuItem(todo_id)
layout = item.hstack(spacing=8)
checkbox = stackit.checkbox("", state=False)
layout.append(checkbox)
layout.append(stackit.label(text))
item.set_root_stack(layout)
self.app.add_item(todo_id, item)
def run(self):
self.app.run()
if __name__ == "__main__":
TodoApp().run()
System Monitor Dashboard¶
Display system information with progress bars:
import stackit
import psutil
import platform
class SystemMonitor:
def __init__(self):
self.app = stackit.StackApp("💻 System")
self.setup_ui()
# Update every 3 seconds
self.timer = stackit.every(3.0, self.update_info)
self.update_info(None)
def setup_ui(self):
# System info header
info_item = stackit.StackMenuItem("Info")
layout = info_item.vstack(spacing=4)
layout.append(stackit.label(f"macOS {platform.mac_ver()[0]}", font_size=11, color="gray"))
info_item.set_root_stack(layout)
self.app.add_item("info", info_item)
self.app.add_separator()
# CPU display
self.cpu_item = stackit.StackMenuItem("CPU")
self.app.add_item("cpu", self.cpu_item)
# Memory display
self.mem_item = stackit.StackMenuItem("Memory")
self.app.add_item("memory", self.mem_item)
# Disk display
self.disk_item = stackit.StackMenuItem("Disk")
self.app.add_item("disk", self.disk_item)
def update_info(self, timer):
# Update CPU
cpu_percent = psutil.cpu_percent(interval=1) / 100.0
layout = self.cpu_item.vstack(spacing=4)
layout.append(stackit.label("CPU Usage", font_size=11, bold=True))
layout.append(stackit.progress_bar(width=200, value=cpu_percent))
layout.append(stackit.label(f"{cpu_percent*100:.1f}%", font_size=10, color="gray"))
self.cpu_item.set_root_stack(layout)
# Update Memory
mem = psutil.virtual_memory()
mem_percent = mem.percent / 100.0
layout = self.mem_item.vstack(spacing=4)
layout.append(stackit.label("Memory Usage", font_size=11, bold=True))
layout.append(stackit.progress_bar(width=200, value=mem_percent))
layout.append(stackit.label(
f"{mem.used / (1024**3):.1f} GB / {mem.total / (1024**3):.1f} GB",
font_size=10,
color="gray"
))
self.mem_item.set_root_stack(layout)
# Update Disk
disk = psutil.disk_usage('/')
disk_percent = disk.percent / 100.0
layout = self.disk_item.vstack(spacing=4)
layout.append(stackit.label("Disk Usage", font_size=11, bold=True))
layout.append(stackit.progress_bar(width=200, value=disk_percent))
layout.append(stackit.label(
f"{disk.used / (1024**3):.1f} GB / {disk.total / (1024**3):.1f} GB",
font_size=10,
color="gray"
))
self.disk_item.set_root_stack(layout)
def run(self):
self.app.run()
if __name__ == "__main__":
SystemMonitor().run()
Music Player Controller¶
A compact music player controller with buttons and sliders:
import stackit
class MusicController:
def __init__(self):
self.app = stackit.StackApp("🎵")
self.playing = False
self.volume = 50
self.setup_ui()
def setup_ui(self):
# Playback controls
controls_item = stackit.StackMenuItem("Controls")
layout = controls_item.hstack(spacing=8)
# Previous button
prev_btn = stackit.button("⏮", target=self, action="previous:")
layout.append(prev_btn)
# Play/Pause button
self.play_btn = stackit.button("▶️", target=self, action="toggle_play:")
layout.append(self.play_btn)
# Next button
next_btn = stackit.button("⏭", target=self, action="next:")
layout.append(next_btn)
controls_item.set_root_stack(layout)
self.app.add_item("controls", controls_item)
# Volume control
volume_item = stackit.StackMenuItem("Volume")
layout = volume_item.vstack(spacing=4)
layout.append(stackit.label("Volume", font_size=11, bold=True))
vol_slider = stackit.slider(width=150, min_value=0, max_value=100, value=self.volume)
layout.append(vol_slider)
volume_item.set_root_stack(layout)
self.app.add_item("volume", volume_item)
def toggle_play_(self, sender):
self.playing = not self.playing
# Update button would require accessing the control
stackit.notification("Music", "", "▶️ Playing" if self.playing else "⏸ Paused")
def previous_(self, sender):
stackit.notification("Music", "", "⏮ Previous Track")
def next_(self, sender):
stackit.notification("Music", "", "⏭ Next Track")
def run(self):
self.app.run()
if __name__ == "__main__":
MusicController().run()
Video Player¶
A menu bar app with embedded video player using AVKit:
import stackit
class VideoPlayerApp:
def __init__(self):
self.app = stackit.StackApp(
title="Video",
icon=stackit.SFSymbol("play.rectangle.fill", color="#FF6B6B")
)
self.setup_ui()
def setup_ui(self):
# Video player with controls
video_url = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
video_player = stackit.video(
video_url,
dimensions=(400, 225),
border_radius=12.0,
show_controls=True,
autoplay=False,
loop=False
)
video_item = stackit.MenuItem(
layout=stackit.vstack([
stackit.label("Video Player", bold=True, font_size=14),
stackit.separator(),
video_player,
stackit.separator(),
stackit.label("Sample video", font_size=10, color="gray"),
], spacing=8)
)
self.app.add(video_item)
def run(self):
self.app.run()
if __name__ == "__main__":
VideoPlayerApp().run()
Web Browser¶
A menu bar app with embedded web view using WebKit:
import stackit
class WebBrowserApp:
def __init__(self):
self.app = stackit.StackApp(
title="Browser",
icon=stackit.SFSymbol("safari.fill", color="#0080FF")
)
self.setup_ui()
def setup_ui(self):
# Web view with URL
web = stackit.web_view(
"https://news.ycombinator.com",
dimensions=(600, 500),
border_radius=12.0
)
web_item = stackit.MenuItem(
layout=stackit.vstack([
stackit.label("Hacker News", bold=True, font_size=14),
stackit.separator(),
web,
], spacing=8)
)
self.app.add(web_item)
# Custom HTML content
html_content = """
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: -apple-system; padding: 20px; }
h1 { color: #0080FF; }
</style>
</head>
<body>
<h1>Welcome</h1>
<p>This is custom HTML content in your menu bar!</p>
</body>
</html>
"""
custom_web = stackit.web_view(
html_content,
dimensions=(400, 200),
border_radius=8.0
)
custom_item = stackit.MenuItem(
layout=stackit.vstack([
stackit.label("Custom HTML", bold=True, font_size=14),
stackit.separator(),
custom_web,
], spacing=8)
)
self.app.add(custom_item)
def run(self):
self.app.run()
if __name__ == "__main__":
WebBrowserApp().run()
Map Viewer¶
An interactive map viewer with annotations using MapKit:
import stackit
class MapViewerApp:
def __init__(self):
self.app = stackit.StackApp(
title="Maps",
icon=stackit.SFSymbol("map.fill", color="#4A90E2")
)
self.setup_ui()
def setup_ui(self):
# San Francisco map with annotations
sf_map = stackit.map_view(
latitude=37.7749,
longitude=-122.4194,
zoom=0.05,
dimensions=(400, 300),
map_type="standard",
show_controls=True,
border_radius=12.0,
annotations=[
{
'latitude': 37.7749,
'longitude': -122.4194,
'title': 'San Francisco',
'subtitle': 'Golden Gate City'
},
{
'latitude': 37.8199,
'longitude': -122.4783,
'title': 'Golden Gate Bridge',
'subtitle': 'Iconic landmark'
}
]
)
sf_item = stackit.MenuItem(
layout=stackit.vstack([
stackit.label("San Francisco", bold=True, font_size=14),
stackit.separator(),
sf_map,
stackit.separator(),
stackit.label("Interactive map with pins", font_size=10, color="gray"),
], spacing=8)
)
self.app.add(sf_item)
# New York - Satellite view
ny_map = stackit.map_view(
latitude=40.7128,
longitude=-74.0060,
zoom=0.03,
dimensions=(400, 300),
map_type="satellite",
show_controls=True,
border_radius=12.0
)
ny_item = stackit.MenuItem(
layout=stackit.vstack([
stackit.label("New York City", bold=True, font_size=14),
stackit.separator(),
ny_map,
stackit.separator(),
stackit.label("Satellite view", font_size=10, color="gray"),
], spacing=8)
)
self.app.add(ny_item)
def run(self):
self.app.run()
if __name__ == "__main__":
MapViewerApp().run()
Activity Rings Dashboard¶
Display fitness-style activity rings using ring charts:
import stackit
class ActivityDashboard:
def __init__(self):
self.app = stackit.StackApp(
title="Activity",
icon=stackit.SFSymbol("figure.run", color="#32D74B")
)
self.setup_ui()
# Update every 5 seconds with new data
self.timer = stackit.every(5.0, self.update_activity)
def setup_ui(self):
# Main activity rings
main_rings = stackit.ring_chart(
data=[85, 65, 45],
dimensions=(120, 120),
colors=["#32D74B", "#0A84FF", "#FF375F"],
ring_width=12.0,
spacing=3.0,
labels=["Move", "Exercise", "Stand"]
)
main_item = stackit.MenuItem(
layout=stackit.vstack([
stackit.label("Today's Activity", bold=True, font_size=14),
stackit.separator(),
main_rings,
stackit.separator(),
stackit.label("Move: 85% | Exercise: 65% | Stand: 45%",
font_size=10, color="gray"),
], spacing=8)
)
self.app.add(main_item)
# Weekly summary rings
self.weekly_item = stackit.MenuItem()
self.app.add(self.weekly_item)
self.update_weekly()
def update_weekly(self):
# Show smaller rings for the week
weekly_rings = stackit.ring_chart(
data=[90, 75, 60, 85],
dimensions=(80, 80),
colors=["#FFD60A", "#FF9F0A", "#FF453A", "#BF5AF2"],
ring_width=8.0,
spacing=2.0
)
layout = stackit.vstack([
stackit.label("Weekly Progress", bold=True, font_size=12),
stackit.separator(),
stackit.hstack([
stackit.label("Week:", font_size=10),
stackit.spacer(),
weekly_rings,
], spacing=8),
], spacing=6)
self.weekly_item.set_layout(layout)
def update_activity(self, timer):
# In a real app, this would fetch actual activity data
import random
# Update main rings with new random values
new_data = [
random.randint(60, 100),
random.randint(50, 90),
random.randint(40, 80)
]
# Force menu to update (would need to recreate the layout)
self.app.update()
def run(self):
self.app.run()
if __name__ == "__main__":
ActivityDashboard().run()
Standalone Window¶
Create a settings window that opens from the menu bar:
import stackit
class SettingsApp:
def __init__(self):
self.app = stackit.StackApp(
title="MyApp",
icon=stackit.SFSymbol("gear", color="#0A84FF")
)
self.setup_ui()
def setup_ui(self):
# Menu item to open settings window
settings_item = stackit.MenuItem(
title="Settings...",
callback=self.open_settings,
key_equivalent="," # ⌘,
)
self.app.add(settings_item)
def open_settings(self, sender):
# Create window
win = stackit.window(
title="Settings",
size=(400, 350),
resizable=True,
closable=True,
)
# Create layout with form controls
content = stackit.vstack([
stackit.label("Application Settings", bold=True, font_size=16),
stackit.separator(),
# Username field
stackit.hstack([
stackit.label("Username:", width=100),
stackit.text_field(placeholder="Enter username", width=250),
], spacing=10.0),
# Email field
stackit.hstack([
stackit.label("Email:", width=100),
stackit.text_field(placeholder="Enter email", width=250),
], spacing=10.0),
# Password field
stackit.hstack([
stackit.label("Password:", width=100),
stackit.secure_text_input(placeholder="Enter password", width=250),
], spacing=10.0),
stackit.separator(),
# Checkboxes
stackit.checkbox("Enable notifications"),
stackit.checkbox("Auto-save settings"),
stackit.spacer(),
# Action buttons
stackit.hstack([
stackit.spacer(),
stackit.button("Cancel", callback=lambda s: win.close(), style="rounded"),
stackit.button("Save", callback=self.save_settings, style="default"),
], spacing=10.0),
], spacing=12.0)
# Add layout to window with padding
stackit.window_layout(win, content, padding=(20, 20, 20, 20))
# Show window
win.makeKeyAndOrderFront_(None)
def save_settings(self, sender):
stackit.notification("Settings", "Your settings have been saved!")
def run(self):
self.app.run()
if __name__ == "__main__":
SettingsApp().run()