Как изменить источник QQuickView

Я работаю над игрой, которая написана на qml и pyqt, но должна быть разделена на два окна (Launcher + the Game). Как правильно переключаться между этими двумя файлами qml? Я не хочу использовать QmlLoader, потому что он не изменяет размеры окна и требует много сигналов! Я пробовал также этот вариант:

view.engine().clearComponentCache()
view.setSource(source())

но это не сработало (перестало работать окно qml... - ошибка классического окна, однако в консоли pycharm не было написано ни одной ошибки)

Мой код выглядит так:

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()

Как вы, наверное, видите, моя структура хаотична, потому что я делаю это переключение впервые, поэтому я не знаю, как это следует делать правильно... Любой совет приветствуется!:)

1 ответ

Решение

Мне пришлось столкнуться с той же проблемой то же самое время назад. Вместо этого загрузите тот же компонент, используя тот же QQuickView Снова и снова я создавал компонент в QML как контейнер содержимого и загружал требуемый компонент как его дочерний элемент. Каждый раз, когда устанавливается новый компонент, мы уничтожаем текущий и снова устанавливаем новый дочерний.

// ContentFrame.qml
Item{
    width: 800
    height: 600
    Item{
        id: contentContainer
        anchors.fill: parent
        anchors.margins: 4
    }
}

Прежде всего, простите, но функциональный код написан на C++, но я думаю, что концепция может быть понята. Я сделал сокращенную версию процесса и надеюсь, что он может быть перенесен на python.

Чтобы загрузить компонент ContentFrame, я использовал класс, производный от QQuickView (ViewManager), у которого есть метод setView. Этот метод загружает компонент (Launcher или Game в вашем случае), устанавливая его как потомок contentContainer и установить его anchor.fill заполнить весь родитель.

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;
}

Есть больше кода, но сердце ViewManager это этот метод. Я не звоню QQmlEngine::clearComponentCache() здесь, потому что я загружаю одни и те же компоненты более одного раза, но в вашем случае это может быть хорошей идеей.

Другие вопросы по тегам