PyQt5: updating MainApplication layout after QAction through widget method
<p>I am trying to create my first memory game.</p>

<p>I recently found out how to have it working thanks to the answer of my question <a href="http://stackoverflow.com/questions/39689134/python-using-output-from-methods-class-inside-another-class/39689457#39689457">here</a>. 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.</p>

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

<ol>
<li><strong>MainApplication</strong> has a menubar which permits to path to a folder of images, and instantiate the <strong>MemoryGame</strong> widget, which in turns initializes an empty QGridLayout.</li>
<li>Clicking File -> open in the menubar the method <strong>showDialog</strong> is called.</li>
<li>When a folder is chosen, <strong>populate_grid</strong> method (of MemoryGame obj) is called.</li>
<li><strong>populate_grid</strong> "should" fill the QGridLayout with each of the image found.</li>
<li>The grid should be displayed to the screen, but it's not....</li>
</ol>

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

<pre><code>#!/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('&amp;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_())
</code></pre>

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

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

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

<pre><code>def initUI(self):
    ...        
    self.widget = MemoryGame()
    self.setCentralWidget(self.widget)
</code></pre>

python python-3.x oop pyqt pyqt5