2010-03-14 48 views
6

estoy escribiendo una idea básica visual, y necesito agregar números de líneas a QTextEdit y resaltar la línea actual. He encontrado esto tutorial pero está escrito en java y escribo mi proyecto en C++ para encontrar un tutorial como ese en C++, o si hay un componente listo para usar?cómo agregar números de líneas a: QTextEdit?

gracias.

Respuesta

14

sé que Qt tutorial recomienda el uso de QPlainTextEdit para las implementaciones de edición de texto, y que la cuestión (excepto la mencionada en el título), es más general que tratar (absolutamente) con un widget QTextEdit, pero logré implementar el comportamiento (números de línea + resaltado de número de línea actual) y Creo que esto podría ser útil para algunas personas (como yo) que realmente quieren seguir con el widget Rich Text, y quieren compartir mi implementación (que está lejos de ser perfecta, bastante rápido ...).

LineNumberArea.h: (Igual que "QPlainTextEdit" tutorial)

class LineNumberArea : public QWidget 
{ 
    Q_OBJECT 

public: 
    LineNumberArea(QTextEdit *editor); 

    QSize sizeHint() const; 

protected: 
    void paintEvent(QPaintEvent *event); 

private: 
    QTextEdit *codeEditor; 
}; 

LineNumberArea.cpp: (Igual que "QPlainTextEdit" tutorial)

LineNumberArea::LineNumberArea(QTextEdit *editor) : QWidget(editor) { 
    codeEditor = editor; 
} 

QSize LineNumberArea::sizeHint() const { 
    return QSize(((QTextEditHighlighter *)codeEditor)->lineNumberAreaWidth(), 0); 
} 

void LineNumberArea::paintEvent(QPaintEvent *event) { 
    ((QTextEditHighlighter *)codeEditor)->lineNumberAreaPaintEvent(event); 
} 

>> 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; 

}; 

>> qtextedithighlighter.cpp:

#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; 
    } 

} 

la esperanza que esto puede ayudar ...

+4

A partir de QT 5.6 uso intersectado en lugar de intersecar: additional_margin = (int) this-> document() -> documentLayout() -> blockBoundingRect (prev_block) .traducido (0, translate_y) .intersected (this-> viewport() -> geometry()). height(); – zuko

Cuestiones relacionadas