Qt ensureVisible() not working in QScrollArea - qt

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.

Related

How to draw something in a tooltip?

I am trying to develop a functionality using Qt which I don't know whether it is possible to implement. Here is the requirement:
When the user hovers over a node (an object derived from QGraphicsItem), a window will be shown near the node, in the window there might be some histograms or buttons that can be clicked to show further information. When the mouse leaves the window, it will automatically close.
I tried to use a tooltip, because it can pop-up near the node and close when the mouse leaves it, but it can only show text. So, it still cannot work that way. I am wondering if there is another way to do this? I did lots of google search, but there is still no answer.
Thanks so much for helping me with this.
If you're ok with using a 3rd party library, Qxt provides a class that provides a tooltip that is QWidget based, which will let you use an arbitrary widget as the tooltip rather than just text.
See: Qxt::ToolTip
you don't have to use tooltip for your app
you can use or call widget or dialog, on hover mouse event
Please refer Qt Example EmbeddedDialog Example, It is advanced, But you can understand how hover Enter/Leaving events are working. I personally prefer don not create instance of Popupdialog for each item, create it if only nesessary. Otherwise create one dialog and pass its reference to all the items through the constructor initialization.
1. These are the API you are intrested on, reimplemet this.
QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) and void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
2. When You create Dialog, You can pass Qt::WindowFlags as Qt::ToolTip.

Panning QScrollArea with a mouse

I have a QScrollArea. I would like to make the context pannable with a mouse - such that, a hand cursor is shown over the area and click-and-hold gets us into dragging mode.
This would be similar to what QGraphicsView can do easily with QGraphicsView::ScrollHandDrag dragging mode, only that I need it for QScrollArea.
Any idea on how to have it out of the box? Or perhaps a piece of code to do it manually? I am certainly not the first one who wants it..
Should not be difficult, especially using event filter. Take a look at my old blog post on Flick list or kinetic scrolling (the code has been moved to http://qt.gitorious.org/qt-labs/graphics-dojo). As a bonus, you can get the momentum effect easily!
Okay. At the end I changed it to QGraphicsView and it was easy to do, worked great out of the box.

Qt Parent child relationship for independent qmainwindows

Using Pyside, but a general Qt question:
I am building a Qt app with a controlling QMainWindow. From this window the user can open other QMainWindows (or QDialogs) and from some of those she can open more. The user is intended to think of the first QMainWindow as "the app" and the others as lots of different views on more or less the same data.
So I'd like all the windows to be independently stackable so the user can set up the screen to their own requirements. In particular I want the user to be able to bring the first QMainWindow on top if wanted. But I don't really want each window to have its own task bar entry (though I can live with that). Also I would like them to minimise and restore together, and I would like them all to close when the first main window closes.
If I parent them all on the first mainwindow it works nicely except they stay on top of it which is not what I want.
Instead I have it kind of working by making them all independent with parent = None. Then I register them all with the main window and close them all when it closes. But this makes them a bit too independent - they minimise separately and have their own task bar entry.
Am I missing some obvious fix to this? Is there any easy way (a flag?) to stop the children staying on top of the parent?
Or is there some UI guideline that I am breaking by desiring this?
Or is there a cleaner design somehow? I thought of adding a dummy parent that they could all descend from but maybe that's messy. Would that parent need a visual presence? I wouldn't want that.
Suggestions?
You can have as many QMainWindows as you want, or parentless QWidgets. I think the best way to handle your situation is to create your own pseudo parent-child relationship like this:
In your QMainWindow subclass, store a QList of all the QWidgets you want it to manage. Then, again in your QMainWindow subclass, reimplement methods such as QWidget::closeEvent(), QWidget::hideEvent() (for when the window is minimized), and QWidget::showEvent() (for when it is restored) so that it also closes, hides, or shows all of the widgets in its QList. Make sure to also delete them in the QMainWindow subclass's destructor. Now, whenever you create a sub-window, pass the main window a pointer to it not as a normal QWidget child, but just so that it can be added to the main window's QList of QWidgets to manage. E.g.:
MainWindowSubclass::addPseudoChild(QWidget *pseudoChild)
{
myListOfPseudoChildren.append(pseudoChild);
}
Another alternative that hasn't been mentioned yet is populating a QMdiArea with QMdiSubWindows. It doesn't do exactly what you asked for, but it's a pretty clean design nonetheless.
So I thought I would add what I eventually settled upon. This was particularly inspired by the comments of #leemes (Thanks - good stuff) and a little experimentation of my own.
I used the code attached here DetachTabExample
to develop a "Detachable Tab" widget and tab bar. This allows tabs to be dragged outside the main window when they become independent windows. Then if closed they return to the tab bar.
Then I placed all my content in the QMainWindow but in separate tabs. The users can drag the ones they want out on to the other monitor. Seems to be working fine. There are still some extra windows that I have floating but it has cut down the clutter and clarified the structure.

How to update a QLayout and get the new dimensions before returning?

This is driving me nuts. I have a custom menu class that, when set visible, shows a list of items located in a particular folder. When a hardware button is pressed, my application gets the latest list of items, populates the menu with them, and returns.
The menu displaying these items uses a QListWidget filled with custom widgets. Each of the widgets contains one or more QLabels in a horizontal layout, and is created at the time the menu is shown. In order to adjust the text displayed based on the menu width available, I need to get the size of the QLabel AFTER it has been resized according to the layout, but before the menu becomes visible to the user. The problem is, my layout does not get updated until all of the functions constructing my list return.
I have tried QApplication::ProcessEvents() and the layout update functions, but none of them have updated the values of my QLabels before returning. I can set a QTimer when the button is initially pressed, and have it show the menu, update the items, and stop itself, but that seems like a terrible solution.
Any help would really be appreciated! I've spent most of a day on this.
Marlon
I had this exact problem and could not find an answer anywhere on the Internet. Calling Layout.update(), Layout.activate(), or widget.adjustSize() (all suggested in various places) all did not work.
I had a widget with a vertical layout that I wanted to add a QLabel to and then immediately use the size of the QLabel.
The only thing that worked reliably was
layout->addWidget(myLabel);
myLabel->show();
size = myLabel->size();
It would seem that layouts will just not recalculate until you either return from a function and allow the Qt event loop to progress or manually call show() yourself.
How to update a QLayout and get the new dimensions before returning?
Don't. You're not meant to do that. It'll drive you "nuts" because you're doing it backwards. Layout updates are handled asynchronously from the event loop. Instead of getting layout dimensions right away, set yourself up to be part of the system. Some options are:
Implement a custom widget that will interact properly with the layout, growing to fill the available width of the layout. Perhaps all you need is a size policy and a way to elide text?
Make a custom layout that takes the special properties of your use case into account.
You want to call QWidget::adjustSize() on your parent widget. This will force the layout recalculations.
Have you tried using layout()->update(); ?
I've tried many but nothing works for me on Qt 5.15.
Only invented little patch - create timer and get size after 20 msec:
QTimer::singleShot(20, this, [this]
{
const auto height = myLayout->contentsRect().height();
// ...
});

Qt - In QGraphicsScene how to put a picture dynamically

Hai,
I am trying to create a chess board. Here I want to do a operation like when ever a coin is clicked and dragged, at that time that picture should be copied and be placed in that old position. I don't know in which function I have to do it. When I made it on mouse pressed, then tyhe copy is coming above the original(layer wise). So somebody please help me
When I made it on mouse pressed, then tyhe copy is coming above the original(layer wise).
When you got this far, simply adding a call to myItemCopy->setZValue( -1 ) would place the copy "behind" the original item.
A different approach, and possibly "cleaner", would be to use QDrag and tell Qt how to paint your item during the Drag operation. See Qt's "Drag and Drop Robot" example for reference: http://doc.qt.io/qt-5/qtwidgets-graphicsview-dragdroprobot-example.html
Happy coding!
Robin
Simplest way to do this will be to put 2 identical QGraphicsItems for each coin/piece at the same location. The top one will be dragged and moved while the bottom one will stay for the visual cue you want.
Once the top one is dropped to the new location, move the bottom one there too.

Resources