QTextEdit: scroll down automatically only if the scrollbar is at the bottom - qt

There's a QTextEdit that displays quite a lot of text. It is NOT editable. Suppose I want to read something around the beginning, scroll up, but then a new line is added and the scrollbar automatically goes to the bottom. I experience similar problems when using various programs (regardless of the language they were written in). How does one deal with this problem?
The behavior I want when a new line is added to the text:
if the scrollbar is at the bottom, scroll down automatically.
if the scrollbar is elsewhere, don't scroll
I suppose that
ensureCursorVisible()
is not the solution, since the QTextEdit is not editable, the user won't click inside it, and the position of the cursor is not the same as the position of the vertical scrollbar.

I would make Scroll bar position listener, which will remember position on scrolling (and also check is it at the bottom or not).
Then, when new line is added, check is it at bottom, if is scroll down, if is somewhere else then scroll back to that position.
Check this QScrollBar, you can grab it from QTextEdit via horizontalScrollBar() and verticalScrollBar().
More concrete, I would connect slot with signal from QScrollBar - valueChanged(int value) and play with values as it is described here.

It is not necessary to connect a scrollbar listener. Just query the scrollbar before appending text:
QScrollBar *scrollbar = textedit->verticalScrollBar();
bool scrollbarAtBottom = (scrollbar->value() >= (scrollbar->maximum() - 4));
int scrollbarPrevValue = scrollbar->value();
The "minus 4" hack in scrollbarAtBottom is necessary since ensureCursorVisible() does not scroll exactly to the bottom, but some fixed amount above. Check it with your font sizes.
Now you can insert the text:
textedit->moveCursor(QTextCursor::End);
// begin with newline if text is not empty
if (! textedit->document()->isEmpty())
textedit->insertHtml(QStringLiteral("<br>"));
textedit->insertHtml(QStringLiteral("My text here."))
After that operation, either scroll to the bottom, or fix the scrollbar such that it does not move at all:
if (scrollbarAtBottom)
textedit->ensureCursorVisible();
else
textedit->verticalScrollBar()->setValue(scrollbarPrevValue);

Related

Scrolling QTableWidget smoothly

I have a QTableWidget with custom Widgets. These widgets are big and fill almost the whole scroll area in height, so that only one row is visible.
I can scroll with the mouse wheel or by dragging the scroll bar, but the row always jumps.
Is there a way to configure the QTableWidget to smoothly scroll, without jumping?
Try to use this:
view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel)
From Qt documentation:
enum ScrollMode { ScrollPerItem, ScrollPerPixel }
verticalScrollMode : ScrollMode
This property holds how the view scrolls its contents in the vertical
direction.
This property controls how the view scroll its contents vertically.
Scrolling can be done either per pixel or per item.

Init QListWidget to show its all content (with no scroll bars)

I was playing with the Config Dialog Example, all was fine. Then I changed something in the right part of the dialog.
Then I found the contentsWidget (QListWidget) in the left part of the dialog became smaller and showed the scroll bars (both Horizontal and Vertical).
I want the QListWidget to show all its content so that no scroll bars are needed.
All items are added at the beginning and fixed. No dynamic.
I guess there is a simply method to let the QListWidget expand to show all its content at the beginning.
Could anyone help me and tell me the magic word?
Here is the code:
contentsWidget = new QListWidget;
contentsWidget->setViewMode(QListView::IconMode);
contentsWidget->setIconSize(QSize(96, 84));
contentsWidget->setMovement(QListView::Static);
contentsWidget->setMaximumWidth(128);
contentsWidget->setSpacing(12);
//contentsWidget->setMinimumWidth(contentsWidget->sizeHintForColumn(0));
//contentsWidget->setMaximumWidth(contentsWidget->sizeHintForColumn(0));
//contentsWidget->adjustSize();
//qDebug()<<contentsWidget->sizeHintForColumn(0);
createIcons();
contentsWidget->setCurrentRow(0);
QHBoxLayout *horizontalLayout = new QHBoxLayout;
horizontalLayout->addWidget(contentsWidget);
horizontalLayout->addWidget(pagesWidget, 1);
I tried contentsWidget->sizeHintForColumn(0), but it didn't work. It was 0. I tried some other methods but nothing worked.
I think you should try:
contentsWidget->setMinimumWidth(128);
This will ensure that no matters what, the size of contentsWidget will always be at least 128, hence large enough to contains the icons.

How do i resize the contents of a QScrollArea as more widgets are placed inside

I have a QScrollArea Widget, which starts empty;
It has a vertical layout, with a QGridLayout, and a vertical spacer to keep it at the top, and prevent it from stretching over the whole scroll area;
Elsewhere in the program, there is a QTextEdit, which when changed, has its contents scanned for "species" elements, and then they are added to the QGridLayout. Any species elements which have been removed are removed too. This bit works;
I have turned the vertical scrollbar on all the time, so that when it appears it does not sit on top of the other stuff in there. Note that the scroll bar is larger than the scroll box already though, despite not needing to be.
This is the problem. The scroll area seems to be preset, and i cannot change it. If i add more rows to the QGridLayout, the scroll area doesn't increase in size.
Instead, it stays the same size, and squeezes the QGridLayout, making it look ugly (at first);
And then after adding even more it becomes unusable;
Note that again, the scroll bar is still the same size as in previous images. The first two images are from Qt Designer, the subsequent 3 are from the program running.
If I resize the window so that the QScrollArea grows, then I see this:
Indicating that there's some layout inside the scroll area that is not resizing properly.
My question is; what do I need to do to make the scrollable area of the widget resize dynamically as I add and remove from the QGridLayout?
If you're coming here from Google and not having luck with the accepted answer, that's because you're missing the other secret invocation: QScrollArea::setWidget. You must create and explicitly identify a single widget which is to be scrolled. It's not enough to just add the item as a child! Adding multiple items directly to the ScrollArea will also not work.
This script demonstrates a simple working example of QScrollArea:
from PySide.QtGui import *
app = QApplication([])
scroll = QScrollArea()
scroll.setWidgetResizable(True) # CRITICAL
inner = QFrame(scroll)
inner.setLayout(QVBoxLayout())
scroll.setWidget(inner) # CRITICAL
for i in range(40):
b = QPushButton(inner)
b.setText(str(i))
inner.layout().addWidget(b)
scroll.show()
app.exec_()
The documentation provide an answer :
widgetResizable : bool
This property holds whether the scroll area should resize the view widget.
If this property is set to false (the default), the scroll area honors the size of its widget.
Set it to true.
Why don't you use a QListView for your rows, it will manage all the issues for you? Just make sure that after you add it you click on the Class (top right window of designer) and assign a layout or it wont expand properly.
I use a QLIstWidget inside a QScrollArea to make a scrollable image list
Try this for adding other objects to the list, this is how I add an image to the list.
QImage& qim = myclass.getQTImage();
QImage iconImage = copyImageToSquareRegion(qim, ui->display_image->palette().color(QWidget::backgroundRole()));
QListWidgetItem* pItem = new QListWidgetItem(QIcon(QPixmap::fromImage(iconImage)), NULL);
pItem->setData(Qt::UserRole, "thumb" + QString::number(ui->ImageThumbList->count())); // probably not necessary for you
QString strTooltip = "a tooltip"
pItem->setToolTip(strTooltip);
ui->ImageThumbList->addItem(pItem);
Update on Artfunkel's answer:
Here's a PySide6 demo that uses a "Populate" button to run the for loop adding items to the scroll area. Each button will also delete itself when clicked.
from PySide6.QtWidgets import *
app = QApplication([])
scroll = QScrollArea()
scroll.setWidgetResizable(True) # CRITICAL
inner = QFrame(scroll)
inner.setLayout(QVBoxLayout())
scroll.setWidget(inner) # CRITICAL
def on_remove_widget(button):
button.deleteLater()
def populate():
for i in range(40):
b = QPushButton(inner)
b.setText(str(i))
b.clicked.connect(b.deleteLater)
inner.layout().addWidget(b)
b = QPushButton(inner)
b.setText("Populate")
b.clicked.connect(populate)
inner.layout().addWidget(b)
scroll.show()
app.exec()

Getting rid of unnecessary scrollbar in QScrollArea

I'm working on a Configuration QDialog. It has several categories (General, Appearance, etc.) that get loaded when the user clicks on them. Each category has its own page. These pages are separate classes in their own right (each has its own ui, cpp, and h). When the Preferences window loads, the pages get instantiated and are loaded into a QStackedWidget. The Stackedwidget is then placed into a QScrollArea so it can scroll if necessary.
However, there is a problem. Ever since I added the QStackedWidget, the QScrollArea always has a vertical scrollbar even when the current page is short enough not to need one: (picture shows the shortest page)
The ScrollArea vertical scroll policy is set to Qt::ScrollBarAsNeeded so logically it should only show a bar if the page is larger than the viewable area.
Here's what I already tried to fix this:
Setting the scroll policy to Qt::ScrollBarAlwaysOff. While this gets rid of the scrollbar, its unacceptable since it doesn't let the user know they need to scroll on long pages.
Setting the Minimum/Maximum heights for the QStackedwidget. This makes the scrollbar go away if I set it for a low enough value, but it is unacceptable since it causes some of the widgets to have a squished appearance.
I know the problem has something to do with the QStackedWidget but since this is the first time I've used QStackedWidget I’m not sure what it is. I've also noticed the scroll is always for the same amount; i.e. the scrollable area is always the same size no matter how large/small the page widget is. For some reason, it's slightly larger than the longest page. At first I thought the vertical spacers I put at at the bottom of each page to tighten up the layout were causing this, but taking them out didn't fix it.
Update: Here's the code that controls the Stackedwidget:
void Newconfig::on_Categories_currentItemChanged(QTreeWidgetItem *current)
{
QModelIndex index=ui->Categories->currentIndex();
int idx=index.row();
QString category=current->text(0);
this->setWindowTitle("Preferences -- " + category);
if (stack->currentWidget() != 0) {
stack->currentWidget()->setSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Ignored);
}
stack->setCurrentIndex(idx);
stack->currentWidget()->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
adjustSize();
}
The QStackedWidget takes the size of the largest widget added to it. In your case, the largest page in your preferences dialog is what is influencing the size of the QStackedWidget and is thus forcing the scroll area to show its scroll bar, even when it doesn't appear to be necessary.
To get around this, you can create a slot that is triggered right before you change the current widget in the QStackedWidget that sets the size policy of the page you are leaving to QSizePolicy::Ignored and the size policy of the page you are about to show to whatever you desire for that page -- QSizePolicy::Expanding for instance. An example of this technique is detailed on this page. There's a lot of code in the example, but the important part is here:
void changeCurrent(int idx)
{
if (stackWidget->currentWidget() !=0) {
stackWidget->currentWidget()->setSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Ignored);
}
stackWidget->setCurrentIndex(idx);
stackWidget->currentWidget()->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
adjustSize();
}
This function is called to change the current index on the QStackedWidget. It causes the page that was just being viewed to have a size policy that has no influence on the overall size of the QStackedWidget and the page that is about to be viewed to have a size policy of consequence.

qt status bar center align?

How can I center align the text in QStatusBar?
By default it is always left aligned.
I know I can add a QLabel and set alignment, but I want to use plain text, and .showMessage(QString, int) method, so I can add the timeout value.
A QStatusBar has three functions of note here:
addPermanentWidget - Places a widget right aligned
addWidget - Places a widget left aligned which can be obscured by status messages
showMessage - Displays a status message
These are well established standards for status bars. While you could hack away to get what you're looking for, I'd suggest you reconsider your needs. Perhaps your QLabel should be placed with addPermanentWidget instead?
Take a look at the docs for more info: http://doc.qt.io/qt-5/qstatusbar.html
If you just want to center the message itself in the whole statusBar, go like this:
QLabel* statusLabel = new QLabel("Your Message");
statusBar()->addWidget(statusLabel,1);
This additional parameter 1 stretches your Label to the complete width of the statusbar.
Greetings

Resources