Example #1
0
class Tictactoe(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setGeometry(250, 250, 600, 300)
        self.setWindowTitle("TenElevenGames OXO")
        self.setWindowIcon(QIcon("icon.jpg"))
        self.show()

        # NERDZONE
        # implement a simple help menu
        # create QAction buttons for the menu
        Instructions = QAction("How to play", self)
        AboutGame = QAction("About", self)
        More = QAction("More", self)

        # add the buttons to the menu
        menu = QMenuBar()
        menu.addAction(Instructions)
        menu.addAction(AboutGame)
        menu.addAction(More)

        # connect the buttons to their respective methods
        # when clicked, each button is supposed to show a popup dialogue
        # with the relevant information as per the name of the button suggests
        Instructions.triggered.connect(self.instructions)
        AboutGame.triggered.connect(self.about)
        More.triggered.connect(self.more)

        # images
        self.cross = QPixmap("cross.gif")
        self.nought = QPixmap("nought.gif")
        self.blank = QtGui.QIcon("blank.gif")

        # create a thread to run parallel to the gui
        self.messageThread = Thread()
        self.messageThread.signalLine.connect(self.threadLine)

        # Game board
        # create game buttons and set their sizes when window changes size
        # connect each button to it's OWN method
        self.buttonArray = []
        # first row
        self.button1 = QPushButton()
        self.button1.setIcon(QtGui.QIcon('blank.gif'))
        self.button1.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button1)
        self.button1.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button1.clicked.connect(self.button1Event)

        self.button2 = QPushButton()
        self.button2.setIcon(QtGui.QIcon('blank.gif'))
        self.button2.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button2)
        self.button2.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button2.clicked.connect(self.button2Event)

        self.button3 = QPushButton()
        self.button3.setIcon(QtGui.QIcon('blank.gif'))
        self.button3.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button3)
        self.button3.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button3.clicked.connect(self.button3Event)
        #
        # second row
        self.button4 = QPushButton()
        self.button4.setIcon(QtGui.QIcon('blank.gif'))
        self.button4.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button4)
        self.button4.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button4.clicked.connect(self.button4Event)

        self.button5 = QPushButton()
        self.button5.setIcon(QtGui.QIcon('blank.gif'))
        self.button5.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button5)
        self.button5.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button5.clicked.connect(self.button5Event)

        self.button6 = QPushButton()
        self.button6.setIcon(QtGui.QIcon('blank.gif'))
        self.button6.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button6)
        self.button6.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button6.clicked.connect(self.button6Event)
        #
        # third row
        self.button7 = QPushButton()
        self.button7.setIcon(QtGui.QIcon('blank.gif'))
        self.button7.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button7)
        self.button7.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button7.clicked.connect(self.button7Event)

        self.button8 = QPushButton()
        self.button8.setIcon(QtGui.QIcon('blank.gif'))
        self.button8.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button8)
        self.button8.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button8.clicked.connect(self.button8Event)

        self.button9 = QPushButton()
        self.button9.setIcon(QtGui.QIcon('blank.gif'))
        self.button9.setIconSize(QtCore.QSize(60, 85))
        self.buttonArray.append(self.button9)
        self.button9.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.button9.clicked.connect(self.button9Event)
        #
        self.Quit = QPushButton("Quit")
        self.Quit.setShortcut("ctrl+v")
        self.Quit.setToolTip("Exit the game: ctrl+v")
        self.Quit.resize(10, 10)
        # self.Quit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.Quit.clicked.connect(self.quit)
        self.connectServer = QPushButton("Connect")
        self.connectServer.setShortcut("ctrl+e")
        self.connectServer.setToolTip("Connect to the server: ctrl+e")
        self.connectServer.clicked.connect(self.connectEvent)

        self.messageBox = QTextEdit()
        self.messageBox.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Expanding)
        # ensure that the QTextEdit allows for reading messages only, no typing/editing. "lock" the QTextEdit.
        self.messageBox.setReadOnly(True)

        # server entry
        self.label = QLabel("Enter server: ")
        self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.getServer = QLineEdit('127.0.0.1')

        # player character display
        self.character = QLabel("Game Character: ")
        self.pixmap = QPixmap('cross.gif')
        self.pixmap2 = QPixmap("nought.gif")
        self.picLabel = QLabel()
        self.picLabel.setSizePolicy(QSizePolicy.Expanding,
                                    QSizePolicy.Expanding)

        # enhancement
        # will most probably remove this later on
        self.comboBox = QComboBox()
        self.comboBox.addItems(
            ['Pink', 'Grey', 'Red', 'Orange', 'Yellow', 'Purple'])
        self.comboBox.activated.connect(self.setColour)
        self.setStyleSheet("background-color: pink")
        self.messageBox.setStyleSheet('background-color: white')
        self.getServer.setStyleSheet('background-color: white')
        # self.comboBox.setStyleSheet('background-color: white')

        hbox = QHBoxLayout()
        hbox.addWidget(menu)
        hbox2 = QWidget()
        hbox2.setLayout(hbox)

        # present everything in a Gridlayout
        # making sure to set span width and length (rows, columns) to avoid overlapping
        grid = QGridLayout()
        grid.addWidget(hbox2, 0, 4, 1, 3)
        grid.addWidget(self.label, 1, 0, 1, 1)
        grid.addWidget(self.getServer, 1, 1, 1, 1)
        grid.addWidget(self.connectServer, 1, 2, 1, 1)
        grid.addWidget(self.comboBox, 1, 3, 1, 1)
        #
        grid.addWidget(self.button1, 2, 0, 1, 1)
        grid.addWidget(self.button2, 2, 1, 1, 1)
        grid.addWidget(self.button3, 2, 2, 1, 1)
        #
        grid.addWidget(self.button4, 3, 0, 1, 1)
        grid.addWidget(self.button5, 3, 1, 1, 1)
        grid.addWidget(self.button6, 3, 2, 1, 1)
        #
        grid.addWidget(self.button7, 4, 0, 1, 1)
        grid.addWidget(self.button8, 4, 1, 1, 1)
        grid.addWidget(self.button9, 4, 2, 1, 1)
        #
        grid.addWidget(self.messageBox, 2, 3, 3, 4)
        grid.addWidget(self.character, 5, 1, 1, 1)
        grid.addWidget(self.picLabel, 5, 2, 1, 1)
        grid.addWidget(self.Quit, 5, 5, 1, 1)
        #
        self.setLayout(grid)

    # event handlers for all buttons
    def connectEvent(self):
        self.messageThread.Connect(self.getServer.displayText(
        ))  # connect to server currently entered in the QLineEdit
        self.messageThread.start(
        )  # call the QThread class start() method in order to call the run() method
        self.messageBox.append("Connected to server.")
        self.connectServer.setEnabled(
            False)  # disable the connect button when it's clicked

    # methods for the game board buttons
    # write to the messageBox when a button is clicked
    # this on happens if it's a player's turn to play
    # A player will not be able to click any of the buttons if it's not their turn to play
    def button1Event(self):
        self.messageThread.send_message(str(0))
        self.messageBox.append("Button 1 clicked")

    def button2Event(self):
        self.messageThread.send_message(str(1))
        self.messageBox.append("Button 2 clicked")

    def button3Event(self):
        self.messageThread.send_message(str(2))
        self.messageBox.append("Button 3 clicked")

    def button4Event(self):
        self.messageThread.send_message(str(3))
        self.messageBox.append("Button 4 clicked")

    def button5Event(self):
        self.messageThread.send_message(str(4))
        self.messageBox.append("Button 5 clicked")

    def button6Event(self):
        self.messageThread.send_message(str(5))
        self.messageBox.append("Button 6 clicked")

    def button7Event(self):
        self.messageThread.send_message(str(6))
        self.messageBox.append("Button 7 clicked")

    def button8Event(self):
        self.messageThread.send_message(str(7))
        self.messageBox.append("Button 8 clicked")

    def button9Event(self):
        self.messageThread.send_message(str(8))
        self.messageBox.append("Button 9 clicked")

    def quit(self):
        exitTheGame = QMessageBox.question(
            self, "Exit the game?", "Do you really want to exit the game.",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if (exitTheGame == QMessageBox.Yes):
            sys.exit()
        else:
            pass

    def handle_message(self, msg):
        # the message might/will be comma separated(multiple components)
        # split it into an array so as to individualise each component for later use----.split(",")
        # remove any trailing or leading white space
        message = msg.lower().strip().split(",")
        # lengthOfMessage = len(message)
        # print(message)

        if (message[0] == "new game"):
            shape = message[1]
            if (shape == "x"):
                self.pixmap2 = QPixmap("cross.gif")
            elif (shape == "o"):
                self.pixmap2 = QPixmap("nought.gif")
            self.picLabel.setPixmap(self.pixmap2)
            self.messageBox.append("The game has started, your character is " +
                                   message[1])

        elif (message[0] == "your move"):
            self.messageBox.append(message[0])
            for i in range(len(self.buttonArray)):
                self.buttonArray[i].setEnabled(
                    True
                )  # enable all buttons when it's a player's turn to play

        # disable all buttons when it's not a player's turn to play
        elif (message[0] == "opponents move"):
            self.messageBox.append(message[0])
            for i in range(len(self.buttonArray)):
                self.buttonArray[i].setEnabled(False)
            # print(message[0])

        # when a player makes a valid move at position n, their given character should
        # be added to the board array at index n
        elif (message[0] == "valid move"):
            shape = message[1]
            chosenPosition = message[2]
            if (shape == "x"):
                self.pixmap = QtGui.QIcon("cross.gif")
            elif (shape == "o"):
                self.pixmap = QtGui.QIcon("nought.gif")
            # found that short way I mentioned in assignment 7 :-)
            # rather than have if-else statements for all the buttons
            # I added them to an array then i just iterate over the array and change
            # the icon for the clicked button
            for i in range(9):
                if (chosenPosition == str(i)):
                    self.buttonArray[i].setIcon(QIcon(self.pixmap))
                else:
                    pass

        elif (message[0] == "invalid move"):
            self.messageBox.append(message[0])

        elif (message[0] == "game over"):
            if (message[1].upper() == "T"):
                self.messageBox.append("<strong>Game over, it's a Tie<strong>")
            else:
                self.messageBox.append(
                    "<strong>Game over, the winner is player {}</strong>".
                    format(message[1].upper()))

        # we shouldn't have a new game button
        # a popup made sense
        # if a player chooses to play again:
        # clear the board
        # send word to the server
        # else:......
        elif (message[0] == "play again"):
            endGame = QMessageBox.question(
                self, 'Play Again?', "Game over. Do you want to play again?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if endGame == QMessageBox.Yes:
                self.messageBox.clear()
                self.messageThread.send_message("y")
                for i in range(len(self.buttonArray)):
                    self.buttonArray[i].setIcon(QtGui.QIcon('blank.gif'))
            else:
                self.messageThread.send_message("n")

        elif (message[0] == "exit game"):
            self.messageBox.append("Game ended\nG.G")  # "Good Game"

    def setColour(self):
        self.setStyleSheet('background-color:' +
                           str(self.comboBox.currentText()))
        # set the background colour of the messageBox and getServer box explicitly
        # so that it is not affected(coloured in) by the color of the window
        # i.e: remains white at all times
        self.messageBox.setStyleSheet('background-color: white')
        self.getServer.setStyleSheet('background-color: white')

    def instructions(self):
        QMessageBox.information(
            self, self.tr("How to play"),
            self.
            tr("The goal of the game is to get three of your given character\n"
               "symbol into a row, column or diagonal of three before your opponent does so."
               ), QMessageBox.Ok)

    def about(self):
        QMessageBox.information(
            self, self.tr("About"),
            self.
            tr("TenElevenGames OXO\n"
               "Project Leader   : Bhekanani Cele\n"
               "Developers         : Yolisa Pingilili\n\t\t    Jane Doe\n\t\t    John Doe\n"
               "Release date       : 15 June 2020\n"
               "Contact info       : [email protected]"), QMessageBox.Ok)

    def more(self):
        QMessageBox.information(
            self, self.tr("More"),
            self.tr("Keep an eye out\nmore games coming soon!"),
            QMessageBox.Ok)

    # handle messages via thread
    def threadLine(self, msg):
        self.handle_message(msg)