4
\$\begingroup\$

I've just finished one of my first projects with a GUI using PyQt5 and I recreated Mastermind, the board game.

The game is played by inputting 4 numbers and getting a return of how many of the numbers are in the code but in the wrong spot and how many numbers are correct (a random 4 digit combo between 0 and 9 inclusive is used). The GUI has 5 user interactives including 4 spinbox inputs limited 0-9 and a submit button.

The results are shown below the submit button as well as saved in a history box which contains the guess number, the guess, and the result of the guess (how many correct or in the wrong place). The player has 15 guesses. If the code is found the player wins, if not they lose.

This was a great project to practice list comprehension as there are many numbered variables and tests. Initially, the code was 450 lines long due to bad variable management and inefficient tests, I have been able to get it down to 120!

Thanks for any feedback. I'm sure it will all be useful as I don't know much!

from PyQt5 import QtCore, QtGui, QtWidgets
import random


class Ui_MainWindow(object):

    def __init__(self):
        self.guessnum = -1
        self.submit_clicks = 0
        self.ans = random.sample(range(0, 10), 4)
        print(self.ans)

    def setupUi(self, MainWindow):
        MainWindow.resize(766, 603)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.Title = QtWidgets.QLabel(self.centralwidget)
        self.Title.setGeometry(QtCore.QRect(260, 20, 251, 81))
        font = QtGui.QFont()
        font.setFamily("MS UI Gothic")
        font.setPointSize(28)
        self.Title.setFont(font)
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(50, 130, 291, 345))

        self.Guess_label = [QtWidgets.QLabel(self.groupBox) for num in range(15)]
        for x in self.Guess_label:
            i = 30 + (21 * self.Guess_label.index(x))
            x.setGeometry(QtCore.QRect(20, i, 55, 16))
        self.Guessinpt = [QtWidgets.QLabel(self.groupBox) for num in range(15)]
        for x in self.Guessinpt:
            i = 30 + (21 * self.Guessinpt.index(x))
            x.setGeometry(QtCore.QRect(100, i, 61, 16))
        self.Results_guess = [QtWidgets.QLabel(self.groupBox) for num in range(15)]
        for x in self.Results_guess:
            i = 30 + (21 * self.Results_guess.index(x))
            x.setGeometry(QtCore.QRect(190, i, 55, 16))
        self.input = [QtWidgets.QSpinBox(self.centralwidget) for num in range(4)]
        for x in self.input:
            i = 380 + (90 * self.input.index(x))
            x.setGeometry(QtCore.QRect(i, 250, 71, 41))
        for x in self.input:
            x.setMaximum(10)

        self.Result_Output = QtWidgets.QLabel(self.centralwidget)
        self.Result_Output.setGeometry(QtCore.QRect(420, 430, 131, 51))
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(450, 330, 201, 51))
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(370, 130, 391, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label.setFont(font)
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(390, 170, 341, 21))
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 766, 26))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        MainWindow.setStatusBar(self.statusbar)
        self.actionNew_Game = QtWidgets.QAction(MainWindow)

        self.pushButton.clicked.connect(self.Test_Input)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Title.setText(_translate("MainWindow", "Mastermind!"))
        self.groupBox.setTitle(_translate("MainWindow", "Guess History"))
        for x in self.Guess_label:
            x.setText((_translate('MainWindow', 'Guess ' + str(self.Guess_label.index(x) + 1))))
        self.Result_Output.setText(_translate("MainWindow", "Result"))
        self.pushButton.setText(_translate("MainWindow", "Submit!"))
        self.label.setText(_translate("MainWindow", "A random 4 digit code has been created! "))
        self.label_2.setText(_translate("MainWindow", "Put 4 numbers in the boxes bellow and read the result!! "))
        self.actionNew_Game.setText(_translate("MainWindow", "New Game"))
        self.actionNew_Game.setStatusTip(_translate("MainWindow", "Start a new game?"))
        self.actionNew_Game.setShortcut(_translate("MainWindow", "Ctrl+N"))

    def Test_Input(self):
        self.numcorrect = 0
        self.poscorrect = 0
        self.user_input = [int(self.input[0].value()), int(self.input[1].value()), int(self.input[2].value()),
                           int(self.input[3].value())]

        for x in self.input:
            if int(x.value()) == self.ans[self.input.index(x)]:
                self.poscorrect += 1
            elif int(x.value()) in self.ans and x not in self.input:
                self.numcorrect += 1

        self.guessnum += 1
        self.Result_Output.setText(
            str(self.numcorrect) + ' Numbers are in the code but in the wrong place!' + '\n' + str
            (self.poscorrect) + ' Numbers are correct!')
        self.Result_Output.adjustSize()

        if self.guessnum == 14:
            self.Title.setText('You Lose!')
            self.Result_Output.setText('You Lose')
        if self.poscorrect == 4:
            self.Result_Output.setText('You Win')
            self.Title.setText('You Win')

        self.Results_guess[self.guessnum].setText(str(self.numcorrect) + ' #|' + str(self.poscorrect) + ' Pos')
        self.Results_guess[self.guessnum].adjustSize()
        self.Guessinpt[self.guessnum].setText(str(self.user_input))

if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
\$\endgroup\$

2 Answers 2

0
\$\begingroup\$

Never, NEVER edit the files created with pyuic, use them as modules instead. Read more on using Designer https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html .

You do 5 for loops, although all this is done in a single loop.

You have a problem with the names of variables and methods, do not give them names with a capital letter.

import sys
import random
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.resize(766, 603)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.Title = QtWidgets.QLabel(self.centralwidget)
        self.Title.setGeometry(QtCore.QRect(260, 20, 251, 81))
        font = QtGui.QFont()
        font.setFamily("MS UI Gothic")
        font.setPointSize(28)
        self.Title.setFont(font)
        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(50, 130, 291, 345))
        self.Result_Output = QtWidgets.QLabel(self.centralwidget)
        self.Result_Output.setGeometry(QtCore.QRect(420, 430, 131, 51))
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(450, 330, 201, 51))
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(370, 130, 391, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label.setFont(font)
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(390, 170, 341, 21))
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 766, 26))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        MainWindow.setStatusBar(self.statusbar)
        self.actionNew_Game = QtWidgets.QAction(MainWindow)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Title.setText(_translate("MainWindow", "Mastermind!"))
        self.groupBox.setTitle(_translate("MainWindow", "Guess History"))
        self.Result_Output.setText(_translate("MainWindow", "Result"))
        self.pushButton.setText(_translate("MainWindow", "Submit!"))
        self.label.setText(_translate("MainWindow", "A random 4 digit code has been created! "))
        self.label_2.setText(_translate("MainWindow", "Put 4 numbers in the boxes bellow and read the result!! "))
        self.actionNew_Game.setText(_translate("MainWindow", "New Game"))
        self.actionNew_Game.setStatusTip(_translate("MainWindow", "Start a new game?"))
        self.actionNew_Game.setShortcut(_translate("MainWindow", "Ctrl+N"))


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self) 
        self.pushButton.clicked.connect(self.test_Input)

        self.guessnum = -1
        self.submit_clicks = 0
        self.ans = random.sample(range(0, 10), 4)
        print(self.ans)

        self.guessinpt, self.results_guess, self.input = [], [], []          # +++
        for num in range(15):                                                # +++
            QtWidgets.QLabel(
                f'Guess {num+1: >2}', 
                self.groupBox,
                geometry=QtCore.QRect(20, 30 + (21 * int(f'{num}')), 55, 16) 
            )
            self.guessinpt.append(
                QtWidgets.QLabel(
                    self.groupBox,
                    geometry=QtCore.QRect(100, 30 + (21 * int(f'{num}')), 61, 16)
                )
            )
            self.results_guess.append(
                QtWidgets.QLabel(
                    self.groupBox,
                    geometry=QtCore.QRect(190, 30 + (21 * int(f'{num}')), 55, 16)
                )
            )            
            if num < 4:
                self.input.append(
                    QtWidgets.QSpinBox(
                        self.centralwidget,
                        geometry=QtCore.QRect(380 + (90 * int(f'{num}')), 250, 71, 41),
                        maximum=9                                                       # 9 !!! 
                    )
                )             

    def test_Input(self):
        self.numcorrect = 0
        self.poscorrect = 0
        self.user_input = [
                           int(self.input[0].value()), 
                           int(self.input[1].value()), 
                           int(self.input[2].value()),
                           int(self.input[3].value())
                          ]
        for x in self.input:
            if int(x.value()) == self.ans[self.input.index(x)]:
                self.poscorrect += 1
            elif int(x.value()) in self.ans and x not in self.input:
                self.numcorrect += 1

        self.guessnum += 1
        self.Result_Output.setText(
            str(self.numcorrect) + ' Numbers are in the code but in the wrong place!' + '\n' + str
            (self.poscorrect) + ' Numbers are correct!')
        self.Result_Output.adjustSize()

        if self.guessnum >= 15:                        # 14
            self.Title.setText('You Lose!')
            self.Result_Output.setText('You Lose')
            return                                     # +++

        if self.poscorrect == 4:
            self.Result_Output.setText('You Win')
            self.Title.setText('You Win')

        self.results_guess[self.guessnum].setText(str(self.numcorrect) + ' #|' + str(self.poscorrect) + ' Pos')
        self.results_guess[self.guessnum].adjustSize()
        self.guessinpt[self.guessnum].setText(str(self.user_input))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    app.setFont(QtGui.QFont("Times", 8, QtGui.QFont.Bold))
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

enter image description here

\$\endgroup\$
0
\$\begingroup\$

Don't inherit from object

Have a read through https://stackoverflow.com/questions/4015417/python-class-inherits-object . I dearly hope that you're in Python 3, in which case - just class UiMainWindow:.

Namespaces

At a guess, you added a Ui_ prefix to your class to maybe plan other UI classes or functions to also take that prefix? If so, the nicer way to do this is make a submodule of your program called ui, then refer to your class via ui.MainWindow.

Names

Use lower_snake_case for your member variables and functions, i.e.

  • setup_ui
  • self.title
  • self.group_box
  • self.guess_label

etc.

Separation of concerns

Your MainWindow isn't just rendering a main window. It's also running the business logic for the entire application. You should tease these two apart - the window class should only be doing display, rendering and control interactions, and a separate game class should track stuff like

        self.guessnum = -1
        self.ans = random.sample(range(0, 10), 4)

and

def Test_Input(self):
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.