Details
-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
5.15.1
-
None
-
MacOs Catalina 10.15.2 and pyqt5
Description
While I was developing my project of a simple calculator, I noticed a bug in the refresh of QLineEdit (the calculator screen). When I press the calculator buttons, QLineEdit does not display my changes. These changes appear only when for example I resize the widget window (see attached video).
I am developing on MacOS Catalina 10.15.2 with pyqt5 5.15.1.
The part of the View was created through Qt Designer and converted into .py file through pyuic and the files are: "Ui_FancyCalc.py and Ui_AboutDialog.py". The Model instead is implemented in the file "calculatormodel2.py". I also implemented my own Observable class in the “observable.py” file, which is imported into the model class. The file where I define my FancyCalc and execute the controller part is "FancyCalc.py".
I am writing this thread because I tried to run the same project on a windows pc and I have not encountered any problems. So I wanted to know if it was a compatibility issue between my OS and the pyqt5 version.
I do not include in the description the parts of code converted by QT Designer, but I attach below all the files necessary to run the project
To run the project just run the "FancyCalc.py" file
FancyCalc.py
import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, qApp from Calculator2.Ui_FancyCalc import Ui_FancyCalc from Calculator2.Ui_AboutDialog import Ui_AboutDialog from Calculator2.calculatormodel2 import CalculatorModel # Fancy Calculator Class class FancyCalc(QMainWindow): def __init__(self): super().__init__() # Set up the user interface from QT Designer. self.ui = Ui_FancyCalc() self.ui.setupUi(self) # Create about dialog and wire actions. self._aboutDialog = AboutDialog() self.ui.actionAbout.triggered.connect(self._aboutDialog.exec_) self.ui.actionQuit.triggered.connect(QApplication.exit) self._model = CalculatorModel() # Connect the number buttons. self.ui.button_0.clicked.connect(lambda: self._model.insert_digit('0')) self.ui.button_1.clicked.connect(lambda: self._model.insert_digit('1')) self.ui.button_2.clicked.connect(lambda: self._model.insert_digit('2')) self.ui.button_3.clicked.connect(lambda: self._model.insert_digit('3')) self.ui.button_4.clicked.connect(lambda: self._model.insert_digit('4')) self.ui.button_5.clicked.connect(lambda: self._model.insert_digit('5')) self.ui.button_6.clicked.connect(lambda: self._model.insert_digit('6')) self.ui.button_7.clicked.connect(lambda: self._model.insert_digit('7')) self.ui.button_8.clicked.connect(lambda: self._model.insert_digit('8')) self.ui.button_9.clicked.connect(lambda: self._model.insert_digit('9')) # Connect updates to input text to the label in the UI. self._model.register(lambda txt: self.ui.calcDisplay.setText(txt)) # Connect the clear button and equal button. self.ui.button_C.clicked.connect(self._model.clr) self.ui.button_eql.clicked.connect(self._model.eql) # Connect the operations buttons. self.ui.button_add.clicked.connect(self._model.add) self.ui.button_sub.clicked.connect(self._model.sub) self.ui.button_mul.clicked.connect(self._model.mul) self.ui.button_div.clicked.connect(self._model.div) self.ui.button_sub.clicked.connect(self._model.sub) # About Dialog class class AboutDialog(QDialog): def __init__(self): super().__init__() # Set up the user interface from QT Designer. self.ui = Ui_AboutDialog() self.ui.setupUi(self) app = QApplication(sys.argv) window = FancyCalc() window.show() sys.exit(app.exec_())
calculatormodel2.py
from Calculator2.observable import Observable # A first version of the calculator model. class CalculatorModel: def __init__(self): super().__init__() self._inserting = False self._accumulator = 0 self._current_op = None self.current_input = Observable('0') # Register slots to listen for model changes. def register(self, slot): self.current_input.register(slot) # Digit insertion def insert_digit(self, d): if not self._inserting: self.current_input.value = d self._inserting = True if d is not '0' else False else: self.current_input.value = self.current_input.value + d # Function that executes the current operation in preparation for next (if any). def execute_current(self): # If we are already inserting digits. if self._inserting: # Is there an operation already pending? If so, apply it to # accumulator and current input, save in accumulator. if self._current_op: self._accumulator = self._current_op(self._accumulator) self.current_input.value = str(self._accumulator) else: # Otherwise save the current input in accumulator. self._accumulator = int(self.current_input.value) # In any case we a *not* inserting anymore self._inserting = False # Public API for all calculator operations that manipulate the model. def add(self): self.execute_current() self._current_op = lambda x: x + int(self.current_input.value) def sub(self): self.execute_current() self._current_op = lambda x: x - int(self.current_input.value) def mul(self): self.execute_current() self._current_op = lambda x: x * int(self.current_input.value) def div(self): self.execute_current() self._current_op = lambda x: x // int(self.current_input.value) def eql(self): self.execute_current() self._current_op = None def clr(self): self.current_input.value = '0' self._inserting = False self._current_op = None
observable.py
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty # A PyQT5 Observable object class class Observable(QObject): valueChange = pyqtSignal(object) def __init__(self, val): super().__init__() self._value = val def register(self, slot): '''Register a slot listening for changes to this observable.''' self.valueChange.connect(slot) # We access the value through this getter/setter property @pyqtProperty(object, notify=valueChange) def value(self): '''pyqtProperty getter for the observable value.''' return self._value # Note that we need to explicitly emit() the signal. Declaring the # pyqtProperty with th notify=valueChanged above will have # benefits when we, for example, want to use this model in QML @value.setter def value(self, newval): '''pyqtProperty setter for the observable value.''' self._value = newval self.valueChange.emit(self._value)