如何创建带有边框标题占位区域的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())); }
问题修复与实现方案
核心问题解析
- 边框粗细异常:直接设置画笔宽度会导致线条向两侧扩展,加上未正确计算绘制区域,导致边框超出控件或显示粗细不均。
- 标题区域边框清除:原代码未处理标题覆盖区域的边框,可通过将标题背景设为和父控件一致,或绘制边框时避开标题区域解决。
- 圆角边框实现:需要使用
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
相关产品推荐
相关产品推荐

