2

I am practicing multi-file programming and am implementing a simple game of Minesweeper. When the user starts a new game, I create a grid of QPushButtons in the body of my window.

The problem is that whenever a player wants to start a new game of minesweeper, I do not know how to resize my window and reset my grid of QPushButtons so that a new game of minesweeper can be played. My initial approach was to nuke the orignal window and create a brand new one with the following

def newEasyGame(self):
app = QApplication([])
window = minesweeperWindow(10, 10, "easy")
window.show()
app.exec()

I am receiving the error QCoreApplication::exec: the event loop is already running.

EDIT: As requested, here is a more complete code block that describes what I am trying to do in more detail

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class minesweeperDemoWindow(QMainWindow):    
    def __init__(self, rows, cols, difficulty):
        super(minesweeperDemoWindow, self).__init__()

        #Central widget that is everything is contained within
        widget = QWidget()
        self.setCentralWidget(widget)

        #Vertical layout with a grid of buttons inside of it
        layout = QVBoxLayout()
        widget.setLayout(layout)

        #create a grid of QPushButtons that act as the playing field
        self.playingField = [[0 for x in range(rows)] for y in range(cols)] 

        grid = QGridLayout()
        #for every row and column, add a button
        for r in range(0, rows):
            for c in range(0, cols):
                button = QPushButton()
                button.setFixedSize(30, 30)
                #keep track of each button's position with myRow and myCol
                button.setProperty("myRow", r)
                button.setProperty("myCol", c)
                button.clicked.connect(self.buttonClicked)
                self.playingField[r][c] = button
                grid.addWidget(self.playingField[r][c], r, c)
        layout.addLayout(grid)
        grid.setSpacing(0)

        #How a user is going to start a new game
        menu = self.menuBar().addMenu("&Start new Game")
        newEasy = QAction("Easy", self, shortcut=QKeySequence.New, triggered=self.newEasyGame)

    #when the Easy menu item is selected, destroy the window and create a new easy game
    def newEasyGame(self):
        app = QApplication([])
        window = minesweeperWindow(10, 10, "easy")
        window.show()
        app.exec_()
  • @eyllanesc apologies for anything that is still unclear. My problem arises from not being able to resize my grid in between games, so my solution was to just nuke the original window and recreate a new one with the correct game grid size – Diamond Brook Apr 21 '19 at 09:24
  • Do not think you should ask about the underlying problem instead of a possible solution that nobody guarantees that works or is correct, that's what is called [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), please edit your question focusing on your grid problem – eyllanesc Apr 21 '19 at 09:27
  • Thank you for the feedback, I know it is important to always ask the right questions, but you provide actual examples of how to do that. Much appreciated! – Diamond Brook Apr 21 '19 at 09:46

1 Answers1

2

You do not have to destroy the window. In this case, a possible solution is to eliminate the widget that contains the QGridLayout. To do so, a method must be created that implements the destruction logic if necessary and creates the buttons.

from PyQt5 import QtCore, QtGui, QtWidgets


class MinesweeperDemoWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MinesweeperDemoWindow, self).__init__(parent)
        levels = [
            (4, 4, "level1"),
            (8, 8, "level2"),
            (16, 16, "level3"),
            (32, 32, "level4"),
        ]
        menu = self.menuBar().addMenu("&Start new Game")
        for r, c, name in levels:
            action = menu.addAction(name)
            action.setData((r, c))
            action.triggered.connect(self.on_triggered)
        r, c, _ = levels[0]
        self.setSize(r, c)

    @QtCore.pyqtSlot()
    def on_triggered(self):
        action = self.sender()
        r, c = action.data()
        self.setSize(r, c)

    def setSize(self, rows, cols):
        # delete the old container
        widget = self.centralWidget()
        if widget is not None:
            widget.deleteLater()
        # create new container
        widget = QtWidgets.QWidget()
        self.setCentralWidget(widget)
        grid = QtWidgets.QGridLayout(widget)
        self.playingField = [[0 for x in range(rows)] for y in range(cols)]
        for r in range(rows):
            for c in range(cols):
                button = QtWidgets.QPushButton()
                button.setFixedSize(30, 30)
                # keep track of each button's position with myRow and myCol
                button.setProperty("myRow", r)
                button.setProperty("myCol", c)
                # button.clicked.connect(self.buttonClicked)
                self.playingField[r][c] = button
                grid.addWidget(button, r, c)
        self.setFixedSize(widget.sizeHint())

    @QtCore.pyqtSlot()
    def buttonClicked(self):
        pass


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MinesweeperDemoWindow()
    w.setSize(4, 4)
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241