commit 1906b070c0f0e841075c21d41e9a1fb137ecbc86 Author: mudabbir-ahmad Date: Tue May 26 16:14:05 2026 +0100 Initial commit diff --git a/CV_Builder.py b/CV_Builder.py new file mode 100644 index 0000000..96b18e3 --- /dev/null +++ b/CV_Builder.py @@ -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() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..140f6b1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[project] +name = "latex-cv-builder" +version = "0.1.0" +requires-python = ">=3.14" +dependencies = []