C++ 如何将行号添加到QTextEdit?
我正在编写一个Visual Basic IDE,需要将行号添加到QTextEdit并突出显示当前行。我已经找到了,但是它是用java编写的,我用C++编写了我的项目。 < p>这里是C++中的等效教程: Qt4:C++ 如何将行号添加到QTextEdit?,c++,ide,qt4,C++,Ide,Qt4,我正在编写一个Visual Basic IDE,需要将行号添加到QTextEdit并突出显示当前行。我已经找到了,但是它是用java编写的,我用C++编写了我的项目。 < p>这里是C++中的等效教程: Qt4: Qt5:我知道Qt教程建议使用QPlainTextEdit实现文本编辑器,并且这个问题(除了标题中提到的以外)比处理QTextEdit小部件(绝对)更一般,但是我成功地实现了这个行为(行号+当前行号突出显示),我认为这对一些人(比如我)可能会有帮助,他们真的希望继续使用富文本小部件,并
Qt5:我知道Qt教程建议使用
QPlainTextEdit
实现文本编辑器,并且这个问题(除了标题中提到的以外)比处理QTextEdit
小部件(绝对)更一般,但是我成功地实现了这个行为(行号+当前行号突出显示),我认为这对一些人(比如我)可能会有帮助,他们真的希望继续使用富文本
小部件,并希望分享我的实现(这远不是完美的-非常快的编码…)
行号rea.h:(与“Qplainntextedit”教程相同)
LineNumberArea.cpp:(与“QPlainTextEdit”教程相同)
>qtextedithighlighter.h:
class QTextEditHighlighter : public QTextEdit
{
Q_OBJECT
public:
explicit QTextEditHighlighter(QWidget *parent = 0);
int getFirstVisibleBlockId();
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
signals:
public slots:
void resizeEvent(QResizeEvent *e);
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void updateLineNumberArea(QRectF /*rect_f*/);
void updateLineNumberArea(int /*slider_pos*/);
void updateLineNumberArea();
private:
QWidget *lineNumberArea;
};
#include "qtextedithighlighter.h"
QTextEditHighlighter::QTextEditHighlighter(QWidget *parent) :
QTextEdit(parent)
{
// Line numbers
lineNumberArea = new LineNumberArea(this);
///
connect(this->document(), SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateLineNumberArea/*_2*/(int)));
connect(this, SIGNAL(textChanged()), this, SLOT(updateLineNumberArea()));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateLineNumberArea()));
///
updateLineNumberAreaWidth(0);
}
int QTextEditHighlighter::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, this->document()->blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
int space = 13 + fontMetrics().width(QLatin1Char('9')) * (digits);
return space;
}
void QTextEditHighlighter::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void QTextEditHighlighter::updateLineNumberArea(QRectF /*rect_f*/)
{
QTextEditHighlighter::updateLineNumberArea();
}
void QTextEditHighlighter::updateLineNumberArea(int /*slider_pos*/)
{
QTextEditHighlighter::updateLineNumberArea();
}
void QTextEditHighlighter::updateLineNumberArea()
{
/*
* When the signal is emitted, the sliderPosition has been adjusted according to the action,
* but the value has not yet been propagated (meaning the valueChanged() signal was not yet emitted),
* and the visual display has not been updated. In slots connected to this signal you can thus safely
* adjust any action by calling setSliderPosition() yourself, based on both the action and the
* slider's value.
*/
// Make sure the sliderPosition triggers one last time the valueChanged() signal with the actual value !!!!
this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
// Since "QTextEdit" does not have an "updateRequest(...)" signal, we chose
// to grab the imformations from "sliderPosition()" and "contentsRect()".
// See the necessary connections used (Class constructor implementation part).
QRect rect = this->contentsRect();
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
updateLineNumberAreaWidth(0);
//----------
int dy = this->verticalScrollBar()->sliderPosition();
if (dy > -1) {
lineNumberArea->scroll(0, dy);
}
// Addjust slider to alway see the number of the currently being edited line...
int first_block_id = getFirstVisibleBlockId();
if (first_block_id == 0 || this->textCursor().block().blockNumber() == first_block_id-1)
this->verticalScrollBar()->setSliderPosition(dy-this->document()->documentMargin());
// // Snap to first line (TODO...)
// if (first_block_id > 0)
// {
// int slider_pos = this->verticalScrollBar()->sliderPosition();
// int prev_block_height = (int) this->document()->documentLayout()->blockBoundingRect(this->document()->findBlockByNumber(first_block_id-1)).height();
// if (dy <= this->document()->documentMargin() + prev_block_height)
// this->verticalScrollBar()->setSliderPosition(slider_pos - (this->document()->documentMargin() + prev_block_height));
// }
}
void QTextEditHighlighter::resizeEvent(QResizeEvent *e)
{
QTextEdit::resizeEvent(e);
QRect cr = this->contentsRect();
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
int QTextEditHighlighter::getFirstVisibleBlockId()
{
// Detect the first block for which bounding rect - once translated
// in absolute coordinated - is contained by the editor's text area
// Costly way of doing but since "blockBoundingGeometry(...)" doesn't
// exists for "QTextEdit"...
QTextCursor curs = QTextCursor(this->document());
curs.movePosition(QTextCursor::Start);
for(int i=0; i < this->document()->blockCount(); ++i)
{
QTextBlock block = curs.block();
QRect r1 = this->viewport()->geometry();
QRect r2 = this->document()->documentLayout()->blockBoundingRect(block).translated(
this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
this->verticalScrollBar()->sliderPosition()
) ).toRect();
if (r1.contains(r2, true)) { return i; }
curs.movePosition(QTextCursor::NextBlock);
}
return 0;
}
void QTextEditHighlighter::lineNumberAreaPaintEvent(QPaintEvent *event)
{
this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), Qt::lightGray);
int blockNumber = this->getFirstVisibleBlockId();
QTextBlock block = this->document()->findBlockByNumber(blockNumber);
QTextBlock prev_block = (blockNumber > 0) ? this->document()->findBlockByNumber(blockNumber-1) : block;
int translate_y = (blockNumber > 0) ? -this->verticalScrollBar()->sliderPosition() : 0;
int top = this->viewport()->geometry().top();
// Adjust text position according to the previous "non entirely visible" block
// if applicable. Also takes in consideration the document's margin offset.
int additional_margin;
if (blockNumber == 0)
// Simply adjust to document's margin
additional_margin = (int) this->document()->documentMargin() -1 - this->verticalScrollBar()->sliderPosition();
else
// Getting the height of the visible part of the previous "non entirely visible" block
additional_margin = (int) this->document()->documentLayout()->blockBoundingRect(prev_block)
.translated(0, translate_y).intersect(this->viewport()->geometry()).height();
// Shift the starting point
top += additional_margin;
int bottom = top + (int) this->document()->documentLayout()->blockBoundingRect(block).height();
QColor col_1(90, 255, 30); // Current line (custom green)
QColor col_0(120, 120, 120); // Other lines (custom darkgrey)
// Draw the numbers (displaying the current line number in green)
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(QColor(120, 120, 120));
painter.setPen((this->textCursor().blockNumber() == blockNumber) ? col_1 : col_0);
painter.drawText(-5, top,
lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) this->document()->documentLayout()->blockBoundingRect(block).height();
++blockNumber;
}
}
>qtextedhighlighter.cpp:
class QTextEditHighlighter : public QTextEdit
{
Q_OBJECT
public:
explicit QTextEditHighlighter(QWidget *parent = 0);
int getFirstVisibleBlockId();
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
signals:
public slots:
void resizeEvent(QResizeEvent *e);
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void updateLineNumberArea(QRectF /*rect_f*/);
void updateLineNumberArea(int /*slider_pos*/);
void updateLineNumberArea();
private:
QWidget *lineNumberArea;
};
#include "qtextedithighlighter.h"
QTextEditHighlighter::QTextEditHighlighter(QWidget *parent) :
QTextEdit(parent)
{
// Line numbers
lineNumberArea = new LineNumberArea(this);
///
connect(this->document(), SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
connect(this->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateLineNumberArea/*_2*/(int)));
connect(this, SIGNAL(textChanged()), this, SLOT(updateLineNumberArea()));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateLineNumberArea()));
///
updateLineNumberAreaWidth(0);
}
int QTextEditHighlighter::lineNumberAreaWidth()
{
int digits = 1;
int max = qMax(1, this->document()->blockCount());
while (max >= 10) {
max /= 10;
++digits;
}
int space = 13 + fontMetrics().width(QLatin1Char('9')) * (digits);
return space;
}
void QTextEditHighlighter::updateLineNumberAreaWidth(int /* newBlockCount */)
{
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}
void QTextEditHighlighter::updateLineNumberArea(QRectF /*rect_f*/)
{
QTextEditHighlighter::updateLineNumberArea();
}
void QTextEditHighlighter::updateLineNumberArea(int /*slider_pos*/)
{
QTextEditHighlighter::updateLineNumberArea();
}
void QTextEditHighlighter::updateLineNumberArea()
{
/*
* When the signal is emitted, the sliderPosition has been adjusted according to the action,
* but the value has not yet been propagated (meaning the valueChanged() signal was not yet emitted),
* and the visual display has not been updated. In slots connected to this signal you can thus safely
* adjust any action by calling setSliderPosition() yourself, based on both the action and the
* slider's value.
*/
// Make sure the sliderPosition triggers one last time the valueChanged() signal with the actual value !!!!
this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
// Since "QTextEdit" does not have an "updateRequest(...)" signal, we chose
// to grab the imformations from "sliderPosition()" and "contentsRect()".
// See the necessary connections used (Class constructor implementation part).
QRect rect = this->contentsRect();
lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
updateLineNumberAreaWidth(0);
//----------
int dy = this->verticalScrollBar()->sliderPosition();
if (dy > -1) {
lineNumberArea->scroll(0, dy);
}
// Addjust slider to alway see the number of the currently being edited line...
int first_block_id = getFirstVisibleBlockId();
if (first_block_id == 0 || this->textCursor().block().blockNumber() == first_block_id-1)
this->verticalScrollBar()->setSliderPosition(dy-this->document()->documentMargin());
// // Snap to first line (TODO...)
// if (first_block_id > 0)
// {
// int slider_pos = this->verticalScrollBar()->sliderPosition();
// int prev_block_height = (int) this->document()->documentLayout()->blockBoundingRect(this->document()->findBlockByNumber(first_block_id-1)).height();
// if (dy <= this->document()->documentMargin() + prev_block_height)
// this->verticalScrollBar()->setSliderPosition(slider_pos - (this->document()->documentMargin() + prev_block_height));
// }
}
void QTextEditHighlighter::resizeEvent(QResizeEvent *e)
{
QTextEdit::resizeEvent(e);
QRect cr = this->contentsRect();
lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
int QTextEditHighlighter::getFirstVisibleBlockId()
{
// Detect the first block for which bounding rect - once translated
// in absolute coordinated - is contained by the editor's text area
// Costly way of doing but since "blockBoundingGeometry(...)" doesn't
// exists for "QTextEdit"...
QTextCursor curs = QTextCursor(this->document());
curs.movePosition(QTextCursor::Start);
for(int i=0; i < this->document()->blockCount(); ++i)
{
QTextBlock block = curs.block();
QRect r1 = this->viewport()->geometry();
QRect r2 = this->document()->documentLayout()->blockBoundingRect(block).translated(
this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
this->verticalScrollBar()->sliderPosition()
) ).toRect();
if (r1.contains(r2, true)) { return i; }
curs.movePosition(QTextCursor::NextBlock);
}
return 0;
}
void QTextEditHighlighter::lineNumberAreaPaintEvent(QPaintEvent *event)
{
this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
QPainter painter(lineNumberArea);
painter.fillRect(event->rect(), Qt::lightGray);
int blockNumber = this->getFirstVisibleBlockId();
QTextBlock block = this->document()->findBlockByNumber(blockNumber);
QTextBlock prev_block = (blockNumber > 0) ? this->document()->findBlockByNumber(blockNumber-1) : block;
int translate_y = (blockNumber > 0) ? -this->verticalScrollBar()->sliderPosition() : 0;
int top = this->viewport()->geometry().top();
// Adjust text position according to the previous "non entirely visible" block
// if applicable. Also takes in consideration the document's margin offset.
int additional_margin;
if (blockNumber == 0)
// Simply adjust to document's margin
additional_margin = (int) this->document()->documentMargin() -1 - this->verticalScrollBar()->sliderPosition();
else
// Getting the height of the visible part of the previous "non entirely visible" block
additional_margin = (int) this->document()->documentLayout()->blockBoundingRect(prev_block)
.translated(0, translate_y).intersect(this->viewport()->geometry()).height();
// Shift the starting point
top += additional_margin;
int bottom = top + (int) this->document()->documentLayout()->blockBoundingRect(block).height();
QColor col_1(90, 255, 30); // Current line (custom green)
QColor col_0(120, 120, 120); // Other lines (custom darkgrey)
// Draw the numbers (displaying the current line number in green)
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
QString number = QString::number(blockNumber + 1);
painter.setPen(QColor(120, 120, 120));
painter.setPen((this->textCursor().blockNumber() == blockNumber) ? col_1 : col_0);
painter.drawText(-5, top,
lineNumberArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int) this->document()->documentLayout()->blockBoundingRect(block).height();
++blockNumber;
}
}
#包括“qtextedhighlighter.h”
QTextEditHighlighter::QTextEditHighlighter(QWidget*父项):
QTextEdit(父级)
{
//行号
lineNumberArea=新的lineNumberArea(本);
///
连接(此->文档(),信号(blockCountChanged(int)),此,插槽(updateLineNumberAreaWidth(int));
连接(this->verticalScrollBar(),信号(valueChanged(int)),this,插槽(updateLineNumberArea/*_2*/(int));
连接(此,信号(textChanged()),此,插槽(updateLineNumberArea());
连接(此,信号(cursorPositionChanged()),此,插槽(updateLineNumberArea());
///
updateLineNumberAreaWidth(0);
}
int QTextEditHighlighter::lineNumberEAWidth()
{
整数位数=1;
int max=qMax(1,this->document()->blockCount());
而(最大值>=10){
最大/=10;
++数字;
}
int space=13+fontMetrics().width(QLatin1Char('9'))*(数字);
返回空间;
}
void QTextEditHighlighter::updateLineNumberAreaWidth(int/*newBlockCount*/)
{
setViewportMargins(lineNumberEAWidth(),0,0,0);
}
void QTextEditHighlighter::updateLineNumberArea(QRectF/*rect\u f*/)
{
QTextEditHighlighter::updateLineNumberArea();
}
void QTextEditHighlighter::updateLineNumberArea(int/*滑块位置*/)
{
QTextEditHighlighter::updateLineNumberArea();
}
void QTextEditHighlighter::updateLineNumberArea()
{
/*
*当信号发出时,滑块位置已根据动作进行调整,
*但该值尚未传播(意味着valueChanged()信号尚未发出),
*并且视觉显示尚未更新。因此,在连接到该信号的插槽中,您可以安全地
*通过自己调用setSliderPosition()来调整任何操作,方法是基于操作和
*滑块的值。
*/
//确保滑块位置在valueChanged()信号与实际值最后一次触发!!!!
此->垂直滚动条()->设置滑块位置(此->垂直滚动条()->滑块位置());
//因为“QTextEdit”没有“updateRequest(…)”信号,所以我们选择
//从“sliderPosition()”和“contentsRect()”中获取信息。
//请参阅使用的必要连接(类构造函数实现部分)。
QRect rect=this->contentsRect();
lineNumberArea->update(0,rect.y(),lineNumberArea->width(),rect.height());
updateLineNumberAreaWidth(0);
//----------
int dy=此->垂直滚动条()->滑块位置();
如果(dy>-1){
行号区域->滚动(0,dy);
}
//Addjust滑块可始终查看当前正在编辑的行的编号。。。
int first_block_id=getFirstVisibleBlockId();
如果(第一个块id==0 | | this->textCursor().block().blockNumber()==第一个块id-1)
此->垂直滚动条()->setSliderPosition(根据此->文档()->documentMargin());
////捕捉到第一行(TODO…)
//如果(第一块\u id>0)
// {
//int slider_pos=this->verticalScrollBar()->sliderPosition();
//int prev_block_height=(int)this->document()->documentLayout()->blockBoundingRect(this->document()->findBlockByNumber(first_block_id-1)).height();
//if(dy document()->documentMargin()+上一块高度)
//此->垂直滚动条()->设置滑块位置(滑块位置-(此->文档()->文档边距()+上一块高度));
// }
}
void qtextedhighlighter::resizeEvent(QResizeEvent*e)
{
QTextEdit::resizeEvent(e);
QRect cr=此->内容引用();
lineNumberArea->setGeometry(QRect(cr.left(),cr.top(),lineNumberAreaWidth(),cr.height());
}
int QTextEditHighlighter::getFirstVisibleBlockId()
{
//检测为其转换边界矩形一次的第一个块
//在绝对坐标中-包含在编辑器的文本区域中
//这是一种代价高昂的方法,但因为“blockBoundingGeometry(…)”并没有这样做
//存在“QTextEdit”。。。
QTextCursor curs=QTextCursor(此->文档());
curs.movePosition(QTextCursor::Start);
对于(int i=0;idocument()->blockCount();++i)
{
QTextBlock=curs.block();
QRect r1=此->视口()->几何体();
QRect r2=this->document()->documentLayout()->blockBoundingRect(block).translated(
此->视口()->几何体().x(),此->视口()->几何体().y()(
此->垂直滚动条()->滑块位置()
)).toRect();
如果(r1.contains(r2,true)){return i;}
curs.movePosition(QTextCursor::NextBlock);
}
返回0;
}
void QTextEditHighlighter::LineNumberEAPAINTEVENT(QPaintEvent*事件)
{
此->垂直滚动条()->设置滑块位置(此->垂直滚动条()->滑块位置());
Q油漆工(线号A);
fillRect(事件->rect(),Qt::lightGray);
int blockNumb
// Here is the key to obtain the y coordinate of the block start
QTextCursor blockCursor(block);
QRect blockCursorRect = this->cursorRect(blockCursor);
painter.drawText(-5, blockCursorRect.y() /* + a little offset to align */,
m_lineNumberArea->width(), fixedLineHeight,
Qt::AlignRight, number);