Qt Flickable Widgets - qt

I've been playing around with the Flickable class from Qt Labs. I understand pretty well how the example works, but I'm having trouble understanding how I can use it to display something other than something dynamically painted inside the paintEvent.
If I have a list of buttons that I want to be Flickable, is there a way to reimplement the Flickable paintEvent to call all the button's paintEvents?
It seems like the Flickable class pretty much figures out which buttons would be visible so all I'd have to do for each button is set its y position and tell it to paint itself at that position.
Any pointers would be appreciated...

In your subclass, you need to reimplement setScrollOffset. There you can do various things. The included ColorList just triggers a repainting update, but theoretically you could also e.g. move widgets around. In fact, there is no need to reimplement paintEvent in any subclass of Flickable.
Note: I wrote that Flickable class.

Related

What to paint in the paintEvent implementation?

The Qt's documentation says:
Many widgets can simply repaint their entire surface when asked to, but some slow widgets need to optimize by painting only the requested region: QPaintEvent::region(). This speed optimization does not change the result, as painting is clipped to that region during event processing. QListView and QTableView do this, for example.
Do this mean that when I write a QWidget's paintEvent function, there's no need to manually write codes about which part of the widget should be painted and it is all handled automatically by qt itself?
when I write a QWidget's paintEvent function, there's no need to manually write codes about which part of the widget should be painted [?]
That's correct. The painter passed to you is already clipped to your widget's rectangle, so you can't paint outside of your widget. If your painting is cheap, then you can certainly repaint the entire widget at once, and ignore the repaint rectangle or region passed in the paint event.
You should also not blindly assume that painting only a part of the widget will be somehow inherently faster. You must measure this!
Could you explain more about the paintevent's propagation direction, usually qt's events are propagated as child->parent, but is the paintevent different?
All widgets that need to be painted are painted. The event is not propagated, it is delivered directly to the relevant widgets, under the direction of the widget compositor.
[...] when a Child widget's paintEvent is called, it also gets its Parent widget's paintEvent called. Why is this?
Two separate widgets happen to be painted. Just because they happen to be a parent and a child doesn't mean much. Each widget cares about its paint event in isolation. The widget compositor (backing store) has the big picture. If the parent is painted for seemingly no reason just prior to the child being painted, then likely the child isn't opaque and the parent is painted so that the child can be composited on top of it. Set Qt::WA_OpaquePaintEvent if your paintEvent does opaque painting and no part of the parent is visible through what's painted.

How can I prevent a QWidget from painting, but still respond to events?

I have a QGLWidget that renders an OpenGL scene inside a Qt application. I would like to add some other translucent Qt widgets that are overlaid on top of the QGLWidget. This is more difficult than with standard widgets since the OpenGL drawing context is unknown to Qt's painter classes. So, if I just do the obvious thing and place a transparent toolbar on top of the QGLWidget for instance, the transparent part of the toolbar instead renders black (it doesn't have access to the OpenGL frame buffer when painting).
It seems that the recommended way for handling this sort of thing is to overpaint the 2D content after drawing the OpenGL scene. The linked example seems very straightforward, as long as you're just drawing simple shapes. Instead, what I would like to do is to defer the painting of some child QWidget objects to be done inside the paint event for the QGLWidget.
So, my problem boils down to this:
Prevent the overlay QWidgets from painting in the normal, 2D context.
In the paint event handler for the QGLWidget, paint the overlays after painting the 3D scene that makes up the background.
The second item on this list seems to be simple: I can use QWidget::render() inside the QGLWidget's paint event to draw the desired widgets on top of the viewport. This works just fine.
The first item is more tricky: I need a way to prevent the widgets from painting during the normal course of events and only paint them in the QGLWidget's paint handler. One obvious way to do this is to hide the overlays using QWidget::hide(). This does allow me to paint the widgets atop the OpenGL scene as I would like. However, since the widgets are hidden, they do not respond to mouse or keyboard events. As an example, I'm using a QToolBar with a few buttons on it, and the toolbar is painted properly, but is non-functional (none of the buttons respond to clicks). So, if going down this path, it seems that I would need a way to force the widget to still respond to events even though it is hidden.
Another approach that I've tried is to intercept the QToolBar's paint event using an event filter, with the goal of preventing the toolbar from painting itself. However, the toolbar is still rendered; I'm assuming that this is because the various buttons on the toolbar are child widgets that are still painted, even if I intercept the parent's paint event.
Any ideas on a way that I could accomplish my goal?
I don't understand your issue completely, but I'll try to answer the question stated in the title. You should use event filters. Install an event filter using widget->installEventFilter(object), where widget is a widget you want to block painting, and object is an object of any of your QObject-derived classes. Reimplement eventFilter of this class:
bool MyClass::eventFilter(QObject* object, QEvent* event) {
if (event->type() == QEvent::Paint) { return true; }
return false;
}
When you return true from your eventFilter, the event is filtered and paint doesn't occur.
You can also try to use widget->setUpdatesEnabled(false) to temporarily disable painting. Don't forget to re-enable it when you're done.

How can you know when a specific part of an item drawn with a QStyledItemDelegate is hovered?

I have a custom QAbstractItemModel used to display information in a QTreeWidget. However, individual indices are drawn using a QStyledItemDelegate. One item that is drawn using the delegate is a pixmap. When the user hovers the mouse over the pixmap (either help event style or hover enter style is fine) I need to do something, what it is isn't important.
So my question is, how can I know when the mouse has hovered over a specific item inside a QTreeWidget, when that item is drawn using a delegate?
In other situations, I could just subclass QLabel, set the pixmap on it, and then do whatever I need in the event() function, but in this case there is no object behind the pixmap, it is just painted onto the screen, so it doesn't actually receive events. Is it possible to use the delegate to paint an actual widget where I want it to so that widget can receive events, or do I have to work around this some other way?
You could subclass QTreeWidget and reimplement the mouseMoveEvent. In the event you can use the itemAt function in order to check if a valid item is at the mouse position and then do what you want.
void MyTreeWidget::mouseMoveEvent(QMouseEvent * event)
{
QTreeWidget::mouseMoveEvent(event);
QTreeWidgetItem* treeItem = itemAt(event->pos());
if (treeItem != NULL)
doSomething(treeItem);
}
You could avoid subclassing QTreeWidget and implement it in the parent widget/main window. Notice however that the itemAt function expects coordinates in the widget's viewport so you should transform the coordinates to tree widget's coordinates. IMHO it is more elegant to subclass it and just implement the mouseMoveEvent function.
EDIT
If you need to detect the position of an icon within the widget item, it is a bit more advanced but you can check my answer to an older question for more details:
Position of icon in QTreeWidgetItem
There is no Qt built-in solution to this problem. The problem can be solved, however, by saving the geometry of the individual items that are painted using the delegate as they are painted. The paint() function, however, has a const modifier so the data structure you use to save the geometry must be mutable. In this case, I don't think this constitutes a breach of the principles of OOP, but is rather a prime example of why the mutable keyword exists and when it should be used.
You then need to subclass QTreeWidget so you can re-implement the mouseMoveEvent() function as webclectic said. Inside that function you can compare the position of the mouse to the geometry of the item that you painted earlier. If the mouse is inside the item, then it is being hovered.

How to anchor QGraphicsWidget/Item (make them static) on QGraphicsView

I want to make something like a hud. I have a QGraphicsScene with a huge QPixmap, that I added to a much smaller QGraphicsView. Now I need to add some control elements, like QLabel, QPushButton and QGraphicsEllipseItem. That's, I think, is not a problem. I found this helpful page Graphics View Classes. But how do I anchor this control elements to the View or maybe better said how do I not anchor them? When I drag the QPixmap, then I don't want that the hud is moving with the QPixmap out of the QGraphicsView.
What do I need to do, to solve this problem. Hope someone can help me.
You should just have to create your widgets and specify the QGraphicsView in the constructor like this:
QPushButton *button1 = new QPushButton(view);

QGraphicsView - how to disable mouse and keyboard scrolling

In my application I have QGraphicsScene with pixmap added and all is viewed in QGraphicsView with scrollbars off.
The program window is smaller then pixmap and when I press arrow keys or move mouse wheel the pixmap is being moved.
How do I disable that so even if the pixmap is bigger than window it won't be moved by keyboard or mouse unless I use my events for that?
(I tried to set interactive property to false but that didn't work)
I believe the easiest solution would be to set FocusPolicy of QGraphicsView to NoFocus and then process all key events in main window.
ui->graphicsView->setFocusPolicy( Qt::NoFocus );
I think the easy way is to use QGraphicsView::setSceneRect() to set the area that the view is allowed to visualize.
If you set the view's sceneRect() to the actual size of the view widget (or just slightly smaller), then it won't be able to scroll or pan outside of this rectangle.
I don't know if there's an easier way, but the moderately easy way would be to make a class that inherits your QGraphicsView class, and overrides the mouse and key press events.
The harder way would involve installing an event filter on the widget.
In my opinion, the hardest brute-force solution would be to detect the change through some sort of signal, and set it back to how it was.

Resources