使用鼠标或键盘单击按钮时,PyQt 小部件刷新行为不同 [英] PyQt widget refresh behavior different when clicking button with mouse or keyboard

查看:68
本文介绍了使用鼠标或键盘单击按钮时,PyQt 小部件刷新行为不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆