mirror of
https://github.com/mudabbir-ahmad/Latex-CV-Builder.git
synced 2026-10-07 18:10:20 +00:00
Initial commit
This commit is contained in:
commit
1906b070c0
2 files changed
+485
No files matched your search
+480
@@ -0,0 +1,480 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
import os
|
||||
|
||||
|
||||
class CVGeneratorApp:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("LaTeX CV Generator")
|
||||
self.root.geometry("1200x800")
|
||||
|
||||
# Create notebook for tabs
|
||||
self.notebook = ttk.Notebook(root)
|
||||
self.notebook.pack(fill='both', expand=True, padx=5, pady=5)
|
||||
|
||||
# Create frames for each section
|
||||
self.personal_frame = ttk.Frame(self.notebook)
|
||||
self.education_frame = ttk.Frame(self.notebook)
|
||||
self.experience_frame = ttk.Frame(self.notebook)
|
||||
self.projects_frame = ttk.Frame(self.notebook)
|
||||
self.skills_frame = ttk.Frame(self.notebook)
|
||||
self.leadership_frame = ttk.Frame(self.notebook)
|
||||
self.output_frame = ttk.Frame(self.notebook)
|
||||
|
||||
# Add tabs
|
||||
self.notebook.add(self.personal_frame, text='Personal Info')
|
||||
self.notebook.add(self.education_frame, text='Education')
|
||||
self.notebook.add(self.experience_frame, text='Experience')
|
||||
self.notebook.add(self.projects_frame, text='Projects')
|
||||
self.notebook.add(self.skills_frame, text='Skills')
|
||||
self.notebook.add(self.leadership_frame, text='Leadership')
|
||||
self.notebook.add(self.output_frame, text='LaTeX Output')
|
||||
|
||||
self.setup_personal_info()
|
||||
self.setup_education()
|
||||
self.setup_experience()
|
||||
self.setup_projects()
|
||||
self.setup_skills()
|
||||
self.setup_leadership()
|
||||
self.setup_output()
|
||||
|
||||
# Button frame at bottom
|
||||
button_frame = ttk.Frame(root)
|
||||
button_frame.pack(fill='x', padx=5, pady=5)
|
||||
|
||||
ttk.Button(button_frame, text="Generate LaTeX", command=self.generate_latex).pack(side='left', padx=5)
|
||||
ttk.Button(button_frame, text="Save to File", command=self.save_to_file).pack(side='left', padx=5)
|
||||
ttk.Button(button_frame, text="Clear All", command=self.clear_all).pack(side='left', padx=5)
|
||||
|
||||
def setup_personal_info(self):
|
||||
ttk.Label(self.personal_frame, text="Personal Information", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
form_frame = ttk.Frame(self.personal_frame)
|
||||
form_frame.pack(fill='x', padx=20, pady=10)
|
||||
|
||||
fields = [
|
||||
("First Name:", "first_name"),
|
||||
("Last Name:", "last_name"),
|
||||
("Address:", "address"),
|
||||
("Phone:", "phone"),
|
||||
("Email:", "email"),
|
||||
("LinkedIn:", "linkedin"),
|
||||
("GitHub:", "github"),
|
||||
]
|
||||
|
||||
self.personal_vars = {}
|
||||
for i, (label_text, var_name) in enumerate(fields):
|
||||
ttk.Label(form_frame, text=label_text).grid(row=i, column=0, sticky='e', pady=5, padx=5)
|
||||
self.personal_vars[var_name] = tk.StringVar()
|
||||
entry = ttk.Entry(form_frame, textvariable=self.personal_vars[var_name], width=50)
|
||||
entry.grid(row=i, column=1, sticky='w', pady=5, padx=5)
|
||||
|
||||
def setup_education(self):
|
||||
ttk.Label(self.education_frame, text="Education", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
# Main education
|
||||
edu_frame = ttk.LabelFrame(self.education_frame, text="Degree Information", padding=10)
|
||||
edu_frame.pack(fill='x', padx=20, pady=10)
|
||||
|
||||
self.edu_vars = {
|
||||
'university': tk.StringVar(),
|
||||
'degree': tk.StringVar(),
|
||||
'start_date': tk.StringVar(),
|
||||
'end_date': tk.StringVar(),
|
||||
'location': tk.StringVar(),
|
||||
}
|
||||
|
||||
fields = [
|
||||
("University:", 'university'),
|
||||
("Degree:", 'degree'),
|
||||
("Start Date:", 'start_date'),
|
||||
("End Date:", 'end_date'),
|
||||
("Location:", 'location'),
|
||||
]
|
||||
|
||||
for i, (label, var_name) in enumerate(fields):
|
||||
ttk.Label(edu_frame, text=label).grid(row=i, column=0, sticky='e', pady=3, padx=5)
|
||||
ttk.Entry(edu_frame, textvariable=self.edu_vars[var_name], width=50).grid(row=i, column=1, sticky='w',
|
||||
pady=3, padx=5)
|
||||
|
||||
# Coursework
|
||||
course_frame = ttk.LabelFrame(self.education_frame, text="Relevant Coursework (comma-separated)", padding=10)
|
||||
course_frame.pack(fill='x', padx=20, pady=10)
|
||||
|
||||
self.coursework_text = tk.Text(course_frame, height=4, width=80)
|
||||
self.coursework_text.pack(pady=5)
|
||||
|
||||
def setup_experience(self):
|
||||
ttk.Label(self.experience_frame, text="Work Experience", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
self.experience_entries = []
|
||||
self.exp_container = ttk.Frame(self.experience_frame)
|
||||
self.exp_container.pack(fill='both', expand=True, padx=20, pady=10)
|
||||
|
||||
ttk.Button(self.exp_container, text="Add Position", command=self.add_experience).pack(pady=5)
|
||||
|
||||
def add_experience(self):
|
||||
exp_frame = ttk.LabelFrame(self.exp_container, text=f"Position {len(self.experience_entries) + 1}", padding=10)
|
||||
exp_frame.pack(fill='x', pady=10)
|
||||
|
||||
vars_dict = {
|
||||
'company': tk.StringVar(),
|
||||
'title': tk.StringVar(),
|
||||
'start_date': tk.StringVar(),
|
||||
'end_date': tk.StringVar(),
|
||||
'location': tk.StringVar(),
|
||||
}
|
||||
|
||||
fields = [
|
||||
("Company:", 'company'),
|
||||
("Title:", 'title'),
|
||||
("Start Date:", 'start_date'),
|
||||
("End Date:", 'end_date'),
|
||||
("Location:", 'location'),
|
||||
]
|
||||
|
||||
for i, (label, var_name) in enumerate(fields):
|
||||
ttk.Label(exp_frame, text=label).grid(row=i, column=0, sticky='e', pady=3, padx=5)
|
||||
ttk.Entry(exp_frame, textvariable=vars_dict[var_name], width=50).grid(row=i, column=1, sticky='w', pady=3,
|
||||
padx=5)
|
||||
|
||||
ttk.Label(exp_frame, text="Responsibilities (one per line):").grid(row=len(fields), column=0, columnspan=2,
|
||||
sticky='w', pady=(10, 3))
|
||||
responsibilities = tk.Text(exp_frame, height=5, width=80)
|
||||
responsibilities.grid(row=len(fields) + 1, column=0, columnspan=2, pady=3)
|
||||
|
||||
self.experience_entries.append((vars_dict, responsibilities))
|
||||
|
||||
def setup_projects(self):
|
||||
ttk.Label(self.projects_frame, text="Projects", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
self.project_entries = []
|
||||
self.proj_container = ttk.Frame(self.projects_frame)
|
||||
self.proj_container.pack(fill='both', expand=True, padx=20, pady=10)
|
||||
|
||||
ttk.Button(self.proj_container, text="Add Project", command=self.add_project).pack(pady=5)
|
||||
|
||||
def add_project(self):
|
||||
proj_frame = ttk.LabelFrame(self.proj_container, text=f"Project {len(self.project_entries) + 1}", padding=10)
|
||||
proj_frame.pack(fill='x', pady=10)
|
||||
|
||||
vars_dict = {
|
||||
'name': tk.StringVar(),
|
||||
'technologies': tk.StringVar(),
|
||||
'date': tk.StringVar(),
|
||||
}
|
||||
|
||||
fields = [
|
||||
("Project Name:", 'name'),
|
||||
("Technologies:", 'technologies'),
|
||||
("Date:", 'date'),
|
||||
]
|
||||
|
||||
for i, (label, var_name) in enumerate(fields):
|
||||
ttk.Label(proj_frame, text=label).grid(row=i, column=0, sticky='e', pady=3, padx=5)
|
||||
ttk.Entry(proj_frame, textvariable=vars_dict[var_name], width=50).grid(row=i, column=1, sticky='w', pady=3,
|
||||
padx=5)
|
||||
|
||||
ttk.Label(proj_frame, text="Description (one per line):").grid(row=len(fields), column=0, columnspan=2,
|
||||
sticky='w', pady=(10, 3))
|
||||
description = tk.Text(proj_frame, height=4, width=80)
|
||||
description.grid(row=len(fields) + 1, column=0, columnspan=2, pady=3)
|
||||
|
||||
self.project_entries.append((vars_dict, description))
|
||||
|
||||
def setup_skills(self):
|
||||
ttk.Label(self.skills_frame, text="Technical Skills", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
skills_frame = ttk.Frame(self.skills_frame)
|
||||
skills_frame.pack(fill='x', padx=20, pady=10)
|
||||
|
||||
self.skills_vars = {
|
||||
'languages': tk.StringVar(),
|
||||
'tools': tk.StringVar(),
|
||||
'technologies': tk.StringVar(),
|
||||
}
|
||||
|
||||
fields = [
|
||||
("Languages:", 'languages'),
|
||||
("Developer Tools:", 'tools'),
|
||||
("Technologies/Frameworks:", 'technologies'),
|
||||
]
|
||||
|
||||
for i, (label, var_name) in enumerate(fields):
|
||||
ttk.Label(skills_frame, text=label).grid(row=i, column=0, sticky='ne', pady=5, padx=5)
|
||||
ttk.Entry(skills_frame, textvariable=self.skills_vars[var_name], width=50).grid(row=i, column=1, sticky='w',
|
||||
pady=5, padx=5)
|
||||
|
||||
def setup_leadership(self):
|
||||
ttk.Label(self.leadership_frame, text="Leadership / Extracurricular", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
self.leadership_entries = []
|
||||
self.lead_container = ttk.Frame(self.leadership_frame)
|
||||
self.lead_container.pack(fill='both', expand=True, padx=20, pady=10)
|
||||
|
||||
ttk.Button(self.lead_container, text="Add Activity", command=self.add_leadership).pack(pady=5)
|
||||
|
||||
def add_leadership(self):
|
||||
lead_frame = ttk.LabelFrame(self.lead_container, text=f"Activity {len(self.leadership_entries) + 1}",
|
||||
padding=10)
|
||||
lead_frame.pack(fill='x', pady=10)
|
||||
|
||||
vars_dict = {
|
||||
'organization': tk.StringVar(),
|
||||
'role': tk.StringVar(),
|
||||
'date': tk.StringVar(),
|
||||
'location': tk.StringVar(),
|
||||
}
|
||||
|
||||
fields = [
|
||||
("Organization:", 'organization'),
|
||||
("Role:", 'role'),
|
||||
("Date:", 'date'),
|
||||
("Location:", 'location'),
|
||||
]
|
||||
|
||||
for i, (label, var_name) in enumerate(fields):
|
||||
ttk.Label(lead_frame, text=label).grid(row=i, column=0, sticky='e', pady=3, padx=5)
|
||||
ttk.Entry(lead_frame, textvariable=vars_dict[var_name], width=50).grid(row=i, column=1, sticky='w', pady=3,
|
||||
padx=5)
|
||||
|
||||
ttk.Label(lead_frame, text="Achievements (one per line):").grid(row=len(fields), column=0, columnspan=2,
|
||||
sticky='w', pady=(10, 3))
|
||||
achievements = tk.Text(lead_frame, height=4, width=80)
|
||||
achievements.grid(row=len(fields) + 1, column=0, columnspan=2, pady=3)
|
||||
|
||||
self.leadership_entries.append((vars_dict, achievements))
|
||||
|
||||
def setup_output(self):
|
||||
ttk.Label(self.output_frame, text="Generated LaTeX Code", font=('Arial', 14, 'bold')).pack(pady=10)
|
||||
|
||||
self.output_text = tk.Text(self.output_frame, wrap='word', width=120, height=40)
|
||||
scrollbar = ttk.Scrollbar(self.output_text, command=self.output_text.yview)
|
||||
self.output_text.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
self.output_text.pack(side='left', fill='both', expand=True, padx=20, pady=10)
|
||||
scrollbar.pack(side='right', fill='y')
|
||||
|
||||
def generate_latex(self):
|
||||
latex_code = self.build_latex_cv()
|
||||
self.output_text.delete(1.0, tk.END)
|
||||
self.output_text.insert(1.0, latex_code)
|
||||
self.notebook.select(6) # Switch to output tab
|
||||
|
||||
def build_latex_cv(self):
|
||||
# Document class and packages
|
||||
latex = r"""\documentclass[11pt,a4paper]{article}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{geometry}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{parskip}
|
||||
|
||||
\geometry{margin=1in}
|
||||
\setlist[itemize]{leftmargin=*, nosep, topsep=2pt, itemsep=2pt}
|
||||
\pagestyle{empty}
|
||||
|
||||
\begin{document}
|
||||
|
||||
"""
|
||||
# Personal Info
|
||||
first_name = self.personal_vars['first_name'].get()
|
||||
last_name = self.personal_vars['last_name'].get()
|
||||
latex += f"\\begin{{center}}\n"
|
||||
latex += f"\\textbf{{\\LARGE {first_name} {last_name}}}\\\\\n"
|
||||
|
||||
address = self.personal_vars['address'].get()
|
||||
phone = self.personal_vars['phone'].get()
|
||||
email = self.personal_vars['email'].get()
|
||||
linkedin = self.personal_vars['linkedin'].get()
|
||||
github = self.personal_vars['github'].get()
|
||||
|
||||
contact_parts = []
|
||||
if address: contact_parts.append(address)
|
||||
if phone: contact_parts.append(f"\\href{{tel:{phone}}}{{{phone}}}")
|
||||
if email: contact_parts.append(f"\\href{{mailto:{email}}}{{{email}}}")
|
||||
if linkedin: contact_parts.append(
|
||||
f"\\href{{https://{linkedin}}}{{linkedin.com/in/{linkedin.replace('https://', '').replace('www.', '')}}}")
|
||||
if github: contact_parts.append(f"\\href{{https://github.com/{github}}}{{github.com/{github}}}")
|
||||
|
||||
latex += " | ".join(contact_parts) + "\n"
|
||||
latex += f"\\end{{center}}\n\n"
|
||||
|
||||
# Education
|
||||
latex += "\\section*{Education}\n"
|
||||
university = self.edu_vars['university'].get()
|
||||
degree = self.edu_vars['degree'].get()
|
||||
start_date = self.edu_vars['start_date'].get()
|
||||
end_date = self.edu_vars['end_date'].get()
|
||||
location = self.edu_vars['location'].get()
|
||||
|
||||
if university:
|
||||
date_str = f"{start_date} -- {end_date}" if start_date and end_date else ""
|
||||
latex += f"\\noindent\\textbf{{{university}}}\\hfill {date_str}\\\\\n"
|
||||
latex += f"\\textit{{{degree}}}\n"
|
||||
if location:
|
||||
latex += f"\\\\{location}"
|
||||
latex += "\n\n"
|
||||
|
||||
# Coursework
|
||||
coursework = self.coursework_text.get(1.0, tk.END).strip()
|
||||
if coursework:
|
||||
latex += "\\subsection*{Relevant Coursework}\n"
|
||||
courses = [c.strip() for c in coursework.split(',') if c.strip()]
|
||||
latex += "\\begin{itemize}\n"
|
||||
for i in range(0, len(courses), 4):
|
||||
row_courses = courses[i:i + 4]
|
||||
latex += " \\item " + " \\quad $\\bullet$ ".join(row_courses) + "\n"
|
||||
latex += "\\end{itemize}\n\n"
|
||||
|
||||
# Experience
|
||||
latex += "\\section*{Experience}\n"
|
||||
for vars_dict, resp_text in self.experience_entries:
|
||||
company = vars_dict['company'].get()
|
||||
title = vars_dict['title'].get()
|
||||
start = vars_dict['start_date'].get()
|
||||
end = vars_dict['end_date'].get()
|
||||
location = vars_dict['location'].get()
|
||||
|
||||
if company:
|
||||
date_str = f"{start} -- {end}" if start and end else ""
|
||||
latex += f"\\noindent\\textbf{{{company}}}\\hfill {date_str}\\\\\n"
|
||||
latex += f"\\textit{{{title}}}"
|
||||
if location:
|
||||
latex += f"\\\\{location}"
|
||||
latex += "\n"
|
||||
|
||||
responsibilities = resp_text.get(1.0, tk.END).strip()
|
||||
if responsibilities:
|
||||
latex += "\\begin{itemize}\n"
|
||||
for resp in responsibilities.split('\n'):
|
||||
if resp.strip():
|
||||
latex += f" \\item {resp.strip()}\n"
|
||||
latex += "\\end{itemize}\n"
|
||||
latex += "\n"
|
||||
|
||||
# Projects
|
||||
latex += "\\section*{Projects}\n"
|
||||
for vars_dict, desc_text in self.project_entries:
|
||||
name = vars_dict['name'].get()
|
||||
tech = vars_dict['technologies'].get()
|
||||
date = vars_dict['date'].get()
|
||||
|
||||
if name:
|
||||
tech_str = f" | {tech}" if tech else ""
|
||||
latex += f"\\noindent\\textbf{{{name}}}\\textit{{{tech_str}}}\\hfill {date}\\\\\n"
|
||||
|
||||
description = desc_text.get(1.0, tk.END).strip()
|
||||
if description:
|
||||
latex += "\\begin{itemize}\n"
|
||||
for desc in description.split('\n'):
|
||||
if desc.strip():
|
||||
latex += f" \\item {desc.strip()}\n"
|
||||
latex += "\\end{itemize}\n"
|
||||
latex += "\n"
|
||||
|
||||
# Skills
|
||||
latex += "\\section*{Technical Skills}\n"
|
||||
languages = self.skills_vars['languages'].get()
|
||||
tools = self.skills_vars['tools'].get()
|
||||
technologies = self.skills_vars['technologies'].get()
|
||||
|
||||
if languages or tools or technologies:
|
||||
if languages:
|
||||
latex += f"\\noindent\\textbf{{Languages:}} {languages}\\\\\n"
|
||||
if tools:
|
||||
latex += f"\\textbf{{Developer Tools:}} {tools}\\\\\n"
|
||||
if technologies:
|
||||
latex += f"\\textbf{{Technologies/Frameworks:}} {technologies}\n"
|
||||
latex += "\n"
|
||||
|
||||
# Leadership
|
||||
latex += "\\section*{Leadership / Extracurricular}\n"
|
||||
for vars_dict, achieve_text in self.leadership_entries:
|
||||
org = vars_dict['organization'].get()
|
||||
role = vars_dict['role'].get()
|
||||
date = vars_dict['date'].get()
|
||||
location = vars_dict['location'].get()
|
||||
|
||||
if org:
|
||||
latex += f"\\noindent\\textbf{{{org}}}\\hfill {date}\\\\\n"
|
||||
latex += f"\\textit{{{role}}}"
|
||||
if location:
|
||||
latex += f"\\\\{location}"
|
||||
latex += "\n"
|
||||
|
||||
achievements = achieve_text.get(1.0, tk.END).strip()
|
||||
if achievements:
|
||||
latex += "\\begin{itemize}\n"
|
||||
for achieve in achievements.split('\n'):
|
||||
if achieve.strip():
|
||||
latex += f" \\item {achieve.strip()}\n"
|
||||
latex += "\\end{itemize}\n"
|
||||
latex += "\n"
|
||||
|
||||
latex += "\\end{document}"
|
||||
return latex
|
||||
|
||||
def save_to_file(self):
|
||||
latex_code = self.output_text.get(1.0, tk.END).strip()
|
||||
if not latex_code:
|
||||
messagebox.showwarning("Warning", "Please generate LaTeX code first!")
|
||||
return
|
||||
|
||||
file_path = filedialog.asksaveasfilename(
|
||||
defaultextension=".tex",
|
||||
filetypes=[("LaTeX files", "*.tex"), ("All files", "*.*")]
|
||||
)
|
||||
|
||||
if file_path:
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(latex_code)
|
||||
messagebox.showinfo("Success", f"CV saved to {file_path}")
|
||||
|
||||
def clear_all(self):
|
||||
if messagebox.askyesno("Confirm", "Are you sure you want to clear all fields?"):
|
||||
# Clear personal info
|
||||
for var in self.personal_vars.values():
|
||||
var.set("")
|
||||
|
||||
# Clear education
|
||||
for var in self.edu_vars.values():
|
||||
var.set("")
|
||||
self.coursework_text.delete(1.0, tk.END)
|
||||
|
||||
# Clear experience
|
||||
for widget in self.exp_container.winfo_children():
|
||||
widget.destroy()
|
||||
self.experience_entries = []
|
||||
ttk.Button(self.exp_container, text="Add Position", command=self.add_experience).pack(pady=5)
|
||||
|
||||
# Clear projects
|
||||
for widget in self.proj_container.winfo_children():
|
||||
widget.destroy()
|
||||
self.project_entries = []
|
||||
ttk.Button(self.proj_container, text="Add Project", command=self.add_project).pack(pady=5)
|
||||
|
||||
# Clear skills
|
||||
for var in self.skills_vars.values():
|
||||
var.set("")
|
||||
|
||||
# Clear leadership
|
||||
for widget in self.lead_container.winfo_children():
|
||||
widget.destroy()
|
||||
self.leadership_entries = []
|
||||
ttk.Button(self.lead_container, text="Add Activity", command=self.add_leadership).pack(pady=5)
|
||||
|
||||
# Clear output
|
||||
self.output_text.delete(1.0, tk.END)
|
||||
|
||||
|
||||
def main():
|
||||
root = tk.Tk()
|
||||
app = CVGeneratorApp(root)
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,5 @@
|
||||
[project]
|
||||
name = "latex-cv-builder"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = []
|
||||
Reference in new issue
Block a user