Refreshing a QWidget - python

I've been having this issue a lot of times.
When I modify some properties of a QWidget after the widget.show(), the widget won't update. Most of the time, a mouse click or when the mouse leaves or enters the widget, the widget will be updated. However, if I leave the mouse, it won't refresh by itself.
Until now I managed to deal with this by doing :
widget.hide()
widget.show()
But this is a very dirty fix. Is there a better way to tell python to refresh the widget ?
Thank you.

To update the widget, you should repaint() it, but calling repaint() directly is not very good, so try:
widget.update()
From doc:
This function does not cause an immediate repaint; instead it
schedules a paint event for processing when Qt returns to the main
event loop. This permits Qt to optimize for more speed and less
flicker than a call to repaint() does.
Calling update() several times normally results in just one
paintEvent() call.
Qt normally erases the widget's area before the paintEvent() call. If
the Qt::WA_OpaquePaintEvent widget attribute is set, the widget is
responsible for painting all its pixels with an opaque color.

Did you already try the
QWidget.update()
This function updates only the visible parts keeping the invisible parts untouched.

Related

Python/PySide: How to make a widget that will stay on top of the main window, but not cover up other widgets?

So I have a script running inside another program (The Foundry's Hiero) and I'm just making a new QWidget object, and calling self.show()
Now, I can set it to self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint), so my window will stay on top of the main window, even if you click on something in the main window.
The problem is, this is a sort of popup window that you configure settings in, and it triggers other QWidget popups. If I set my window to WindowStaysOnTopHint, those subdialogs that my widget triggers end up beneath my widget.
Is there a way in PySide/PyQt to make a window stay on top/keep focus from the main application window in particular, but not everything?
You can use the QApplication.focusChanged signal to raise your widget up when Hiero's main window is selected. Then you would just need to remove the WindowStaysOnTopHint flag.
I'm not familiar with Hiero's API, but I'm guessing you could try something like:
def raiseMyWidget(old, new):
if new == hiero.ui.mainWindow():
myWidget.raise_()
QtWidgets.QApplication.instance().focusChanged.connect(raiseMyWidget)
Hope this helps! You can take advantage of the old parameter or some other means to make sure that your widget isn't raised above the others as well.

How would I go about making a overlay widget

How would I go about making a overlay widget with qt?
I've considered using a QPaintEvent or a QGraphicsScene, but I want to be able to add widgets and for the widget to not occupy space in a layout, causing other widgets to shift when the popup appears.
I believe the best solution is to parent the so called overlay widget to the window or even have the overlay widget be in its own window.
The first solution might be easier to do, but the overlay widget is bound to the inside of the window.
If you go with the second solution, you will have to play with the windows flags to make it borderless.
In both cases, you may have to use the raise() function to make sure your overlay widget is on top.
Discussing "using a QPaintEvent or a QGraphicsScene" is off-topic. How you draw the widget does not impact how the widget will interact with the widget stack.
If you want an example, you can take a look at the code of QCompleter which does something similar. In particular look for QCompleter::setPopup() and QCompleterPrivate::showPopup().

How to trigger a signal when one widget comes within some distance/area of another widget?

I want to create an area around a widget such that if any other widgets come in that area, the widget sends a signal. Calculating the distance between the widget and every other widget might be an option, but the problem is there might be several widgets and it might be tedious. Specifically speaking, the widget is a QLabel and I am using QPoint to place the widgets. Is there an efficient way to solve the problem?
It seems as though you want Widget1 to emit a signal when any other widget (e.g., Widget2) is moved such that it lands within the region of interest around Widget1. One way to do this is override the moving widgets' moveEvent() handler functions to notify Widget1 of their new position.
This handler is called after a widget has been moved, and is already at the new position. The simplest thing might be to just stick a custom signal at the top of this, before calling the parent's implementation of the handler. Something like:
MyWidget::moveEvent(QMoveEvent *ev) {
emit widgetMoved(ev->pos()); // emit signal with this widget's new position
QLabel::moveEvent(ev);
}
This requires subclassing QLabel, declaring the widgetMoved() signal, and reimplementing the handler. You could also send the this pointer in the signal, so that Widget1 knows immediately which widget sent the event. (This isn't necessary, since you can get a pointer to the sending widget with QObject::sender(), but it might be easier.)
Then write a slot in Widget1, connected to this signal, that computes the distance between this moved widget and itself, and emit a signal if that distance is less than whatever the ROI size is.

Pyqt QMdiSubWindow resize with variable number of widgets

I have an QMdiSubWindow which contains a couple of widgets. One of these widgets is a QWidget with a QGridLayout() which can contain an arbitrary number of sub-widgets which is determined at runtime (initially none). I can't seem to workout how to get the MDISubWindow to automatically resize when the number of sub-widgets in the grid layout changes. Should I be re-implementing the sizeHint() somewhere? ie in the main widget or the sub-widgets?
The QMdiSubWindow resizes fine when I drag the resize handle with the mouse and snaps to show the correct size.
I've tried calling .resize() and .updateGeometry() on both the widget and the QMdiSubWindow but It doesn't obviously work. Any clues would be much appreciated.
#ekhumoro suggested I try adjustSize(), which didn't work initially. I did some more searching and ended up with the following solution which worked for me
QTimer.singleShot(1, self.parent.windows[self.uuid].adjustSize)
where
self.parent.windows[self.uuid]
is the QMdiSubWindow object. I'm guessing that just calling self.adjustSize() doesn't work because the sizeHint is not updated until the later in the event queue.

Force repaint in wxPython Canvas

I've got a Canvas which manipulates objects in the mouse event handler. After modifying the objects, I want to trigger the OnPaint() event for the same Canvas to show (rerender) the changes. What is the right way to do this? It doesn't let me call OnPaint() directly. Also, is triggering an event from another event "wrong" in some sense, or likely to lead to trouble?
I would just call self.Refresh() or maybe RefreshRect() and pass the area that needs to be repainted.
I tried self.refresh() as suggested by Mike Driscoll. That worked partially. I'm moving my own objects with mouse events and then repainting the image - rendering my objects. So the mouse events were updating coordinates and the repaint was updating the display. The problem is that refresh() seems to be called lazily or has lower priority than the mouse events. Refresh() thus produces a choppy display.
Since I am using a buffered display via the wxDemo for Scrolled Area, I took the following line from the Paint event:
dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
And called my drawing function with that DC from within the mouse event handler. Upon completion dc is freed and (according to the comments in the demo) copied to the screen. It was not previously clear to me how the Paint event was handling DCs, so I was trying to call the event. Turns out you can use that DC and just render from inside the mouse events. This results in smooth dragging of my custom drawn objects.

Categories