QML import item from sibling folder - qt

Suppose I have the following structure in my qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>Style.qml</file>
</qresource>
<qresource prefix="/components">
<file>Test.qml</file>
</qresource>
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.0
import "components"
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Test {
color: "red"
}
}
Test.qml
import QtQuick 2.0
import ".."
Rectangle {
width: Style.test * 200
height: 200
}
Style.qml
import QtQuick 2.0
pragma Singleton
QtObject {
property real test: 1.0
}
I have for some time tried to import Style.qml from Test.qml with import "../" but I keep getting ReferenceError: Style is not defined
I know the import statement is the cause for this and I have tried a few different variants of what "might" work but the docs are lacking on this and I am stuck. Help appreciated.

With the following code, I have no errors, and see the expected visual result. I'd suggest including a more complete example, if you can't spot your problem from this working sample. Note that all paths I give here are relative from the "root" project directory (so e.g. main.cpp is a file in the "root", components/Test.qml is located in a "components" subdirectory):
main.cpp:
#include <QQmlApplicationEngine>
#include <QGuiApplication>
int main(int argc, char **argv) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine qAppEngine(QUrl("qrc:/main.qml"));
return app.exec();
}
main.qml
import "components"
Test {
}
components/Test.qml:
import ".."
Style {
}
Style.qml:
import QtQuick 2.6
import QtQuick.Window 2.2
Window {
visible: true
width: 500
height: 500
color: "red"
}
Built with the following:
test.pro:
QT += quick qml
RESOURCES += test.qrc
SOURCES += main.cpp
test.qrc:
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>main.qml</file>
<file>Style.qml</file>
<file>components/Test.qml</file>
</qresource>
</RCC>

Related

How can I organize QML files into nested folders?

I have a sample project at https://github.com/jh3010-qt-questions/qml_location
If my hierarchy looks like:
$ tree qml_location/
qml_location/
├── MyDeepComponent.qml
├── MyDeepComponentForm.ui.qml
├── main.cpp
├── main.qml
├── qml.qrc
└── qml_location.pro
Then I can write main.qml like:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
MyDeepComponent {}
}
and it will work.
However, I would like to organize some QML files into a folder hierarchy and not have them all at the same level.
For example, if I move to:
$ tree qml_location/
qml_location/
├── main.cpp
├── main.qml
├── qml
│   ├── MyDeepComponent.qml
│   ├── MyDeepComponentForm.ui.qml
│   └── more
│   ├── MyDeeperComponent.qml
│   └── MyDeeperComponentForm.ui.qml
├── qml.qrc
└── qml_location.pro
and have a main.qml that looks like:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column
{
MyDeepComponent {}
MyDeeperComponent {}
}
}
Qt Creator tells me that MyDeepComponent and MyDeeperComponent are Unknown.
When I try to run, I get the error: MyDeepComponent is not a type
What can I do so this will work?
One caveat, I do not want to place a special or additional import at the top of main.qml. Is this still possible?
qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>qml/MyDeepComponent.qml</file>
<file>qml/MyDeepComponentForm.ui.qml</file>
<file>qml/more/MyDeeperComponent.qml</file>
<file>qml/more/MyDeeperComponentForm.ui.qml</file>
</qresource>
</RCC>
qml_location.pro
QT += quick
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
This answer doesn't meet the requirement of no imports, but it is worth mentioning anyway that it can be solved with a couple of imports at the top of main.qml.
https://github.com/jh3010-qt-questions/qml_location/tree/import_solution
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import "qml"
import "qml/more"
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column
{
MyDeepComponent
{
}
MyDeeperComponent
{
}
}
}
You can do something like this:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column
{
Loader {
source: "qrc:/qml/MyDeepComponent.qml"
}
Loader {
source: "qrc:/qml/more/MyDeeperComponent.qml"
}
}
}
Though I'm not quite sure why you would want to. Might help to describe the problem you are trying to solve?
https://github.com/jh3010-qt-questions/qml_location/tree/create_object_solution
createObject can be used to load the component directly into the Column.
main.qml looks like:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property var componentNames: [ "qml/more/MyDeeperComponent.qml", "qml/MyDeepComponent.qml" ]
function generateObjects()
{
function generateOneObject( name )
{
var component
var componentObject
function finishCreation()
{
componentObject = component.createObject( contentColumn );
}
component = Qt.createComponent( `qrc:/${name}` )
if ( component.status === Component.Ready )
{
finishCreation()
}
else
{
component.statusChanged.connect( finishCreation );
}
}
for ( var index in componentNames )
{
generateOneObject( componentNames[ index ] )
}
}
Component.onCompleted: {
generateObjects()
}
Column
{
id: contentColumn
}
}
Another solution is to move main.qml into the qml folder. This allows main.qml to find MyDeepComponent because they are siblings. To find MyDeeperComponent, main.qml can import the "more" directory.
This solution in represented in the all_in_one_solution branch.
directory structure
$ tree qml_location/
qml_location/
├── main.cpp
├── qml
│   ├── MyDeepComponent.qml
│   ├── MyDeepComponentForm.ui.qml
│   ├── main.qml
│   └── more
│   ├── MyDeeperComponent.qml
│   └── MyDeeperComponentForm.ui.qml
├── qml.qrc
├── qml_location.pro
└── qml_location.pro.user
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/qml/main.qml"));
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();
}
qml.qrc
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/MyDeepComponent.qml</file>
<file>qml/MyDeepComponentForm.ui.qml</file>
<file>qml/more/MyDeeperComponent.qml</file>
<file>qml/more/MyDeeperComponentForm.ui.qml</file>
</qresource>
</RCC>
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import "more"
Window
{
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Column
{
MyDeepComponent
{
}
MyDeeperComponent
{
}
}
}
EDIT: I'm reading your part of the question where you state that you don't want an import at the top. I don't think this is possible, without throwing a bunch of addImportPath in your main (for each folder that you are to create) or heavy fiddling with qrc-aliases. Leaving the rest of the answer for a future visitor or for if you change your mind ;-)
You can use a qmldir file to make them available in a namespace of choice. But there is a bit of a learning curve. The qmldir file has to be situated in folder which resembles the namespace.
For example if you want to use MyApplication as namespace, you have to put the qmldir file in a folder "MyApplication" (within the qml import path). If you want MyOrg.application1 as namespace, you have to put qmldir in "MyOrg/application1".
For your situation, I choose the first option. I would make a separate qrc file for the importable qml files, which is also needed if you want to easily put all qml files in a "qml" folder:
qml/qml.qrc:
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/qml/MyApplication">
<file>MyDeepComponent.qml</file>
<file>MyDeepComponentForm.ui.qml</file>
<file>more/MyDeeperComponent.qml</file>
<file>more/MyDeeperComponentForm.ui.qml</file>
<file>qmldir</file>
</qresource>
</RCC>
A simple qmldir would look something like this:
module MyApplication
MyDeepComponent 1.0 MyDeepComponent.qml
MyDeeperComponent 1.0 more/MyDeeperComponent.qml
You can see that I used a prefix, such that the files don't have to move. If you are to go to great heights with the app and want a MyApplication 2.0 namespace, you can use the prefix /qml/MyApplication.2

Qt Virtual Keyboard in QQuickWidget

It is possible to show Virtual Keyboard in QQuickWidget or in QWidget?
I have QWidget application and I need to have better control where VirtualKeyboard is shown.
Today I spend all my day to find a solution, unfortunately without success.
The following code shows that it is valid to use virtualkeyboard in QQuickWidget.
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickWidget>
int main(int argc, char *argv[])
{
qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QQuickWidget widget;
widget.setResizeMode(QQuickWidget::SizeRootObjectToView);
widget.setSource(QStringLiteral("qrc:/main.qml"));
widget.show();
return app.exec();
}
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.VirtualKeyboard 2.14
Rectangle {
id: window
width: 640
height: 480
TextEdit{
text: "Hello world"
anchors.centerIn: parent
}
InputPanel {
id: inputPanel
z: 99
x: 0
y: window.height
width: window.width
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: window.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
}
}
qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>
59777221.pro
QT += quickwidgets virtualkeyboard
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += main.cpp
RESOURCES += qml.qrc
├── 59777221.pro
├── main.cpp
├── main.qml
└── qml.qrc

qml qt 5.12 using modules controls1 and controls2 in one program

I can't use QtQuick.Controls 1.2 and QtQuick.Controls 2.12 in one program. Although in Qt 5.10 this worked. Now I get this message when I load main.qml:
"qrc:/MyButton.qml 2 module QtQuick.Controls 2.12 is not installed"
Qt 5.12. Build on macOS for ios or ios emulator.
pro file
QT += quick quickcontrols2
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp
RESOURCES += qml.qrc
QML_IMPORT_PATH =
QML_DESIGNER_IMPORT_PATH =
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
Window {
visible: true
Button {
id: button1
text: "Controls 1 button"
anchors.centerIn: parent
}
MyButton {
text: "Controls 2 button"
anchors {
top: button1.bottom
horizontalCenter: parent.horizontalCenter
}
}
}
MyButton.qml
import QtQuick 2.9
import QtQuick.Controls 2.12
Button {
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>MyButton.qml</file>
</qresource>
</RCC>
Perhaps there are some restrictions or rules that need to be followed to make it work? If use only Constools 1.2 or only Constols 2.12, then main.qml loaded successfully.
p.s. Build on windows for windows and build on windows for android works fine. The problem is only with build on mac os for ios/ios emulator.

Accept/reject signals of dialog not catched

Upgrading Qt to v5.10.1 the Dialog doesn't emit accept/reject signals. The last known version that works smooth was 5.10.0. My question is - does I miss something or I use some component in wrong way? Or it is regression (and I will report it?)
Issue reproduced on macOS 10.12+ & Win10 (VC 2015/2017)
Simplified source code sample:
CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(bug-test LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 COMPONENTS Core Quick REQUIRED)
add_executable(${PROJECT_NAME} "main.cpp" "qml.qrc")
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick)
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty()) return -1;
return app.exec();
}
main.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: applicationWindow
visible: true
Button {
text: qsTr("Push me!")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
onClicked: dlgLoader.source = "qrc:/MsgDialog.qml"
}
Loader {
id: dlgLoader
onStatusChanged: {
if (dlgLoader.status == Loader.Ready) {
item.parent = ApplicationWindow.overlay
item.open()
}
}
}
Connections {
target: dlgLoader.item
onClosed: dlgLoader.source = ""
}
}
MsgDialog.qml
import QtQuick 2.9
MsgDialogForm {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: Math.min(applicationWindow.width, applicationWindow.height) / 5 * 4
onAccepted: console.log("Ok clicked; save answer")
onRejected: console.log("Cancel clicked; don't save")
}
MsgDialogForm.ui.qml
import QtQuick 2.9
import QtQuick.Controls 2.3
Dialog {
id: dialog
modal: true
standardButtons: Dialog.Yes | Dialog.No
closePolicy: Popup.CloseOnEscape
}
qml.qrc
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>qtquickcontrols2.conf</file>
<file>MsgDialog.qml</file>
<file>MsgDialogForm.ui.qml</file>
</qresource>
</RCC>
Just start the app; push the button and click one of dialog buttons. Nothing is shown in QtCreator console but dialog is closed.
When a new item is established in the Loader the previous item is deleted from the memory, in your case you are doing it when the window is closed, but the accepted or rejected signal is emited after the window is closed.
A possible solution is to create a signal that is emited after receiving the message.
MsgDialog.qml
import QtQuick 2.9
MsgDialogForm {
signal finished()
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: Math.min(applicationWindow.width, applicationWindow.height) / 5 * 4
onAccepted: {
console.log("Ok clicked; save answer")
finished()
}
onRejected: {
console.log("Cancel clicked; don't save")
finished()
}
}
main.qml
...
Connections {
target: dlgLoader.item
onFinished: dlgLoader.source = ""
}

Using QtCreator 5.3 for Qt Quick UI project, how to link QML button resource to C++ function call

This is my first post on StackOverflow so please excuse any formatting mistakes I might have made.
I'm building a Qt Quick UI project using Qt Quick Controls 1.1 and I have a simple Button in my QML code that I would like to call into my C++ action class. I see a number of examples on this with earlier versions of Qt, but they do not seem to work in 5.3. I chose Qt Quick Controls 1.1 in the project settings. I know this must not be too complicated to do, but I can't seem to find examples using QtCreator 5.3
Here is my main.qml file:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
ApplicationWindow {
id: parentWnd
visible: true
width: 640
height: 480
Action {
id: actionSend
onTriggered: console.log("SEND")
}
Button {
id: send
text: "Send Request"
action: actionSend
signal sendSignal()
}
}
Here is my main.cpp:
#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
Here is the action class where I would like the QML button to call "doSend":
#include<QDebug>
#include<QObject>
class Action : public QObject
{
Q_OBJECT
public:
Action();
public slots:
void doSend();
};
Finally here is my project file:
TEMPLATE = app
QT += qml quick widgets
SOURCES += main.cpp \
action.cpp
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)
HEADERS += \
action.h
When I run this, I see the button and I see the logging of "SEND" to the console so I know the QML Action is setup correctly. Any help on how to make the Action call into my action class would be much appreciated!
There are three issues that you're running into here.
The first is that you haven't registered your Action class with QML in main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qmlRegisterType<Action>("StevesModule", 1, 0, "Action");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
The second is that Action is a QML type in the Qt Quick Controls module. It doesn't know anything about your Action class in C++. If you want to use your Action class instead of Qt Quick Controls' Action type, you must import it in your QML file:
import StevesModule 1.0
The third is that you're not calling the doSend() slot anywhere. You can do this in the onClicked handler of Button:
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import StevesModule 1.0
ApplicationWindow {
id: parentWnd
visible: true
width: 640
height: 480
Action {
id: actionSend
}
Button {
id: send
text: "Send Request"
onClicked: actionSend.doSend()
}
}

Resources