How to resize QWidget without intermediate paint - python

During the study, Qt encountered such a problem. Suppose I have a QWidget on the QMainWindow. How can I make sure that when I resize QMainWindow, QWidget on this QMainWindow do not repaint content until the resizing does not stop.
Yeah, I saw this example How to disable multiple auto-redrawing at resizing widgets in PyQt?
But when I tried this method it's just locked widget content. I just wondered if it was possible to make sure that the contents of the QWidget did not change while we resizing MainWindow. Please tell me, is this possible?
Thanks a lot.

I'm still guessing slightly as to what you want exactly but it sounds as if you essentially want two modes for your paintEvent method -- the normal one that takes care of rendering the widget most of the time and a second, lightweight, mode that can be used whilst the widget is being resized.
If that's the case then you could try something like the following...
#!/usr/bin/python3
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class widget(QWidget):
def __init__(self):
super().__init__()
self.resize_timer = QTimer(self)
self.resize_timer.setSingleShot(True)
self.resize_timer.setInterval(100)
self.resize_timer.timeout.connect(self.delayed_update)
def delayed_update(self):
self.update()
def paintEvent(self, event):
if self.resize_timer.isActive():
print("painting deferred")
# Your `lightweight' rendering goes here and will be used
# while the widget is being resized.
else:
print("painting now")
# Full rendering code goes here.
def resizeEvent(self, event):
super(widget, self).resizeEvent(event)
self.resize_timer.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
f = widget()
f.show()
sys.exit(app.exec_())
Note that it is essentially just a simple modification of the code in the answer you linked to.

Related

How to get objectName of a pyqtgraph plotwidget during a mouse wheelEvent?

I am trying to identify the object name of a pyqtgraph plotwidget I am mouse wheeling on. However, I can only seem to get the object id "PyQt5.QtWidgets.QWidget object at 0x0000018ED2ED74C8". If I use the QApplication.widgetAt(event.globalPos()).objectName I get nothing, even though I have set the object name. Can you help me?
Sample code:
# Import packages
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
import pyqtgraph as pg
import sys
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.graphLayout = QHBoxLayout()
self.graph = pg.PlotWidget(name="graph1")
self.graph.setObjectName("graph1")
self.graphLayout.addWidget(self.graph)
self.setLayout(self.graphLayout)
def wheelEvent(self, event):
hoveredWidget = QApplication.widgetAt(event.globalPos())
print(hoveredWidget.objectName())
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
A PlotWidget is actually a subclass of QAbstractScrollArea, which is a complex widget that has at least three children widgets: the scroll bars (even when they're hidden) and, most importantly, the viewport, which actually is the "content" of the scroll area.
This means that using widgetAt() you are not getting the plot widget (the scroll area), but its viewport. In fact, in your case you can get the plot widget by checking the parent:
def wheelEvent(self, event):
hoveredWidget = QApplication.widgetAt(event.globalPos())
if hoveredWidget and hoveredWidget.parent():
print(hoveredWidget.parent().objectName())
Be careful when intercepting events from a parent widget, especially for widget as complex as scroll areas: it's not guaranteed that you will receive them, as the children could accept them, preventing further propagation to their parent(s).
If you need more control over them, it's usually better to implement the respective methods in their subclasses or installing an event filter on the instances.
Note that, for the reason above, if you want to filter events on a scroll area you might prefer to install the filter on the viewport:
self.graph.viewport().installEventFilter(self)

How to show tooltip while not focusing at pyqt5 python?

I want to show tooltip while not focusing.
I made the code by referring to this PyQt Window Focus
But, it works after click window just one. Works fine, but window always blink at taskbar.
And I think this method is inefficient.
I think it's as if os are not resting while waiting for task to come, but checking every moment for task to come.
This is a simple window window, so it won't use up the cpu much, but I want to code it more efficiently.
Is there any way to improve this?
Or this method right because focusoutEvent excuted only one? ( Cpu resource 0% )
If right, how can I remove blink at taskbar?
I check reference focusPolicy-prop
import sys, os
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.initUI()
def initUI(self):
vbox = QVBoxLayout()
vbox.addStretch(2)
btn = QPushButton("Test")
btn.setToolTip("This tooltip")
vbox.addWidget(btn)
vbox.addStretch(1)
self.setLayout(vbox)
self.setGeometry(300, 300, 300, 200)
self.show()
def focusOutEvent(self, event):
self.setFocus(True)
self.activateWindow()
self.raise_()
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyApp()
sys.exit(app.exec_())
You are having an XY problem: trying to find a solution (usually unorthodox and overly complicated) for a problem that is originated elsewhere.
What you want to do is to show tooltips even if the window is not focused, not to restore the focus of the window; to achieve this you must not reactivate the window when it loses focus (which not only is WRONG, but is both a wrong way and reason for doing so).
You just have to set the WA_AlwaysShowToolTips widget attribute on the top level window (and remove the unnecessary focusOutEvent override, obviously).
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.setFocusPolicy(QtCore.Qt.ClickFocus)
self.initUI()
self.setAttribute(QtCore.Qt.WA_AlwaysShowToolTips, True)
Note that the attribute must be set on a widget that is a top level window, so, unless you're using a QMainWindow or you are absolutely sure that the QWidget will always be a window, it's usually better to do this instead:
self.window().setAttribute(QtCore.Qt.WA_AlwaysShowToolTips, True)
Besides that, the blinking is normal on windows, and has nothing to do with CPU usage:
activateWindow():
[...] On Windows, if you are calling this when the application is not currently the active one then it will not make it the active window. It will change the color of the taskbar entry to indicate that the window has changed in some way. This is because Microsoft does not allow an application to interrupt what the user is currently doing in another application.

Briefly flash a picture

I'm trying to modify a program written using pyQt (specifically, Anki). I want the program to briefly flash a picture (stored as a file on my hard drive) and then continue running normally.
This code will be inserted at some arbitrary point in the program. This is an ad-hoc single-user patch to an existing program - it does not need to be fast or elegant or easily-maintained.
My problem is that I know very little about pyQt. Do I need to define an entire new "window", or can I just run some sort of "notification" function with an image inside it?
QSplashScreen will be useful for this. It is mainly used for displaying certain image/text while a program loads, but your case also looks ideal for this. You can close it with clicking on it, or additionally you can set a timer to auto-close it after some time.
Here is a simple example with a dialog that has one button. When pressed it'll show the image and close after 2 seconds:
import sys
from PyQt4 import QtGui, QtCore
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
layout = QtGui.QVBoxLayout()
self.setLayout(layout)
self.b1 = QtGui.QPushButton('flash splash')
self.b1.clicked.connect(self.flashSplash)
layout.addWidget(self.b1)
def flashSplash(self):
# Be sure to keep a reference to the SplashScreen
# otherwise it'll be garbage collected
# That's why there is 'self.' in front of the name
self.splash = QtGui.QSplashScreen(QtGui.QPixmap('/path/to/image.jpg'))
# SplashScreen will be in the center of the screen by default.
# You can move it to a certain place if you want.
# self.splash.move(10,10)
self.splash.show()
# Close the SplashScreen after 2 secs (2000 ms)
QtCore.QTimer.singleShot(2000, self.splash.close)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Dialog()
main.show()
sys.exit(app.exec_())

pyQt QLayout issue

This is driving me a little crazy. Hope someone can clear this up for me. Running the following code results in the first print statement being a list with one element the QVBoxLayout object. I set two objects to layout why do I get only one?
The second print statement gives two objects the QHBoxLayout and QPushButton. Isn't QPushButton a child of layout?
I would expect layout.children() to give me two objects QPushButton and QVBoxLayout
and self.children() to give me one object QHBoxLayout. What am I missing?
from PySide.QtGui import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
layout.addWidget(QPushButton("foo"))
layout.addLayout(QVBoxLayout())
print layout.children()
print self.children()
app = QApplication([])
main = Main()
main.show()
sys.exit(app.exec_())
I guess the note from the documentation explains this clearly enough:
Note: Widgets in a layout are children of the widget on which the
layout is installed, not of the layout itself. Widgets can only have
other widgets as parent, not layouts.

QWidget showFullScreen produces multiple resizeEvents

I have a QT application written in python using PySide and I stumbled across a little problem regarding the showFullScreen method of the QGLWidget (although the problem occurs with every other widget too):
The problem is, that the widget doesn't have its 'final' resolution after the program returns from showFullScreen.
The switch seems to be triggered asynchronously between 5 and 10 milliseconds later.
This is a problem for me because I have to do some layout calculations which depend on the widget's size after it is shown.
Below is a little reproducer which subclasses QGLWidget. Using this reproducer you will take notice, that resizeEvent will be called twice after showFullScreen.
I'm looking for a convinient way of knowing which resizeEvent is the 'final' one, or a way of knowing, when the widget really is in fullscreen mode. Is there maybe any signals I could connect to?
Thanks a lot for any help on this.
#!/usr/bin/python
import sys
from PySide.QtGui import QApplication
from PySide.QtCore import QTimer
from PySide.QtOpenGL import QGLWidget
class TestWidget(QGLWidget):
def __init__(self, parent=None):
super(TestWidget, self).__init__(parent)
self._timer = QTimer()
self._timer.setInterval(5)
self._timer.timeout.connect(self.showsize)
self._timer.start()
def resizeEvent(self, event):
print "Resize event:", event.size().width(), event.size().height()
def showsize(self):
w = widget.size().width()
print "Timer: ", w, widget.size().height()
if w == 1680:
self._timer.stop()
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = TestWidget()
widget.showFullScreen()
print "After showFullScreen:", widget.size().width(), widget.size().height()
# this will always be 640 480...1680 1050 is what I'm expecting
app.exec_()
One possibility is to check that if the resize event is spontaneous or not. From limited testing here (using Qt C++ on Linux), the second resize event is spontaneous, while the first one is not.
You could do your calculations only when the event is spontaneous.
Note: I'm not sure how portable this is, it might depend on your window manager/windowing system.

Categories

Resources