PDA

View Full Version : Vertical centering of a QTextEdit



miwarre
25th November 2009, 10:02
This post is actually a sequel to this thread (http://www.qtcentre.org/forum/f-newbie-4/t-qtextedit-text-vertical-center-alignment-15749.html). I created a new thread to give some visibility to a solution which might have some interest; please advise if this breaks some forum policy.

THE PROBLEM

Vertically centering a text in a QWebView widget is not very complex; with some experiments, I found that the following HTML text is enough:

<html><body><table height='100%' width='100%'><tr><td valign='middle' style='text-align: center;'>%1</td></tr></table></body></html>
where %1 is supposed to be .arg'ed with the text to display.

If a (read-only) QTextEdit is used instead of a QWebView, neither the above HTML not any other combination of HTML tags I could think of achieves the intended result: the text always remains at the top of the widget. The point in using QTextEdit instead of QWebView is to avoid the overhead of a full-fledged web browser (also in terms of executable file size and, IIUC, in terms of licensing) when only a few words of styled text have to be shown in a small widget.

I tried a number of other tricks and none worked: QTextEdit::setAlignment(Qt::AlignVCenter) does not work (and is in fact excluded from the relevant documentation). QTextFormatChar::setVerticalAlignment(QTextCharFor mat::AlignMiddle) applies to a different purpose. Attempting to set the page size of the QTextEdit document to the size of the widget (myTextEdit.document()->setPageSize( QSize(myTextEdit.width(), myTextEdit.height()) ) brought no result. In fact I made more attempts than I can remember...

I picture the situation imagining that QTextEdit has no concept of itself as a physical frame; it is rather a viewport on a document which is only 'tall' as itself and then centering vertically makes no sense (in the middle of what? Of a document, which is only high as the text it contains?). Actual Qt code may be different, but this is the picture I made for myself.

THE SOLUTION

The basic idea is to simulate the vertical centering by playing with the top margin of the document root frame. This is the function I use:

void ValignMiddle(QTextEdit * pTextEdit)
{
// retrieve total text document height and widget height
int nDocHeight = (int)pTextEdit->document()->size().height();
if(nDocHeight == 0) // if no document, do nothing
return;
int nCtrlHeight= pTextEdit->height(); // the TextEdit control height

// access the document top frame and its format
QTextFrame * pFrame = pTextEdit->document()->rootFrame();
QTextFrameFormat frameFmt = pFrame->frameFormat();

// get current top margin and compute the 'black' height of the document
int nTopMargin = (int)frameFmt.topMargin(); // the frame top margin
int nBBDocH = nDocHeight - nTopMargin; // the height of the document 'bounding box'

// compute and set appropriate frame top margin
if(nCtrlHeight <= nBBDocH) // if the control is shorter than the document
nTopMargin = 2; // set a nominal top margin
else // if the control is taller than the document
nTopMargin = (nCtrlHeight - nBBDocH)/2 - 2; // set half of the excess as top margin
frameFmt.setTopMargin(nTopMargin);

// SET A BORDER!! (using the same colour as the background)
frameFmt.setBorder(1);
frameFmt.setBorderBrush(QColor(0xFFFFFF));
pFrame->setFrameFormat(frameFmt); // apply the new format
}

(nBBDocH should probably also discount the root frame bottom margin; for my application, it made no real difference. The "-2" in line 21 is a rough visual correction as a bottom margin slightly larger than the top margin 'looks better').

The real TRICK are lines 25 and 26 (it took me two days of trials and errors to discover it). The frame top margin is only obeyed if the frame has some border (if I do not wnat it to show, I use the same colour as the background); if no border is defined, the top margin is ignored and the text appears at the top of the widget.

The above code can of course be improved, but works; I use it in several contexts. However, it needs two additions:

the top margin needs to be re-computed when the widget is resized.

the actual document height is not computed until after the widget is initially shown (this only applies if the QTextEdit widget is filled with static text when the dialogue it belongs to is initially constructed).

So two event handlers have to be added:

void MyDialogueClass::resizeEvent(QResizeEvent * event)
{
// when the dialogue box is resized, the widget is probably resized too.
// Re-display the QTextEdit contents from scratch, as the document / margins dimensions are obsolete
DisplayTextEditText(/* needed params */);
ValignMiddle(ui->myTextEdit);
}

void MyDialogueClass::showEvent(QShowEvent * event)
{
// once the widget is shown, recompute the top margin from the now-actual doc sizes
ValignMiddle(ui->myTextEdit);
}
With these three methods, the text ends up properly centered in the middle of the QTextEdit widget.

In most situations, it probably makes sense to subclass QTextEdit and include these 3 methods in the subclassed widget itself (I didn't show it, for the sake of simplicity).

A little question to end: vertical centering of a text in a widget is a rather common need; is all the above really needed?

Cheers,

M.

hamren
28th February 2014, 00:16
A little question to end: vertical centering of a text in a widget is a rather common need; is all the above really needed?

If what you want to do is show HTML text, then it is not. A much easier way to center the text is to use a QLabel.

This program that takes two arguments, a style sheet and som HTML text, and displays it centered in a QLabel:


# include <QLabel>
# include <QApplication>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *window = new QLabel;

window->resize (320,240);
window->setStyleSheet(argv[1]);
window->setText (argv[2]);

window->show();
return app.exec();
}


BTW, by putting a QLabel inside a QPushButton, it is possible to create fancy, HTML-styled buttons.

Scorp2us
28th February 2014, 19:43
That HTML should work ina text edit.

Alterativea are to consult QFontMetrics and construct your document accordingly.
Why you are wanting to vertically center text in a text edit is beyond me.

pvjlieuthier
11th August 2015, 14:54
@miwarre, I know it's been almost 6 years, but I want to thank you for sharing this solution. It may be for a very specific use case, but it works well. And I didn't even need to set the border (Qt 5.5).

neuronet
5th October 2015, 05:01
That HTML should work ina text edit.

Alterativea are to consult QFontMetrics and construct your document accordingly.
Why you are wanting to vertically center text in a text edit is beyond me.

Not sure about original author, but I need to because I have a line editor built out of text edit, can't use line edit b/c I have delegate user creates/edits html that then shows as rich text. See Summerfield PyQt book, chapter 13-14 he has a worked out example, but doesn't deal with vertical alignment.