Read more about Building & Packaging a Simple Python Desktop App into a Standalone Exe
Read more about Building & Packaging a Simple Python Desktop App into a Standalone Exe
Building & Packaging a Simple Python Desktop App into a Standalone Exe

free note

Everything here is step‑by‑step, beginner‑friendly, and fully reproducible.

1. Create the Project Folder -> Create a folder anywhere on your system:

Code

TextLineCounterApp/

├── app.py → main Python script

├── requirements.txt → optional dependency list

└── logs/ → folder where the app will write logs.Make sure the logs/ folder exists (empty is fine)

2. Add the Python Application Code. Open app.py and paste the following complete working code:

import os

import datetime

import tkinter as tk

from tkinter import filedialog, messagebox

def get_base_dir():

import sys

if hasattr(sys, "_MEIPASS"):

return sys._MEIPASS

return os.path.dirname(os.path.abspath(__file__))

BASE_DIR = get_base_dir()

LOGS_DIR = os.path.join(BASE_DIR, "logs")

def ensure_logs_dir():

if not os.path.exists(LOGS_DIR):

os.makedirs(LOGS_DIR, exist_ok=True)

def log_message(message: str):

ensure_logs_dir()

log_file = os.path.join(LOGS_DIR, "app.log")

timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

with open(log_file, "a", encoding="utf-8") as f:

f.write(f"[{timestamp}] {message}\n")

def count_lines_in_file(file_path: str) -> int:

with open(file_path, "r", encoding="utf-8", errors="ignore") as f:

return sum(1 for _ in f)

def on_select_file():

file_path = filedialog.askopenfilename(

title="Select a text file",

filetypes=[("Text files", "*.txt"), ("All files", "*.*")]

)

if not file_path:

return

try:

line_count = count_lines_in_file(file_path)

result_label.config(text=f"Lines in file: {line_count}")

log_message(f"Processed file: {file_path} | Lines: {line_count}")

except Exception as e:

messagebox.showerror("Error", f"Failed to process file:\n{e}")

log_message(f"Error processing file {file_path}: {e}")

def create_main_window():

window = tk.Tk()

window.title("Text Line Counter")

window.geometry("400x200")

title_label = tk.Label(window, text="Text Line Counter", font=("Arial", 16))

title_label.pack(pady=10)

select_button = tk.Button(window, text="Select Text File", command=on_select_file)

select_button.pack(pady=10)

global result_label

result_label = tk.Label(window, text="No file selected yet.")

result_label.pack(pady=10)

footer_label = tk.Label(window, text="Logs are stored in the 'logs' folder.", font=("Arial", 8))

footer_label.pack(side=tk.BOTTOM, pady=5)

return window

if __name__ == "__main__":

ensure_logs_dir()

log_message("Application started.")

main_window = create_main_window()

main_window.mainloop()

log_message("Application closed.")

This app: Opens a GUI window.Lets the user select a .txt file. Counts the number of lines.

Writes logs into the logs/ folder. Works both in development and after packaging

3. Test the App Before Packaging. run the app -> "python app.py"

You should see: A window titled Text Line Counter.A button to select a text file .A result label .Logs created inside logs/app.log

If this works, you’re ready to package.

4. Install auto‑py‑to‑exe

Run-> "pip install auto-py-to-exe"

Then launch it: "auto-py-to-exe"

This opens a GUI tool that wraps PyInstaller.

5. Configure auto‑py‑to‑exe (Important Section)

Inside the GUI:

A. Script Location -> Select your app.py.

B. Onefile vs Onedir -> Choose: One Directory (Recommended)

This gives you a folder with: app.exe

DLLs ,Python runtime,Your custom folders (logs/resources)

C. Console Window -> Choose: Window Based (no console)

D. Add Custom Folders -> Scroll to Additional Files → click Add Folder.

Source: TextLineCounterApp/logs

Destination: logs

This ensures the logs/ folder appears next to the .exe.

E. Output Directory -> Choose where the final build should go.

F. Build Click: "Convert .py to .exe"

Wait until you see “Completed Successfully”.

6. Inspect the Build Output

Inside your output folder, you’ll see:

TextLineCounterApp/

├── TextLineCounterApp.exe

├── logs/

├── python DLLs

└── runtime files

This folder is fully portable.

7. Test the Packaged EXE

Double‑click: "TextLineCounterApp.exe"

Test:

Select a text file

See the line count

Check logs/app.log

If everything works, your app is ready for distribution.

8. Prepare the App for Users -> Zip the entire folder: "TextLineCounterApp_v1.0.zip"

This is what you will share with users.

9. How Users Will Install & Use the App

Step 1 — Download

User downloads: "TextLineCounterApp_v1.0.zip"

Step 2 — Extract -> "Right‑click → Extract All"

Step 3 — Run Inside the extracted folder: "Double‑click TextLineCounterApp.exe"

Step 4 — Use

Click Select Text File

Choose a .txt file

See the line count

Logs appear in the logs/ folder

No Python installation required.

10. Optional: Add a README for Users .Create a file named README.txt:

Code

Text Line Counter App

---------------------

How to Use:

1. Extract the ZIP file.

2. Open the folder.

3. Double-click "TextLineCounterApp.exe".

4. Click "Select Text File" and choose a .txt file.

5. The app will show the number of lines.

6. Logs are stored in the "logs" folder.

No Python installation is required.

Include this file before zipping.

11. Troubleshooting

App doesn’t open -> Ensure you extracted the ZIP. Ensure antivirus didn’t block the .exe

Logs not created -> Make sure the logs/ folder exists. Ensure you added it in auto‑py‑to‑exe

File dialog doesn’t open -> Run the .exe from inside the folder. Don’t move the .exe alone

12. Finally You now know how to:

Build a simple Python GUI app

Create a clean folder structure

Package it using auto‑py‑to‑exe

Include custom folders

Generate a portable .exe

Distribute it to users

Ensure it runs without Python installed

This is the same workflow used for:

PDF tools

OCR utilities

Data processing apps

Internal business tools

Personal productivity apps

Once you master this, you can package any Python project into a Windows application.

Thanks,

Jyoti.

You can publish here, too - it's easy and free.