Why is my calculator app breaking when I make my QPushButtons class variables instead of instance variables?

-1

I'm at an impasse with this little project. I have a calculator with buttons for basic operations, a screen, and a history showing the previous numbers in the expression. As far as I can tell, I need to make the calculator's screen and history global so I can access them from the calculator's button's methods.

Here's the unfinished but functioning code, this is 99% GUI:

import sys
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QHBoxLayout, QVBoxLayout, QWidget, QLineEdit
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import *

class Calculator(QWidget):

    def __init__(self):
        super().__init__()

        # All our buttons are here
        zero = QPushButton("0")
        one = QPushButton("1")
        two = QPushButton("2")
        three = QPushButton("3")
        four = QPushButton("4")
        five = QPushButton("5")
        six = QPushButton("6")
        seven = QPushButton("7")
        eight = QPushButton("8")
        nine = QPushButton("9")
        plus = QPushButton("+")
        minus = QPushButton("-")
        times = QPushButton("*")
        divide = QPushButton("/")
        dot = QPushButton(".")
        equals = QPushButton("=")

        # User's history will appear above the screen
        history = QLabel()

        # Creating a Regex validator to limit inputs to numbers and appropriate symbols
        regex = QRegExp("\\d*(?:\\.\\d+)?")
        num_validator = QRegExpValidator(regex)

        # Screen is a one line text box using the above Regex validator
        screen = QLineEdit()
        screen.setAlignment(Qt.AlignRight)
        screen.setValidator(num_validator)

        # The calculator has a four column layout
        first_col = QVBoxLayout()
        second_col = QVBoxLayout()
        third_col = QVBoxLayout()
        fourth_col = QVBoxLayout()

        # Creating Layout
        zero_and_divide_container = QHBoxLayout()
        button_container = QHBoxLayout()
        screen_and_button_container = QVBoxLayout()

        first_col.addWidget(seven)
        first_col.addWidget(four)
        first_col.addWidget(one)

        second_col.addWidget(eight)
        second_col.addWidget(five)
        second_col.addWidget(two)

        third_col.addWidget(nine)
        third_col.addWidget(six)
        third_col.addWidget(three)

        fourth_col.addWidget(plus)
        fourth_col.addWidget(minus)
        fourth_col.addWidget(times)

        button_container.addLayout(first_col)
        button_container.addLayout(second_col)
        button_container.addLayout(third_col)
        button_container.addLayout(fourth_col)

        zero_and_divide_container.addWidget(dot)
        zero_and_divide_container.addWidget(zero)
        zero_and_divide_container.addWidget(equals)
        zero_and_divide_container.addWidget(divide)

        screen_and_button_container.addWidget(history)
        screen_and_button_container.addWidget(screen)
        screen_and_button_container.addLayout(button_container)
        screen_and_button_container.addLayout(zero_and_divide_container)

        self.setLayout(screen_and_button_container)
        self.show()


def main():
    app = QApplication([])
    calc = Calculator()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

This does what it's supposed to. However, as stated before, I need to make the screen and history global so the button methods can access their contents. When I try to do that in the simplest way, by just moving the history and screen out of the method, like so:

import sys
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QHBoxLayout, QVBoxLayout, QWidget, QLineEdit
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import *


class Calculator(QWidget):

    # Screen will appear above buttons
    screen = QLineEdit()

    # User's history will appear above the screen
    history = QLabel()

    def __init__(self):
        super().__init__()
        zero = QPushButton("0")
        one = QPushButton("1")
        two = QPushButton("2")
        three = QPushButton("3")
        four = QPushButton("4")
        five = QPushButton("5")
        six = QPushButton("6")
        seven = QPushButton("7")
        eight = QPushButton("8")
        nine = QPushButton("9")
        plus = QPushButton("+")
        minus = QPushButton("-")
        times = QPushButton("*")
        divide = QPushButton("/")
        dot = QPushButton(".")
        equals = QPushButton("=")

        # Creating a Regex validator to limit inputs to numbers and appropriate symbols
        regex = QRegExp("\\d*(?:\\.\\d+)?")
        num_validator = QRegExpValidator(regex)

        # Screen is a one line text box using the above Regex validator
        self.screen.setAlignment(Qt.AlignRight)
        self.screen.setValidator(num_validator)

        # The calculator has a four column layout
        first_col = QVBoxLayout()
        second_col = QVBoxLayout()
        third_col = QVBoxLayout()
        fourth_col = QVBoxLayout()

        # Creating Layout
        zero_and_divide_container = QHBoxLayout()
        button_container = QHBoxLayout()
        screen_and_button_container = QVBoxLayout()

        first_col.addWidget(seven)
        first_col.addWidget(four)
        first_col.addWidget(one)

        second_col.addWidget(eight)
        second_col.addWidget(five)
        second_col.addWidget(two)

        third_col.addWidget(nine)
        third_col.addWidget(six)
        third_col.addWidget(three)

        fourth_col.addWidget(plus)
        fourth_col.addWidget(minus)
        fourth_col.addWidget(times)

        button_container.addLayout(first_col)
        button_container.addLayout(second_col)
        button_container.addLayout(third_col)
        button_container.addLayout(fourth_col)

        zero_and_divide_container.addWidget(dot)
        zero_and_divide_container.addWidget(zero)
        zero_and_divide_container.addWidget(equals)
        zero_and_divide_container.addWidget(divide)

        screen_and_button_container.addWidget(self.history)
        screen_and_button_container.addWidget(self.screen)
        screen_and_button_container.addLayout(button_container)
        screen_and_button_container.addLayout(zero_and_divide_container)

        self.setLayout(screen_and_button_container)
        self.show()


def main():
    app = QApplication([])
    calc = Calculator()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

The window never opens and I get exit code -1073740791 (0xC0000409). What am I doing wrong here?

python
pyqt
asked on Stack Overflow Jun 26, 2020 by Padaca

1 Answer

2

I recommend you run your code from the terminal to get a more explanatory error message, in your case I get:

QWidget: Must construct a QApplication before a QWidget
Aborted (core dumped)

And there the error is pointed out, QWidget must be created after QApplication but in your case they are created before, also I do not see any advantage in that they are variables of the class instead of attributes.

The solution is to move it to:

# ...
class Calculator(QWidget):
    def __init__(self):
        super().__init__()

        # Screen will appear above buttons
        self.screen = QLineEdit()

        # User's history will appear above the screen
        self.history = QLabel()

        zero = QPushButton("0")
        # ...
answered on Stack Overflow Jun 26, 2020 by eyllanesc

User contributions licensed under CC BY-SA 3.0