为什么QSpinBox会跳过Step值的两倍

时间:2016-11-22 15:58:01

标签: python-2.7 pyqt5

我有一个简单的python qt应用程序,我想通过matplotlib动态绘制一些东西。我试图按照thisthat这样的说明操作。它按预期工作除了一件事,我的spinbox在点击鼠标时更新两次,我不明白为什么。好吧,在开发期间valueChanged.connect()工作正常,直到我达到某一点。然后调用update_figure()两次,显示的值跳过两次而不是所需的值。我试着做一个最小的工作示例,你在下面找到。如果sleep中没有update_figue命令,我的旋转框行为正常,sleep会调用两次,值会跳过两次,延迟时间为sleep

为什么sleep中的update_figure会影响旋转框,或者这里发生了什么?这是我构建connect()的方式吗?

BTW:我在滑块上观察到了相同的行为。

干杯

(我认为这不重要,但这是在openSuse Leap上)

编辑:我甚至可以让这个例子变得更简单"。

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

这是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>

1 个答案:

答案 0 :(得分:2)

sleep()来电阻止qt处理鼠标释放,QSpinBox&#34;认为&#34;按下鼠标足够长的时间再次增加/减少值(自动重复)。

请参阅https://bugreports.qt.io/browse/QTBUG-33128

解决方案可能是:

def update_figure(self):
    for _ in xrange(10):
        QApplication.processEvents() 
        sleep(0.1)
    print "Only once :-)"

无论如何,在执行长时间操作时阻止UI并不是一个好习惯,通常会将工作委托给另一个线程。

修改

这会影响Qt4Qt5两者,但行为略有不同。

没有setAutoRepeat,因为在QAbstrectSpinBox中,此行为是使用样式实现的,而QAbstractButton中的行为是使用计时器实现的。

解决这个问题的方法是创建一个ProxyStyle并设置一个非常长的&#34;自动重复&#34;间隔

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