2
votes

I'm working on a PyQt project. I'd like to put themes in a different folder than my main qml and allow different one to be loaded potentially. I have my directory setup like in the image below. How can I reference the properties in the theme? I'm planning on making this being a pretty big project eventually so would like to have a good directory structure that keeps things organized. Currently i don't get any exceptions but the background color does not match the color in my Theme.qml file.

Directory structure

main.qml

import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import './Themes/'

ApplicationWindow
{
    id: mainWindow
    width: 640
    height: 480
    visible: true
    color:  Theme.primaryBackgroundColor
    title: qsTr("SmartDraw")
    flags: Qt.FramelessWindowHint | Qt.Window

    header: Rectangle {
        id: windowHeader
        height: 38
        width: parent.width
        color: "#0e6afa"

        MouseArea {
            id: windowResizeUp
            height: 2
            anchors.bottom: windowDragArea.top
            anchors.left: parent.left
            anchors.right: minimize.left
            cursorShape: Qt.SizeVerCursor
            property real lastMousePosY: 0

            onPressed: {
                lastMousePosY = mouse.y
            }
            onMouseYChanged:
            {
                var dy = (mouseY - lastMousePosY)
                mainWindow.y += dy
                mainWindow.height -= dy
            }
        }

        MouseArea {
            id: windowDragArea
            height: parent.height - 2
            anchors.bottom: parent.bottom
            anchors.left: parent.left
            anchors.right: minimize.left

            property point lastMousePos: Qt.point(0, 0)
            onPressed: { lastMousePos = Qt.point(mouseX, mouseY); }
            onMouseXChanged: mainWindow.x += (mouseX - lastMousePos.x)
            onMouseYChanged: mainWindow.y += (mouseY - lastMousePos.y)
        }

        Button {
            id: minimize
            width: 30
            height: parent.height
            anchors.right: maximize.left
            onClicked: mainWindow.showMinimized()

            background: Rectangle {
                width: parent.width
                height: parent.height
                color: windowHeader.color
            }

            Rectangle {
                color: "white"
                height: 2
                width: Math.round(parent.width*(2.0/3.0))
                anchors.centerIn: parent
            }
        }

        Button {
            id: maximize
            width: 30
            height: parent.height
            anchors.right: close.left
            onClicked: mainWindow.visibility == Window.Maximized ? mainWindow.showNormal() : mainWindow.showMaximized()

            background: Rectangle {
                width: parent.width
                height: parent.height
                color: windowHeader.color
            }

            Rectangle {
                color: "white"
                width: 15
                height: 15
            }
        }

        Button {
            id: close
            width: 30
            anchors.right: parent.right
            height: parent.height
            onClicked: Qt.quit()

            background: Rectangle {
                width: parent.width
                height: parent.height
                color: windowHeader.color
            }

            Text {
               color: "white"
               text: "X"
            }
        }
    }


    footer: Rectangle {
        id: windowFooter
        color: "#0e6afa"
        height: 23

        MouseArea {
            id: windowResizeBottomLeft
            width:  4
            height: 4
            anchors.left: parent.left
            anchors.bottom: parent.bottom
            cursorShape: Qt.SizeBDiagCursor

            property point lastMousePos: Qt.point(0,0)

            onPressed: {
                lastMousePos = Qt.point(mouse.x,mouse.y)
            }

            onMouseYChanged:
            {
                var dx = (mouseX - lastMousePos.x)
                var dy = (mouseY - lastMousePos.y)

                mainWindow.x += dx
                mainWindow.width -= dx
                mainWindow.height += dy
            }
        }

        MouseArea {
            id: windowResizeDown
            x: 4
            height: 2
            anchors.bottom: parent.bottom
            anchors.left: windowResizeBottomLeft.right
            anchors.right: parent.right
            cursorShape: Qt.SizeVerCursor

            property real lastMousePosY: 0

            onPressed: {
                lastMousePosY = mouse.y
            }

            onMouseYChanged:
            {
                var dy = (mouseY - lastMousePosY)
                mainWindow.height += dy
            }
        }
    }
}

Theme.qml

pragma Singleton
import QtQuick 2.8

QtObject {
    //Text Properties
    readonly property fontSize: 
    readonly property color primaryTextColor: "#D0D0D0"
    readonly property color disabledTextColor: "#909090"

    //F
    readonly property color focusedIconColor: "#D0D0D0"
    readonly property color diabledIconColor: 

    readonly property color primaryBackgroundColor: "#2A2A2A"
    readonly property color secondaryBackgroundColor: "#363636"

    //All Button Properties
    readonly property color disabledButtonColor: "#777777"

    //Primary Button Properties
    readonly property real primaryButtonBorderWidth: 2
    readonly property real primaryButtonBorderRadius: 5
    readonly property color primaryButtonBorderColor: "#D0D0D0"

    readonly property color primaryButtonColor: "#007acc"
    readonly property color primaryButtonHoverColor: "#018deb"
    readonly property color primaryButtonPressedColor: "#0165a8"

    //Toolbar Button Properties
}

Loading the QML file

#Setup the application window & configure for high Dpi
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" 
app = QGuiApplication(sys.argv)
app.setAttribute(Qt.AA_EnableHighDpiScaling)

#Initialize the QML rendering engine
engine = QQmlApplicationEngine()

#Load the main window element
ctx = engine.rootContext()
qml_file = os.path.join(dirname,'qml','main.qml')
engine.load(QUrl.fromLocalFile(os.path.abspath(qml_file)))
#Show the Application Window
win = engine.rootObjects()[0]
win.show()

#Execute and cleanup
app.exec_()
1

1 Answers

1
votes

A simple option is to use a qresource, but before that your folder Themes needs to create a qmldir:

qmldir

singleton Theme 1.0 Theme.qml

So in the end your project will have the following structure:

|-- main.py
`-- qml
    |-- Themes
    |   |-- Theme.qml
    |   `-- qmldir
    `-- main.qml

Now create a .qrc that will have the .qml and place it on the side of the .py:

qml.qrc

<RCC>
    <qresource prefix="/">
        <file>qml/main.qml</file>
        <file>qml/Themes/Theme.qml</file>
        <file>qml/Themes/qmldir</file>
    </qresource>
</RCC>

Now you must convert the .qrc to .py using pyrcc5:

pyrcc5 qml.qrc -o qml_rc.py

Obtaining the following structure:

|-- main.py
|-- qml
|   |-- Themes
|   |   |-- Theme.qml
|   |   `-- qmldir
|   `-- main.qml
|-- qml.qrc
`-- qml_rc.py

Then you must import the qml_rc.py in the main.py and modify the QUrl:

import os
import sys
from PyQt5 import QtCore, QtGui, QtQml

import qml_rc

if __name__ == '__main__':
    os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" 
    app = QtGui.QGuiApplication(sys.argv)
    app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    #Initialize the QML rendering engine
    engine = QtQml.QQmlApplicationEngine()
    #Load the main window element
    ctx = engine.rootContext()
    engine.load(QtCore.QUrl("qrc:/qml/main.qml"))
    #Show the Application Window
    if not engine.rootObjects():
        sys.exit(-1)
    #Execute and cleanup
    sys.exit(app.exec_())

You can find the complete project here


UPDATE:

Also in you can ignore the .qrc:

|-- main.py
`-- qml
    |-- Themes
    |   |-- Theme.qml
    |   `-- qmldir
    `-- main.qml

with the following main.py:

import os
import sys
from PyQt5 import QtCore, QtGui, QtQml

if __name__ == '__main__':
    os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" 
    app = QtGui.QGuiApplication(sys.argv)
    app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
    #Initialize the QML rendering engine
    engine = QtQml.QQmlApplicationEngine()
    #Load the main window element
    ctx = engine.rootContext()
    dirname = os.path.dirname(os.path.abspath(__file__))
    qml_file = os.path.join(dirname, 'qml','main.qml')
    engine.load(QtCore.QUrl.fromLocalFile(qml_file))
    #Show the Application Window
    if not engine.rootObjects():
        sys.exit(-1)
    #Execute and cleanup
    sys.exit(app.exec_())

You can find the complete project here