Home > Software engineering >  Getting the button name in PyQT5
Getting the button name in PyQT5

Time:12-10

I'm working on a simple tic-tac-toe game with gui using PyQt5. The interface is all set up but I'm having a bit of trouble with a function. The interface is a 3x3 grid of buttons. When you click one of these buttons, it dropsdown a choice of X or O. Whichever choice you click on, that becomes the text of the button.

class App(QMainWindow):
    game_board = {
        'btn1': '', 'btn2': '', 'btn3': '',
        'btn4': '', 'btn5': '', 'btn6': '',
        'btn7': '', 'btn8': '', 'btn9': ''
    }

    def __init__(self):
        super().__init__()
        self.title = 'Tic-Tac-Toe'
        self.top = 100
        self.left = 100
        self.width = 375
        self.height = 375
        
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.top, self.left, self.width, self.height)

...___...

        # buttons that make up the gameboard
        self.btn1 = QPushButton(self.game_board['btn1'], self)
        self.btn2 = QPushButton(self.game_board['btn2'], self)
        self.btn3 = QPushButton(self.game_board['btn3'], self)
        self.btn4 = QPushButton(self.game_board['btn4'], self)
        self.btn5 = QPushButton(self.game_board['btn5'], self)
        self.btn6 = QPushButton(self.game_board['btn6'], self)
        self.btn7 = QPushButton(self.game_board['btn7'], self)
        self.btn8 = QPushButton(self.game_board['btn8'], self)
        self.btn9 = QPushButton(self.game_board['btn9'], self)

        self.btns = {'btn1': self.btn1, 'btn2': self.btn2, 'btn3': self.btn3, 'btn4': self.btn4, 'btn5': self.btn5, 'btn6': self.btn6, 'btn7': self.btn7, 'btn8': self.btn8, 'btn9': self.btn9}

        x_o_menu = QMenu(self)
        x_o_menu.addAction('X', self.x_action)
        x_o_menu.addAction('O', self.o_action)
        
        i = 1

        for value in self.btns.values():
            if i < 4:
                value.setGeometry(75*i, 75, 75, 75)
            elif i < 7:
                value.setGeometry(75*(i-3), 150, 75, 75)
            elif i < 10:
                value.setGeometry(75*(i-6), 225, 75, 75)

            i  = 1

            value.setMenu(x_o_menu)
        
        self.show()

...___...

    def x_action(self):
        sender = self.sender()
        self.statusBar().showMessage(sender.text()   ' was pressed')

    def o_action(self):
        pass

The closest solution I've found so far has been to use self.sender() but that doesn't return the button, rather it returns the dropdown option (which as I'm typing this I realize I could probably use to turn x_action and o_action into a single method), but I still have no way of knowing which button to update the text for. Is there a way to do this without giving a unique menu to each button?

CodePudding user response:

A QMenu, just like a QAction, can be shown/used by any parent. It can also be shown without any actual parent. So your approach is actually invalid.

Since your board will clearly have a limited range of buttons, adding a unique menu to each button won't create that big overhead, so a possibility is to create individual menus for each button.

A better approach, though, would be to create a subclass of QPushButton and add a unique QMenu for that button. Then you need to create a custom signal for the button class, and you can connect that signal from the main program using the custom signature or, eventually, a lambda.

Please note that creating direct child widgets for a QMainWindow is discouraged, as well as using fixed geometries; as the documentation suggests:

Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.

You should properly create a central widget (a basic QWidget would suffice), then use setCentralWidget() and set up a layout manager for that central widget. Then, you can add the buttons to that layout.

  • Related