Python 使用鼠标或键盘单击按钮时,PyQt小部件刷新行为不同

Python 使用鼠标或键盘单击按钮时,PyQt小部件刷新行为不同,python,pyqt,pyqt5,Python,Pyqt,Pyqt5,我不熟悉使用PyQt;我试图设置一个非常简单的主窗口,有3行编辑和1个按钮 以下是我想要的行为: 当前两个按钮的内容变为有效时,该按钮被启用 单击该按钮时,第二行编辑的内容将重置为空sting,并禁用该按钮 下面是代码-视图代码是使用Qt设计器和PUIC5生成的 一切似乎都很好,除了用鼠标单击按钮外-行编辑的内容显示为未擦除(实际上是),按钮显示为启用(实际上是禁用)。如果触发了窗口刷新(即最小化和还原),窗口小部件将正确显示。使用键盘单击按钮时,所有内容都正确刷新 我不知道是什么原因造成了这个

我不熟悉使用PyQt;我试图设置一个非常简单的主窗口,有3行编辑和1个按钮

以下是我想要的行为:

当前两个按钮的内容变为有效时,该按钮被启用

单击该按钮时,第二行编辑的内容将重置为空sting,并禁用该按钮

下面是代码-视图代码是使用Qt设计器和PUIC5生成的

一切似乎都很好,除了用鼠标单击按钮外-行编辑的内容显示为未擦除(实际上是),按钮显示为启用(实际上是禁用)。如果触发了窗口刷新(即最小化和还原),窗口小部件将正确显示。使用键盘单击按钮时,所有内容都正确刷新

我不知道是什么原因造成了这个奇怪的东西。。。任何帮助都将不胜感激

[编辑]:我刚刚发现,只有用回车键“点击”按钮时,它才能正常工作,如果使用空格,则会注意到相同的缺陷行为。。。这让我想知道“回车”和其他点击的意思有什么区别

先谢谢你

主要python代码:

from PyQt5 import QtWidgets, QtCore
import sys

import view1


class Model(QtCore.QObject):
    text1_changed = QtCore.pyqtSignal(str)
    text2_changed = QtCore.pyqtSignal(str)
    text3_changed = QtCore.pyqtSignal(str)
    canprocess_changed = QtCore.pyqtSignal(bool)

    def __init__(self):
        super().__init__()
        self._text1 = ''
        self._text2 = ''
        self._text3 = ''
        self._canprocess = False

    @property
    def text1(self):
        return self._text1

    @text1.setter
    def text1(self, value):
        self._text1 = value
        self.text1_changed.emit(value)

    @property
    def text2(self):
        return self._text2

    @text2.setter
    def text2(self, value):
        self._text2 = value
        self.text2_changed.emit(value)

    @property
    def text3(self):
        return self._text3

    @text3.setter
    def text3(self, value):
        self._text3 = value
        self.text3_changed.emit(value)

    @property
    def canprocess(self):
        return self._canprocess

    @canprocess.setter
    def canprocess(self, value):
        self._canprocess = value
        self.canprocess_changed.emit(value)


class Controller(QtCore.QObject):
    def __init__(self, model: Model):
        super().__init__()
        self._model = model
        self._text1valid = False
        self._text2valid = False

    def istext1valid(self, text) -> bool:
        return text[:3] == 'abc'

    def istext2valid(self, text) -> bool:
        return text[:3] == 'def'

    def _validity_update(self):
        self._model.canprocess = self._text1valid and self._text2valid

    @QtCore.pyqtSlot(str)
    def change_text1(self, text):
        self._text1valid = self.istext1valid(text)
        self._validity_update()
        if self._text1valid:
            self._model.text1 = text

    @QtCore.pyqtSlot(str)
    def change_text2(self, text):
        self._text2valid = self.istext2valid(text)
        self._validity_update()
        if self._text2valid:
            self._model.text2 = text

    @QtCore.pyqtSlot(str)
    def change_text3(self, text):
        self._model.text3 = text

    @QtCore.pyqtSlot()
    def process(self):
        # do real stuff
        self._text2valid = False
        self._validity_update()
        self._model.text2 = ''

    def init_view(self):
        self._text1valid = False
        self._text2valid = False
        self._validity_update()
        self._model.text1=''
        self._model.text2=''
        self._model.text3='an init value'



class MainAppWindow(QtWidgets.QMainWindow, view1.Ui_MainWindow):
    def __init__(self, model: Model, controller: Controller):
        super().__init__()
        self._model = model
        self._controller = controller
        self.setupUi(self)

    def setupUi(self, window):
        super().setupUi(self)
        self.bProceed.clicked.connect(self._controller.process)
        self.ledit1.editingFinished.connect(lambda: self._controller.change_text1(self.ledit1.text()))
        self.ledit2.editingFinished.connect(lambda: self._controller.change_text2(self.ledit2.text()))
        self.ledit3.editingFinished.connect(lambda: self._controller.change_text3(self.ledit3.text()))

        self._model.text1_changed.connect(self.ledit1.setText)
        self._model.text2_changed.connect(self.ledit2.setText)
        self._model.text3_changed.connect(self.ledit3.setText)
        self._model.canprocess_changed.connect(self.bProceed.setEnabled)

        self._controller.init_view()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainmodel = Model()
    maincontroller = Controller(mainmodel)
    MainWindow = MainAppWindow(mainmodel, maincontroller)
    MainWindow.show()
    sys.exit(app.exec_())

生成的用户界面:

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(435, 204)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
        self.formLayout.setObjectName("formLayout")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setObjectName("label_2")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2)
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setObjectName("label")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
        self.ledit1 = QtWidgets.QLineEdit(self.centralwidget)
        self.ledit1.setObjectName("ledit1")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.ledit1)
        self.ledit2 = QtWidgets.QLineEdit(self.centralwidget)
        self.ledit2.setObjectName("ledit2")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.ledit2)
        self.bProceed = QtWidgets.QPushButton(self.centralwidget)
        self.bProceed.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.bProceed.setDefault(True)
        self.bProceed.setObjectName("bProceed")
        self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.bProceed)
        self.ledit3 = QtWidgets.QLineEdit(self.centralwidget)
        self.ledit3.setObjectName("ledit3")
        self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.ledit3)
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setObjectName("label_3")
        self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_3)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 435, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        MainWindow.setTabOrder(self.ledit1, self.ledit2)
        MainWindow.setTabOrder(self.ledit2, self.bProceed)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label_2.setText(_translate("MainWindow", "Text2"))
        self.label.setText(_translate("MainWindow", "Text1"))
        self.bProceed.setText(_translate("MainWindow", "Proceed"))
        self.label_3.setText(_translate("MainWindow", "Text3"))


事实证明,自从MacOSX特定的v5.10版本以来,这一直是PyQt5的一个已知错误,它影响到几乎所有通过编程更改内容的小部件

解决方案是对任何内容已更改的小部件进行子类化,并显式调用repaint()

例如,以下是我用于按钮的代码:

class OSxPushButton(QtWidgets.QPushButton):
"""
A class to correct an OSX bug affecting widgets update when attributes are
programmatically modified.
"""
def __init__(self, parent=None):
    super().__init__(parent)

def setEnabled(self, a0: bool) -> None:
    super().setEnabled(a0)
    self.repaint()

对于如此复杂的结构,有没有具体的原因?您可以通过一个单独的函数来实现相同的结果(启用/禁用按钮),该函数在任何行编辑发生更改时只检查所有行编辑的内容,而无需调用大量函数和不必要地连接太多信号。在任何情况下,你有什么PyQt版本,在什么操作系统上?@musicamante:发布的代码只是真实代码的一个模型-我只是试图重现真实应用程序的机制。Os:OSX catalina PyQT:5.15.0 Python:3.6Hi@Christian,你能添加一个到bug报告的链接吗?另外,
update()
通常优于
repaint()
(请参阅)您好,@JKSH-我不确定pyQT是否有bug报告跟踪器。我从邮件列表中得到了答案和解决方案。下面是相应存档的链接:在PyQt==5.15.1的Windows上有相同的问题。update()和/或.repaint()对我来说很好@JKSH