You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建带有边框标题占位区域的QTextEdit控件

实现类似Gmail的带标题占位QTextEdit控件

我需要创建一个带有标题(caption)占位区域的QTextEdit控件,样式参考Gmail的输入框效果。目前已计算出标题前后顶部边框的起止位置,但不知道如何清除标题区域的边框;尝试重写paintEvent绘制边框时,出现边框粗细异常的问题,同时不清楚如何实现圆角边框。


现有代码

textedit.h

class TextEdit : public QTextEdit
{
    Q_OBJECT

public:
    QLabel* caption;
    TextEdit(QWidget* parent = 0) : QTextEdit(parent)
    {
        caption = new QLabel(this);
        caption->setText("Email or phone");
        
        caption->setStyleSheet(R"(
                font: 14px;
                font-weight: 900;
                color: #3762ab;
                
                border-radius: 4px;
                background-color: rgba(0, 0, 0, 30);
                margin-left: 32px;
        )");
        
        caption->adjustSize();

        auto caption_rect = caption->contentsRect();

        int caption_x = caption_rect.x();
        int caption_width = caption_rect.x() + caption_rect.width();

        qDebug() << "Position where the left border should end:\n" 
            << caption_x << "\n";
        qDebug() << "Position where the border start after the caption:\n" 
            << caption_width << "\n";
    }
};

MainWindow代码

#include "textedit.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    
    QTextEdit* textEdit = new TextEdit(this);

    ui.centralWidget->setStyleSheet("#centralWidget { background-color: #121212; }");
    textEdit->setStyleSheet(R"(
        QTextEdit
        {
            border-radius: 4px;
            border: 3px solid #3762ab; 
            background-color: transparent;
            margin-top: 10px;
        }
    )");

    textEdit->setGeometry(100, 50, 300, 70);
}

重写的paintEvent代码

void paintEvent(QPaintEvent* event)
{
    QTextEdit::paintEvent(event);
    QPainter painter(viewport());

    int borderWidth = 4;

    QPen pen;
    pen.setWidth(borderWidth);
    painter.setRenderHint(QPainter::Antialiasing, true);

    QPoint center = viewport()->rect().center();
    auto r = this->contentsRect();

    // Top border
    pen.setColor(Qt::red);
    painter.setPen(pen);
    painter.drawLine(QLine(0, 0, caption_x, borderWidth));
    painter.drawLine(QLine(caption_width, 0, r.width(), borderWidth));
    
    // Left border
    pen.setColor(Qt::white);
    painter.setPen(pen);
    painter.drawLine(QLine(0, 0, borderWidth, r.height()));

    // Bottom border
    pen.setColor(Qt::green);
    painter.setPen(pen);
    painter.drawLine(QLine(0, r.height() - borderWidth, r.width(), r.height()));

    // Right border
    pen.setColor(Qt::blue);
    painter.setPen(pen);
    painter.drawLine(QLine(r.width() - borderWidth, 0, r.width(), r.height()));
}

问题修复与实现方案

核心问题解析

  1. 边框粗细异常:直接设置画笔宽度会导致线条向两侧扩展,加上未正确计算绘制区域,导致边框超出控件或显示粗细不均。
  2. 标题区域边框清除:原代码未处理标题覆盖区域的边框,可通过将标题背景设为和父控件一致,或绘制边框时避开标题区域解决。
  3. 圆角边框实现:需要使用QPainterPath绘制带圆角的矩形路径,再对顶部路径进行截断。

修正后的完整代码

textedit.h

#include <QTextEdit>
#include <QLabel>
#include <QPainter>
#include <QPainterPath>

class TextEdit : public QTextEdit
{
    Q_OBJECT

public:
    QLabel* caption;
    int captionStartX;
    int captionEndX;

    TextEdit(QWidget* parent = nullptr) : QTextEdit(parent)
    {
        caption = new QLabel(this);
        caption->setText("Email or phone");
        
        // 设置标题背景与父控件一致,覆盖下方边框
        caption->setStyleSheet(R"(
            font: 14px;
            font-weight: 900;
            color: #3762ab;
            background-color: #121212;
            padding: 0 4px;
            margin-left: 32px;
        )");
        
        caption->adjustSize();
        // 计算标题在控件内的起止坐标
        QPoint captionPos = caption->mapTo(this, QPoint(0, 0));
        captionStartX = captionPos.x();
        captionEndX = captionPos.x() + caption->width();

        // 关闭默认边框,避免与自定义绘制冲突
        this->setStyleSheet(R"(
            QTextEdit {
                background-color: transparent;
                padding: 10px 8px 8px 8px;
                border: none;
            }
        )");
    }

protected:
    void paintEvent(QPaintEvent* event) override
    {
        QTextEdit::paintEvent(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing, true);

        int borderWidth = 3;
        int borderRadius = 4;
        QColor borderColor = QColor("#3762ab");

        // 调整画笔样式,避免线条粗细异常
        QPen pen(borderColor, borderWidth);
        pen.setCapStyle(Qt::FlatCap);
        pen.setJoinStyle(Qt::RoundJoin);
        painter.setPen(pen);

        // 计算有效绘制区域,避免线条超出控件边界
        QRectF drawRect = this->rect().adjusted(
            borderWidth / 2.0, 
            borderWidth / 2.0, 
            -borderWidth / 2.0, 
            -borderWidth / 2.0
        );

        // 绘制带圆角的完整边框路径
        QPainterPath fullPath;
        fullPath.addRoundedRect(drawRect, borderRadius, borderRadius);
        
        // 绘制左侧、底部、右侧边框
        painter.drawPath(fullPath);

        // 绘制顶部的两段边框,避开标题区域
        painter.drawLine(QLineF(drawRect.left(), drawRect.top(), captionStartX - 4, drawRect.top()));
        painter.drawLine(QLineF(captionEndX + 4, drawRect.top(), drawRect.right(), drawRect.top()));
    }
};

MainWindow代码

#include "textedit.h"
#include <QMainWindow>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    
    TextEdit* textEdit = new TextEdit(this);

    ui.centralWidget->setStyleSheet("#centralWidget { background-color: #121212; }");
    textEdit->setGeometry(100, 50, 300, 70);
    // 确保标题显示在最上层
    textEdit->caption->raise();
}

关键说明

  • 标题背景覆盖:将标题背景色设为与父控件一致,直接覆盖下方边框,无需复杂的裁剪操作。
  • 画笔样式调整:FlatCap确保线条端点平整,RoundJoin让边框转角更圆润,避免出现锯齿。
  • 绘制区域计算:通过adjusted方法缩小绘制区域,防止边框线条超出控件范围。
  • 圆角边框实现:使用QPainterPath::addRoundedRect创建圆角路径,再单独绘制顶部的两段线条,实现Gmail式的标题占位效果。

内容的提问来源于stack exchange,提问作者Cesar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:31:02