DEV Community

Amy
Amy

Posted on

Gantt Chart Python: A Step-by-Step Guide With Code Examples

Project schedules become difficult to follow when tasks overlap, deadlines move, and ownership remains unclear. A plain task list can show what needs doing, yet it rarely shows how work connects across time.

That confusion grows quickly. A two-day delay in design might postpone development, testing, and launch. Without a visual timeline, you may miss the risk until your deadline is already under pressure.

Here’s the solution: build a Gantt chart in Python. You can turn task names, start dates, durations, and progress values into a clear project timeline with a few lines of code. This guide shows you how.

How to Create a Gantt Chart in Python

A Gantt chart in Python is a timeline visualization that displays project tasks as horizontal bars across calendar dates. Each bar usually represents a task’s start date, duration, owner, status, or progress.

You can create one with Python libraries such as Matplotlib or Plotly. Matplotlib works well for static charts, while Plotly adds interactive hover labels, zooming, and filtering.

What You Need Before Coding

Prepare four basic values for every task:

  • A task name, such as “Design landing page.”
  • A start date, such as “2025-03-03.”
  • A duration measured in days.
  • An optional progress percentage or status label.

You also need Python and a charting library. Install the most useful packages with this command:

pip install matplotlib pandas plotly

Step 1: Define Your Project Tasks

Start with a Python list containing dictionaries. Each dictionary represents one task and keeps related values together.

tasks = [
    {
        "task": "Requirements",
        "start": "2025-03-03",
        "duration": 3,
        "status": "Complete"
    },
    {
        "task": "Design",
        "start": "2025-03-06",
        "duration": 5,
        "status": "In progress"
    },
    {
        "task": "Development",
        "start": "2025-03-11",
        "duration": 8,
        "status": "Planned"
    },
    {
        "task": "Testing",
        "start": "2025-03-19",
        "duration": 4,
        "status": "Planned"
    }
]

This structure makes each task easy to update. For example, changing development from eight days to ten requires one simple edit.

Step 2: Convert Dates and Calculate End Dates

Matplotlib needs numeric positions for dates. Pandas can convert text dates into date values and calculate each task’s end date.

import pandas as pd

df = pd.DataFrame(tasks)

df["start"] = pd.to_datetime(df["start"])
df["end"] = df["start"] + pd.to_timedelta(df["duration"], unit="D")

print(df)

The new end column gives every task a finishing point. A task starting on March 6 for five days ends on March 11.

Step 3: Draw the Timeline With Matplotlib

Use barh() to draw horizontal bars. The bar position comes from the start date, while the width comes from the duration.

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

fig, ax = plt.subplots(figsize=(11, 6))

for index, row in df.iterrows():
    ax.barh(
        y=index,
        width=row["duration"],
        left=row["start"],
        height=0.6,
        color="#4C78A8"
    )

ax.set_yticks(range(len(df)))
ax.set_yticklabels(df["task"])
ax.invert_yaxis()

ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))

plt.xticks(rotation=45)
plt.xlabel("Date")
plt.ylabel("Task")
plt.title("Project Gantt Chart")
plt.tight_layout()
plt.show()

The result places each task on its own row. Longer tasks stretch farther across the calendar, making schedule length easy to compare.

Step 4: Add Status Colors

Status colors help you spot completed, active, and upcoming work. Create a color mapping, then select a color inside the drawing loop.

status_colors = {
    "Complete": "#59A14F",
    "In progress": "#F28E2B",
    "Planned": "#BDBDBD"
}

fig, ax = plt.subplots(figsize=(11, 6))

for index, row in df.iterrows():
    ax.barh(
        y=index,
        width=row["duration"],
        left=row["start"],
        height=0.6,
        color=status_colors[row["status"]]
    )

ax.set_yticks(range(len(df)))
ax.set_yticklabels(df["task"])
ax.invert_yaxis()

ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))

plt.xticks(rotation=45)
plt.title("Project Timeline by Status")
plt.tight_layout()
plt.show()

For example, green bars show finished tasks, orange bars show active work, and gray bars show planned work.

Step 5: Show Progress Inside Each Bar

Progress overlays make the chart more informative. Draw a lighter bar first, then place a darker bar over it using the completion percentage.

tasks = [
    {
        "task": "Requirements",
        "start": "2025-03-03",
        "duration": 3,
        "progress": 1.0
    },
    {
        "task": "Design",
        "start": "2025-03-06",
        "duration": 5,
        "progress": 0.6
    },
    {
        "task": "Development",
        "start": "2025-03-11",
        "duration": 8,
        "progress": 0.2
    },
    {
        "task": "Testing",
        "start": "2025-03-19",
        "duration": 4,
        "progress": 0.0
    }
]

df = pd.DataFrame(tasks)
df["start"] = pd.to_datetime(df["start"])

fig, ax = plt.subplots(figsize=(11, 6))

for index, row in df.iterrows():
    ax.barh(
        y=index,
        width=row["duration"],
        left=row["start"],
        height=0.6,
        color="#D9E2F3"
    )

    ax.barh(
        y=index,
        width=row["duration"] * row["progress"],
        left=row["start"],
        height=0.6,
        color="#2F5597"
    )

ax.set_yticks(range(len(df)))
ax.set_yticklabels(df["task"])
ax.invert_yaxis()

ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))

plt.xticks(rotation=45)
plt.title("Project Progress Gantt Chart")
plt.tight_layout()
plt.show()

A progress value of 0.6 fills 60 percent of the task bar. This gives you a quick visual estimate without adding many labels.

Build an Interactive Timeline With Plotly

Plotly Express includes a dedicated timeline function called px.timeline(). It creates an interactive chart where you can hover over tasks, zoom into dates, and hide categories.

Prepare the Required Columns

Plotly expects a task label, a start value, and an end value. Create those columns with Pandas before plotting.

import pandas as pd
import plotly.express as px

tasks = [
    {
        "Task": "Requirements",
        "Start": "2025-03-03",
        "Finish": "2025-03-06",
        "Status": "Complete"
    },
    {
        "Task": "Design",
        "Start": "2025-03-06",
        "Finish": "2025-03-11",
        "Status": "In progress"
    },
    {
        "Task": "Development",
        "Start": "2025-03-11",
        "Finish": "2025-03-19",
        "Status": "Planned"
    },
    {
        "Task": "Testing",
        "Start": "2025-03-19",
        "Finish": "2025-03-23",
        "Status": "Planned"
    }
]

df = pd.DataFrame(tasks)

df["Start"] = pd.to_datetime(df["Start"])
df["Finish"] = pd.to_datetime(df["Finish"])

Create the Interactive Chart

fig = px.timeline(
    df,
    x_start="Start",
    x_end="Finish",
    y="Task",
    color="Status",
    title="Interactive Project Gantt Chart",
    color_discrete_map={
        "Complete": "#59A14F",
        "In progress": "#F28E2B",
        "Planned": "#BDBDBD"
    }
)

fig.update_yaxes(autorange="reversed")
fig.update_layout(
    xaxis_title="Date",
    yaxis_title="Task",
    hovermode="closest"
)

fig.show()

Plotly suits project reviews because stakeholders can inspect individual tasks without reading every label. Matplotlib remains a strong choice for reports and static presentations.

Matplotlib Compared With Plotly

Need Better choice Reason
Print-ready chart Matplotlib It gives precise control over size, fonts, colors, and layout.
Hover details Plotly It reveals task information interactively.
Small script Matplotlib The drawing process stays simple and lightweight.
Team review Plotly Zooming and filtering make exploration easier.

Add Dependencies and Milestones

A basic timeline shows when tasks happen. A practical planning chart should also show relationships and major checkpoints.

Represent Task Dependencies

Dependencies indicate that one task relies on another. For example, development may begin only after design finishes.

dependencies = [
    ("Requirements", "Design"),
    ("Design", "Development"),
    ("Development", "Testing")
]

You can draw arrows between task bars with Matplotlib annotations. First, map task names to their row positions.

task_positions = {
    task: index for index, task in enumerate(df["task"])
}

for previous_task, next_task in dependencies:
    previous_row = df[df["task"] == previous_task].iloc[0]
    next_row = df[df["task"] == next_task].iloc[0]

    ax.annotate(
        "",
        xy=(next_row["start"], task_positions[next_task]),
        xytext=(previous_row["end"], task_positions[previous_task]),
        arrowprops={
            "arrowstyle": "->",
            "color": "#555555",
            "connectionstyle": "arc3,rad=0.15"
        }
    )

This approach works well for a short workflow. A large plan may need a dedicated scheduling library or a simpler dependency view.

Mark Important Milestones

A milestone represents a significant date, such as a release, approval, or customer review. Plot it with a vertical line or diamond marker.

milestones = {
    "Design approval": "2025-03-11",
    "Release": "2025-03-23"
}

for label, date in milestones.items():
    date = pd.to_datetime(date)

    ax.axvline(
        date,
        color="#D62728",
        linestyle="--",
        linewidth=1
    )

    ax.text(
        date,
        len(df) - 0.5,
        label,
        rotation=90,
        color="#D62728",
        va="top",
        ha="right"
    )

For example, a release line lets you see whether testing finishes before launch. If the testing bar crosses that line, the schedule needs attention.

Improve Readability and Accuracy

A technically correct chart can still confuse people. Clear labels, consistent dates, and sensible spacing make the timeline useful during real planning discussions.

Choose a Useful Date Scale

Daily intervals work for short projects. Weekly intervals work better when a plan covers several months.

ax.xaxis.set_major_locator(mdates.WeekdayLocator(interval=1))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))

Too many date labels create visual noise. Too few labels hide timing details, so match the scale to the project length.

Keep Task Names Short

Use “Design checkout flow” instead of a long paragraph. Put extra context into hover labels, annotations, or a companion planning view.

Short labels also prevent chart rows from becoming too wide. A chart that fits on a laptop screen is easier to review than one requiring constant horizontal scrolling.

Use Colors With Meaning

Choose colors that communicate status consistently. If orange means active work today, keep that meaning throughout the chart.

  • Green can represent completed work.
  • Orange can represent active work.
  • Gray can represent planned work.
  • Red can highlight risk or delay.
  • Blue can represent neutral scheduled work.

Use patterns or labels when accessibility matters. Color alone may not distinguish statuses for every viewer.

Handle Weekends and Holidays

A duration of five calendar days includes weekends. That may misrepresent a team working Monday through Friday.

For business-day calculations, use Pandas’ business-day offset:

df["end"] = df["start"] + pd.offsets.BusinessDay(4)

This calculates a five-business-day window when the starting date counts as day one. Confirm your organization’s holiday rules before using the result.

Use ONES.com Alongside Python Planning

Python gives you flexible chart creation. ONES.com can support the broader project workflow around that visualization, especially when tasks, ownership, progress, and collaboration change frequently.

ONES.com product screenshot

Capabilities That Support Timeline Work

  • Task planning: Break large deliverables into manageable work items.
  • Milestone tracking: Monitor important release and approval points.
  • Dependency visibility: Identify work that must happen in sequence.
  • Status management: Track planned, active, blocked, and completed work.
  • Team collaboration: Keep conversations connected to project activity.
  • Progress reporting: Review completion trends during project meetings.
  • Workflow customization: Adapt fields and stages to your planning process.
  • Permission control: Give people appropriate access to project areas.

Here’s why this matters: a Python chart usually captures a planning moment, while a project platform can reflect ongoing changes.

For example, you might generate a weekly timeline from current task details, then use ONES.com to manage ownership and updates between reviews.

When This Combination Makes Sense

Use Python when you need a custom visual, a repeatable reporting script, or a chart with special formatting.

Use ONES.com when the team needs shared planning, task discussions, progress updates, and workflow coordination.

The best part? You do not have to choose one approach for every situation. A custom chart can explain the schedule, while a project workspace supports the work behind it.

Common Challenges

Dates Appear as Numbers

Problem: Matplotlib may display dates as numeric values when the axis formatter is missing.

Solution: Add a date locator and formatter:

ax.xaxis.set_major_locator(mdates.DayLocator(interval=2))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))

Also confirm that the start column uses pd.to_datetime().

Tasks Appear in the Wrong Order

Problem: Horizontal bars may display the first task at the bottom.

Solution: Reverse the y-axis:

ax.invert_yaxis()

Plotly charts typically need the equivalent setting:

fig.update_yaxes(autorange="reversed")

Bars Do Not Match Their Durations

Problem: The chart may use an end date as the bar width, producing inconsistent results.

Solution: Use duration for Matplotlib width:

ax.barh(
    y=index,
    width=row["duration"],
    left=row["start"]
)

Use start and finish values separately with Plotly’s timeline function.

Overlapping Tasks Hide Each Other

Problem: Multiple tasks on one row can become difficult to read.

Solution: Give every task its own y-axis position. Grouping by phase can still work, provided each bar receives enough vertical space.

The Chart Becomes Too Crowded

Problem: Long plans with many tasks create unreadable labels and overlapping annotations.

Solution: Split the view by phase, team, or quarter. You can also show only milestones in an executive view and keep task-level detail elsewhere.

FAQs

Which Python library is best for a Gantt chart?

Matplotlib is a strong choice for static charts, reports, and custom styling. Plotly is better when you need hover details, zooming, and interactive exploration. Start with Matplotlib if your goal is learning the chart structure. Choose Plotly when other people need to inspect task details during a review.

Can Python create a Gantt chart without Pandas?

Yes. You can use Python’s built-in date tools with Matplotlib. Pandas makes date conversion and duration calculations easier, especially when many tasks are involved. For a tiny example with three tasks, a list and datetime may be enough. Larger planning workflows benefit from Pandas’ filtering and date features.

How do I show weekends in a Python timeline?

Calendar-day durations naturally include weekends. To skip weekends, calculate task endings with Pandas business-day offsets or a custom calendar. A five-day task starting Monday can finish Friday when business-day rules apply. Confirm whether your planning team counts the start date and how it handles public holidays.

How can I add task dependencies?

Store predecessor and successor pairs, then draw arrows between the related bars. With Matplotlib, ax.annotate() can connect the predecessor’s ending point to the successor’s starting point. Keep arrows limited to important relationships. Too many connections can make a chart harder to understand than the original task list.

Can I export the chart for a presentation?

Yes. Matplotlib can save a rendered chart with plt.savefig(). Use a high resolution and a suitable format, such as PNG or SVG. Plotly can produce interactive HTML output, which works well for browser-based reviews. Test the exported result before sharing it because long labels may need extra margins.

Conclusion

A Python Gantt chart turns project timing into a visual schedule. Start with task names, start dates, and durations, then add status colors, progress, dependencies, and milestones.

Use Matplotlib for precise static visuals and Plotly for interactive exploration. Keep date scales readable, account for business days, and avoid packing too much detail into one view.

The original problem is schedule confusion. The pressure comes from hidden delays and unclear dependencies. The practical solution is a timeline that makes timing visible and a workflow that keeps project information current.

Once your first chart works, improve it gradually. Add progress overlays, milestone markers, and dependency arrows only when they help someone make a better project decision.

Top comments (0)