Skip to main content
Provided the updated code as github repo link
Source Link

EDIT (November 2024): I have uploaded the updated code to this github repo: https://github.com/umbe1987/memory if you want to have a look :)

ORIGINAL POST

EDIT (November 2024): I have uploaded the updated code to this github repo: https://github.com/umbe1987/memory if you want to have a look :)

ORIGINAL POST

code updated
Source Link
import os, sys, glob, math, random
from PyQt5.QtWidgets import (QMainWindow, QWidget,
                             QGridLayout, QPushButton, QApplication,
                             QAction, QFileDialog, QLabelQMessageBox)
from PyQt5.QtGui import QPixmap, QIcon, QCloseEvent
from PyQt5.QtMultimedia import QSound
from PyQt5 import QtCore

app_dir = os.path.dirname(__file__)
back_card_img = 'back'
sound_folder = 'sound'
sound_success = 'success.wav'
sound_fail = 'fail.wav'
sound_end = 'end.wav'
image_folder = 'img'

class MemoryGame(QMainWindow):
    def __init__(self):
        super().__init__()
        self.back_card = r'os./backpath.jpg'join(app_dir, back_card_img)  # remove the extension from the image name if present
        self.status = []{}
        self.cell_width = 100
        self.cell_height = 150
        self.initUI()

    def initUI(self):
        self.statusBar()

        self.sounds = {
            'success': QSound(os.path.join(app_dir, sound_folder, sound_success)),
            'fail': QSound(os.path.join(app_dir, sound_folder, sound_fail)),
            'end': QSound(os.path.join(app_dir, sound_folder, sound_end))
        }

        openFile = QAction('Open', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Search image folder')
        openFile.triggered.connect(self.showDialog)

        menubar = self.menuBar()
        self.fileMenu = menubar.addMenu('&File')
        self.fileMenu.addAction(openFile)

        self.gridWidget = QWidget(self)
        self.gridLayout = QGridLayout(self.gridWidget)
        self.setCentralWidget(self.gridWidget)

        self.setGeometry(3000, 3000, 350, 300)
        self.setWindowTitle('Memory Game!')
        self.show()
        # self.showFullScreen()

    def showDialog(self):
        """Seacrh for a folder of images, and create two dictionaries:
        - one with grid_position : image
        - one with grid_position : back_card"""
        folder = str(QFileDialog.getExistingDirectory(self, "Select Directory",
            '.', QFileDialog.ShowDirsOnly))

        self.list_images(folder)
        if self.n_img > 0:
            self.fill_dict(self.images) # create the grid_positionstatus:image Dict
            self.empty_dict()
          status = self.init_grid(){}

        folder = str(QFileDialog.getExistingDirectory(self,
                                                      "Select Directory",
                                                      os.path.join(app_dir, image_folder),
                                                      QFileDialog.ShowDirsOnly))

        # if the user selected a folder
        if folder:
            self.list_images(folder)

            # if the selected foldser contains images, limit the number to 16
            if self.n_img > 0:
                if self.n_img > 16:
                    del self.images[16:]
                self.fill_dict(self.images)  # create the grid_position:image Dict
                self.empty_dict()
                self.init_grid()

    def list_images(self, folder):
        """List the (JPEG) images within the selected folder"""
        imagesextensions = glob.glob(os'.jpg', '.jpeg', '.gif', '.png')
        images = [os.path.join(folder, '*f) for f in os.jpg'listdir(folder) if f.lower().endswith(extensions)]
        n_img = len(images)
        
        self.images = images
        self.n_img = n_img

    def fill_dict(self, images):
        grid_cell = images * 2
        random.shuffle(grid_cell)
        self.card_dict = {}
        n_cols = math.ceil(math.sqrt(len(grid_cell)))
        n_rows = math.ceil(math.sqrt(len(grid_cell)))
        max_rows = 4
        if n_rows > max_rows:
            n_cols += n_rows - max_rows
            n_rows = max_rows

        positions = [(i, j) for i in range(n_colsn_rows) for j in range(n_rowsn_cols)]
        for p, cell in zip(positions, grid_cell):
            if cell == '':
                continue
            self.card_dict[p] = cell

    def empty_dict(self):
        # copy the card_dict to make a back_dict (with the back_card)
        self.back_dict = dict(self.card_dict)
        for k, v in self.back_dict.items():
            self.back_dict[k] = self.back_card
        
    def init_grid(self):
        """Initialize the grid according to the number of images
        found in the selected folder."""

        for pos, img in self.back_dict.items():
            btn = QPushButton(self)
            btn.clicked.connect(self.buttonClicked)
            pixmap = QPixmap(self.back_dict[pos])
            scaled = pixmap.scaled(pixmapself.width()/3cell_width, pixmapself.height()/3cell_height) # can be done better....
            btn.setIcon(QIcon(scaled))
            btn.setIconSize(scaled.rect().size())
            btn.setFixedSize(scaled.rect().size())
            del (pixmap)

            self.gridLayout.addWidget(btn, *pos)

    def restart(self):
        self.status = {}
        self.init_grid()

        return

    def turn_card(self, btn, location):
        """When a button (image) is clicked, turn the card."""

        ifpixmap len= QPixmap(self.statuscard_dict[location]) 
 == 0 or     scaled = pixmap.scaled(self.cell_width, self.cell_height)
        btn.setIcon(QIcon(scaled))
        btn.setIconSize(scaled.rect().size())
        btn.setFixedSize(scaled.rect().size())
        del (pixmap)

        n_cards = len(self.status.keys()) 

 % 2      # if game just started, turn the card
        if n_cards == 0:
 or           self.card_dict[location]status[location] in= self.statuscard_dict[location]

            return

        # if the number of card is odd
        elif n_cards % 2 != 0:
            # if card already present, keep it...
            if self.card_dict[location] in self.status.appendvalues() and location not in self.card_dict[location]status.keys():
            pixmap    self.status[location] = QPixmap(self.card_dict[location])
            scaled = pixmap  if self.scaled(pixmapstatus == self.width()/3,card_dict:
 pixmap                   self.heightend_game()/3)
            btn        return
                self.setIcon(QIconsounds['success'].play(scaled))
            btn    return

            # if the same card is clicked, do nothing.setIconSize(scaled.rect().size())
            btn.setFixedSize(scaledelif location in self.rect()status.sizekeys()):
            del(pixmap)    pass
            # ...otherwise restart the grid
            else:
            self    ''' wait 1 sec and restart (second argument MUST be a method;
                cannot use time.statussleep, =because []GUI freezes)'''
                QtCore.QTimer.singleShot(1000, self.init_gridrestart)
                self.sounds['fail'].play()
                return

        self.status[location] = self.card_dict[location]

    def buttonClicked(self):
        button = self.sender()
        idx = self.gridLayout.indexOf(button)
        location = self.gridLayout.getItemPosition(idx)
        # print ("Button", button, "at row/col", location[:2])
        self.turn_card(button, location[:2])

    def end_game(self):
        self.sounds['end'].play()

        msgBox = QMessageBox()
        msgBox.setIcon(QMessageBox.Question)
        msgBox.setWindowTitle("!!!You won!!!")
        msgBox.setInformativeText("What's next now?")
        quit = msgBox.addButton('quit', QMessageBox.RejectRole)
        restartBtn = msgBox.addButton('play again', QMessageBox.ActionRole)
        changeBtn = msgBox.addButton('change cards', QMessageBox.ActionRole)
        msgBox.setDefaultButton(restartBtn)

        msgBox.exec_()
        msgBox.deleteLater()

        if msgBox.clickedButton() == quit:
            self.close()
        elif msgBox.clickedButton() == restartBtn:
            self.restart()
        elif msgBox.clickedButton() == changeBtn:
            self.showDialog()

        return

    def closeEvent(self, event):

        reply = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


if __name__ == '__main__':
    
    app = QApplication(sys.argv)
    ex = MemoryGame()
    sys.exit(app.exec_())
import os, sys, glob, math
from PyQt5.QtWidgets import (QMainWindow, QWidget,
    QGridLayout, QPushButton, QApplication,
    QAction, QFileDialog, QLabel)
from PyQt5.QtGui import QPixmap, QIcon

class MemoryGame(QMainWindow):
    def __init__(self):
        super().__init__()
        self.back_card = r'./back.jpg'
        self.status = []
        self.initUI()

    def initUI(self):
        self.statusBar()

        openFile = QAction('Open', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Search image folder')
        openFile.triggered.connect(self.showDialog)

        menubar = self.menuBar()
        self.fileMenu = menubar.addMenu('&File')
        self.fileMenu.addAction(openFile)

        self.gridWidget = QWidget(self)
        self.gridLayout = QGridLayout(self.gridWidget)
        self.setCentralWidget(self.gridWidget)

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Memory Game!')
        self.show()

    def showDialog(self):
        """Seacrh for a folder of images, and create two dictionaries:
        - one with grid_position : image
        - one with grid_position : back_card"""
        folder = str(QFileDialog.getExistingDirectory(self, "Select Directory",
            '.', QFileDialog.ShowDirsOnly))

        self.list_images(folder)
        if self.n_img > 0:
            self.fill_dict(self.images) # create the grid_position:image Dict
            self.empty_dict()
            self.init_grid()

    def list_images(self, folder):
        """List the (JPEG) images within the selected folder"""
        images = glob.glob(os.path.join(folder, '*.jpg'))
        n_img = len(images)
        
        self.images = images
        self.n_img = n_img

    def fill_dict(self, images):
        grid_cell = images * 2
        self.card_dict = {}
        n_cols = math.ceil(math.sqrt(len(grid_cell)))
        n_rows = math.ceil(math.sqrt(len(grid_cell)))
        positions = [(i,j) for i in range(n_cols) for j in range(n_rows)]
        for p, cell in zip(positions, grid_cell):
            if cell == '':
                continue
            self.card_dict[p] = cell

    def empty_dict(self):
        # copy the card_dict to make a back_dict (with the back_card)
        self.back_dict = dict(self.card_dict)
        for k, v in self.back_dict.items():
            self.back_dict[k] = self.back_card
        
    def init_grid(self):
        """Initialize the grid according to the number of images
        found in the selected folder."""

        for pos, img in self.back_dict.items():
            btn = QPushButton(self)
            btn.clicked.connect(self.buttonClicked)
            pixmap = QPixmap(self.back_dict[pos])
            scaled = pixmap.scaled(pixmap.width()/3, pixmap.height()/3) # can be done better....
            btn.setIcon(QIcon(scaled))
            btn.setIconSize(scaled.rect().size())
            btn.setFixedSize(scaled.rect().size())
            del(pixmap)

            self.gridLayout.addWidget(btn, *pos)
            
    def turn_card(self, btn, location):
        """When a button (image) is clicked, turn the card."""

        if len(self.status) == 0 or len(self.status) % 2 == 0 or self.card_dict[location] in self.status:
            self.status.append(self.card_dict[location])
            pixmap = QPixmap(self.card_dict[location])
            scaled = pixmap.scaled(pixmap.width()/3, pixmap.height()/3)
            btn.setIcon(QIcon(scaled))
            btn.setIconSize(scaled.rect().size())
            btn.setFixedSize(scaled.rect().size())
            del(pixmap)
        else:
            self.status = []
            self.init_grid()
            
    def buttonClicked(self):
        button = self.sender()
        idx = self.gridLayout.indexOf(button)
        location = self.gridLayout.getItemPosition(idx)
        # print ("Button", button, "at row/col", location[:2])
        self.turn_card(button, location[:2])
        
if __name__ == '__main__':
    
    app = QApplication(sys.argv)
    ex = MemoryGame()
    sys.exit(app.exec_())
import os, sys, glob, math, random
from PyQt5.QtWidgets import (QMainWindow, QWidget,
                             QGridLayout, QPushButton, QApplication,
                             QAction, QFileDialog, QMessageBox)
from PyQt5.QtGui import QPixmap, QIcon, QCloseEvent
from PyQt5.QtMultimedia import QSound
from PyQt5 import QtCore

app_dir = os.path.dirname(__file__)
back_card_img = 'back'
sound_folder = 'sound'
sound_success = 'success.wav'
sound_fail = 'fail.wav'
sound_end = 'end.wav'
image_folder = 'img'

class MemoryGame(QMainWindow):
    def __init__(self):
        super().__init__()
        self.back_card = os.path.join(app_dir, back_card_img)  # remove the extension from the image name if present
        self.status = {}
        self.cell_width = 100
        self.cell_height = 150
        self.initUI()

    def initUI(self):
        self.statusBar()

        self.sounds = {
            'success': QSound(os.path.join(app_dir, sound_folder, sound_success)),
            'fail': QSound(os.path.join(app_dir, sound_folder, sound_fail)),
            'end': QSound(os.path.join(app_dir, sound_folder, sound_end))
        }

        openFile = QAction('Open', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Search image folder')
        openFile.triggered.connect(self.showDialog)

        menubar = self.menuBar()
        self.fileMenu = menubar.addMenu('&File')
        self.fileMenu.addAction(openFile)

        self.gridWidget = QWidget(self)
        self.gridLayout = QGridLayout(self.gridWidget)
        self.setCentralWidget(self.gridWidget)

        self.setGeometry(0, 0, 350, 300)
        self.setWindowTitle('Memory Game!')
        self.show()
        # self.showFullScreen()

    def showDialog(self):
        """Seacrh for a folder of images, and create two dictionaries:
        - one with grid_position : image
        - one with grid_position : back_card"""

        if self.status:
            self.status = {}

        folder = str(QFileDialog.getExistingDirectory(self,
                                                      "Select Directory",
                                                      os.path.join(app_dir, image_folder),
                                                      QFileDialog.ShowDirsOnly))

        # if the user selected a folder
        if folder:
            self.list_images(folder)

            # if the selected foldser contains images, limit the number to 16
            if self.n_img > 0:
                if self.n_img > 16:
                    del self.images[16:]
                self.fill_dict(self.images)  # create the grid_position:image Dict
                self.empty_dict()
                self.init_grid()

    def list_images(self, folder):
        """List the (JPEG) images within the selected folder"""
        extensions = ('.jpg', '.jpeg', '.gif', '.png')
        images = [os.path.join(folder, f) for f in os.listdir(folder) if f.lower().endswith(extensions)]
        n_img = len(images)

        self.images = images
        self.n_img = n_img

    def fill_dict(self, images):
        grid_cell = images * 2
        random.shuffle(grid_cell)
        self.card_dict = {}
        n_cols = math.ceil(math.sqrt(len(grid_cell)))
        n_rows = math.ceil(math.sqrt(len(grid_cell)))
        max_rows = 4
        if n_rows > max_rows:
            n_cols += n_rows - max_rows
            n_rows = max_rows

        positions = [(i, j) for i in range(n_rows) for j in range(n_cols)]
        for p, cell in zip(positions, grid_cell):
            if cell == '':
                continue
            self.card_dict[p] = cell

    def empty_dict(self):
        # copy the card_dict to make a back_dict (with the back_card)
        self.back_dict = dict(self.card_dict)
        for k, v in self.back_dict.items():
            self.back_dict[k] = self.back_card

    def init_grid(self):
        """Initialize the grid according to the number of images
        found in the selected folder."""

        for pos, img in self.back_dict.items():
            btn = QPushButton(self)
            btn.clicked.connect(self.buttonClicked)
            pixmap = QPixmap(self.back_dict[pos])
            scaled = pixmap.scaled(self.cell_width, self.cell_height)
            btn.setIcon(QIcon(scaled))
            btn.setIconSize(scaled.rect().size())
            btn.setFixedSize(scaled.rect().size())
            del (pixmap)

            self.gridLayout.addWidget(btn, *pos)

    def restart(self):
        self.status = {}
        self.init_grid()

        return

    def turn_card(self, btn, location):
        """When a button (image) is clicked, turn the card."""

        pixmap = QPixmap(self.card_dict[location]) 
        scaled = pixmap.scaled(self.cell_width, self.cell_height)
        btn.setIcon(QIcon(scaled))
        btn.setIconSize(scaled.rect().size())
        btn.setFixedSize(scaled.rect().size())
        del (pixmap)

        n_cards = len(self.status.keys()) 

        # if game just started, turn the card
        if n_cards == 0:
            self.status[location] = self.card_dict[location]

            return

        # if the number of card is odd
        elif n_cards % 2 != 0:
            # if card already present, keep it...
            if self.card_dict[location] in self.status.values() and location not in self.status.keys():
                self.status[location] = self.card_dict[location]
                if self.status == self.card_dict:
                    self.end_game()
                    return
                self.sounds['success'].play()
                return

            # if the same card is clicked, do nothing...
            elif location in self.status.keys():
                pass
            # ...otherwise restart the grid
            else:
                ''' wait 1 sec and restart (second argument MUST be a method;
                cannot use time.sleep, because GUI freezes)'''
                QtCore.QTimer.singleShot(1000, self.restart)
                self.sounds['fail'].play()
                return

        self.status[location] = self.card_dict[location]

    def buttonClicked(self):
        button = self.sender()
        idx = self.gridLayout.indexOf(button)
        location = self.gridLayout.getItemPosition(idx)
        self.turn_card(button, location[:2])

    def end_game(self):
        self.sounds['end'].play()

        msgBox = QMessageBox()
        msgBox.setIcon(QMessageBox.Question)
        msgBox.setWindowTitle("!!!You won!!!")
        msgBox.setInformativeText("What's next now?")
        quit = msgBox.addButton('quit', QMessageBox.RejectRole)
        restartBtn = msgBox.addButton('play again', QMessageBox.ActionRole)
        changeBtn = msgBox.addButton('change cards', QMessageBox.ActionRole)
        msgBox.setDefaultButton(restartBtn)

        msgBox.exec_()
        msgBox.deleteLater()

        if msgBox.clickedButton() == quit:
            self.close()
        elif msgBox.clickedButton() == restartBtn:
            self.restart()
        elif msgBox.clickedButton() == changeBtn:
            self.showDialog()

        return

    def closeEvent(self, event):

        reply = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MemoryGame()
    sys.exit(app.exec_())
Tweeted twitter.com/StackCodeReview/status/785008034116493312
deleted 59 characters in body; edited tags
Source Link
200_success
  • 144.2k
  • 22
  • 188
  • 473

So this is my very first time in this site. I have written this very basic Python code witjwith PyQt5 to create a Memory Game. As I am not very familiar with OOP (but I'm trying to be!), I hope somebody could point me to where I could improve my game.

Here is the code, thanks in advance for any suggestion:

So this is my very first time in this site. I have written this very basic Python code witj PyQt5 to create a Memory Game. As I am not very familiar with OOP (but I'm trying to be!), I hope somebody could point me to where I could improve my game.

Here is the code, thanks in advance for any suggestion:

So this is my very first time in this site. I have written this very basic Python code with PyQt5 to create a Memory Game. As I am not very familiar with OOP (but I'm trying to be!), I hope somebody could point me to where I could improve my game.

Source Link
Loading