Custom Selection Area Rendering for QTableView

Implementation Overview

This article demonstrates how to implement custom selection area rendering for Qt's QTableView component. While Qt provides built-in selection functionality, it lacks customization options for border colors and visual details. The solution involves custom painting techniques to achieve Excel-like selection visuals.

Visual Results

The implementation produces a clean selection rectangle with customizable borders that highlight selected cell ranges effectively.

Technical Approach

The solution addresses three key aspects: determining the selection area, calculating border requirements, and implementing the rendering mechanism.

Selection Area Detection

Qt's selection model provides the necessary signals to track selection changes. The QItemSelectionModel::selectionChanged signal is connected to handle selection updates:

connect(tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
        this, &CustomTableWidget::handleSelectionChange);

Border Calculation Logic

For each selected cell, the algorithm determines which borders should be drawn by checking adjacent cells:

void CustomTableWidget::handleSelectionChange(const QItemSelection &selected, 
                                            const QItemSelection &deselected)
{
    QModelIndexList selectedIndices = tableView->selectionModel()->selectedIndexes();
    
    int rowCount = model()->rowCount();
    int columnCount = model()->columnCount();
    
    // Create selection grid
    QVector<qvector>> selectionGrid(rowCount, QVector<bool>(columnCount, false));
    
    foreach (const QModelIndex &index, selectedIndices) {
        selectionGrid[index.row()][index.column()] = true;
    }
    
    QMap<qmodelindex borderflags=""> borderData;
    foreach (const QModelIndex &index, selectedIndices) {
        BorderFlags flags = NoBorder;
        
        // Check top border
        if (index.row() == 0 || !selectionGrid[index.row()-1][index.column()]) {
            flags |= TopBorder;
        }
        
        // Check right border
        if (index.column() == columnCount-1 || !selectionGrid[index.row()][index.column()+1]) {
            flags |= RightBorder;
        }
        
        // Check bottom border
        if (index.row() == rowCount-1 || !selectionGrid[index.row()+1][index.column()]) {
            flags |= BottomBorder;
        }
        
        // Check left border
        if (index.column() == 0 || !selectionGrid[index.row()][index.column()-1]) {
            flags |= LeftBorder;
        }
        
        borderData[index] = flags;
    }
    
    updateTableView(borderData);
}</qmodelindex></bool></qvector>

Rendering Implementation

The rendering is split between a custom delegate for selection highlighting and viewport painting for borders.

Selection Highlighting

A custom QStyledItemDelegate handles selection area filling:

void SelectionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                             const QModelIndex &index) const
{
    QStyledItemDelegate::paint(painter, option, index);
    
    if (selectedCells.contains(index)) {
        painter->save();
        painter->fillRect(option.rect, QColor(100, 0, 0, 100));
        painter->restore();
    }
}

Border Drawing

Custom border rendering occurs in the table view's paintEvent after calling the base implementation:

void CustomTableView::paintEvent(QPaintEvent *event)
{
    QTableView::paintEvent(event);
    
    QPainter painter(viewport());
    painter.setRenderHint(QPainter::Antialiasing);
    
    QPen borderPen(Qt::blue);
    borderPen.setWidth(2);
    painter.setPen(borderPen);
    
    foreach (const QModelIndex &index, borderCells.keys()) {
        BorderFlags flags = borderCells.value(index);
        QRect cellRect = visualRect(index);
        
        if (flags & TopBorder) {
            painter.drawLine(cellRect.topLeft(), cellRect.topRight());
        }
        if (flags & RightBorder) {
            painter.drawLine(cellRect.topRight(), cellRect.bottomRight());
        }
        if (flags & BottomBorder) {
            painter.drawLine(cellRect.bottomLeft(), cellRect.bottomRight());
        }
        if (flags & LeftBorder) {
            painter.drawLine(cellRect.topLeft(), cellRect.bottomLeft());
        }
    }
}

This approach ensures that selection borders are drawn as clean lines around the selected area while maintaining Qt's standard rendering for other table elements.

Tags: QTableView Qt CustomPainting SelectionRendering ItemDelegate

Posted on Sat, 03 Oct 2026 16:11:46 +0000 by trink