I am working on game, which is written in qml and pyqt, but should be divided into two windows (Launcher + the Game). What is the proper way to switch between these two qml files? I don´t want to use QmlLoader, because it doesnt resize the window and It needs to many signals! I was trying also this variant:
view.engine().clearComponentCache()
view.setSource(source())
but it didn´t work (qml window stopped working... -classic windows error, however no error was written in pycharm console)
My code looks like this:
from PyQt5.QtCore import pyqtProperty, QRectF, QUrl, QObject, pyqtSignal, pyqtSlot, QVariant
from PyQt5.QtGui import QColor, QGuiApplication, QPainter, QPen
from PyQt5.QtQml import qmlRegisterType
from PyQt5.QtQuick import QQuickItem, QQuickPaintedItem, QQuickView
from PyQt5 import QtNetwork as QN
from PyQt5 import QtCore as QC
from multiprocessing import Process
import server as S
import client as C
from time import time, sleep
class Launcher(QQuickItem):
PORTS = (9998, 9999)
PORT = 9999
SIZEOF_UINT32 = 4
changeUI = pyqtSignal()
changeName= pyqtSignal(int, str)
connection= pyqtSignal(int, bool)
connected= pyqtSignal(QVariant)
@pyqtSlot(name="startGame")
def start_game(self):
#app.exit()
startGame()
print ("startGame")
@pyqtProperty(str)
def name(self):
print ("return name")
return self._name
@name.setter
def name(self, n):
print ("set name")
self._name= n
@pyqtSlot(name= "terminate")
def terminate_server(self):
if not self.server: return
self.server.terminate() #Bye
self.server.join()
#self.bstopServer.setEnabled(False)
#self.bserver.setEnabled(True)
self.server = None
@pyqtSlot()
def _quit(self):
if self.server:
self.server.terminate() #Bye
self.server.join()
app.exit()
@pyqtSlot()
def back(self):
self.client.disconnect()
def __init__(self, parent=None):
super(Launcher, self).__init__(parent)
self.socket= QN.QTcpSocket() #Yeah, the game will be over internet
self.server = None
self.client = None #client is a QObject
self._turnedOn = False
self._players = 1
self._name = "YourName"
class Game(QQuickItem):
def __init__(self, parent= None):
super(Game, self).__init__(parent)
self.client = True #I should get the client from the launcher, but I don´t know how
def startGame():
view.engine().clearComponentCache()
view.setResizeMode(QQuickView.SizeViewToRootObject)
view.showFullScreen()
view.setSource(
QUrl.fromLocalFile(
os.path.join(os.path.dirname(__file__),'Game.qml')))
view.show()
#app.exec_()
def startLauncher():
view.engine().clearComponentCache()
view.setResizeMode(QQuickView.SizeViewToRootObject)
view.setSource(
QUrl.fromLocalFile(
os.path.join(os.path.dirname(__file__),'Launcher.qml')))
view.show()
app.exec_()
if __name__ == '__main__':
import os
import sys
app = QGuiApplication(sys.argv)
qmlRegisterType(Launcher, "ParanoiaLauncher", 1, 0, "App")
qmlRegisterType(Game, "ParanoiaGame", 1, 0, "App")
view = QQuickView()
startLauncher()
As you might see, my structure is kind of chaotic, because I do this switching behaviour for the first time, so I dont really know, how it should be done right... Every advice welcomed! :)
I had to face same problem same time ago. Instead load the same component using the same QQuickView
over and over again, I created a component in QML as content container and I load the required component as child of it. Every time a new component is set, we destroy the current one and set the new one as child again.
// ContentFrame.qml
Item{
width: 800
height: 600
Item{
id: contentContainer
anchors.fill: parent
anchors.margins: 4
}
}
Before anything, forgive me but functional code is written in C++, but I think that the concept can be undertood. I made a shorten version of the process and I hope it can be ported to python.
To load the ContentFrame component, I used a class derived from QQuickView (ViewManager) that has a method called setView. This method loads a component (Launcher or Game in your case), set it as child of contentContainer
and set its anchor.fill
to fill the whole parent.
ViewManager::ViewManager() : QQuickView("ContentFrame.qml")
{
// More ctor code
}
// Other stuff
void ViewManager::setView(const QString &filename)
{
QQuickItem *mostTopItem = rootObject(); //QQuickView::rootObject() method
QQuickItem *contentItem->findChild<QQuickItem*>("contentContainer");
if(m_current != NULL)
{
m_current->setProperty("visible", false);
}
// Call a method to load the component and create an instance of it
QQuickItem *newItem = createOurComponentFromFile(filename);
newItem->setProperty("parent", QVariant::fromValue<QObject*>(contentItem));
// When "delete item" in C++, the object will be destroyed in QQmlEngine
// in pyqt???
QQmlEngine::setObjectOwnership(newItem, QQmlEngine::CppOwnership);
newItem->setProperty("anchors.fill", QVariant::fromValue<QObject*>(contentItem));
// Cleanup current object
if(m_current != NULL)
{
delete m_current;
}
m_current = newItem;
}
There are more code, but the heart of the ViewManager is this method. I don't call QQmlEngine::clearComponentCache()
here because I load the same components more than once, but in your case it could be a good idea.