QCalendarWidget in PyQt6

While I was working on a desktop application that needed to allow users to select dates for event scheduling. The PyQt6 QCalendarWidget was the perfect solution for this task.

In this article, I will walk you through everything you need to know about implementing and customizing the QCalendarWidget in your PyQt6 applications.

So let’s get in!

Basic Implementation of QCalendarWidget

Let’s start with the simplest implementation of a QCalendarWidget in Python PyQt6:

import sys
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QCalendarWidget
from PyQt6.QtCore import QDate

class CalendarApp(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Simple Calendar")
        self.setGeometry(300, 300, 400, 300)

        # Create a calendar widget
        self.calendar = QCalendarWidget()

        # Set today's date as selected
        self.calendar.setSelectedDate(QDate.currentDate())

        # Connect signal for date selection
        self.calendar.selectionChanged.connect(self.on_date_selected)

        # Create layout and add calendar
        layout = QVBoxLayout()
        layout.addWidget(self.calendar)
        self.setLayout(layout)

    def on_date_selected(self):
        selected_date = self.calendar.selectedDate()
        print(f"Selected date: {selected_date.toString('MM/dd/yyyy')}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = CalendarApp()
    window.show()
    sys.exit(app.exec())

I executed the above example code and added the screenshot below.

QCalendarWidget in PyQt6

This code creates a simple window with a calendar widget. When you click on a date, it will print the selected date in the console in MM/dd/yyyy format.

Customize the Calendar’s Appearance

One of the great things about QCalendarWidget in PyQt6 is how customizable it is. Let’s look at some common ways to customize the appearance:

# Set the first day of the week to Monday
self.calendar.setFirstDayOfWeek(1)  # Qt.Monday is 1

# Set the grid visible
self.calendar.setGridVisible(True)

# Set the date format
self.calendar.setDateFormat("yyyy-MM-dd")

# Customize colors
from PyQt6.QtGui import QColor, QTextCharFormat
from PyQt6.QtCore import Qt

fmt = QTextCharFormat()
fmt.setBackground(QColor(0, 255, 0, 50))  # Light green with some transparency
fmt.setForeground(QColor(0, 0, 255))  # Blue text

# Apply this format to a specific date
special_date = QDate(2023, 11, 15)
self.calendar.setDateTextFormat(special_date, fmt)

# Change header format
header_fmt = QTextCharFormat()
header_fmt.setBackground(QColor(100, 150, 250))
header_fmt.setForeground(Qt.GlobalColor.white)
self.calendar.setHeaderTextFormat(header_fmt)

I executed the above example code and added the screenshot below.

Create QCalendarWidget PyQt6

These customizations allow you to change the appearance of your calendar to match your application’s design.

Handle Calendar Events and Signals

Python QCalendarWidget emits signals when the user interacts with it. Here are the most common signals and how to connect them in PyQt6:

# When the selected date changes
self.calendar.selectionChanged.connect(self.on_date_selected)

# When the user clicks on a date
self.calendar.clicked.connect(self.on_date_clicked)

# When the user double-clicks on a date
self.calendar.activated.connect(self.on_date_activated)

# When the current page (month/year) changes
self.calendar.currentPageChanged.connect(self.on_page_changed)

def on_date_selected(self):
    selected_date = self.calendar.selectedDate()
    print(f"Date selected: {selected_date.toString()}")

def on_date_clicked(self, date):
    print(f"Date clicked: {date.toString()}")

def on_date_activated(self, date):
    print(f"Date activated (double-clicked): {date.toString()}")

def on_page_changed(self, year, month):
    print(f"Calendar page changed to: {month}/{year}")

I executed the above example code and added the screenshot below.

How to QCalendarWidget in PyQt6

These signals make it easy to respond to user interactions with the calendar.

Read Create a Basic Window in PyQt6

Restrict Date Selection

In many applications, you might want to limit which dates a user can select. For example, in a booking system, you might want to prevent users from selecting dates in the past:

# Set minimum date to today (can't select dates before today)
self.calendar.setMinimumDate(QDate.currentDate())

# Set maximum date to 1 year from now
max_date = QDate.currentDate().addYears(1)
self.calendar.setMaximumDate(max_date)

# Disable specific dates (e.g., holidays)
def update_disabled_dates(self):
    # Get the format for disabled dates
    fmt = QTextCharFormat()
    fmt.setForeground(QColor(200, 200, 200))  # Light gray
    fmt.setBackground(QColor(240, 240, 240))  # Even lighter gray

    # List of holidays or unavailable dates
    holidays = [
        QDate(2023, 12, 25),  # Christmas
        QDate(2023, 11, 23),  # Thanksgiving
        QDate(2023, 7, 4),    # Independence Day
    ]

    # Apply the format to each holiday
    for holiday in holidays:
        self.calendar.setDateTextFormat(holiday, fmt)

This makes it clear to users which dates they can and cannot select.

Check out Create Dynamic Tables with QTableWidget in PyQt6

Integrate with Other Widgets

QCalendarWidget works well with other widgets. Here’s an example of integrating it with QLineEdit to create a date picker:

from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QCalendarWidget, QLineEdit, QPushButton
from PyQt6.QtCore import QDate

class DatePickerApp(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Date Picker")
        self.setGeometry(300, 300, 400, 400)

        # Create a line edit for displaying the selected date
        self.date_edit = QLineEdit()
        self.date_edit.setReadOnly(True)
        self.date_edit.setText(QDate.currentDate().toString("MM/dd/yyyy"))

        # Create a button to show/hide the calendar
        self.pick_button = QPushButton("Pick a date")
        self.pick_button.clicked.connect(self.toggle_calendar)

        # Create a calendar widget
        self.calendar = QCalendarWidget()
        self.calendar.setSelectedDate(QDate.currentDate())
        self.calendar.clicked.connect(self.update_date)
        self.calendar.setVisible(False)  # Hidden by default

        # Create layout
        layout = QVBoxLayout()
        layout.addWidget(self.date_edit)
        layout.addWidget(self.pick_button)
        layout.addWidget(self.calendar)
        self.setLayout(layout)

    def toggle_calendar(self):
        self.calendar.setVisible(not self.calendar.isVisible())
        if self.calendar.isVisible():
            self.pick_button.setText("Hide calendar")
        else:
            self.pick_button.setText("Pick a date")

    def update_date(self, date):
        self.date_edit.setText(date.toString("MM/dd/yyyy"))
        self.calendar.setVisible(False)
        self.pick_button.setText("Pick a date")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = DatePickerApp()
    window.show()
    sys.exit(app.exec())

This creates a clean date picker component that shows and hides the calendar as needed.

Read Use QListWidget in PyQt6

Practical Examples

Let’s look at a more practical example: an appointment scheduling application for a dental office in New York:

import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QCalendarWidget, 
                             QVBoxLayout, QHBoxLayout, QWidget, QLabel, 
                             QPushButton, QComboBox, QMessageBox, QTimeEdit)
from PyQt6.QtCore import QDate, QTime, Qt
from PyQt6.QtGui import QTextCharFormat, QColor

class DentalAppointmentScheduler(QMainWindow):
    def __init__(self):
        super().__init__()
        
        self.setWindowTitle("NYC Dental Clinic - Appointment Scheduler")
        self.setGeometry(100, 100, 800, 600)
        
        # Main widget and layout
        main_widget = QWidget()
        main_layout = QHBoxLayout()
        
        # Calendar section
        calendar_layout = QVBoxLayout()
        calendar_label = QLabel("Select Appointment Date:")
        self.calendar = QCalendarWidget()
        self.calendar.setGridVisible(True)
        self.calendar.setFirstDayOfWeek(Qt.DayOfWeek.Monday)
        
        # Set dates - clinic not open on weekends
        self.update_calendar_availability()
        
        # Time selection
        time_layout = QVBoxLayout()
        time_label = QLabel("Select Appointment Time:")
        self.time_selector = QTimeEdit()
        self.time_selector.setDisplayFormat("hh:mm AP")
        self.time_selector.setTime(QTime(9, 0))  # 9:00 AM default
        
        # Dentist selection
        dentist_label = QLabel("Select Dentist:")
        self.dentist_selector = QComboBox()
        self.dentist_selector.addItems([
            "Dr. Smith - General Dentistry",
            "Dr. Johnson - Orthodontics",
            "Dr. Williams - Pediatric Dentistry",
            "Dr. Brown - Cosmetic Dentistry"
        ])
        
        # Book button
        self.book_button = QPushButton("Book Appointment")
        self.book_button.clicked.connect(self.book_appointment)
        
        # Add widgets to layouts
        calendar_layout.addWidget(calendar_label)
        calendar_layout.addWidget(self.calendar)
        
        time_layout.addWidget(time_label)
        time_layout.addWidget(self.time_selector)
        time_layout.addWidget(dentist_label)
        time_layout.addWidget(self.dentist_selector)
        time_layout.addWidget(self.book_button)
        time_layout.addStretch()
        
        # Add layouts to main layout
        main_layout.addLayout(calendar_layout, 2)
        main_layout.addLayout(time_layout, 1)
        
        # Set main layout
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)

    def update_calendar_availability(self):
        today = QDate.currentDate()
        self.calendar.setMinimumDate(today)
        max_date = today.addMonths(3)
        self.calendar.setMaximumDate(max_date)

        # Disable weekends visually
        weekend_format = QTextCharFormat()
        weekend_format.setForeground(QColor("lightgray"))

        current_date = QDate(today)
        while current_date <= max_date:
            if current_date.dayOfWeek() in [Qt.DayOfWeek.Saturday, Qt.DayOfWeek.Sunday]:
                self.calendar.setDateTextFormat(current_date, weekend_format)
            current_date = current_date.addDays(1)

    def book_appointment(self):
        selected_date = self.calendar.selectedDate()
        selected_time = self.time_selector.time().toString("hh:mm AP")
        selected_dentist = self.dentist_selector.currentText()

        if selected_date.dayOfWeek() in [Qt.DayOfWeek.Saturday, Qt.DayOfWeek.Sunday]:
            QMessageBox.warning(self, "Unavailable", "Appointments cannot be booked on weekends.")
            return

        QMessageBox.information(
            self,
            "Appointment Confirmed",
            f"Your appointment is booked for:\n"
            f"Date: {selected_date.toString('dddd, MMMM d, yyyy')}\n"
            f"Time: {selected_time}\n"
            f"Dentist: {selected_dentist}"
        )

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = DentalAppointmentScheduler()
    window.show()
    sys.exit(app.exec())

This dental appointment scheduler demonstrates the practical use of QCalendarWidget, QTimeEdit, and user input widgets in PyQt6. It provides an intuitive way to book appointments while enforcing scheduling rules like weekday availability.

In this QCalendarWidget in PyQt6 tutorial, I explained about basic implementation of QCalendarWidget, customizing the appearance, handling events and signals, integrating with another widget, and a practical example.

You may read:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.