When writing my own applications, I use the concept of Scenes (such as in the Unity framework):
- Each scene is an asset that must be available at all times, and should not be released from memory;
- Each scene defines what's being currently rendered at the given time;
- Only one scene can be active at a time;
- If a new scene is set as current, the old one's contents must be properly released from memory.
This way, there's no need to open/close different windows for each different interface. There's also no need to destroy them: we can simply unparent an old scene, and set the new scene as the current scene to prevent it from being deleted by the Qt API.
Using this approach, we can store each scene's references since their creation inside some type of data structure (in this case a dictionary), and set them as current whenever we see fit.
This example shows this kind of scene logic I use at the moment, though anyone can use it if you want to. This example shows the kind of logic you're asking for:
from PySide2.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
from PySide2.QtWidgets import QLabel, QPushButton, QWidgetItem
# A modified version of what's written here:
# https://stackoverflow.com/questions/9374063/remove-all-items-from-a-layout/9383780#9383780
def clearLayout(layout):
if (layout is not None):
while (True):
child = layout.takeAt(0)
if (child is not None):
if (isinstance(child, QWidgetItem)):
widget = child.widget()
if (widget is not None):
widget.close()
widget.deleteLater()
elif (not isinstance(child, QSpacerItem)):
clearLayout(child.layout())
else:
break
# Create our base scene class:
class Scene(QWidget):
def __init__(self, mwin):
QWidget.__init__(self)
self.setLayout(QVBoxLayout())
self.window = mwin
# Virtual method. Should be overwritten by subclasses.
def start(self):
pass
def finish(self):
clearLayout(self.layout())
# Crate your custom scenes:
class Scene1(Scene):
def __init__(self, mwin):
Scene.__init__(self, mwin)
def start(self):
layout = self.layout()
backbt = QPushButton('Back To Scene 3')
nextbt = QPushButton('Foward To Scene 2')
# Assign Scene1 Logic
backbt.clicked.connect(lambda: self.window.setScene('scene3'))
nextbt.clicked.connect(lambda: self.window.setScene('scene2'))
layout.addWidget(QLabel('Scene 1'))
layout.addWidget(backbt)
layout.addWidget(nextbt)
class Scene2(Scene):
def __init__(self, mwin):
Scene.__init__(self, mwin)
def start(self):
layout = self.layout()
backbt = QPushButton('Back To Scene 1')
nextbt = QPushButton('Foward To Scene 3')
# Assign Scene2 Logic
backbt.clicked.connect(lambda: self.window.setScene('scene1'))
nextbt.clicked.connect(lambda: self.window.setScene('scene3'))
layout.addWidget(QLabel('Scene 2'))
layout.addWidget(backbt)
layout.addWidget(nextbt)
class Scene3(Scene):
def __init__(self, mwin):
Scene.__init__(self, mwin)
def start(self):
layout = self.layout()
backbt = QPushButton('Back To Scene 2')
nextbt = QPushButton('Foward To Scene 1')
# Assign Scene3 Logic
backbt.clicked.connect(lambda: self.window.setScene('scene2'))
nextbt.clicked.connect(lambda: self.window.setScene('scene1'))
layout.addWidget(QLabel('Scene 3'))
layout.addWidget(backbt)
layout.addWidget(nextbt)
print('Scene3: ', len(self.findChildren(QWidget)))
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.currScene = None
# Assign scenes to the main window:
# 1. prevent garbage collection
# 2. allows us to retrieve them from any other
# scene using only a string key, given we
# pass the MainWindow reference to each scene.
# 3. All the imports should go into this module
# alone. All other scenes do not need to import
# each other modules. They just need to use the
# MainWindow.setScene method with the right key.
self.scenes = {}
self.scenes['scene1'] = Scene1(self)
self.scenes['scene2'] = Scene2(self)
self.scenes['scene3'] = Scene3(self)
# Start with scene1
self.setScene('scene1')
def setScene(self, name):
# Releases the old scene, hides it and unparents it
# so it can be used again.
if (self.currScene is not None):
self.currScene.finish()
self.currScene.hide()
# unparent to take back ownership of the widget
self.currScene.setParent(None)
# Set the current reference.
self.currScene = self.scenes.get(name)
# Sets the new scene as current, start them, and
# display them at the screen.
if (self.currScene is not None):
self.setCentralWidget(self.currScene)
self.currScene.start()
self.currScene.show()
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec_()
About the circular references. By using this approach, you could redefine every class here into separate modules. This way you would avoid using circular references:
- Scene1.py imports
- Scene2.py imports
- Scene3.py imports
- MainWindow.py imports
- Scene1.py
- Scene2.py
- Scene3.py
- main.py imports