umbe1987 umbe1987 - 2 months ago 24
Python Question

PyQt5: updating MainApplication layout after QAction through widget method

I am trying to create my first memory game.

I recently found out how to have it working thanks to the answer of my question here. The working code is posted in the same link, but now I am trying to keep the widgets and the main application classes separated 1) because I think it's better, and 2) because I would like to in order to learn better how OOP works.

So, I here post my new code, which seems to work except the very last part. The concept:


  1. MainApplication has a menubar which permits to path to a folder of images, and instantiate the MemoryGame widget, which in turns initializes an empty QGridLayout.

  2. Clicking File -> open in the menubar the method showDialog is called.

  3. When a folder is chosen, populate_grid method (of MemoryGame obj) is called.

  4. populate_grid "should" fill the QGridLayout with each of the image found.

  5. The grid should be displayed to the screen, but it's not....



I am pretty sure the problem is in the very final row of populate_grid, where I add elements to the grid layout, but I can't find out how to solve the problem.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
Memory game 3

My first memory game in PyQt5.

author: Umberto Minora
last edited: September 2016
"""

import os
import sys
import glob
import math

from PyQt5.QtWidgets import (QMainWindow, QWidget,
QGridLayout, QPushButton, QApplication,
QAction, QFileDialog, QLabel)

from PyQt5.QtGui import QPixmap

class MainApplication(QMainWindow):
"""This is the main application.
All widgets should be put inside it."""
def __init__(self, widget):
super().__init__()

self.widget = widget
self.initUI()

def showDialog(self):
folder = str(QFileDialog.getExistingDirectory(self, "Select Directory",
'.', QFileDialog.ShowDirsOnly))

images = glob.glob(os.path.join(folder, '*.jpg'))

if images:
self.widget.populate_grid(images)

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.setCentralWidget(self.widget)

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

class MemoryGame(QWidget):
"""This is the Memory Game Widget"""
def __init__(self):
super().__init__()

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

def populate_grid(self, images):
n_cols = math.ceil(math.sqrt(len(images)))
n_rows = math.ceil(math.sqrt(len(images)))
positions = [(i,j) for i in range(n_cols) for j in range(n_rows)]
for position, img in zip(positions, images):
if img == '':
continue
pixmap = QPixmap(img)
scaled = pixmap.scaled(pixmap.width()/3, pixmap.height()/3)
del(pixmap)
lbl = QLabel(self)
lbl.setPixmap(scaled)
self.gridLayout.addWidget(lbl, *position)

if __name__ == '__main__':

app = QApplication(sys.argv)
ex = MainApplication(MemoryGame())
sys.exit(app.exec_())

Answer

The reason why the images aren't showing is because you put the gridWidget inside the MemoryGame widget, which doesn't have a layout itself. The MemoryGame widget should actually replace the gridWidget, so all you need to do is this:

class MemoryGame(QWidget):
    """This is the Memory Game Widget"""
    def __init__(self):
        super().__init__()
        self.gridLayout = QGridLayout(self)

I also think the way you create the MemoryGame widget is unnecessarily convoluted. Custom widget classes should be treated like any other. There's no need to pass it into the MainApplication constructor like that - just create it directly inside initUi:

def initUI(self):
    ...        
    self.widget = MemoryGame()
    self.setCentralWidget(self.widget)
Comments