This release is a pre-release and may not be stable for production use.
BareMacGUI is a thin Python layer for native macOS windows. Real AppKit controls through PyObjC, put together with short with blocks. No own drawing layer, no widget toolkit of its own: Dark Mode, system fonts and standard behaviour come from macOS.
The package and import name is baremacgui. The API shape follows BareWinGUI, the author's library for Windows.
Status: beta (0.1.0b1). The API may still change in small ways. The automated tests build every control on real macOS runners; clicking, typing and shortcuts are only partly checked by hand. See "What is verified" below.
from baremacgui import App, Button, Label, VBox, Window, show_info
app = App("Hello")
with Window("Hello", size=(320, 160)) as win:
with VBox(padding=16, gap=8):
Label("Hello world")
button = Button("Click me", default=True)
button.on_click(lambda: show_info("It works!", "Hello", parent=win))
app.run()
Showcase
This window is examples/16_showcase.py, 136 lines of Python. Nothing in it is drawn by the library: the sidebar, the table with its checkboxes and SF Symbols, the icon grid, the switch, the slider with tick marks, the segmented control, the color well and the calendar field are AppKit views, so the look, the fonts and Dark Mode are the system's own.
| Dark mode | Icon grid and drop zone | Settings-style rows |
|---|---|---|
The screenshots come from GitHub's macOS runner, where the window is not in focus, so the accent color shows grey in places (a focused window uses your accent color). Run it yourself: python examples/16_showcase.py.
Install
From a clone:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[examples]"
python examples/00_hello.py
Needs macOS and Python 3.10 or newer. The one dependency is PyObjC (pyobjc-framework-Cocoa), installed automatically.
What is included
| Area | Classes and functions |
|---|---|
| Windows | App, Window (styles standard, unified, sidebar; on_close, on_resize, on_destroy, size, add_shortcut, default_button, cancel_button, show_modal), Sheet, Popover, Toolbar, Sidebar / Page |
| Layout | VBox, HBox, Spacer; options padding, gap, align, justify, grow, width/height/min_*/max_* |
| Containers | GroupBox, TabControl / Tab, SplitView |
| Controls | Label, TextBox (single line, multi line, password), ComboBox, DropDown, Button (SF Symbols), CheckBox, RadioGroup, SpinBox, Slider, ProgressBar, Image, Separator, SearchField, SegmentedControl, DatePicker, TimePicker, Switch (NSSwitch), ColorWell (NSColorWell), DropZone (files dragged from Finder) |
| Lists | ListBox, Table (text cells; editable=, sortable=True), IconGrid, Tree |
| Menus | MenuBar, Menu, MenuItem (shortcuts, check marks), MenuSeparator, ContextMenu (context_menu= on any control) |
| Dialogs | show_info, show_warning, show_error, ask_yes_no, ask_ok_cancel, input_box, open_file, save_file, pick_folder, choose_color, choose_font, notify (a banner; the first nine with parent= they become sheets; then= gets the answer) |
| Other | StatusItem (menu-bar item), App(background=True), Settings (NSUserDefaults), Window(autosave=...), clipboard, Timer, Event, Custom(view) for any AppKit view, errors BareMacGUIError, PlatformError, LayoutError |
The full list with signatures is in docs/REFERENCE.md, generated from the docstrings.
The same reference, a guide and the examples are also a searchable web app: open docs/site/index.html in a browser (no server needed).
Events
Handlers are plain functions without arguments; read the state from the controls.
ok = Button("OK", on_click=save) # as a constructor option
ok.on_click(save) # register another handler
@ok.on_click # as a decorator
def save(): ...
ok.on_click = save # assignment replaces all handlers
An exception inside a handler does not crash the app: it goes to app.on_error if you set one, otherwise it is printed to the terminal.
Threads
Run slow work in a thread and hand the result back with app.invoke(function, *args), or wait for a value with app.call(function, ...). Controls are never touched from other threads.
Window styles
Window(..., style="unified"): Finder-like, a transparent title bar withwith Toolbar():for buttons,SegmentedControl,SearchField(examples/04_finder.py).Window(..., style="sidebar"): System-Settings-like, aSidebarwith onePageper entry (examples/05_settings_sidebar.py).- Escape hatch: every control exposes its view as
.ns;Custom(view)puts any NSView you made into the layout.
Menus
A Mac has one menu bar for the whole app, so a MenuBar belongs to a window: while that window is the key window, its menus sit between the standard App menu and the Edit and Window menus. Shortcuts are written Cmd+S, Cmd+Shift+N, F5 (Ctrl+S also means Cmd, so code written for BareWinGUI keeps working). & access-key markers in labels are removed.
Differences from BareWinGUI
Compared by reading the constructor signatures of both libraries (not by running the Windows one). Names that stay different on purpose, because the native thing is different:
Table,Tree,IconGrid,Sidebar,SplitView,Switch,ColorWell,DropZone,StatusItem,Settingshave no or different counterparts (BareWinGUI calls its tableListViewand its treeTreeView).DatePicker(calendar=True)shows the calendar style of NSDatePicker; BareWinGUI haslong=for the long date format.Slider(vertical=True, ticks=n, snap=True)counts tick marks (the native way) instead of BareWinGUI'sticksbool andtick_frequency;RadioGroupis one control withitems, not single radio buttons.- Dialogs take
parent=andthen=(macOS sheets).choose_colorreturns a hex string likeColorWell(BareWinGUI: an RGB tuple);choose_colorandchoose_fonthave no Cancel, because the system panels have none: they return what is selected when the user closes the panel. Cmdis the shortcut modifier; "Ctrl" in a shortcut string also means Cmd, so one string works on both systems.
Stability
Beta: names are decided, small changes can still happen. The parts that are expected to stay as they are: the with-block layout (Window, VBox, HBox, Spacer, GroupBox, TabControl), the standard controls, menus, dialogs, App, Timer, Settings. Still moving, because they have been seen on the fewest Macs: Sidebar, Toolbar, Popover, StatusItem, IconGrid, build_app, notify.
Accessibility
Every control takes tooltip="..." (the hint on hover) and accessibility_label="..." (what VoiceOver reads). Controls with visible text (a Button("Save"), a CheckBox) are read from their title by AppKit; give a label to everything without text of its own: icon buttons, Image(..., description="Warning"), Slider, ProgressBar, SegmentedControl with symbols, SearchField. The examples do this. Whether the labels read well with VoiceOver has not been checked by hand.
Make it a double-clickable app
python -m baremacgui build my_script.py --name "My Tool" --icon icon.png
creates My Tool.app in the current folder: an Info.plist, the script in Resources and a small launcher that starts the Python that built it. The app has its own name and icon in Finder, and the script gets the same name and Dock icon through App. Options: --dest, --identifier, --version, --background (menu-bar app, no Dock icon), --include (extra files or folders next to the script). From code: baremacgui.build_app(...).
What it is not: the bundle is not self-contained (it needs the Python and the packages it was built with, at the same place), it is not signed or notarized, and it will not run on another Mac. Signing, notarization and a self-contained bundle need tools outside this library (for example py2app, or Apple's codesign and notarytool).
Screenshots
From GitHub's macOS runner (workflow "Screenshots", light appearance):
Examples
Run them with python examples/<file>.py:
00_hello.py: a label and a button01_header_check.py: security-header checker (a real small tool, worker thread)02_settings.py: tabs, group box, combo box, check boxes, spin box03_widgets.py: a tour of the standard controls04_finder.py: Finder-style window with toolbar and navigation05_settings_sidebar.py: sidebar window06_everything.py: file browser with toolbar, tree, table, icon grid, popover, sheet and sheet dialogs07_editor.py: text editor with menu bar, shortcuts, file dialogs08_form.py: registration form with live validation and a date picker09_progress.py: long task in a worker thread with progress bar, cancel and timer10_tasks.py: editable, sortable table11_notes.py: split view with list and editor, notes kept withSettings12_menubar.py: menu-bar clock without a Dock icon13_drop.py: drop files from Finder onto aDropZone14_checklist.py: table with a checkbox column and SF Symbols (Table(kinds=...))16_showcase.py: the best-of window: sidebar, table with checkboxes and symbols, icon grid, switch, color well, tick-mark slider, calendar15_windows.py: several windows, each with its own menus; Cmd+N opens another one
tests/test_examples_mac.py builds every example on macOS CI and tests the helper functions they define.
What is verified
Checked by hand on an Intel Mac (macOS 15.5) with Python 3.14 and PyObjC 12.2.2, in dark appearance (light appearance only with an early prototype):
- All four examples start and render: boxes,
Spacer,TextBox,ComboBox,Button,CheckBox,SpinBox,GroupBox,TabControl(General tab),RadioGroup,DropDown,Slider,ProgressBar(driven from a worker thread viaApp.invoke),Image(SF Symbol),Separator,ListBox,Table,HBox(align="stretch"),show_info,on_click,on_change,on_selectandon_activate. - Example 03 also looked right in light appearance.
Window(style="sidebar")withSidebarandPage(example 05: sidebar with symbols, page switching, window title following the page,on_change) in dark appearance.Window(style="unified")withToolbar,SearchField,SegmentedControland iconButtons (example 04) in dark appearance; the view-basedTablerestyle and the navigation added to the example afterwards were not rechecked.- Screenshots from GitHub's macOS runner (a newer macOS than the Mac above, light and dark;
tools/shoot.py, workflow "Screenshots") show all examples, and in example 06 the icon view with selection, the tree, the popover, the new-folder sheet and the delete sheet. The scenarios call the handler functions and set controls from code; they do not simulate mouse clicks. - The test suite passes on GitHub Actions for Ubuntu, macOS on Apple Silicon and macOS on Intel (
macos-latest,macos-15-intel), Python 3.10 and 3.14. On the macOS runners the tests build every control and container, exercise properties and events by calling them from code, create menus, context menus, timers and pickers, and run each example up toapp.run(). No mouse or keyboard input is simulated. - The wheel builds and installs into a clean environment, and
import baremacguidoes not import AppKit (CI jobpackage). - Examples 07 (editor), 12 to 16 (menu-bar clock, drop zone, checklist, several windows, showcase) on the same Mac: they start and look right (showcase in light and dark). In example 15 the menu bar shows the menus of the front window. Dropping files and a folder from Finder onto the drop zone works. The menu-bar clock appears, updates, its menu works, and it quits from the menu and with Ctrl-C (both fixed after a first run showed them broken). The sheet in the editor (discard changes) reacts to Return, Escape and Tab (fixed after a first run).
- Example 02: the saved theme and the window position survive a real quit and restart.
- A built
.app(python -m baremacgui build) starts, shows its name and the default icon in Finder and Dock. - VoiceOver was tried briefly on examples 04 and 05 and read the sidebar; a proper walk-through has not been done.
- Screenshots of examples 07 to 09 (editor, form, progress) from the macOS runner look right in light and dark appearance; the menu bar of the editor is not visible in them.
Not yet checked on real hardware:
- Clicking through example 06 by hand: the icon view, double-click navigation, the popover, the sheet and the delete dialog. They look right in screenshots from the GitHub macOS runner (see below), but real mouse input was not simulated there. The most fragile: the blocking sheet form (without
then), the double-click on icons, and hiding/showing the table and icon grid in example 06. Sidebardetails: dragging the divider, hiding it, the minimum window size, light appearance.- Example 04 after the
Tablerestyle: back/forward navigation, double-click into folders, search filter, light appearance. - The Advanced tab of example 03, the file dialogs,
input_box. - The menu bar shortcuts (Cmd+C, Cmd+V, Cmd+A, Cmd+Q), custom
MenuItemshortcuts,Window.add_shortcut,default_button(Return) andcancel_button(Escape). ContextMenuon right-click forTable,IconGrid,TreeandTextBox.TextBox.on_changewhile typing (single and multi line), and that setting.textfrom code does not fire it.Window.on_closeveto with the red button,Window.show_modal, the menu bar switching between windows.input_box,ask_ok_cancel, the date and time pickers with the mouse,Timerwhile a menu is open.- Editing a
Tablecell by double-click and Return/Tab/Escape behaviour, clicking a header to sort (CI only drives it from code), and that an edit in a sorted table does not re-sort until the next click. SplitViewand example 11 are built on the macOS runners (a screenshot shows the divider moved from code). Not checked by hand: dragging the dividers by mouse, remembering them across a restart (autosave=), window resizing withfixed=panes.ColorWellwith the real color panel,Switchby mouse,clipboardpasting into other apps.Table(kinds=...): CI toggles the checkbox withperformClick; clicking it by mouse, and how the checkbox and symbol cells look in light and dark, are unchecked.tooltipandaccessibility_labelare set and read back on the real views (CI). How VoiceOver speaks them, and the tooltip appearing on hover, are unchecked.- Several windows: switching by clicking between windows, and the list of windows in the Window menu, are not explicitly checked.
.appbundles:--background(no Dock icon) is unchecked; so is a bundle with your own--iconand with--include.DropZone: the highlight while hovering and the cursor for refused items.TableandTreeon_activateignoring double-clicks on the header or empty space (usesclickedRow).- Light appearance for the other examples.
- Apple Silicon, other macOS versions, multiple displays.
- The CI workflow itself.
Limits
- macOS only, standard AppKit controls only. No custom drawing, no rich text, no cell types other than text in tables.
- Needs PyObjC. "Bare" here means thin, not dependency-free.
- Dialogs without
parentare application-modal. Withparentand withoutthen, the call waits through a nested modal loop (see the unverified list);then=is the dependable form. - Menu labels are English.
App.run()hands control to AppKit. When the last window closes the app terminates (so far the process ends there); put cleanup intoWindow.on_destroy, not afterapp.run(). Unverified.choose_colorandchoose_fontrun the shared system panels modally until they are closed; CI drives them with a timer that sets a value and closes the panel. Picking by mouse, and the look of the panels, are unchecked.- Running a script from a terminal gives a plain Python process: the menu bar title and Dock icon are Python's. A packaged
.appis a separate step.
Development
Design, decisions and acceptance testing are by the author. Most of the code, tests and documentation were written by an AI (Claude, Anthropic) to the author's specification. Anything marked unverified above has not been run on a Mac yet.
pip install -e ".[test]"
python -m pytest
Off macOS only the pure-Python tests run; the control tests are skipped.
tools/shoot.py runs an example, drives it from code and takes screenshots; the manual GitHub workflow "Screenshots" does that on a macOS runner and publishes the images to the ci-screenshots branch.
License
MIT, see LICENSE.
Metadata
Release files for baremacgui 0.1.0b1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| baremacgui-0.1.0b1.tar.gz | 90.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| baremacgui-0.1.0b1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 167.7 kB
Release files / baremacgui-0.1.0b1.tar.gz
| Download URL | baremacgui-0.1.0b1.tar.gz |
|---|---|
| Size | 90.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
68f36c886c5098ea3bcb5a0bf6d7b1156bb2c7ab3d4d7b1d3b45fb197afb54ff
|
|
BLAKE2b-256 checksum How to use checksums |
99428a03331833a3f04da52d9a60c5dbd06dbb20829af7d716b9e39a55874f31
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / baremacgui-0.1.0b1-py3-none-any.whl
| Download URL | baremacgui-0.1.0b1-py3-none-any.whl |
|---|---|
| Size | 77.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4616b69cbf365cdf38cab330de98ef34c8a44e133bba1412043dc3a44b60e8c6
|
|
BLAKE2b-256 checksum How to use checksums |
bca45e55469b99ec2bbffd4d67cf9a22362e61540c63fdc393a287549f7b5686
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logSecurity for baremacgui 0.1.0b1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for baremacgui-0.1.0b1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.