Build Python desktop
apps visually
Buildfy is a drag-and-drop GUI builder that generates clean, runnable Python code for CustomTkinter — no boilerplate, no guessing the API.
Installation
Buildfy runs entirely in your browser — no installation required. Just open the app and start building. The only prerequisite is Python with CustomTkinter installed on your machine to run the exported code.
Open Buildfy v3.0
Navigate to the Buildfy web app in any modern browser (Chrome, Firefox, Safari, Edge). No sign-up required to start building.
Install Python & CustomTkinter
To run exported apps, install Python 3.10+ and CustomTkinter on your machine.
Run your exported app
Export your design as a .py file and run it with python app.py.
# Install CustomTkinter pip install customtkinter # Run your exported Buildfy app python my_app.py
Quick start
Build your first app in under two minutes. This walkthrough creates a simple login form.
Set your canvas size
In the Base Properties section of the right panel, set Width to 400 and Height to 300. Type a title like Login.
Drag widgets onto the canvas
From the left panel, drag a Label, two Entry widgets, and a Button onto the canvas. Position them to form a login layout.
Edit widget properties
Click each widget to select it, then edit its text, color, and size in the right panel. Set the password Entry's show property to *.
Export to Python
Click File → Export Python or press ⌘E / Ctrl+E. Review the code in the preview, then download the .py file.
Run it
Open a terminal and run python login.py. Your CustomTkinter app launches immediately.
Toolbar
The toolbar spans the top of the Buildfy window and is divided into three zones: left (brand + menus), center (wordmark), and right (tools + zoom).
Menu system
All three menus (File, Edit, View) use a macOS-style frosted-glass dropdown. On Mac, shortcuts display as ⌘⇧⌥ symbols. On Windows/Linux they display as Ctrl/Shift/Alt text.
Widget panel
The left sidebar lists every available widget organized into collapsible categories. Use the search bar at the top to filter by name.
Canvas
The canvas is your app's visual editor. It shows a floating inner window (your future Python app) centered on a pannable grid background. Every widget placed here maps 1:1 to Python code.
Navigation
| Action | How |
|---|---|
| Pan canvas | Space + drag |
| Zoom in / out | Ctrl+Scroll or toolbar ± buttons |
| Reset zoom | ⌘0 / Ctrl+0 |
Selecting widgets
Snapping & guides
Right-click context menu
Properties panel
The right panel updates live whenever you select a widget. It is organized into three tabs: Layout, Style, and Behavior.
Property reference — CTkButton
| Property | Type | Description |
|---|---|---|
| text | string | Label displayed on the button |
| width | int | Widget width in pixels (set in constructor) |
| height | int | Widget height in pixels (set in constructor) |
| fg_color | hex string | Background fill color |
| hover_color | hex string | Color on mouse hover |
| text_color | hex string | Label text color |
| corner_radius | int | Border radius in pixels |
| font | tuple | Font as ("Arial", 14) or CTkFont |
| state | "normal" | "disabled" | Interactive state |
| command | function name | Python callback on click |
ctk.CTkButton(self, width=120, height=36, ...) and use only x= and y= in .place().Layers panel
The Layers section lists every widget on the canvas in z-order (topmost first). Use it to select, reorder, rename, show/hide, and lock widgets.
Basic widgets
The core display and interaction widgets. These map to fundamental CustomTkinter classes.
Input widgets
Interactive controls that accept user input. Each generates correct Python with proper post-placement calls for value initialization.
value= to CTkSlider, CTkProgressBar, CTkSwitch, or CTkCheckBox constructors — it will crash. Buildfy handles this automatically by emitting .set() and .select() calls after .place().Container widgets
Containers hold other widgets. In the generated Python code, child widgets placed inside a container use the container as their master.
Display widgets
Node editor
The node editor lets you wire widget behaviors visually without writing Python event handlers. Open it via the Nodes button in the toolbar (⌘J / Ctrl+J) or by right-clicking any widget and choosing Edit Behavior (Nodes).
Node categories
Generated Python
When you export, the node graph produces a _setup_events method that is automatically called from __init__:
def _setup_events(self): self.button_a1b2.configure(command=self._button_a1b2_click) def _button_a1b2_click(self): if value > 50: self.label_c3d4.configure(text="High!") else: self.entry_e5f6.place_forget()
Code panel
Toggle the live code panel with the Code button in the toolbar (⌘K / Ctrl+K). It opens as a resizable split pane alongside the canvas.
# Generated by Buildfy v3.0 import customtkinter as ctk import tkinter as tk ctk.set_appearance_mode("dark") class App(ctk.CTk): def __init__(self): super().__init__() self.title("My App") self.geometry("800x600") self._setup_widgets() def _setup_widgets(self): self.button_a1b2 = ctk.CTkButton( self, width=120, height=36, text="My Button", fg_color="#F0525C" ) self.button_a1b2.place(x=40, y=40)
Export & save
Save project (.bfy)
Projects save as .bfy JSON files. Press ⌘S / Ctrl+S or use File → Save. The format captures all widget state, positions, properties, and node graphs.
{
"version": "3.0",
"canvas": {
"width": 800, "height": 600,
"title": "My App", "theme": "dark"
},
"widgets": [
{
"id": "btn_a1b2c3", "type": "CTkButton",
"name": "Button_a1b2",
"x": 120, "y": 80, "width": 120, "height": 36,
"properties": {
"text": "Click Me",
"fg_color": "#F0525C"
},
"locked": false, "visible": true, "zIndex": 0
}
],
"nodeGraphs": { "global": {} }
}
Export Python (.py)
Use File → Export Python or ⌘E / Ctrl+E. A modal shows a syntax-highlighted preview with a validation banner before you download.
Export Python — My_App.py
# Generated by Buildfy v3.0 # Requirements: pip install customtkinter import customtkinter as ctk import tkinter as tk ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") class App(ctk.CTk): def __init__(self): super().__init__() self.title("My App") self.geometry("800x600") self.resizable(True, True) self._setup_widgets() def _setup_widgets(self): self.button_a1b2 = ctk.CTkButton( self, width=120, height=36, text="Click Me", fg_color="#F0525C", font=("Inter", 13) ) self.button_a1b2.place(x=120, y=80) def main(): app = App() app.mainloop() if __name__ == "__main__": main()
Export formats
| Format | Shortcut | Description |
|---|---|---|
| Python (.py) | ⌘E | Complete runnable CustomTkinter app |
| PNG image | ⇧⌘E | Canvas rendered as a flat PNG screenshot |
| JSON (.bfy) | — | Raw widget tree for backup or sharing |
Keyboard shortcuts
All shortcuts use ⌘ on macOS and Ctrl on Windows/Linux. Buildfy detects your OS automatically.
File
| Action | Shortcut |
|---|---|
| New project | ⌘N |
| Open project | ⌘O |
| Save | ⌘S |
| Save As | ⇧⌘S |
| Export Python | ⌘E |
| Export PNG | ⇧⌘E |
Edit
| Action | Shortcut |
|---|---|
| Undo | ⌘Z |
| Redo | ⇧⌘Z |
| Cut | ⌘X |
| Copy | ⌘C |
| Paste | ⌘V |
| Duplicate | ⌘D |
| Delete | ⌫ |
| Select all | ⌘A |
| Group | ⌘G |
| Deselect | Esc |
Canvas & view
| Action | Shortcut |
|---|---|
| Zoom in | ⌘+ |
| Zoom out | ⌘− |
| Reset zoom | ⌘0 |
| Pan canvas | Space + drag |
| Nudge widget 1px | ↑↓←→ |
| Nudge widget 10px | ⇧ + ↑↓←→ |
| Toggle node editor | ⌘J |
| Toggle code panel | ⌘K |
| Toggle preview | ⌘P |
.bfy file format
Buildfy projects are stored as .bfy files — standard JSON with a defined schema. You can inspect or edit them manually.
Schema
| Key | Type | Description |
|---|---|---|
| version | string | Buildfy version that created the file (e.g. "3.0") |
| canvas.width | int | App window width in pixels |
| canvas.height | int | App window height in pixels |
| canvas.title | string | Window title shown in the title bar |
| canvas.theme | "dark" | "light" | "system" | CustomTkinter appearance mode |
| widgets[] | array | Ordered array of widget objects |
| widgets[].id | string | Unique identifier (e.g. "btn_a1b2c3") |
| widgets[].type | string | CTk class name (e.g. "CTkButton") |
| widgets[].name | string | Python variable name used in generated code |
| widgets[].x, .y | int | Position on canvas (always integers) |
| widgets[].width, .height | int | Widget size (passed to constructor) |
| widgets[].properties | object | Widget-specific props (text, fg_color, etc.) |
| widgets[].zIndex | int | Stacking order (0 = bottom) |
| nodeGraphs | object | Serialized node editor graphs per widget |
Python output reference
Every exported file follows the same template. Understanding it helps you extend or debug generated code.
# Generated by Buildfy v3.0 # Requirements: pip install customtkinter import customtkinter as ctk import tkinter as tk ctk.set_appearance_mode("dark") # "dark" | "light" | "system" ctk.set_default_color_theme("blue") # from Base Properties > Color class App(ctk.CTk): def __init__(self): super().__init__() self.title("My Application") self.geometry("800x600") self.resizable(True, True) self._setup_widgets() self._setup_events() # only if node graph is non-empty def _setup_widgets(self): # ── Widgets ──────────────────────────────── self.button_a1b2 = ctk.CTkButton( self, width=120, height=36, text="Click Me", fg_color="#F0525C", font=("Inter", 13) ) self.button_a1b2.place(x=120, y=80) # x and y ONLY self._button_a1b2_x = 120 # stored for show/hide self._button_a1b2_y = 80 # post-placement value calls: self.slider_b2c3.set(50) # CTkSlider self.pb_c3d4.set(0.7) # CTkProgressBar self.switch_d4e5.select() # CTkSwitch (on) def _setup_events(self): # Generated from node editor self.button_a1b2.configure( command=self._button_a1b2_click ) def _button_a1b2_click(self): self.label_e5f6.configure(text="Clicked!") def main(): app = App() app.mainloop() if __name__ == "__main__": main()
width= or height= to .place() — they belong in the constructor.2. Never pass
value= to CTkProgressBar, CTkSlider, CTkSwitch, or CTkCheckBox constructors — use .set() or .select() after .place().3. CTkRadioButton always needs a
variable=tk.IntVar().
FAQ
My app crashes with "width and height must be passed to the constructor"
You (or a previous version of Buildfy) generated code with width= and height= inside the .place() call. CustomTkinter forbids this. Move them to the widget constructor: ctk.CTkButton(self, width=120, height=36, ...), and use only x= and y= in .place().
My app crashes with "'value' are not supported arguments"
CTkProgressBar, CTkSlider, CTkSwitch, and CTkCheckBox do not accept value= in their constructors. Remove it and add a .set() or .select() call after the .place() call.
CTkRadioButton crashes on launch
RadioButtons require a shared tk.IntVar. Declare it before the widget: self.rb_var = tk.IntVar(value=0), then pass variable=self.rb_var to each button in the group.
Node connections don't stick
Drag from an output port (right side, colored circle) to an input port (left side, gray circle). Make sure to release the mouse directly on the target port. The connection appears as a colored Bezier curve.
Can I use Buildfy offline?
Buildfy is a browser-based app and requires an internet connection to load. Once loaded, most editing features work without network access. Saving files downloads them to your machine so they are always available locally.
What Python version do I need?
Python 3.10 or later is recommended. CustomTkinter 5.x requires Python 3.8 minimum, but several features behave best on 3.10+.