How can I set subtitle on QML MediaPlayer? - qt

Subtitle support added to Qt6 Multimedia and we can show a subtitle on MediaPlayer very simple. But I can't use this feature in QML, setSubtitleText seems not find in QML or at least not callable.
MediaPlayer {
id: mediaPlayer
Button{
onClicked: {
mediaPlayer.videoOutput.videoSink.setSubtitleText("I'm a subtitle!")
}
}
console.log(mediaPlayer.videoOutput.videoSink)
QVideoSink(0x203f364a8b0)
mediaPlayer.videoOutput.videoSink.setSubtitleText
undefined
mediaPlayer.videoOutput.videoSink.setSubtitleText("some text")
TypeError: Property 'setSubtitleText' of object
QVideoSink(0x203f364a8b0) is not a function

Related

Re-using Component of TextField responding to wheel events, with signal handling

I enriched a TextField (displaying a float) so that it can be changed by mouse wheel, all while being still editable by hand.
I found the quirk with forceActiveFocus and onClicked here (I wish I could just let all events pass down the widget stack like in Qt) and use onWheel to change the value (please let me know if this is not the best way to do this):
TextField{
text: cxxObject.floatAttribute.toString()
onEditingFinished: { cxxObject.floatAttribute=parseFloat(text); }
MouseArea{
anchors.fill: parent
propagateComposedEvent: true
onClicked: { parent.forceActiveFocus(); }
onWheel: { parent.text=parseFloat(parent.text)-.5*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
I would like to re-use this component instead of TextField in several places (without copy&paste), so I tried to declare the component like this:
Component{
id: wheeledFloatTextField
property real initValue: 0.
property real dWheel: 0.5
signal editingFinished(real value);
TextField{
text: parent.initValue.toString();
// re-emit signal to the component
// so that user-defined slot can be defined when re-used
onEditingFinished: parent.editingFinished(parseFloat(text));
// validator: ...
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: { parent.forceActiveFocus(); }
onWheel: {
parent.text=parseFloat(parent.text)-parent.parent.dWheel*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
}
}
and re-use:
Loader{
sourceComponent: wheeledFloatTextField
initValue: cxxObject.floatAttribute;
onEditingFinished: { cxxObject.floatAttribute=value; }
}
I am however getting (at the line where Component is used):
Component objects cannot declare new properties.
What is wrong? I was some posts (like How do you assign a QML Item to a component property in QML and then use that object inside the component? and https://developer.blackberry.com/native/documentation/dev/custom_components/index.html) from which I am gathering I might need to wrap the inside of Component (which is as-if it were a separate .qml file and does not define a scope) in something like Item or Container but I am not sure what to do. Any hint?
I would like to keep the definition inline first, later move to a separate file.
If you have the component declared in a separate file, you can (should) omit the top-level Component. For maximum reusability of components, it is reccomended to declare them in a separate file.
A Component can not have any properties declared. It is basically stopping the object creation in a prototypical state. That is useful if you want to configure the object, for later creation, for example lazy initialization (delegates).
If you have a property of type Component and you use the myProp: SomeType {...} syntax, it will automatically just create a component from that.
I think the best solution is to put your TextField in a seperate file, and add the properties to the root-node so it is customizable.
File1 (e.g. "CustomTextField.qml")
TextField{
property real initValue: 0.
property real dWheel: 0.5
signal editingFinished(real value);
text: initValue.toString();
// re-emit signal to the component
// so that user-defined slot can be defined when re-used
onEditingFinished: editingFinished(parseFloat(text));
// validator: ...
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: { parent.forceActiveFocus(); }
onWheel: {
parent.text=parseFloat(parent.text)-parent.parent.dWheel*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
}
You can then reuse the Component in all known ways like in a Loader:
Loader {
sourceComponent: CustomTextField { // Property type is component, so it automatically creates a Component instead of the full-blown object, until it is loaded.
initValue: 12
dWheel: 42
}
...
}
or without Loader
CustomTextField {
...
}
Of course you can keep it inline, but even then, you have to add the properties to the root-element inside the Component.
Component {
id: componentId // only thing you can set besides one Object in a Component
TextField{
id: componentRoot // You can't reference this id from outside the Component!!!
property real initValue: 0.
property real dWheel: 0.5
signal editingFinished(real value);
text: initValue.toString();
// re-emit signal to the component
// so that user-defined slot can be defined when re-used
onEditingFinished: editingFinished(parseFloat(text));
// validator: ...
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: { parent.forceActiveFocus(); }
onWheel: {
parent.text=parseFloat(parent.text)-parent.parent.dWheel*wheel.angleDelta.y/120;
parent.editingFinished();
}
}
}
}
This has the down-side that you will always need a separate object to instantiate the Component, like a Loader which adds overhead and complicates communication in the file between the objects, since to address it, you will need to use: loaderId.item.property which might be expensive in lookup, you need to ensure that item is defined e.t.c.

Access QML Child Component from C++ without ObjectName?

I am creating a plotting component in QML. The structure of my QML component looks like this:
Rectangle {
id: canvas
objectName: "myPlot"
Rectangle {
id: plot
PlotArea {
id: pa
} // this is my c++ QQuickItem
MouseArea {} // for handling interaction with the plot, like zooming
XAxis{}
YAXis{}
}
}
here PlotArea is my c++ class. I need to interact with this QML component from C++, more precisely, I need to call a member function of PlotArea to add data to the plot. The usual way is to use findChild<QObject*>("objectName"), but as the component will get reused, I cannot give PlotArea and object name.
How can I access PlotArea if I have a pointer to "myPlot"? I tried
QObject *plot = rootObjects.value(0)->findChild<QObject*>("plotObject");
PlotArea * myplot = (plot->findChild<PlotArea*>("pa"));
but this does not work. However, what works is the above approach if I do
PlotArea {
id: pa
objectName: "pa"
} // this is my c++ QQuickItem
But I wonder if this is safe, as there will be serveal PlotAreas in my application, and all of them have the name "pa".
You shouldn't interact with QML from C++ unless you're writing unit tests. See this documentation for an explanation.
The better approach is to expose a C++ type to QML.
Singleton
For example, you could register the C++ class that adds the plot data as a singleton:
qmlRegisterSingletonType<PlotDataAdder>("App", 1, 0, "PlotDataAdder", plotDataAdderCreationFunction);
Then you could do this in QML:
import App 1.0
PlotArea {
id: plotArea
Component.onCompleted: PlotDataAdder.addData(plotArea)
}
This is an imperative approach.
Named Type
If it's possible in your situation, you could do it declaratively instead with qmlRegisterType():
qmlRegisterType<PlotDataSource>("App", 1, 0, "PlotDataSource");
Then, in QML:
import App 1.0
Window {
PlotDataSource {
id: plotDataSource
}
PlotArea {
id: plotArea
plotData: plotDataSource
}
}
You said the plot data is dynamically generated by hardware, so this approach would work in that case, whereas the first approach using singletons would be more difficult.
Context Property
Or, if you can't create the C++ class on-demand (if it comes from a third party library, for example), use setContextProperty():
engine.rootContext()->setContextProperty("plotDataSource", plotDataSource);
Then, in QML:
import App 1.0
Window {
PlotArea {
id: plotArea
plotData: plotDataSource
}
}
The code in below only one solution to the problem, but not the correct answer because it messes up the ownership of QML objects. The PlotArea generate from QML engine but using in the C++ side. To correctly solving the problem recommend redesigning the PlotArea.
C++:
QObject* test = engine.rootObjects()[0]->findChild<QObject*>("test");
PlotArea* plotArea = test->property("plotArea").value<PlotArea*>();
QML:
Item{
objectName: "test"
property var plotArea : plotArea
Item{
PlotArea{
id: plotArea
}
}
}

Change Loader source and update view

I have an application in which i am loading some resources inside a main file with a Loader object:
main.qml:
ApplicationWindow {
property string pageSource: pageLoader.source
...
Loader {
id: pageLoader
source: "page1.qml"
}
}
In page1.qml i have a layout that wraps some buttons and text fields. Loading this page in pageLoader works fine. As you can see i have a foo Button that should switch the source of the Loader to page2.qml, but for some reason it's not working.
page1.qml:
Item {
ColumnLayout {
TexField {...}
TexField {...}
Button {
id: foo
onClicked: {
pageSource = "page2.qml"
}
}
}
}
By checking some console logs i was able to know that i am actually changing the source of the loader, but the view is not updating as expected and all controls from page1.qml is not replaced by page2.qml. This second page has a ListView. Toggling active property of Loader doesn't work either. Can anyone please shed a light?

Qt - How to connect signal from loader item, if loader source changes dynamically

I have an application which needs the screen to be switched between multiple available screens. I am checking if this is possible with loader in qml.
The issue i am facing is connecting signals from loaded item.
I use an application example in qt documentation and found CreateConnection in application qml cannot have if condition.
I also tried to make it signal slot connection in a function and call in on source change of loader, but that too did not work.
Application.qml
import QtQuick 2.0
Item {
width: 100; height: 100
Loader {
id: myLoader
source: "MyItem.qml"
}
Connections {
target: myLoader.item
// here i tried using if (myLoader.item == "qrc:MyItemOne.qml") , but can't use if
onChangeToSecond: {
myLoader.source = "MyItemTwo.qml"
}
onChangeToFirst: {
myLoader.source = "MyItemOne.qml"
}
}
}
MyItemOne.qml
import QtQuick 2.0
Rectangle {
id: myItem
signal changeToSecond()
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: myItem.changeToSecond()
}
}
MyItemTwo.qml
import QtQuick 2.0
Rectangle {
id: myItem
signal changeToFirst()
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: myItem.changeToFirst()
}
}
Someone knows any way to use loader for this, or loader should not be used for this?
Your code works fine if I use MyItemOne.qml as the initial value for the myLoader.source (Qt5.6.0). However, it will print out a warning:
QML Connections: Cannot assign to non-existent property "onChangeToFirst"
which happens because MyItemOne does not define the changeToFirst signal. The ignoreUnknownSignals property of Connections element can be used to suppress the warning, or both screens should define the same set of signals.
Loader can be used if it does not matter that the previous view is always fully unloaded when switching.

Using a Loader for a string of QML

In Qt 5.3 I've been using the Loader QML element for loading screens, loading the view from a QML file in the background. Now I'm trying to load a string of QML dynamically. Qt.createQmlObject enables me to do so, but I can't seem to get the Loader element to play along.
Seems like Loader only takes a URL (QUrl) or component (QQmlComponent), but Qt.createQmlObject creates an object (QObject).
I'm new to QML, but from my understanding components are reusable elements, similar to classes, and objects are the instances of those classes. I thus can't seem to wrap my head around why Loader wouldn't work with objects.
How can I show a loading screen while (asynchronously) parsing and initializing a string of QML?
Example QML code:
Item {
Rectangle {id: content}
Loader {id: loader}
Component.onCompleted: {
var obj = Qt.createQmlObject('import QtQuick 2.3; Rectangle {}', content);
loader.source = obj; // Throws error.
}
}
It's not possible using the current API. As Loader's documentation says, it loads objects via a URL that points to a QML file or a Component:
import QtQuick 2.0
Item {
Rectangle {
id: content
anchors.fill: parent
color: "grey"
Loader {
id: loader
sourceComponent: myComponent
anchors.fill: parent
anchors.margins: 40
}
}
property Component myComponent: Qt.createComponent("MyComponent.qml", Component.Asynchronous)
}
MyComponent.qml:
import QtQuick 2.2
Rectangle {
color: "red"
}

Resources