How to show tooltip while not focusing at pyqt5 python? - 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.

Related

PyQt5 MainWindow with flag instantly goes out of scope

I created UI using Qt Designer. Then I converted the ui file to a .py file (pyuic -x) - it works fine if launched directly. Then I tried to subclass my ui in a separate file to implement additional logic. And this is where things start to go wrong. Inheriting from QMainWindow and my Qt Designer file works OK with no issues, as expected. However, the moment I set any WindowFlag for my QMainWindow (any flag - I tried these: StaysOnTop, FramelessWindowHint) and run the file, the window appears and instantly disappears. The program continues to run in a console window, or as a PyCharm process, but the window is gone. It looks to me like it is getting out of scope - but why setting a simple flag would make any difference to the garbage collector? Could someone explain this behaviour?
Minimum code required to reproduce this phenomenon:
from ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
class Logic(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
self.show()
# self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
# self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Logic()
sys.exit(app.exec_())
The window should appear and stay on the screen until one (or more) of the flags are uncommented. I use Python 3.8 (32-bit) with PyQt5. Run environment provided by PyCharm. Windows 10.
From the documentation of setWindowFlags():
Note: This function calls setParent() when changing the flags for a window, causing the widget to be hidden. You must call show() to make the widget visible again..
So, just move self.show() after setting the flags, or call it from outside the __init__ (after the instance is created), which is the most common and suggested way to do so, as it's considered good practice to show a widget only after it has been instanciated.

QWidget becomes unresponsive when trying to update progressbar from external script [duplicate]

This question already has an answer here:
PyQt QProgressBar not working when I use it with Selenuim
(1 answer)
Closed 3 years ago.
I'm trying to learn how to update a progress bar in my main script when my for loop is running in an external script. It's my first time trying to use signals so I'm unsure if I've used them correctly. Here is what I've tried so far.
The first thing that I tried doing was creating a class "Signal" which would have the pyqtSignal that is keeping track of the progress bar. Then I created an a signal object and I have it emit a signal every time the for loop runs. Then in my main script I try connecting the signal object that I created to the setValue method of the progressbar. The progress bar updates till 5% and after that the window becomes unresponsive. I have two main questions regarding the code:
What is the reason for the window becoming unresponsive?
How to get the progress bar in the main script to update so that the window doesn't become unresponsive?
main.py
import sys
from external import *
from PyQt5.QtWidgets import QWidget, QApplication, QProgressBar,
QPushButton, QVBoxLayout
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Testing')
self.setGeometry(300, 300, 290, 150)
self.vbox = QVBoxLayout()
self.setLayout(self.vbox)
self.progressbar = QProgressBar(self)
self.button = QPushButton("run", self)
self.vbox.addWidget(self.progressbar)
self.vbox.addWidget(self.button)
c.update.connect(self.progressbar.setValue)
self.button.clicked.connect(test)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
external.py
from PyQt5.QtCore import pyqtSignal, QObject
import time
class Signal(QObject):
update = pyqtSignal(int)
c = Signal()
def test():
for i in range(100):
c.update.emit(i)
time.sleep(1)
Even if the for loop code is in another file it doesn't matter, as it's executed within the PyQt QApplication event loop (the one started with app.exec_()).
A time.sleep blocks everything until completed, including the GUI, that's why the interface becomes unresponsive.
You need to run the loop function in a separate thread.
This is a very basic solution:
import threading
[...]
def initUI(self):
[...]
c.update.connect(self.updateProgressBar)
self.button.clicked.connect(self.runTest)
self.show()
def runTest(self):
threading.Thread(target=test).start()
self.button.setEnabled(False)
def updateProgressBar(self, value):
self.progressbar.setValue(value)
if value == 100:
self.button.setEnabled(True)
NB: I've set the range of the test function to 101, so that when it reaches 100 it enables the button again, since I've disabled it to avoid multiple and concurrent executions of the function.
That said, I wouldn't suggest you to follow this kind of approach, as it would certainly create issues in the future.
Have a look at this answer, which might let you understand a better (and suggested) approach with threads.

How to resize QWidget without intermediate paint

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.

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_())

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