I'm new to using PyQt; I'm trying to setup a very simple MainWindow with 3 line edits and 1 pushbutton.
Here is the behavior I'd like to get:
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.
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.
[Edit]: 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?
Thank you in advance
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_())
Generated UI:
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"))