Qt: performing an action (scroll) after a widget has been added - qt

I have a horizontal scroll area and dynamically added widgets inside it.
I want it to scroll to the very end whenever a new widget is added, so that the user sees the last widget using this method:
void scrollToEnd()
{
scroll->horizontalScrollBar()->setValue(100000);
}
...
layout->addWidget(widget);
scrollToEnd();
However, there's a delay between calling layout->addWidget() and actual widget appearing. So calling scrollToEnd() does nothing.
If I make apause by, for example, showing a MessageBox, everything works fine.
Is there a way to wait till the widget is displayed, and then scroll the scroll area?

addWidget probably schedules the actual addition of the widget to after the reentering of the event loop, so you should do the same and call the scrollToEnd method asynchronously (it has to be a slot):
layout->addWidget(widget);
QTimer::singleShot(0, this, SLOT(scrollToEnd()));

Related

Difference between hide, close and show in qt

What is the difference between hide,close and show of pushbutton or any widget in terms of memory?
Which is better if I don't want to use widget again?
First as said #Hayt, read the documentation.
For the actual answer:
hide() is the same as setVisible(false).
show() is the same as setVisible(true).
close() attempts to close the widget by triggering a QCloseEvent, if the event is accepted the result is:
The same as calling hide() if Qt::WA_DeleteOnClose attribute is not set on the widget which is the default.
The same as calling deleteLater() if Qt::WA_DeleteOnClose is set.
In term of memory, any of the 3 will not change anything (except for close() if you have set Qt::WA_DeleteOnClose). If you do not want to use the widget ever, the best is to delete it:
delete pointerToMyWidget;
or
pointerToMyWidget->deleteLater();
The second form is generally safer as the 1st one can be dangerous depending on where your write it. (e.g you delete it in a slot called by a signal emitted by the widget you delete).
According to Qt, you can read this :
CLOSE :
Closes this widget. Returns true if the widget was closed; otherwise
returns false.
First it sends the widget a QCloseEvent. The widget is hidden if it
accepts the close event. If it ignores the event, nothing happens. The
default implementation of QWidget::closeEvent() accepts the close
event.
If the widget has the Qt::WA_DeleteOnClose flag, the widget is also
deleted. A close events is delivered to the widget no matter if the
widget is visible or not.
The QApplication::lastWindowClosed() signal is emitted when the last
visible primary window (i.e. window with no parent) with the
Qt::WA_QuitOnClose attribute set is closed. By default this attribute
is set for all widgets except transient windows such as splash
screens, tool windows, and popup menus.
.
HIDE : Hides the widget. This function is equivalent to
setVisible(false).
Note: If you are working with QDialog or its subclasses and you invoke
the show() function after this function, the dialog will be displayed
in its original position.
.
SHOW : Shows the widget and its child widgets. This function is
equivalent to setVisible(true).
If you don't need to use your widget, call close(). You can manage the event to destroy your widget.
hide() only hides. It's only graphical, you can't see your widget but you don't destroy it.
But I think that the name fo the function are enough explicit to understand!

Qt ensureVisible() not working in QScrollArea

I have 2 column. One of the columns is filled with lots of QWidgets.
When I drop a widget in a column I call this method:
void MainWindow::scrollToItem(Product_View *item) {
QPoint point = item->mapToParent(QPoint());
ui->scrollArea->ensureVisible(point.x(), point.y());
}
When I am at the bottom of the first column and I drag a widget in the next one. The scrollarea scrolls properly to the bottom so that I can see where I dropped it.
But when I drop the widget back in the first column, it scrolls but not entirely to the bottom. Its of about 150px (EDIT: 150px is not true. I only see about 5px of the Widget)
Can anybody help me?
EDIT:
maybe good to know my layout.
I have a mainwindow with a QScrollArea.
The scrollArea has a QWidget called scrollAreaWidgetContents and it has a QHBoxLayout.
The widget has 5 columns. And these have QVBoxLayout for my drag and drop widgets.
JEEZ ANOTHER EDIT:
I notice it only goes wrong with the last item.
I JUST KEEP ON EDITING
It is now clear to me that the scrollbar just isn't going all the way down.
QScrollBar *bar = ui->scrollArea->verticalScrollBar();
bar->setValue(bar->maximum());
This code also shows the same behaviour. What should I do with this thread? And should I create a new one?
The ensureVisible function only takes a point, so using it will only guarantee that one corner of your widget is visible (the top left, I believe?). Try using ensureWidgetVisible instead - this should make sure the entire widget makes it on-screen.
Hope that helps!
Although this is an older post I encountered the same problem and it gave me a lot of trouble finding a solution.
My problem:
Had to add a new line to a widget and then make sure the scroll bar scrolls down to it in order for people to view it. OP describes the issue well in his answer.
The things I tried are:
1. (Best way) To call processEvents() on the app object. I tried it after I saw this post and ratzian's answer.
2. Implement your custom ScrollArea,that extends QScrollArea and override the resize handler to be able to call ensureWidgetVisible on the added widget. Of course it somehow needs to know about that widget object.
3. Add a QTimer and start the timer upon adding a new widget. The method that the timer calls will need to call ensureWidgetVisible() on the new widget object and afterwards stop the timer.
I am aware that 3 is a bit hacky since it doesnt know when the resize event took place and so the timer will need to be set to a suboptimal value. (e.g resize is called in 33 ms and you set timer to 500 ms, you get the idea).
I hope this manages to help people who struggle with the same problem.
I found my problem. Not the solution.
If I drag the widget back to the first column, my code calls ensureWidgetVisible.
After that, my scrollArea resizes because of the new item. So thats why my widget isn't entirely visible.

Why does my QGraphicsWidget receive events it shouldn't after QDrag?

I have a simple QGraphicsWidget, MyGraphicsWidget. Here's my mouseMoveEvent(), which seems to work fine :
void MyGraphicsWidget::mouseMoveEvent (QGraphicsSceneMouseEvent *event)
{
QPointF p = event->scenePos() - m_StartPos;
if(p.manhattanLength() < 20)
return;
//omitted drawing a rounded rect on the drag
QDrag *drag = new QDrag(event->widget());
drag->start(Qt::MoveAction);
}
The scene's dropEvent() just moves this widget to its new position, and I don't have a press/move event for the scene itself, so those should get passed on correctly to the widgets within.
However, once the drag completes, the next mouse press will be on this widget. So if I try to click and drag another widget, I'll be stuck dragging this one on accident, despite the fact that my cursor is not on this widget. I've printed out the event->pos() and event->scenePos(), and both reported that the cursor is where it appears to be (not on the widget at all). If I click once before trying to click and drag, everything works normally. Is there maybe something I need to implement within mouseReleaseEvent() or my mouseMoveEvent() ?
Thanks.
It's working now. I'm pretty sure this was the issue:
MyGraphicsWidget had another custom GraphicsWidget sitting on top of it, and in its mousePressEvent, I was calling QGraphicsWidget::mousePressEvent(event); at the start of the event. The rest of the event was never getting triggered, which was messing it all up. I moved that line to the end of the method, and everything seems okay now.

QPainter redraw on window gaining/losing focus

I am learning Qt and am trying to paint a simple display for my program with QPainter.
I draw static elements (frames etc.) once and only update dynamic elements afterwards.
Everything works fine, except for when the window loses focus. As soon as that happens, the whole area is cleared (dynamic elements keeps being painted as before).
Is it possible to prevent this behaviour? If not, how do I determine if the window have lost focus?
When your widget is uncovered, the paintEvent member will be called. The event passed in has a region() member that tells you what part of the widget should be redrawn. You can use that to redraw the static parts if/when necessary.
While I did not find why the screen was repainted, the focus can be triggered by using
eventFilter(QObject *, QEvent *event) {
if (event->type() == QEvent::ActivationChange){}
}
and paint function can be called from here. Although a slight delay must be added, as the trigger usually fires before the window looses focus (hence still clearing the repaint).

How to force calling of QWidget::paintEvent() when its hovered by other window?

I have a problem:
I'm creating a widget which displays current date's day number. It's like a button, but it's not derived from QPushButton class. Just from QWidget. So I've reimplemented enterEvent(), leaveEvent(), mousePressEvent(), mouseReleaseEvent(). I do call update() inside these methods and widget has realistic button behavior (paintEvent() is reimplemented also).
But when I change system date and hover that widget with other window, my widget doesn't get paintEvent() and the old date is displayed. Only when I place mouse over it, widget repaints it's contents.
I guess there is a feature (like buffering) which paints old contents on hovering with other window to avoid unnecessary recalculations. But I need to disable it. Tried to set many attributes (the Qt::WidgetAttribute enum). But it doesn't work.
I think you should find a way to detect that the system time has changed and call update() when that happens. Any other method (like detecting the "hovering" of a window or waiting for a mouse event) will cause the update to occur too late.

Resources