Qt QProgressBar not updating regularly when text is not visible - python

I have a QProgressBar that is set to not display text. It appears that this causes the progress bar to not update regularly, but only after some chunks (roughly 5-10%).
The following minimum Python example shows a window with two progress bars which get updated in sync. The one not displaying text is lagging behind.
#!/usr/bin/env python3
from PyQt5 import QtCore, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.progressBar1 = QtWidgets.QProgressBar()
self.progressBar2 = QtWidgets.QProgressBar()
layout.addWidget(self.progressBar1)
layout.addWidget(self.progressBar2)
self.progressBar1.setTextVisible(False)
self.progress = 0
QtCore.QTimer.singleShot(1000, self.updateProgress)
def updateProgress(self):
self.progress = (self.progress+1) % 100
self.progressBar1.setValue(self.progress)
self.progressBar2.setValue(self.progress)
QtCore.QTimer.singleShot(1000, self.updateProgress)
app = QtWidgets.QApplication([])
win = Window()
win.show()
app.exec()
Is this a bug?
Everything I could find about QProgressBar not updating correctly was about the main thread being stuck and not processing events, which doesn't seem to be the case here.
The same behavior occurs in Python (PyQt5) and C++ (Qt 5.15.2, Qt 6.4.1)

It's because the indicator bar of the progress bar is updated in chunks. See the QStyle::PM_ProgressBarChunkWidth in the reference.(See the implementation if you are interested.)
You can either change the style property or call repaint() manually, like these.
...
class Window(QtWidgets.QWidget):
def __init__(self):
...
self.progressBar1.setStyleSheet('QProgressBar::chunk {width: 1px;}')
...
...
class Window(QtWidgets.QWidget):
def updateProgress(self):
...
self.progressBar1.repaint()
...

Related

PyQt5 Getting second screen functions to work

how are you doing?
I have been coding for a couple months. I started with Python, now I am trying to learn PyQt5.
I have created two windows using the Designer Tool. I created a subclass for each specific page, so that it wont mess up my software once i make changes in the Designer.
I managed to make my second window open from the first window. It works well. However, nothing works in window 2 when its opened by window 1. However, when i run window 2 by itself, it does not work.
I am sure it is a inheritance problem that I am messing up. It seems my sofware is inheriting the window 2 layout but not its functions.
I have attached the code. Is it possible for someone to help me out? If possible, i would like a brief explanation on how to fix it too. I want to learn so I can teach others as well.
This is my main window:
from nonregusrscreen1 import NonReg
from Main import Ui_MainWindow # needs to import from the main file
from PyQt5 import QtCore, QtGui, QtWidgets #importing PyQt functionality
class mainWin(QtWidgets.QMainWindow, Ui_MainWindow): #mainWin is new subclass. QtWidgets.Whatever the window was / you need to get inheritance from Ui_MainWindow, otherwise you wil lahve to use self.ui.pushbutton....
def __init__(self, *args, **kwargs): #passing arguments from whatever was called
super().__init__(*args, **kwargs)
self.ui = Ui_MainWindow()
self.setupUi(self)
self.pushButton_2.clicked.connect(self.openwindow) # you have to include UI, otherwise it will not work.
def openwindow(self):
self.window = QtWidgets.QDialog()
self.ui = NonReg()
self.ui.setupUi(self.window)
self.window.show()
if __name__ == '__main__':
app = QtWidgets.QApplication([])
w = mainWin()
w.show()
app.exec_()
This is my second window, here you can see the pushbutton_2 function that only works when the file is opened by itself (not opened using the first window)
from nonregusrscreen import Ui_NonRegUserScreen
from PyQt5 import QtCore, QtGui, QtWidgets
class NonReg(QtWidgets.QDialog, Ui_NonRegUserScreen):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ui = Ui_NonRegUserScreen
self.setupUi(self)
self.pushButton_2.clicked.connect(self.closewindow)
def closewindow(self):
self.window = exit()
if __name__ == '__main__':
app = QtWidgets.QApplication([])
w = NonReg()
w.show()
app.exec_()
Thank you, i truly appreciate your help.

Calling method after init doesn't show application

I have a simple PySide Gui which I want to show, however there currently is a blocking task which is a long process, populating the dropdown list with items. Which means the UI does not show until the dropdown list is finished being populated. I want to know is there a way to force show the UI before it attempts to populate the list. I would prefer the dialog show so users know they opened the tool before assuming it's crashed or something.
I'm using Qt since my application needs to run in both PySide and PySide2. I was initially trying to use the qApp.processEvents() but it doesn't seem to be available in the Qt wrapper, or I may have been missing something. If you know what the equivalent would be, I'm fine with the process events being the solution. Optionally if there is an elegant way to populate the list from a background thread somehow...
from Qt import QtGui, QtCore, QtWidgets
class Form(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.resize(200,50)
self.items = QtWidgets.QComboBox()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.items)
self.setLayout(layout)
# init
self.long_process()
def long_process(self):
for i in range(30000):
self.items.addItem('Item {}'.format(i))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
A good option for these cases is always to use QTimer:
class Form(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
[...]
# init
timer = QtCore.QTimer(self)
timer.timeout.connect(self.on_timeout)
timer.start(0)
def on_timeout(self):
self.items.addItem('Item {}'.format(self.counter))
self.counter += 1
if self.counter == 30000:
self.sender().stop()

QSpinBox signal for arrow buttons

I know this question has been asked in similar ways, but I'm getting lost in the vast world of the many signals I can choose from when using a QSpinBox (or QDoubleSpinBox). I want to connect my function to the editingFinished signal (fine, it works perfectly), but this will not also connect to the arrow-buttons - so I need signals for those as well. I don't want to call my function every time valueChanged is emitted - only when editing is finished, or when the arrows are used.
One way to do this is to reimplement the stepBy method and emit a custom signal. The main advantage of this approach is that it will handle changes made using the up/down keys, as well as the arrow buttons. Here is a basic demo:
import sys
from PyQt4 import QtCore, QtGui
class SpinBox(QtGui.QSpinBox):
stepChanged = QtCore.pyqtSignal()
def stepBy(self, step):
value = self.value()
super(SpinBox, self).stepBy(step)
if self.value() != value:
self.stepChanged.emit()
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.spin = SpinBox()
self.spin.editingFinished.connect(self.handleSpinChanged)
self.spin.stepChanged.connect(self.handleSpinChanged)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.spin)
def handleSpinChanged(self):
print(self.spin.value())
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 150, 50)
window.show()
sys.exit(app.exec_())

PyQt4 Label not showing

I am very new to PyQt4 and this question is probably very simple but I have tried many different things and nothing works. I am trying to make a label in PyQt4.
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class Display(QtGui.QWidget):
def __init__(self):
super(Display, self).__init__()
self.time = Time() #Another class in the program
self.ShowFullScreen()
self.setStyleSheet("background-color: black;")
self.show()
self.MainDisplay()
def MainDisplay(self):
self.timedisplay = QtGui.QLabel(self)
self.timedisplay.setStyleSheet("font: 30pt Helvetica; color: white;")
self.timedisplay.setText(time.GetTime())
self.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
GUI = Display()
sys.exit(app.exec())
The label doesn't show up, and there is no error message. What am I doing wrong?
I use PySide and not Qt, but they are supposed to be 99.99% compatible. The main problem is your call to the show() function, which makes the window visible. You have two calls to show. The first time it is called, you have not yet called MainDisplay so the QLabel hasn't been created yet. The second time you call show the window is already visible, so nothing changes.
If you create the widgets first and call show once, at the end, it will work the way you want. With this code the label shows up.
There are other issues:
You will have to change the import statements back the way you had them.
I did not have your Time class, so I just wrote a simple piece of text into the label.
The function ShowFullScreen should be showFullScreen.
The function that runs the event loop in QtApp is named exec_ not exec.
import sys
from PySide import QtCore
from PySide import QtGui
class Display(QtGui.QWidget):
def __init__(self):
super(Display, self).__init__()
self.setStyleSheet("background-color: black;")
self.MainDisplay()
self.showFullScreen()
def MainDisplay(self):
self.timedisplay = QtGui.QLabel(self)
self.timedisplay.setStyleSheet("font: 30pt Helvetica; color: white;")
self.timedisplay.setText("What time is it now?")
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
GUI = Display()
sys.exit(app.exec_())

Hover issue in PyQt

I want to do hover. I saw an example and then write a script which will be use as I made program. I am facing one problem that hover only occur if you put mouse on the left corner of button. I want that it will happen for all the button that if i move cursor on button then it should change.
Here is my code:
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import pyqtSignal
import os,sys
class HoverButton(QtGui.QToolButton):
def enterEvent(self,event):
print("Enter")
button.setStyleSheet("background-color:#45b545;")
def leaveEvent(self,event):
button.setStyleSheet("background-color:yellow;")
print("Leave")
app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
button = QtGui.QToolButton(widget)
button.setMouseTracking(True)
buttonss = HoverButton(button)
button.setIconSize(QtCore.QSize(200,200))
widget.show()
sys.exit(app.exec_())
Is this what you're looking for
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import pyqtSignal
import os,sys
class Main(QtGui.QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QtGui.QVBoxLayout(self) # layout of main widget
button = HoverButton(self)
button.setIconSize(QtCore.QSize(200,200))
layout.addWidget(button) # set your button to the widgets layout
# this will size the button nicely
class HoverButton(QtGui.QToolButton):
def __init__(self, parent=None):
super(HoverButton, self).__init__(parent)
self.setMouseTracking(True)
def enterEvent(self,event):
print("Enter")
self.setStyleSheet("background-color:#45b545;")
def leaveEvent(self,event):
self.setStyleSheet("background-color:yellow;")
print("Leave")
app = QtGui.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
In your code you had a button in a button and the nested button wasn't assigned to a QLayout widget. Although, I'm not sure why you're adding a button inside of a button. One thing that I've learned from working with GUI's is that it's so much easier if you modularize your code. Now you can take this custom button and apply it somewhere else.
You should use the stylesheet as
QToolButton:hover
{
background-color: rgb(175,175,175);
}
You probably want focus and blur, rather than enter and leave will only fire when the mouse actually enters or leaves the boundary of the button and will probably just be short duration impulses rather than toggles. focus and blur will toggle with the hover.

Categories