How to stop a paint event from propagating? - c++

I have two widgets, A and B, A has B as its parent.
Inside the A widget, I have a timer to trigger the repaint slot of itself. Thus, the paintEvent of widget A is triggered. However, I found B's paintEvent is also triggered. How could I trigger only A's paintevet?
I have tried to accept A's paintEvent as:
void A::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
paintA();
event->accept();
}
But it doesn't help. What should I do?

When a widget is sent a paint event, so are all of its enabled children. You can work around this by installing an event filter on the child widget and discarding any paint events you don't want.

You can't because Qt must do composition of the widgets.
Options:
Consider making the widget a non-child and display it as a separate "window". You can use Qt::FramelessWindowHint and Qt::WA_TranslucentBackground to make it look as a child widget. This option will give you perfect results as it leaves the composition to the underlying OS, which, at least on Desktop, will not repaint the bottom widget unless requested.
Consider caching. Use QPixmapCache to cache all drawing of your bottom widget to one window-sized pixmap, which will be very fast to draw when needed.

Related

Qt paintEvent() unnormally triggered

I'm a Qt beginner.
So I have my MainWindow with a QSlider and a QPixmap. I redefined the paintEvent( QPaintEvent* event ) and
connect( slider, SIGNAL(valueChanged(int)), this, SLOT(centerChange(int)) );
with a slot:
void MainWindow::centerChange(int value)
{
center = value;
update();
}
So I wanted repainte the Pixmap only if the slider's value is changed. But I notice that everytime when I make a mouse-in or a mouse-out to the slider, the repaint is triggered. Why is this happenning?
Thanks.
Paint event may be triggered at any time by the underlying Qt drawing system. You should not assume that paint event can be triggered only by you. You need to change the logic in your app.
The documentation says:
A paint event is a request to repaint all or part of a widget. It can happen for one of the following reasons:
repaint() or update() was invoked,
the widget was obscured and has now been uncovered, or
many other reasons.
(emphasis mine). Specifically, the underlying OS could trigger a repaint event whenever it feels like.
Based on your requirements, don't reimplement paintEvent then. Just do your QPixmap updating when the slider value changed.

Qt how to set global mouseReleaseEvent when i have pile of widgets

is there away to set global mouseReleaseEvent?
what i mean is i have QMainWindow and on it QFrame and init QListView and In it
i have Widgets that constarct the QListView and inside the widget i have al sort of lables and text fields.
so i want to detect mouseRelease any where in my app i have to implement in all widgets the mouseReleaseEvent?
void ItemWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
;
}
event->accept();
}
According to the QMouseEvent documentation, the widget that receives the mouse press will also get the mouse release. So, you shouldn't have to look any further for your mouse release than the widget that received the original press. This is usually referred to as a mouse "grab."
You may also want to check that Qt::WA_NoMousePropagation is not set on one of your children. If it is set, it will ensure that your mouse event does not bubble up.
If you really want to catch all mouse release events, you could try installing an event filter on the QApplication itself. That is a bit of a heavy solution, but you should get every mouse release event.
Looking at the (somewhat older) documentation here indicates that events are by default ignored by a widget and are propagated to their parents. So, if you make all your widgets children of your main application widget (or 'grandchildren', etc.) then you should only need to install the event handler on the application widget.

qt hiding a control on showEvent()

I call show() on a window and it has several controls and all controls are displayed.
One of the controls is a custom control that inherits from QFrame.
I want to hide this control if a particular flag is set. So, I have
void MyCustomControl::showEvent ( QShowEvent * /* evt */ )
{
if (!m_visibleAllowed)
hide();
}
While this hides the control, it makes the control goofy; it looks frozen. When the window is resized, the area where the control is supposed to be does not get refreshed. Searching around forums, the idea that I get is that hiding the control is not supposed to be done on showEvent() is that true? if so then how/where should I try to hide the control. If hiding the control from showEvent() is possible, how can I prevent the control getting frozen.
Thanks for you time.
If the problem is with calling hide() during your show event (I can't confirm that it's explicitly disallowed, but it doesn't sound like a good idea in general) and calling hide from your show event is where you really need to have this code then you could use a single shot timer:
QTimer::singleShot( 0, this, SLOT(hide()) );
which will simply defer the execution of the hide() function until the next round of the event loop.
Maybe you could use a QStackedLayout or a QStackedWidget that has two widgets in the stack: your control, and a "blank" QWidget. If you did that, instead of using show() and hide() on your control, you switch what's on top of the stack.
That way you never try to render a hidden widget - if your control isn't visible, you render the blank QWidget instead - and I suspect this will solve your graphics glitches.
Hope this helps!

How to get mouse pressed events in the root widget of a hierarchy in Qt

I have the following problem with Qt (no answer on this site seemed to address exactly this problem so I create my own question).
I have an application with a MainWindow class which inherits from QWidget. At a certain point there is a table inside the main window and I want to catch all mouse pressed events outside that table.
My first solution was to reimplement the method
/* virtual */ void MainWindow::mousePressEvent(QMouseEvent *event)
In this method, I check the position of the event and check that it is not within the QRect of the table. Unfortunately, I realized that mousePressEvent() is not always called. I suspect that if I click on another child widget of MainWindow, that widget consumes the event and does not pass it through to the parent.
So the only alternative idea I had was to reimplement the mousePressEvent() method for all the widgets contained in MainWindow. This is of course not feasible, because:
There a lot of them: it would be very complex, time-consuming, error-prone, and difficult to maintain if one had to change all the widget classes that are instantiated inside MainWindow.
Some of the subwidgets are implemented in some library modules developed in a parallel project, so I cannot change those.
In other cases, the subwidgets use Qt classes directly.
Even if I defined custom subclasses for 2 and 3, I would have to make sure that these subclasses are used everywhere instead of the original classes. This might imply again falling back to case 2.
So this alternative solution seems unfeasible to me.
Summarizing: Do you know if there is a simple method to catch all mouse clicks on the main window from within the MainWindow class?
You could do this by installing an event filter in the main window. Take a look at QObject::installEventFilter() in the Qt docs.
You can set the attribute Qt::WA_TransparentForMouseEvents with QWidget::setAttribute to all the child widgets except the table to get the mouse events in the MainWindow (which will only work if the table is a direct child of MainWindow).
Or do the opposite, and add a transparent widget above your whole MainWindow with a hole at the position of the table. And you set/unset the Qt::WA_TransparentForMouseEvents to that widget when you want it to let the clicks pass or to catch them.
The hole can be created with QWidget::setMask() and QRegion::substracted().

How to paint with QPainter only after a specific event?

I have a main window with some widgets on it, each needs its own graphic. I would like to use QPainter to draw shapes, lines, etc. on them, but only after a specific event, like the press of a button.
The problem is, if I just create a QPainter in any function, it won't work:
QPainter::setPen: Painter not active
The QPainter methods can only be called inside a paintEvent(QPaintEvent *) function! This raises the following problems:
I have to derive my custom classes for all the widgets I would like to paint on, so I can't use the Designer to place my widgets. This can get frustrating with a large number of widgets.
The widgets redraw themselves after each paint event of the window, like moving it around, or moving other windows in front of it. I do a lot of drawing in those widgets, so they will visibly blink in these cases.
Is there a better and simpler way to solve this? I started to think about just displaying images, and re-manufacturing those images only when the specific buttons are pressed. I doubt that it's the most elegant solution...
You can use custom widgets in the designer: Creating Custom Widgets for Qt Designer.
Qt Designer's plugin-based architecture allows user-defined and third party custom widgets to be edited just like you do with standard Qt widgets.
For your second question, one of the approaches is to create a QPixmap for each of your widgets. When your widget's appearance needs to be changed, you draw in that pixmap (using QPainter's constructor that takes a QPaintDevice - QPixmap is a QPaintDevice).
In your widget's paintEvent function, you simply fill your widget with that "cache" pixmap. This way, you only do the (potentially expensive) painting when it's actually necessary.