umbe1987 umbe1987 - 2 months ago 8
Python Question

python: using output from method's class inside another class

I am trying to develop my first app with PyQt5 (a memory game).

I have created two classes: MainApplication, which inherits from QMainWindow, and GridWidget, which inherits from QWidget. My aim is to let the user specify a folder with some images (jpg) using the fileMenu of the menuBar.

So, in the MainApplication I created the method showDialog which connects to the fileMenu and outputs a list of filenames (names of the images within the selected folder), stored in a list variable. I would like to be able to pass this to the GridWidget, so that it can creates and fill the grid.

I am kind of new to OOP programming, so maybe the organization of my script is not the best, and I am open to suggestions to improve it. My idea was to add GridWidget into MainApplication, but I don't know how to pass the output of showDialog to this. Any suggestion would be appreciated.

Here is my code so far:

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

"""
Memory game

My first memory game in PyQt5.

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

import os, sys, glob
from PyQt5.QtWidgets import (QMainWindow, QWidget,
QGridLayout, QPushButton, QApplication,
QAction, QFileDialog)
from PyQt5.QtGui import QPixmap

class MainApplication(QMainWindow):

def __init__(self):
super().__init__()

self.initUI()

def initUI(self):
self.statusBar()

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

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

self.form_widget = GridWidget(self)

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

def showDialog(self):

folder = str(QFileDialog.getExistingDirectory(self, "Select Directory",
'/home', QFileDialog.ShowDirsOnly))

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

if images:
return images * 2

class GridWidget(QWidget):

def __init__(self):
super().__init__()

grid = QGridLayout()
self.setLayout(grid)

names = self.showDialog() # DA SISTEMARE!!!!!!

positions = [(i,j) for i in range(int(len(names)/2)) for j in range(int(len(names)/2))]

for position, name in zip(positions, names):

if name == '':
continue
button = QPushButton(name)
grid.addWidget(button, *position)

if __name__ == '__main__':

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


EDIT

Thanks to @ekhumoro's answer, now the code is working. Here is the code I am actually running (it's not the complete game, just an initial import of images from a folder).

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

"""
Memory game 2

My first memory game in PyQt5.

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

import os, sys, glob, math
from PyQt5.QtWidgets import (QMainWindow, QWidget,
QGridLayout, QPushButton, QApplication,
QAction, QFileDialog, QLabel)
from PyQt5.QtGui import QPixmap

class MainApplication(QMainWindow):
def __init__(self):
super().__init__()
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 populateGrid(self, images):
names = images * 2
n_cols = math.ceil(math.sqrt(len(names)))
n_rows = math.ceil(math.sqrt(len(names)))
positions = [(i,j) for i in range(n_cols) for j in range(n_rows)]
for position, name in zip(positions, names):
if name == '':
continue
pixmap = QPixmap(name)
scaled = pixmap.scaled(pixmap.width()/3, pixmap.height()/3)
del(pixmap)
lbl = QLabel(self)
lbl.setPixmap(scaled)
self.gridLayout.addWidget(lbl, *position)

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

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

if images:
self.populateGrid(images)


if __name__ == '__main__':

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

Answer

I think it will be simpler if you keep everything in one class. You should make each child widget an attribute of the class, so you can access them later using self within the methods of the class. All the program logic will go in the methods - so it's just a matter of calling methods in response to signals and events.

Here is what the class should look like:

class MainApplication(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.statusBar()

        openFile = QAction('Open', self)
        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 populateGrid(self, images):
        names = images * 2
        positions = [(i,j) for i in range(int(len(names)/2)) for j in range(int(len(names)/2))]
        for position, name in zip(positions, names):
            if name == '':
                continue
            button = QPushButton(name)
            self.gridLayout.addWidget(button, *position)

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

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

        if images:
            self.populateGrid(images)