QML singletone style nested files - qt

I've got a defined style for the QML application as a separate file MyStyle.qml:
pragma Singleton
import QtQuick 2.15
import QtQml 2.15
QtObject {
property color color1: "#ffffff"
...
}
I want to modify it with another file for ultra resolution >2k MyStyle_2k.qml.
MyStyle{
color1: "#000000"
...
}
The style is registered in main.cpp:
QScreen* screen = QGuiApplication::primaryScreen();
auto screenHeight = screen->geometry().height();
QUrl styleUrl;
if(screenHeight > 1440){
styleUrl = QUrl("qrc:/gui/style/MyStyle_2k.qml");
}else{
styleUrl = QUrl("qrc:/gui/style/MyStyle.qml");
}
qmlRegisterSingletonType(styleUrl, "MyStyle", 1, 0, "MyStyle");
Unfortunately, it doesn't work because of errors:
QQmlApplicationEngine failed to load component
qrc:/path/other_file.qml: Type MyStyle/MyStyle unavailable :9:1: Composite Singleton Type MyStyle is not creatable.
Is it possible to modify qml singleton by another file?

You should name both of your files MyStyle.qml but organize them in the following way:
qrc:/gui/style/MyStyle.qml
qrc:/gui/style/+highres/MyStyle.qml
Then, you can let Qt know which version of MyStyle.qml to load via the use of file selectors. Note the usage of the plus sign + as a prefix to highres. This is how Qt organizes file overrides based on default or custom file selectors. The easiest way to declare highres as a custom file selector is via QT_FILE_SELECTOR environment variable, e.g.
QScreen* screen = QGuiApplication::primaryScreen();
auto screenHeight = screen->geometry().height();
if (screenHeight > 1440) {
qputenv("QT_FILE_SELECTOR", QString("highres").toUtf8());
}
This code needs to happen very early on in your main.cpp because QT_FILE_SELECTOR will be checked once and will not be rechecked later on.
From https://doc.qt.io/qt-6/qfileselector.html:
Further selectors will be added from the QT_FILE_SELECTORS environment variable, which when set should be a set of comma separated selectors. Note that this variable will only be read once; selectors may not update if the variable changes while the application is running. The initial set of selectors are evaluated only once, on first use.
If you do not wish to use QT_FILE_SELECTOR environment variable, then, the proper way to define a file selector for QML use is via QQmlFileSelector:
QScreen* screen = QGuiApplication::primaryScreen();
auto screenHeight = screen->geometry().height();
QQmlFileSelector* selector = null;
if (screenHeight > 1440) {
selector = new QQmlFileSelector(&engine);
selector->setExtraSelectors(QStringList() << QString("highres"));
}
The advantage of using QQmlFileSelector is you can control the file selectors that are active in runtime. i.e. you can add/remove a file selector. However, this also means that every time you change a file selector, you have to force components that are already loaded to reload. However, it comes at the cost of additional coding requirements, in that, you need to instantiate the QQmlFileSelector configure it, force a reload of components (which is even harder to do for singletons), and clean up code when your program exits (note I have not provided any of this, since, it is left up to you how to instantiate and release QQmlFileSelector consistent to how you manage memory in your app).
For more details, you should consult the documentation directly.
https://doc.qt.io/qt-6/qfileselector.html
https://doc.qt.io/qt-6/qqmlfileselector.html

You are trying to create an instance of a singleton type which is not possible hence the error message. If you want to create an instance of the style and modify it inside QML you need to use qmlRegisterType().
Or you need to write MyStyle.color1: "#000000" to modify the singleton value.
EDIT: You could create an instantiable style like InternalMyStyle.qml set all the default values there and re-use it in MyStyle.qml and MyStyle_2k.qml as shown below. Both MyStyle.qml and MyStyle_2k.qml are still singletons.
InternalMyStyle.qml
import QtQuick
QtObject {
property color color1: "#ff00ff"
}
MyStyle.qml
pragma Singleton
import QtQuick
InternalMyStyle {}
MyStyle_2k.qml
pragma Singleton
import QtQuick
InternalMyStyle {
color1: "#ff0000"
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QScreen>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QScreen *screen = QGuiApplication::primaryScreen();
auto screenHeight = screen->geometry().height();
QUrl styleUrl;
if (screenHeight > 1440)
styleUrl = QUrl(u"qrc:/untitledMyStyle/MyStyle_2k.qml"_qs);
else
styleUrl = QUrl(u"qrc:/untitledMyStyle/MyStyle.qml"_qs);
qmlRegisterSingletonType(styleUrl, "MyStyle", 1, 0, "MyStyle");
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/untitledMyStyle/main.qml"_qs);
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
&app,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
CMakeLists.txt
...
qt_add_qml_module(appuntitledMyStyle
URI untitledMyStyle
VERSION 1.0
QML_FILES
main.qml
MyStyle.qml
MyStyle_2k.qml
InternalMyStyle.qml
)
...

The only working solution I found is using single file as singleton Style.qml with state group.
main.cpp
...
QScreen* screen = QGuiApplication::primaryScreen();
auto screenHeight = screen->geometry().height();
if(screenHeight > 1440){
engine.rootContext()->setContextProperty("StyleState", "2k");
}else{
engine.rootContext()->setContextProperty("StyleState", "");
}
...
State.qml
pragma Singleton
import QtQuick 2.15
import QtQml 2.15
QtObject {
id: root
property var widthA: 50
property var widthB: 100
property alias customStyle: customState.state // alias for call dircectly from qml
property var states: StateGroup{
id: customState
state: StyleState
states: [
State{
name: "2k"
PropertyChanges{
target: root
widthA: 100
widthB: 200
}
}
]
}
}

Related

Redux Persist + Redux toolkit $CombinedState error

I'm trying to add redux persist to redux toolkit but for some reason I get an Exported variable 'store' has or is using name '$CombinedState' from external module ".../node_modules/redux/index" but cannot be named. error on vscode.
This is my store configuration file with the added persist config, which if I remove, works fine.
import { configureStore } from "#reduxjs/toolkit";
import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import { createEpicMiddleware } from "redux-observable";
import rootEpic from "onsite/redux/rootEpic";
import rootReducer from "onsite/redux/rootReducer";
const epicMiddleware = createEpicMiddleware();
const persistConfig = {
key: "root",
storage: storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
// Line that shows error
const store = configureStore({
reducer: persistedReducer,
middleware: [epicMiddleware],
});
export default store;
epicMiddleware.run(rootEpic);
Other things I have tried are putting the combineReducers declaration (from rootReducerimport) into the same file or converting the file into plain javascript, with same or similar results. For some reason typescript decides to still haunt me on a javascript file :)
The code still runs, so I'm tempted to leave it like that, but I would like to get rid of it.
I had the same issue, and here's what I figured out:
You need to include Store from redux, and use it as your type definition for your own store return value. Short answer:
import { Store } from 'redux';
[...]
const store:Store = configureStore([...])
[...]
export default store;
Longer answer:
As I understand it, what was happening is that Store type uses $CombinedState as part of its definition. When configureStore() returns, it inherits the State type. However since State is not explicitly used in your code, that now means that your code includes a value that references $CombinedState, despite it not existing anywhere in your code either. When you then try to export it out of your module, you're exporting a value with a type that doesn't exist within your module, and so you get an error.
You can import State from redux (which will in turn explicity cause your code to bring in $CombinedState), then use it to explicitly define store that gets assigned the return of configureStore(). You should then no longer be exporting unknown named types.
You can also be more specific with your Store type, as it is a generic:
const store:Store<RootState>
Although I'm not entirely sure if that would be a circular dependency, since your RootState depends on store.
Adding a
import { $CombinedState } from '#reduxjs/toolkit'
in that file should usually resolve that error.

QML Connections: Implicitly defined onFoo properties in Connections are deprecated

I got the following error message when upgraded to Qt 5.15:
QML Connections: Implicitly defined onFoo properties in Connections are deprecated.
Use this syntax instead: function onFoo(<arguments>) { ... }
The corresponding QML code is pasted below
Connections {
target: AppProxy
onLogsReady: function(logs) {
textLogs.text = logs
}
}
where the onLogsReady is a signal defined in the AppProxy class:
class AppProxy : public QObject {
Q_OBJECT
Q_DISABLE_COPY(AppProxy)
public:
AppProxy(QObject* parent = 0);
~AppProxy();
signals:
void logsReady(QString logs);
// ...
};
I wonder how to suppress this warning.
in Qml 5.15 there is a new syntax for connections. In your case it would look like this:
Connections {
target: AppProxy
function onLogsReady(logs) {
textLogs.text = logs
}
}
You can read more about it here: https://doc.qt.io/qt-5/qml-qtqml-connections.html
In addtion to #luffy and #Lidekys solution in my case adding this line to pro file of the project is solved issue.
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
#luffy answer is correct, but not completely. If you'll just make these changes, at least for me, it didn't fix the issue. What fixed it was adding "import QtQml 2.15" (like stated in https://doc.qt.io/qt-5/qml-qtqml-connections.html) in the qml files that were affected by these changes.
Not sure if this helps, just wanted to add in to the issue.

how to know the version of lib in qml

I work in Qt5.9.3, writing a qml program in linux platform without x window.
Following is my code:
import Qt3D.Core 2.13
Entity {
id: sceneRoot
...
Transform{
id: torusTransform
scale3D: Qt.vector3d(1.5, 1, 0.5)
rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45)
...
}
However ,there is a underline in Transform in Qt creater.
The underline in Transform means something error happened.I am sure that Qt53DCore lib has added in .pro file.
So my question is:
How can I know the version of Qt3D.core ?
And how to ensure the specified qml type in the lib?
I don't know if there are a easy way but you can check the old Qt documentation to have an idea of which version you need.
In your example you should use import Qt3D.Core 2.0.
I search the "Entity" keyword in Qt5.9.3 folder, in .../Qt5.9.3/qml/Qt3D/Core path, there is a file named plugins.qmltypes.
I open it and found the code:
Component {
name: "Qt3DCore::Quick::Quick3DEntity"
defaultProperty: "data"
prototype: "Qt3DCore::QEntity"
exports: ["Qt3D.Core/Entity 2.0"]
exportMetaObjectRevisions: [0]
Property { name: "components"; type: "Qt3DCore::QComponent"; isList: true; isReadonly: true }
}
"exports: ["Qt3D.Core/Entity 2.0"]" tell me the version and qml type name.

Remove Warning- QML Connections: Cannot assign to non-existent property "onValueChanged"

My code runs fine as expected but why do I get the warning messsage:
QML Connections: Cannot assign to non-existent property "onValueChanged"
Here is how I linking to signal.
Connections {
target: myModel
onValueChanged: {
console.log("Valued changedRecieved in QML")
}
myModel is a C++ class that I am exposing to QML using engine.rootContext()->setContextProperty("myModel", &model);
Is there a way to remove this warning?
In your Connections scope just set ignoreUnknownSignals: true

How to bind to root context object signal from QML

I'm creating QML application (QT 5.2)
Assume I have some object set as root context:
viewer.rootContext()->setContextObject(&view_model);
Now I want to bind to a signal of view_model from QML:
Connections {
target: ??? WHAT SHOULD GO HERE ???
onSignalStateChanged: console.log("signal")
}
Cannot figure out what should be the target.
Important: I don't want to use setContextProperty.
It seems you can't use Connections if you don't have access to object instance (via context property for example). But you still could use following:
function onSignal() {
console.log( "signal" );
}
Component.onCompleted: {
onSignalStateChanged.connect( onSignal );
}

Resources