Build a Simple Job Recommendation Engine with Python and Tkinter
Source: Dev.to
Job Recommendation Engine Tutorial
Ever wanted to create a desktop app that recommends jobs based on your skills? In this tutorial, we’ll build a Job Recommendation Engine using Python, Tkinter, and ttkbootstrap.
We’ll cover:
- Setting up the project
- Defining the job data structure
- Fetching and filtering jobs
- Building a simple recommendation engine
- Displaying results with a Tkinter UI
- Adding pagination and search
Let’s get started! 🚀
1. Setup & Install Dependencies
First, make sure you have Python 3 installed. Then install the required packages:
pip install ttkbootstrap pillow requests
Packages explained
| Package | Purpose |
|---|---|
ttkbootstrap | Modern Tkinter UI styling |
Pillow | Handle and display images (company logos) |
requests | Fetch logos from URLs |
2. Define the Job Data Structure
We’ll use a dataclass to represent each job. This makes it easy to store and manage job info.
from dataclasses import dataclass
from typing import List
@dataclass
class Job:
title: str
company: str
url: str
description: str
skills: List[str]
location: str
logo_url: str = ""
score: float = 0.0
✅ This gives each job a title, company, location, skills, logo, and a score for recommendations.
3. Fetch Jobs
For this tutorial we’ll use a static list of jobs. Later you could replace this with API calls.
from typing import List
def fetch_jobs(query: str) -> List[Job]:
jobs_data = [
Job(
"Python Developer",
"TechCorp",
"https://example.com/job1",
"Develop backend applications using Python.",
["Python", "Django"],
"Remote",
"https://via.placeholder.com/100x100.png?text=TechCorp",
),
Job(
"Frontend Engineer",
"Webify",
"https://example.com/job2",
"Build responsive web apps with React.",
["JavaScript", "React"],
"NY, USA",
"https://via.placeholder.com/100x100.png?text=Webify",
),
Job(
"Data Scientist",
"DataWorks",
"https://example.com/job3",
"Analyze large datasets and build ML models.",
["Python", "ML", "Pandas"],
"SF, USA",
"https://via.placeholder.com/100x100.png?text=DataWorks",
),
]
# Filter jobs by query keyword
query_tokens = set(query.lower().split())
matched_jobs = []
for job in jobs_data:
text_to_match = " ".join(
[
job.title.lower(),
job.description.lower(),
" ".join(job.skills).lower(),
]
)
if any(token in text_to_match for token in query_tokens):
matched_jobs.append(job)
return matched_jobs
💡 Tip: This simple search matches keywords in the job title, description, or skills.
4. Build a Simple Recommendation Engine
We’ll assign a score to each job based on keyword matches to rank results.
def recommend_jobs(query: str, candidates: List[Job], top_n: int = 5):
query_tokens = set(query.lower().split())
recommendations = []
for job in candidates:
text_to_match = " ".join(
[
job.title.lower(),
job.description.lower(),
" ".join(job.skills).lower(),
]
)
score = sum(1 for token in query_tokens if token in text_to_match)
recommendations.append((job, score))
recommendations.sort(key=lambda x: x[1], reverse=True)
return recommendations[:top_n]
✅ Higher scores mean better matches. This is a simple content‑based recommendation.
5. Load Company Logos
We’ll use Pillow to fetch and display images in the app.
import io
import requests
from PIL import Image, ImageTk
logo_cache = {}
def load_image(url: str, size: tuple = (80, 80)):
"""Download an image, resize it, and cache the PhotoImage."""
if not url:
return None
if url in logo_cache:
return logo_cache[url]
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
img = Image.open(io.BytesIO(resp.content))
img = img.resize(size, Image.ANTIALIAS)
photo = ImageTk.PhotoImage(img)
logo_cache[url] = photo
return photo
except Exception:
return None
💡 Tip: Caching avoids downloading the same image multiple times.
6. Build the Tkinter UI
Now let’s build the user interface with ttkbootstrap.
import tkinter as tk
import ttkbootstrap as tb
from ttkbootstrap.widgets.scrolled import ScrolledText
# ----------------------------------------------------------------------
# Main window
# ----------------------------------------------------------------------
app = tb.Window(
title="Job Recommendation Engine",
themename="flatly",
size=(980, 720),
)
# ----------------------------------------------------------------------
# Top Section – Search Bar
# ----------------------------------------------------------------------
top = tb.Frame(app, padding=15)
top.pack(fill=tk.X)
tb.Label(
top,
text="Job Recommendation Engine",
font=("Segoe UI", 16, "bold"),
).pack(anchor=tk.W)
query_entry = tb.Entry(top, font=("Segoe UI", 12))
query_entry.pack(fill=tk.X, pady=8)
tb.Button(
top,
text="Search",
bootstyle="primary",
command=lambda: print("Search clicked!"),
).pack(anchor=tk.E)
# ----------------------------------------------------------------------
# Results Area
# ----------------------------------------------------------------------
result_frame = tb.Frame(app, padding=(15, 5))
result_frame.pack(fill=tk.BOTH, expand=True)
result_box = ScrolledText(result_frame, autohide=True)
result_box.pack(fill=tk.BOTH, expand=True)
text = result_box.text
text.configure(state="disabled", wrap="word")
# This is where job results will be displayed.
# ----------------------------------------------------------------------
# Pagination Controls
# ----------------------------------------------------------------------
nav = tb.Frame(app, padding=10)
nav.pack(fill=tk.X)
prev_btn = tb.Button(nav, text="← Prev", bootstyle="secondary")
prev_btn.pack(side=tk.LEFT)
page_label = tb.Label(nav, text="Page 1", font=("Segoe UI", 10))
page_label.pack(side=tk.LEFT, padx=10)
next_btn = tb.Button(nav, text="Next →", bootstyle="secondary")
next_btn.pack(side=tk.LEFT)
# ----------------------------------------------------------------------
# (Optional) Hook up pagination and search logic here
# ----------------------------------------------------------------------
💡 You can now hook the Search, Prev, and Next buttons to the functions defined earlier to navigate between result pages.
7. Putting It All Together
Below is a minimal example that ties everything together. Feel free to expand it with more features (e.g., detailed job view, saving favorites, etc.).
def display_results(recommendations):
"""Populate the ScrolledText widget with job cards."""
text.configure(state="normal")
text.delete("1.0", tk.END)
for job, score in recommendations:
# Load logo (cached)
logo = load_image(job.logo_url)
# Build a simple text block for each job
block = f"""\
{job.title} ({job.location})
Company: {job.company}
Score: {score}
Skills: {', '.join(job.skills)}
Link: {job.url}
"""
text.insert(tk.END, block + "\n" + "-" * 40 + "\n\n")
text.configure(state="disabled")
def on_search():
query = query_entry.get()
candidates = fetch_jobs(query) # In a real app, fetch all jobs first
recommendations = recommend_jobs(query, candidates, top_n=10)
display_results(recommendations)
# Bind the search button
search_btn = tb.Button(
top,
text="Search",
bootstyle="primary",
command=on_search,
)
search_btn.pack(anchor=tk.E, pady=5)
# Start the Tkinter main loop
if __name__ == "__main__":
app.mainloop()
That’s it! You now have a functional desktop job‑recommendation app built with Python, Tkinter, and ttkbootstrap. 🎉
Feel free to experiment with more sophisticated recommendation algorithms, integrate real job APIs, or polish the UI further. Happy coding!
Connecting the Search Input with Fetching, Recommending, and Displaying Jobs
Use threading to keep the UI responsive while performing the search.
import threading
def perform_search():
"""Called when the user presses the Search button."""
query = query_entry.get().strip()
if not query:
return
# Run the heavy work in a background thread
threading.Thread(target=search_thread, args=(query,), daemon=True).start()
def search_thread(query):
"""Background worker that fetches jobs, ranks them, and prints the results."""
candidates = fetch_jobs(query)
recommendations = recommend_jobs(query, candidates)
for job, score in recommendations:
print(f"{job.title} @ {job.company} | Score: {score}")
Full‑screen Controls
- Enter fullscreen mode
- Exit fullscreen mode
Try It Out
-
Run the script
python job_recommender.py -
Type a keyword (e.g.,
PythonorReact) in the search box. -
Double‑click a job title to open its link (if you’ve implemented that feature).
-
Navigate through results with the Prev / Next buttons.
Full Source Code
The complete project is available on GitHub:
https://github.com/rogers-cyber/JOBREC
Conclusion
Congrats! 🎉 You now have a working Job Recommendation Engine featuring:
- Python data classes
- Simple content‑based recommendations
- A Tkinter UI with search, results, and pagination
Next Steps
- Replace the static job list with real API calls.
- Add fuzzy matching for smarter search.
- Enhance the UI with images, icons, and filters.
