Метод QtQuick GeometryChanged работает только в одном направлении для загруженных компонентов - PullRequest
0 голосов
/ 01 декабря 2018

Я работаю над созданием приложения Python + QtQuick, для которого требуется класс, начинающийся с QQuickPaintedItem .У меня возникли проблемы с управлением макетом и размером окрашенного предмета.

В частности, у меня есть приложение, которое выглядит так:

enter image description here

Слева находится набор кнопок, а в правой части окна находится Загрузчик (есть веская причина для этого, это лишь минимальный пример, демонстрирующий проблему).

Основной файл qml (main.qml) выглядит следующим образом:

import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3

ApplicationWindow {
    id: mainWindow
    visible: true
    width: 720
    height: 480
    title: qsTr("QML Sampler")
    onClosing: main.close_application()

    RowLayout {
        anchors.fill: parent

        ColumnLayout {
            id: sideBar
            Layout.fillHeight: true
            Layout.preferredWidth: 200
            Layout.minimumWidth: 150
            Layout.maximumWidth: 350

            Button {
                id: buttonScreen1
                text: "Button 1"
                Layout.fillWidth: true
            }

            Button {
                id: buttonScreen2
                text: "Button 2"
                Layout.fillWidth: true
            }
        }

        Loader {
            id: contentAreaLoader
            Layout.fillHeight: true
            Layout.preferredWidth: 520
            Layout.minimumWidth: 300

            source: 'loaded_content.qml'
        }
    }
}

Загруженный файл содержимого - это тот, который фактически содержит пользовательский класс и выглядит следующим образом (loaded_content.qml):

import QtQuick 2.7
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3

import CustomPaintedItem 1.0

ColumnLayout {
    anchors.fill: parent

    Label {
        text: "Here's a thing!"
    }

    CustomPaintedItem {
        Layout.fillHeight: true
        Layout.fillWidth: true
    }
}

Когда размер окна изменяется, я хочу иметь возможность получить новые измерения QQuickPaintedItem, поэтому в коде я переопределяю метод QQuickPaintedItem.geometryChanged().В этом примере я просто печатаю новую и старую геометрию.

Остальной код для всего этого приведен ниже.Здесь задействованы три файла python, один для основного, один для класса CustomPaintedItem и один для основного контроллера.

main.py выглядит следующим образом:

import os
import sys

from PyQt5.QtQml import QQmlApplicationEngine, qmlRegisterType
from PyQt5.QtWidgets import QApplication

from main_controller import MainController
from custom_painted_item import CustomPaintedItem


def main():
    print('main()')

    qmlRegisterType(CustomPaintedItem, "CustomPaintedItem", 1, 0, "CustomPaintedItem")

    app = QApplication(sys.argv)
    engine = QQmlApplicationEngine()
    main_controller = MainController()
    context = engine.rootContext()
    context.setContextProperty("main", main_controller)
    script_directory = os.path.dirname(os.path.abspath(__file__))
    engine.load(os.path.join(script_directory, 'main.qml'))

    main_controller.start()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

И контроллер,main_controller.py выглядит следующим образом:

from PyQt5.QtCore import QObject, pyqtSlot


class MainController(QObject):
    def __init__(self):
        super().__init__()
        print('ctor')

    def start(self):
        print('start')

    @pyqtSlot()
    def close_application(self):
        print('close')

И, наконец, custom_painted_item.py:

from PyQt5.QtQuick import QQuickPaintedItem
from PyQt5.QtCore import QTimer


class CustomPaintedItem(QQuickPaintedItem):
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        QTimer.singleShot(100, self.update)

    def paint(self, QPainter):
        QTimer.singleShot(100, self.update)

    def geometryChanged(self, old_geom, new_geom):
        print('OLD: {0} x {1}'.format(old_geom.width(), old_geom.height()))
        print('NEW: {0} x {1}'.format(new_geom.width(), new_geom.height()))

Когда этот код выполняется, я могу настроить размер окна в измерении y (сделать его выше или короче) и увидеть, что высота изменяется, как и ожидалось:

OLD: 520.0 x 455.0
NEW: 520.0 x 454.0
OLD: 520.0 x 454.0
NEW: 520.0 x 453.0

Однако, когда я настраиваю размер окна в измерении x (делая его шире или уже), сообщаемая ширина никогдаизменяется от начального значения 520 (ширина окна 720, минус предпочтительная ширина боковой панели, 200).На самом деле, метод geometryChanged() даже не вызывается, когда я изменяю ширину.

Если вы закомментируете компонент Loader и замените его непосредственно компонентом ColumnLayout, например:

//        Loader {
//            id: contentAreaLoader
//            Layout.fillHeight: true
//            Layout.preferredWidth: 520
//            Layout.minimumWidth: 300

//            source: 'loaded_content.qml'
//        }

        ColumnLayout {
            Layout.fillHeight: true
            Layout.preferredWidth: 520
            Layout.minimumWidth: 300

            Label {
                text: "Here's a thing!"
            }

            CustomPaintedItem {
                Layout.fillHeight: true
                Layout.fillWidth: true
            }
        }

Затем метод geometryChanged() правильно вызывается для изменения размера x и y.Я не могу сказать, является ли это ошибкой, что-то, чего я не понимаю в загрузчике, или что-то в макетах.

Если это имеет значение, я использую Python 3.6 и PyQt 5.11.3.

Кто-нибудь может дать мне некоторое представление о том, как заставить эту работу работать с загрузчиком?Спасибо!

1 Ответ

0 голосов
/ 01 декабря 2018

Макеты обрабатывают только дочерние элементы, в случае, если загрузчик не используется: ColumnLayout занимает доступное пространство, так как maximumWidth установлено, и поэтому оно будет бесконечным (все, что может быть эквивалентноLayout.fillWidth: true).С другой стороны, загрузчик, который не является макетом, не имеет такой политики размера, поэтому, если вы хотите, чтобы он занимал все доступное пространство, вы должны явно использовать Layout.fillWidth: true.

Loader {
    id: contentAreaLoader
    Layout.fillHeight: true
    Layout.preferredWidth: 520
    Layout.minimumWidth: 300
    Layout.fillWidth: true // <----
    source: 'loaded_content.qml'
}
...