Resizing a Widget on the fly
-
I have two QTextEdit objects, side by side. The one on the right contains text being edited, and the one on the left contains notes about the text. This is similar to the way that QtCreator displays code -- we have the code, then a widget to the left that contains line numbers and other things that refer to the code. What's important is that the text of the two must match up line-by-line.
Keeping the two text widgets lined up isn't hard. Connect the valueChanged() signal from the vertical scroll bar of the text widget to a slot that calls setValue() for the vertical scroll bar of the notes-about-the-text widget. However, there is a problem. If the text widget has a horizontal scroll bar, but the notes widget does not, then the height of the two widgets is not the same.
Here's my attempt at a solution. The idea is to hear about every resize() event of the text widget, and adjust the size of the notes widget accordingly. This overrides the resize() method of QTextWidget.
@void TextWidget:: resizeEvent(QResizeEvent *e)
{
// Call the super-class's method.
QTextEdit::resizeEvent(e);fRect = this->viewport()->rect();
// This doesn't work.
notesWidget->setMaximumHeight(fRect.height());// This doesn't work either.
QRect geom = notesWidget->geometry();
geom.setWidth(fRect.width());
geom.setHeight(fRect.height());
notesWidget->setGeometry(geom);
}@The above almost works, but not quite, and I think part of the problem is that when the two widgets were created, it was done with this
@theLayout = new QHBoxLayout();
theLayout->addWidget(notesWidget);
theLayout->setAlignment(notesWidget,Qt::AlignTop);
theLayout->addWidget(textWidget);textWidget->setLineWrapMode(QTextEdit::NoWrap);@
You need to do something like this so that the tops of the two widgets are lined up. Otherwise, the notes widget may be a little shorter than the text widget (it never has a horizontal scroll bar) and the two widgets are no longer lined up.
When you run the above, you get flukey behavior. When the window is stretched or shrunk vertically, it seems to work fine, but when the window is stretched horizontally, the notes widget is only half the expected height. If the window is shrunk vertically, it works fine again.
-
Some further information and more weirdness...
In the first code snippet above, if you make no attempt to adjust the geometry of notesWidget (i.e., omit lines 8-15), then the notesWidget appears as only half the size that you would expect. It's like addStretch() was called to insert space below notesWidget. If you omit line 4 of the second code snippet too (don't AlignTop), then it all works, except that the two widgets get out of alignment when the text widget has a horizontal scroll bar and you scroll to the end of the text. That's because the notesWidget is too tall, and remedying that is the whole point.
Also, there's a typo at the end of the original post. In the last sentence, I meant to say that if the window is shrunk horizontally it works fine again. It works no matter what you do to the vertical size.
-
Layouts will override the geometry information you set, so your code and the layouts will fight out a size;)
One approach is to force the display of scrollbars at all times in both widgets. Another is to write a custom widget containing the two widgets you want to have aligned. That widget must not contain a layout and makes sure that the two widgets are properly aligned and positioned. The layout will then resize the custom widget, which in turn will then make sure that the children are properly positioned without any layout overriding the geometry you set on them.
-
Thanks. Your explanation of the cause makes sense.
I thought of the idea of forcing the presence of scrollbar in the notes widget to match whether the text widget has one, but I don't want the scrollbar in the notes widget to be visible. I just want the notes widget to be laid out as though it had a scroll bar. There doesn't seem to be any way to force a widget to "have" a scrollbar while making it invisible. I'll have to go the custom widget route.
-
I was able to find a solution that doesn't require writing a custom widget after all. The idea is to use QVBoxLayout.addSpacing() with a value equal to the height of a horizontal scroll bar. Insert this space below the notes widget when the text widget does have a horizontal scroll bar and remove it when there is no scroll bar. To do this, you need to detect when the status of the horizontal scroll bar of the text widget goes in and out of use -- resizeEvent() is where to do that. When this status changes, rebuilt the layout with or without the extra space below the notes widget.
This would be easier if blank widgets of fixed size were available. QSpacerItem is conceptually the right thing, but it's not a QWidget, so you can't call QVBoxLayout.removeItem() on one.
-
You can create fixed-sized widgets, no problem there.
You could also adjust the contentsmargins of the non-scrollbar widget to account for the scrollbar height.