Initial commit

This commit is contained in:
bobbert committed 2026-05-26 16:14:05 +01:00
commit 1906b070c0
2 files changed
+485

No files matched your search

+480
View File
@@ -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()
+5
View File
@@ -0,0 +1,5 @@
[project]
name = "latex-cv-builder"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = []