VBAtoPython
Start Conversion

VBA UserForms to Python GUI (tkinter)

VBA UserForms give Excel developers drag-and-drop GUI building. In Python, the standard library includes tkinter — a cross-platform toolkit that replaces every UserForm control with a native widget, no COM objects or Excel dependency required.

UserForm Basics: VBA vs Python

A VBA UserForm is a class module with a visual designer. In Python, you build the same UI programmatically with tkinter. The form becomes a class that inherits from or wraps a tk.Tk or tk.Toplevel window.

VBA

' UserForm1 code module
Private Sub UserForm_Initialize()
    Me.Caption = "Data Entry"
    Me.Width = 400
    Me.Height = 300
    txtName.Value = ""
    cboStatus.AddItem "Active"
    cboStatus.AddItem "Inactive"
End Sub

Python (tkinter)

import tkinter as tk
from tkinter import ttk

class DataEntryForm:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Data Entry")
        self.root.geometry("400x300")

        self.txt_name = ttk.Entry(self.root)
        self.txt_name.pack(pady=5)

        self.cbo_status = ttk.Combobox(
            self.root,
            values=["Active", "Inactive"]
        )
        self.cbo_status.pack(pady=5)

        self.root.mainloop()

Common Controls Mapping

Every VBA UserForm control has a direct tkinter equivalent. The table below maps the most commonly used controls.

VBA Controltkinter WidgetNotes
TextBoxttk.Entry / tk.TextEntry for single-line, Text for multi-line
Labelttk.LabelStatic text display
CommandButtonttk.ButtonUse command= for click handler
ComboBoxttk.ComboboxPass values= list
ListBoxtk.ListboxUse insert() to add items
CheckBoxttk.CheckbuttonBind to tk.BooleanVar()
OptionButtonttk.RadiobuttonShare a tk.StringVar() for grouping

Button Click Events

VBA uses Private Sub CommandButton1_Click() event handlers. In tkinter, you pass a callback function via the command= parameter.

VBA

Private Sub btnSubmit_Click()
    Dim name As String
    name = txtName.Value

    If name = "" Then
        MsgBox "Please enter a name."
        Exit Sub
    End If

    Cells(1, 1).Value = name
    Unload Me
End Sub

Python (tkinter)

from tkinter import messagebox

def submit(self):
    name = self.txt_name.get()

    if not name:
        messagebox.showwarning(
            "Validation", "Please enter a name."
        )
        return

    self.result = name
    self.root.destroy()

btn = ttk.Button(
    self.root,
    text="Submit",
    command=self.submit
)
btn.pack(pady=10)

Getting and Setting Control Values

VBA uses the .Value property on most controls. In tkinter, each widget type has its own approach — Entry uses .get() and .delete()/.insert(), while Combobox and Checkbutton use bound StringVar or BooleanVar objects.

VBA

' Get values
name = txtName.Value
status = cboStatus.Value
isActive = chkActive.Value

' Set values
txtName.Value = "John"
cboStatus.Value = "Active"
chkActive.Value = True

Python (tkinter)

# Get values
name = self.txt_name.get()
status = self.cbo_status.get()
is_active = self.chk_var.get()  # BooleanVar

# Set values
self.txt_name.delete(0, tk.END)
self.txt_name.insert(0, "John")
self.cbo_status.set("Active")
self.chk_var.set(True)

InputBox and MsgBox Replacements

VBA's built-in InputBox and MsgBox functions have direct equivalents in tkinter's simpledialog and messagebox modules — no custom form needed.

VBA

' Simple input
answer = InputBox("Enter your age:")

' Message boxes
MsgBox "Done!", vbInformation
MsgBox "Continue?", vbYesNo

' Yes/No handling
If MsgBox("Delete?", vbYesNo) = vbYes Then
    ' proceed
End If

Python (tkinter)

from tkinter import simpledialog, messagebox

# Simple input
answer = simpledialog.askstring(
    "Input", "Enter your age:"
)

# Message boxes
messagebox.showinfo("Info", "Done!")
messagebox.askyesno("Confirm", "Continue?")

# Yes/No handling
if messagebox.askyesno("Confirm", "Delete?"):
    # proceed
    pass

Modal vs Modeless Forms

VBA forms default to modal (UserForm1.Show) but can be shown modeless with UserForm1.Show vbModeless. In tkinter, the main window runs mainloop(), and secondary windows use Toplevel. Use grab_set() to make a window modal.

VBA

' Modal (blocks until closed)
UserForm1.Show

' Modeless (runs alongside code)
UserForm1.Show vbModeless

Python (tkinter)

# Main window (blocks at mainloop)
root = tk.Tk()
root.mainloop()

# Modal dialog (secondary window)
dialog = tk.Toplevel(root)
dialog.grab_set()       # make modal
dialog.transient(root)  # stay on top
root.wait_window(dialog)

# Modeless (no grab_set)
panel = tk.Toplevel(root)

Layout: Absolute Positioning vs Grid/Pack

VBA UserForms use absolute pixel coordinates (Left, Top, Width, Height). While tkinter supports .place() for absolute positioning, the idiomatic approach uses .grid() for form layouts and .pack() for simple stacking. Grid-based layouts adapt to different screen sizes automatically.

VBA (absolute coordinates)

' In the form designer:
' lblName.Left = 20
' lblName.Top = 30
' txtName.Left = 100
' txtName.Top = 30
' lblEmail.Left = 20
' lblEmail.Top = 60
' txtEmail.Left = 100
' txtEmail.Top = 60

Python (tkinter grid)

# Row 0
ttk.Label(root, text="Name:").grid(
    row=0, column=0, padx=10, pady=5,
    sticky="w"
)
ttk.Entry(root).grid(
    row=0, column=1, padx=10, pady=5
)

# Row 1
ttk.Label(root, text="Email:").grid(
    row=1, column=0, padx=10, pady=5,
    sticky="w"
)
ttk.Entry(root).grid(
    row=1, column=1, padx=10, pady=5
)

Complete Form Conversion Example

Below is a realistic data-entry UserForm converted end-to-end. The VBA version relies on the Excel host; the Python version is self-contained.

VBA — UserForm with TextBox, ComboBox, CheckBox, and Button

Private Sub UserForm_Initialize()
    Me.Caption = "Add Employee"
    cboRole.AddItem "Developer"
    cboRole.AddItem "Designer"
    cboRole.AddItem "Manager"
End Sub

Private Sub btnSave_Click()
    If txtName.Value = "" Then
        MsgBox "Name is required.", vbExclamation
        Exit Sub
    End If

    Dim nextRow As Long
    nextRow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Row + 1
    Sheet1.Cells(nextRow, 1).Value = txtName.Value
    Sheet1.Cells(nextRow, 2).Value = cboRole.Value
    Sheet1.Cells(nextRow, 3).Value = chkFullTime.Value
    Unload Me
End Sub

Python — equivalent tkinter class

import tkinter as tk
from tkinter import ttk, messagebox
import csv

class AddEmployeeForm:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Add Employee")

        # Name
        ttk.Label(self.root, text="Name:").grid(
            row=0, column=0, padx=10, pady=5, sticky="w"
        )
        self.txt_name = ttk.Entry(self.root, width=30)
        self.txt_name.grid(row=0, column=1, padx=10, pady=5)

        # Role
        ttk.Label(self.root, text="Role:").grid(
            row=1, column=0, padx=10, pady=5, sticky="w"
        )
        self.cbo_role = ttk.Combobox(
            self.root,
            values=["Developer", "Designer", "Manager"]
        )
        self.cbo_role.grid(row=1, column=1, padx=10, pady=5)

        # Full-time checkbox
        self.full_time_var = tk.BooleanVar()
        ttk.Checkbutton(
            self.root, text="Full-time",
            variable=self.full_time_var
        ).grid(row=2, column=1, padx=10, pady=5, sticky="w")

        # Save button
        ttk.Button(
            self.root, text="Save",
            command=self.save
        ).grid(row=3, column=1, padx=10, pady=10, sticky="e")

        self.root.mainloop()

    def save(self):
        name = self.txt_name.get()
        if not name:
            messagebox.showwarning(
                "Validation", "Name is required."
            )
            return

        with open("employees.csv", "a", newline="") as f:
            writer = csv.writer(f)
            writer.writerow([
                name,
                self.cbo_role.get(),
                self.full_time_var.get()
            ])
        self.root.destroy()

if __name__ == "__main__":
    AddEmployeeForm()

When to Use tkinter vs a Web UI

Tkinter is the right choice when you need a quick desktop replacement for a UserForm. But if your form is consumed by multiple users or needs to run on a server, consider a web-based alternative.

ScenarioRecommended Tool
Single-user desktop tool, quick migrationtkinter
Internal data dashboard or rapid prototypeStreamlit
Multi-user web app with custom logicFlask / Django
Complex desktop app with rich widgetsPyQt / PySide
Jupyter-based analysis with interactive inputsipywidgets

Migration Strategy Recommendations

  • Separate logic from UI: Before converting, extract your business logic from UserForm event handlers into standalone functions. This makes the Python version testable and UI-agnostic.
  • Start with simpledialog/messagebox: If your UserForm is just an InputBox or MsgBox wrapper, skip building a full GUI — use tkinter's built-in dialogs instead.
  • Use grid layout, not place: Resist the urge to replicate VBA's absolute coordinates with .place(). The .grid() manager produces layouts that scale across screen resolutions.
  • Replace Excel storage with files or databases: UserForms that write to Sheet1.Cells should write to CSV, SQLite, or a database in Python. This removes the Excel dependency entirely.
  • Consider if you need a GUI at all: Many VBA UserForms exist only because VBA lacks a good CLI. In Python, argparse or a simple script with input() prompts may be sufficient.
  • Test incrementally: Convert one form at a time. Verify that each control reads and writes the correct values before moving on to event wiring and validation logic.

Have a VBA UserForm you want to convert?

Paste your UserForm code into the converter and get a Python equivalent with tkinter widgets, event handlers, and layout — instantly.