使用鼠标或键盘单击按钮时,PyQt 小部件刷新行为不同 [英] PyQt widget refresh behavior different when clicking button with mouse or keyboard
问题描述
我是 PyQt 的新手;我正在尝试设置一个非常简单的 MainWindow,其中包含 3 行编辑和 1 个按钮.
I'm new to using PyQt; I'm trying to setup a very simple MainWindow with 3 line edits and 1 pushbutton.
这是我想要的行为:
当前两个按钮的内容变为有效时,按钮被启用.
When the content of the two first buttons becomes valid, the pushbutton is enabled.
当按钮被点击时,第二行编辑的内容被重置为空字符串并且按钮被禁用.
When the pushbutton is clicked, the content of the second line edit is reset to an empty sting and the pushbutton is disabled.
下面是代码 - 视图代码是使用 Qt Designer 和 pyuic5 生成的.
Below is the code - the view code is generated using Qt Designer and pyuic5.
一切似乎都很好,除了用鼠标单击按钮时 - 行编辑的内容显示为未擦除(实际上是)并且按钮看起来像已启用(实际上已禁用).如果触发窗口刷新(即最小化和恢复),小部件将正确显示.当使用键盘点击按钮时,一切都会正确刷新.
Everything seems to be fine, except when the pushbutton is clicked with the mouse - the content of the line edit appears as not erased (actually it is) and the push button looks like enabled (actually it is disabled). If a refresh of the window is triggered (ie minimize and restore), the widgets are correctly displayed. When the pushbutton is clicked using the keyboard, everything is correctly refreshed.
我不知道是什么导致了这个奇怪的神器......任何帮助将不胜感激.
I can't figure out what cause this strange artifact... any help would be greatly appreciated.
:我刚刚发现它只有在点击"按钮时才能正常工作.使用回车键,如果使用空格,则会注意到相同的缺陷行为......这让我想知道回车"和回车"之间有什么区别.以及任何其他点击意味着什么?
: I've just discovered it works fine only when the pushbutton is "clicked" with the enter key, if space is used the same flawed behavior is noticed... It makes me wonder what is the difference between "enter" and any other click mean?
提前致谢
主要python代码:
Main python code:
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"))
推荐答案
事实证明,自 v5.10 版本以来,PyQt5 一直是 Mac OSX 特有的一个已知错误,它影响几乎所有以编程方式更改内容的小部件.
It turns out that has been a know bug of PyQt5 since version v5.10 specific to Mac OSX which affects almost any widget whose content is programmatically changed.
解决方案是将任何内容发生更改的小部件子类化并显式调用 repaint().
The solution has been to subclass any widget whose content is changed and to explicitly call repaint().
例如,这是我用于按钮的代码:
As an example, here is the code I use for the pushbutton:
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 小部件刷新行为不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!