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.
Related
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.
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.
i'm having some trouble in building my app, I have a GUI made in pyqt4 and Python, I use QThread to check the cpu load every 2 seconds and I want to diplay it on a progress bar. My GUI is in one class and my Qthread is in the other.
This is my code: pyqt classes, my code printscreen
I want to know how to pass my values collected in QThread to Qobjects in my other Class.
import sys,os,module,config_read,time,threading,datecs_print,mysql.connector as mariadb,commandList
import psutil,logging
from PyQt4 import QtGui, uic ,QtSql,QtCore
from PyQt4.QtCore import QThread, SIGNAL
import resources
import webbrowser
sys.stderr = open("errlog.txt", "w")
class systemValues(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def cpuRunValue(self):
while (1):
for x in range(2):
p = psutil.cpu_percent(1,False)
return p
def cpuProgressBarUpdate(self):
while(1):
# MyWindow.setcpuBarValue(val=77)
MyWindow.cpuBar.setValue(value=77)
def run(self):
# self.cpuRunValue()
print(self.cpuRunValue())
# self.cpuProgressBarUpdate()
class MyWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QDialog.__init__(self)
super(MyWindow, self).__init__()
file_path = os.path.abspath("ui/sales_window.ui")
uic.loadUi(file_path, self)
self.myThread = systemValues()
self.myThread.start()
def setcpuBarValue(self):
threading.Thread(target=self.cpuBar.setValue(systemValues.cpuRunValue())).start()
This is my code, I get no error. I just cant transfer my value I get from cpuRunValue() to QprogressBar from MyWindow. I'm not very experienced with this.
PS: I eliminated lots of code that's not necessary, but please let me know if you need more info.
Thank You.
If you want to use threads in Qt then you must understand them well. For instance read this page first. Certainly don't mix the QThread with the threading package from the Python standard library.
I hope you're not offended if I think that your probably fairly new to programming. I advise you to not use threads (in general and in Qt) until you have more experience. Threads are hard to use correctly, even for seasoned programmers, and therefore should only be used if necessary. For this reason the Qt documentation on threads even includes a section about alternatives.
You want to execute a function every 2 seconds, so in your case the best solution is to use a QTimer and connect its timeout signal to a slot that updates the progress bar. Like so:
#!/usr/bin/env python
import sys
import psutil
from PyQt5 import QtCore, QtWidgets
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.progressBar = QtWidgets.QProgressBar()
self.layout.addWidget(self.progressBar)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateCpu)
self.timer.start(100) # Every 0.1 seconds
def updateCpu(self):
cpu = psutil.cpu_percent(None, False)
#print("cpu = {}%".format(cpu))
self.progressBar.setValue(cpu)
def main():
app = QtWidgets.QApplication(sys.argv)
win = MyWidget()
win.show()
win.raise_()
app.exec_()
if __name__ == "__main__":
main()
Note that I changed the first parameter of cpu_percent to None. This makes the call return immediately instead of lasting one second (and therefore you can update the progress bar more often if you want). Setting it to None causes the first call of cpu_percent to return a meaningless value, but in your cause that's not an issue I think. See the documention.
Finally, even though you removed a lot of unnecessary code, your code example was not yet minimal (e.g. the import datecs_print is not necessary to run it, and I don't have this module). I was also not complete (e.g. the "ui/sales_window.ui" file was missing so I couldn't run it). Please read the page on how to make an MCVE. You will get a lot more help next time if you include a small program that we can just copy-paste-execute.
Hope this helps
I have a grid of items in PyQt, and when the user modifies the window size I need to increase/decrease the number of columns accordingly. The number of rows are handled by a scrollarea, so I don't need to worry about changes in the y direction (if that matters).
Inside my implementation of QMainWindow, I know it's possible to override the resizeEvent() function, which will be triggered for any and all window adjustments. However, using that to rebuild the grid everytime is horribly inefficient. Just to test the function to see how it worked, I had resizeEvent merely print a string, and that caused my window adjustments to be slightly laggy and visually imperfect (jittery rather than smooth). I'll probably run a simple division operation on the window size to see if it has gotten larger or smaller enough to change the number of columns, but even that, when run a hundred times per adjustment, might cause lag issues. Rebuilding the entire grid might even take a second to do, so it would be preferable not to need to do it as the user is manipulating the window.
Is there a more efficient way to do it, or is resizeEvent my only option? Ideally, I'd like an event that triggered only once the user finished adjusting the window and not an event that triggers for practically every pixel movement as they happen (which can be hundreds or thousands of times per adjustment in the span of 1 second).
I'm using PyQt5, but if you're more familiar with PyQt4, I can figure out your PyQt4 solution in the context of PyQt5. Same for a C++ Qt4/5 solution.
It looks like the only real problem is detecting when resizing has completed. So long as this is carefully controlled, the actual laying out can be done in any way you like.
Below is a solution that uses a timer to control when a resize-completed signal is emitted. It doesn't appear to be laggy, but I haven't tested it with any complex layouts (should be okay, though).
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
resizeCompleted = QtCore.pyqtSignal()
def __init__(self):
QtGui.QWidget.__init__(self)
self._resize_timer = None
self.resizeCompleted.connect(self.handleResizeCompleted)
def updateResizeTimer(self, interval=None):
if self._resize_timer is not None:
self.killTimer(self._resize_timer)
if interval is not None:
self._resize_timer = self.startTimer(interval)
else:
self._resize_timer = None
def resizeEvent(self, event):
self.updateResizeTimer(300)
def timerEvent(self, event):
if event.timerId() == self._resize_timer:
self.updateResizeTimer()
self.resizeCompleted.emit()
def handleResizeCompleted(self):
print('resize complete')
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 300)
window.show()
sys.exit(app.exec_())
I think you need a FlowLayoutfot this purpose, which automatically adjusts the number of columns on resizing the widget containing it. Here is the documentation for FlowLayout and here is the PyQt version of the same layout.
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_())