Reputation: 4043
I have a simple python qt application in which I want to plot something dynamically via matplotlib. I was trying to follow instructions like this and that. It works as expected except for one thing, my spinbox updates twice upon mouse click and I do not understand why. Well, during the development the valueChanged.connect()
worked fine until I got to a certain point. Then the update_figure()
was called twice and the displayed value jumped by two instead of the desired one. I tried to make a minimum working example, you find below. Without the sleep
command in update_figue
my spinbox behaves normal, with sleep
it is called twice and the value jumps by two, with an delay given by sleep
.
Why does the sleep
in update_figure
influence the spinbox, or what is happening here? Is it the way I constructed the connect()
?
BTW: I observed the same behaviour on a slider.
Cheers
(I do not think it matters, but this is on openSuse Leap)
EDIT: I could make the example even "more minimal".
from PyQt5.uic import loadUiType
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
from time import sleep
Ui_MainWindow, QMainWindow = loadUiType('main.ui')
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, ):
super(Main, self).__init__()
self.setupUi(self)
self.spinBox_dac.valueChanged.connect(self.update_figure)
def update_figure(self):
##### The following line makes the difference!
sleep(1) #####################################
##### why?
print "damn...once or twice?"
return
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
Here is the main.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>185</width>
<height>107</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>1100</width>
<height>905</height>
</size>
</property>
<property name="windowTitle">
<string>mini</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QSpinBox" name="spinBox_dac">
<property name="geometry">
<rect>
<x>80</x>
<y>40</y>
<width>61</width>
<height>28</height>
</rect>
</property>
<property name="minimum">
<number>3</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>
Upvotes: 0
Views: 1452
Reputation: 1046
The sleep()
call prevent qt to process the mouse-release and the QSpinBox
"thinks" that the mouse is pressed long enough to increase/decrease the value again (auto-repeat).
See https://bugreports.qt.io/browse/QTBUG-33128
A solution could be:
def update_figure(self):
for _ in xrange(10):
QApplication.processEvents()
sleep(0.1)
print "Only once :-)"
Anyway it's not a good practice to block the UI while performing long operations, the work is usually delegated to another thread.
This affect Qt4
and Qt5
both, but it behave a bit differently.
There is no setAutoRepeat
because in the QAbstrectSpinBox
this behavior is implemented using styles, while in QAbstractButton
is implemented with a timer.
The way around this is to create a ProxyStyle
and set a very long "auto-repeat" interval
from PyQt5.uic import loadUiType
from PyQt5.QtWidgets import QApplication, QMainWindow, QProxyStyle, QStyle
import sys
from time import sleep
Ui_MainWindow, QMainWindow = loadUiType('main.ui')
class CustomStyle(QProxyStyle):
def styleHint(self, hint, option=None, widget=None, returnData=None):
if hint == QStyle.SH_SpinBox_KeyPressAutoRepeatRate:
return 10**10
elif hint == QStyle.SH_SpinBox_ClickAutoRepeatRate:
return 10**10
elif hint == QStyle.SH_SpinBox_ClickAutoRepeatThreshold:
# You can use only this condition to avoid the auto-repeat,
# but better safe than sorry ;-)
return 10**10
else:
return super().styleHint(hint, option, widget, returnData)
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, ):
super(Main, self).__init__()
self.setupUi(self)
self.spinBox_dac.setStyle(CustomStyle())
self.spinBox_dac.valueChanged.connect(self.update_figure)
def update_figure(self):
sleep(1)
print "Only once!"
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
Upvotes: 3