QPainter redraw on window gaining/losing focus - qt

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).

Related

Qt5: paintEvent is not called after an update() in mouseMoveEvent

I am using a QAbstractScrollArea + a custom QWidget derivative widget as "the" widget
(having sizeHint() overriden).
The application needs to constantly update the QWidget content during mouseMove (kind of
a drag, but not exactly, it is a drawing tool to draw lines between objects, thus the mouse
cursor needs to change + the line needs to be drawn on the fly).
The application works fine, e.g. I can render the content properly, scrollbars visible,
I can manual update the viewport during scrollContentsBy, etc.
However regardless that I manually fire a update() (or repaint()) call in mouseMoveEvent,
the paintEvent will only be called after the "drag" process is over, e.g. the mouseReleaseEvent is fired.
And I'm clueless why this happens. Obviously if I do the update directly within the
mouseMoveEvent, it'll be rejected by QPainter (as I can't instantiate a QPainter within
the mouseMoveEvent - Qt drops "paintEngine should no longer be called" when trying to do that).
Any suggestion is welcomed :)
Thanks,
Meh, lame me. Never never mix "update()" with "viewport()->update()"

Qt Main window loosing focus and going to background unexpectedly

Going up the learning curve and everything has been making sense so far except for this one:
I have a main window from where I call one or more dialog like this:
d->setWindowFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint);
d->setGeometry(etc...
d->show();
The dialogs are not bound to the main window, and they are actually set to be displayed right beside the main window looking like an extension.
To close the dialogs I use a mouse 'enterEvent' on the mainwindow to send a close signal to the dialog(s) which will use hide().
void MainWidget::enterEvent(QEvent *event)
{
emit signal_related_close();
}
This works but randomly this causes a very annoying issue. When the signal is sent to close the dialogs, the main window would loose focus and go in the background (behind whatever application that is on the screen). Note that it does not minimize but only goes in the background and like I said it is random (about one out of 3 times I do it). I can't seem to get a pattern.
I initially thought the signal was messed up and instead used a dialog::leaveEvent() but that didn't help. I also tried using dialog::mouseMoveEvent and using that to hide the dialog but that would still randomly loose focus on the application.
The only method that doesn't loose focus is using a push button on each dialog that will initiate the same hide commands but that's not great for my UI. So it would seem that using the mouse enterEvent/leaveEvent,mouseMoveEvent will cause that random issue.
Anyone experienced this issue before? Any idea what could be wrong? Thx.
I can't explain why but this problem occurred when using QEvent or any mouse related event together with recursive children widgets. For example take this code in the parent widget constructor:
ChildWidget c1 = new ChildWidget (this);
ChildWidget c2 = new ChildWidget (c1);
Childwidget c3 = new ChildWidget (c2);
Using QEvent in the parent widget will randomly make the window go to the background and loose focus.
Setting all the ChildWidget to the same parent stopped the problem.

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.

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.

Qt - top level widget with keyboard and mouse event transparency?

I want an app's main window to ignore mouse and keyboard events, passing them to applications underneath it in the window manager Z-order.
I see how to make child widgets ignore keyboard or mouse events, but how about the main window?
I'm trying to make a desktop widget that always sits just over the background and is totally invisible to keyboard and mouse events. (Pass through)
Qt::X11BypassWindowManagerHint gets me keyboard pass through (although sadly X11 specific, but fine for now), so how about mouse events?
Is there a OS-agnostic way to be transparent to keyboard events?
EDIT:
The key word here is transparency.
I don't want to EAT mouse and keyboard events, I want the window manager to know I don't want them at all. Those events should be directed to whatever application is under me in the zorder.
For example, I want to be able to click on desktop icons that are covered by my widget and interact with them as if the widget was not there.
I found the following solution (tested on Linux, also works on Windows according to #TheSHEEEP):
setWindowFlags(windowFlags() | Qt::WindowTransparentForInput);
It has been added in more recent qt release (i did not find when)
see http://doc.qt.io/qt-5/qt.html
On Windows you can set WS_EX_TRANSPARENT
To do this in Qt use the following code:
Include the header,
#if _WIN32
#include <windows.h>
#endif
and put the following code into the constructor.
#if _WIN32
HWND hwnd = (HWND) winId();
LONG styles = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, styles | WS_EX_TRANSPARENT);
#endif
Maybe what you want is
widget->setAttribute(Qt::WA_TransparentForMouseEvents)
? That's what QRubberBand uses to let it's parent handle the mouse events. As for keyboard events, a QWidget doesn't get any keyboard events unless it has set itself a focusPolicy().
setFocusPolicy( Qt::NoFocus );
should therefore take care of the keyboard events.
Use Qt's event filters: they will allow your application to eat whichever events you specify (i.e. keyboard and mouse events) but still process other events such as paint events.
bool FilterObject::eventFilter(QObject* object, QEvent* event)
{
QKeyEvent* pKeyEvent = qobject_cast<QKeyEvent*>(event);
QMouseEvent* pMouseEvent = qobject_cast<QMouseEvent*>(event);
if (pKeyEvent || pMouseEvent)
{
// eat all keyboard and mouse events
return true;
}
return FilterObjectParent::eventFilter(object, event);
}
Maybe I'm missing something here, but have you tried subclassing the QMainWindow class and overriding the QWidget::event() method to always return false? If you need to handle some events, you could add that intelligence here as well.
This technique should allow you to inspect the events coming in to the application and ignore them if desired without having to eat them using an event filter.
If this doesn't work you could attempt to redirect the events to the desktop by calling QCoreApplication::notify() and passing the event to the desktop widget obtained by calling QApplication::desktop(). I have no idea if this would work, but it seemed like it might be worth giving a try.
I think that overriding is supposed to work:
bool YourMainWindow::event( QEvent *event )
{
event ->accept();
return true;
}
that's some of what the QWidget class documentation says about event() member function:
This function returns true if the
event was recognized, otherwise it
returns false. If the recognized event
was accepted (see QEvent::accepted),
any further processing such as event
propagation to the parent widget
stops.

Resources