I am trying to create Qt application, where will be possible to change view between video and images depends on called function. There is short example of my .qml file structure:
ApplicationWindow {
Rectangle {
Rectangle {
id: Container
function dispImage(_title, _path)
{
//show imgae
}
function dispVideo(_title, _path)
{
//show video
}
Rectangle {
id: titleContainer
Text {
id: title
}
}
Rectangle {
id: image
Image {
id: image
}
}
Rectangle {
id: video
Image {
id: image
}
}
}
}
Which one mechanism should I use? Is this possible to do that by calling proper function from code?
There are different ways to achieve that.
You can use a Loader and load a different Component, or you can simply hide one and show the other:
function dispImage(_title, _path)
{
image.visible = true
video.visible = false
//...
}
function dispVideo(_title, _path)
{
image.visible = false
video.visible = true
//...
}
However that code you have there isn't likely to work, seeing how you have the same id: image 3 times, you also have an id that begins with a capital letter, which is also "illegal" in QML. Maybe next time try posting working code to demonstrate some initial effort to accomplish your task.
Related
From this discussion on StackOverflow, I am well able to save an image out of a QML item into a file as png/jpeg.
How can I overlay or merge two different qml layers & merge them into one, to save it into a png/jpeg ?
Note: I am able to save a single QQuickItem. Just need to know how to overlay 2 QQuickItems
Just have the two qml objects be children of a root Item and then grab that root item, it will capture all its content.
Just make sure the root item is big enough to enclose the children, and the children are not in negative space, because it will only capture what's inside of the footprint of the root item.
You can also do manual composition, from C++ or even QML.
The problem described in your comment is you can't move stuff around, so what can you do? Instead of having the original QML objects as parents of the same root, you can have two Image elements, then you capture item A and set the capture result to serve as a source of image A, then do the same for item B, and finally, you capture the root item, which will capture the two images together.
OK, here is a quick example, it looks a little complicated, because grabs are async, and you have to wait for the individual grab results to be completed before you can grab the "final" root item, thus the usage of the timer. In this example, different items are laid out in a row, but you can compose them any way that you like:
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
Rectangle {
id: s1
visible: false
width: 200
height: 200
color: "red"
}
Rectangle {
id: s2
visible: false
width: 200
height: 200
color: "blue"
}
Row {
id: joiner
visible: false
Image { id: t1 }
Image { id: t2 }
}
Image {
id: result
y: 200
}
Timer {
id: finish
interval: 10
onTriggered: joiner.grabToImage(function(res) {result.source = res.url})
}
Component.onCompleted: {
s1.grabToImage(function(res) {t1.source = res.url})
s2.grabToImage(function(res) {t2.source = res.url; finish.start() })
}
}
First the two rectangles are captured and used as sources for the images in joiner, then joiner is captured and displayed in the result image, all objects except the final result image are hidden.
Even easier, you can use this nifty little helper to quickly join any number of items in a single image:
Item {
id: joinHelper
visible: false
property Component ic: Image { }
property var cb: null
Row { id: joiner }
Timer {
id: finish
interval: 100
onTriggered: joiner.grabToImage(joinHelper.cb)
}
function join(callback) {
if (arguments.length < 2) return // no items were passed
var i
if (joiner.children.length) { // clean previous captures
for (i = 0; i < joiner.children.length; ++i) {
joiner.children[i].destroy()
}
}
cb = callback // set callback for later
for (i = 1; i < arguments.length; ++i) { // for every item passed
var img = ic.createObject(joiner) // create empty image
// need to capture img by "value" because of JS scoping rules
// otherwise you end up with only one image - the final one
arguments[i].grabToImage(function(temp){ return function(res){temp.source = res.url}}(img))
}
finish.start() // trigger the finishing step
}
}
And you use it like this:
joinHelper.join(function(res) { result.source = res.url }, s1, s2)
It still uses a row, but you can easily tweak it to do your own layouting. It works by passing the final callback and all items you want to capture, internally it creates an image for every item, puts them in the container, and then triggers the finishing timer.
Note that depending on how fast the system is and how complex the items are and what their count is, you may need to up the timer interval, because the final callback needs to be executed only after all captures were completed, the image sources were assigned and the images were resized to give the row its proper dimensions.
I also annotated most things to make it easier to understand.
This question seems to be closely related to this one
The solution is to render the Items in question into an texture of a second item, that you don't need to render to the screen. You can compose this Item as you want by adding multiple ShaderEffectSources as children, position them relatively to each other as you like, and set their sources to the Items you want to grab to Image.
Then you grab the Item to Image.
A generic example, that exposes a function to grab a list of Items to one Image, where each Item is stacked ontop of each other, with an opacity of 0.2 each:
import QtQuick 2.0
Rectangle {
id: root
visible: false
color: 'white'
function grabMultipleToImage(url, objects) {
imgRep.url = url
width = Math.max.apply(root, objects.map(function(e) { return e.width }))
height = Math.max.apply(root, objects.map(function(e) { return e.height }))
imgRep.ready = 0
imgRep.model = objects
}
Repeater {
id: imgRep
onReadyChanged: {
if (ready > 0 && ready === model.length) {
console.log(root.width, root.height, imgRep.url)
root.grabToImage(function (res) { res.saveToFile(imgRep.url); model = null })
}
}
property int ready: 0
property string url
delegate: ShaderEffectSource {
sourceItem: modelData
width: modelData.width
height: modelData.height
opacity: 0.2
live: false
Component.onCompleted: { imgRep.ready++ }
}
}
}
The usage of this would be like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Button {
text: 'grab'
onClicked: {
test.grabMultipleToImage('testimg.jpg', [rect1, rect2, rect3])
}
}
ImageGrabber {
id: test
}
Rectangle {
x: 100
y: 205
id: rect1
color: 'blue'
width: 10
height: 20
}
Rectangle {
x: 250
y: 12
id: rect2
color: 'green'
width: 20
height: 30
}
Rectangle {
x: 100
y: 100
id: rect3
color: 'red'
width: 100
height: 5
}
}
But if you have the need for more complex merging, you can also create this object manually and grab it when ever you want.
Without metering it, according to the note from the documentation
Note: This function will render the item to an offscreen surface and copy that surface from the GPU's memory into the CPU's memory, which can be quite costly. For "live" preview, use layers or ShaderEffectSource.
this solution should be more efficient than using Images with ItemGrabResults as sources for it keeps the stuff in the GPU memory until it is grabed and stored.
I am working on the following QML framework for music apps user-interface prototyping:
https://github.com/tiagmoraismorgado/TMM_QML_UI_UX_FRAMEWORK_WIP in the file instiationTest, i need to add a type Button1_1 and then use it to regenerate the whole gui based on combostochasticselector.
i know i have to instantiate the randomPicking() function, from the instantiationtest file, on top of the mouseArea Pressed statement of Button1_1, but i am not being able to do so.
if anyone could provide me any insight i would be pretty thankful
here is the code of the main file
(...)
Window {
id: root
width: Screen.width
height: Screen.height
visible: true
visibility: "FullScreen"
title: qsTr("instantiationTest")
color: "black"
CombosStochasticSelector {}
}
on the combostochasticselector, you can see the following:
Item {
property var model: model1
anchors.fill: parent
id: randomMIDIkeyboardSelector;
property var random: 0;
function randomSelection(min, max) {(...)}
function createMidiKeyboard(itemToBeInstantiated) {
var component = Qt.createComponent(itemToBeInstantiated)
if (component.status === Component.Ready) {
var midiKeyboard = component.createObject(randomMIDIkeyboardSelector, {model: model});
}
else if (component.status === Component.Error) {
}
}
function randomPicking() {
random = parseInt(randomSelection(1, 13));
if(random == 1) {createMidiKeyboard("./../_Combos/Combo1.qml");}
(...)
return random;
}
Component.onCompleted: {
randomPicking();
}
}
on the Button1_1 you can see the following:
import QtQuick 2.6
QMLOpenGLToggleButtonPrimitive {}
It instantiates a press released kind of button made out of Rectangle QML Primitives
kind regards
Tiago
I want to show a BusyIndicator while a long process is going on. The problem is it does not show up when I make it run and shows afterwards when the process is completed. According to the docs
The busy indicator should be used to indicate activity while content is being loaded or the UI is blocked waiting for a resource to become available.
I have created a minimal code that based upon the original code
Window {
id: win
width: 300
height: 300
property bool run : false
Rectangle {
anchors.fill: parent
BusyIndicator {
anchors.centerIn: parent
running: run
}
MouseArea {
anchors.fill: parent
onClicked: {
run = true
for(var a=0;a<1000000;a++) { console.log(a) }
run = false
}
}
}
}
So when the Rectangle is clicked I want to display the BusyIndicator for the time till the calculations gets completed.
For example purpose I have used the for loop here. In actual scenario I call a function (which inserts some 1000 rows into the Database) through the ContextProperty. But in that case too the BusyIndicator is not displayed.
Am I doing it the right way? Or what would be the best way to do it?
You cannot view your BusyIndicator just because long operation in onClicked handler blocks application GUI and indicator does not update. You should run such operation in a different thread to avoid freezing of GUI. Simple example:
QML
Window {
id: win
width: 300
height: 300
property bool run : false
Rectangle {
anchors.fill: parent
BusyIndicator {
id: busy
anchors.centerIn: parent
running: win.run
}
MouseArea {
anchors.fill: parent
onClicked: {
win.run = true
thread.sendMessage({run : true});
}
}
WorkerScript {
id: thread
source: "handler.js"
onMessage: {
win.run = messageObject.run;
}
}
}
}
handle.js
WorkerScript.onMessage = function(message) {
if(message.run === true) {
for(var a=0;a<1000000;a++) { console.log(a) }
}
WorkerScript.sendMessage({run : false});
}
There is a way to do this using QQuickWindow's afterSynchronizing signal:
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
width: 400
height: 400
visible: true
Component.onCompleted: print(Qt.formatDateTime(new Date(), "mm:ss:zzz"), "QML loaded")
onAfterSynchronizing: {
print(Qt.formatDateTime(new Date(), "mm:ss:zzz"), "Window content rendered")
if (!loader.item) {
loader.active = true
}
}
Item {
anchors.fill: parent
BusyIndicator {
running: !loader.item
anchors.centerIn: parent
}
Loader {
id: loader
active: false
anchors.fill: parent
sourceComponent: Text {
wrapMode: Text.Wrap
Component.onCompleted: {
for (var i = 0; i < 500; ++i) {
text += "Hello, ";
}
}
}
}
}
}
The idea is to use a Loader to have control over when the expensive operation happens. You could also use a dynamically loaded component via Qt.createQmlObject(), or Qt.createComponent() to dynamically load a component in a separate file.
If you run the example, you'll see that you get the following output:
qml: 58:12:356 QML loaded
qml: 58:12:608 Window content rendered
We use QQuickWindow's afterSynchronizing signal to know when the content of the window has been displayed, and only act on it the first time (via if (!loader.item)).
When the signal is initially emitted, we can be sure that the BusyIndicator has started its animation, so the user will actually see a spinning icon.
Once the Loader has finished loading the text, its item property will become non-null and the BusyIndicator will disappear.
Run into the same problem today! I will assume you are controlling your BusyIndicator from a C++ property called busy. And you are setting busy to true just before your calculations and to false just after. Doing this solved it for me. It's not a very elegant solution but it works:
QML
BusyIndicator {
running: CPPModule.busy
}
CPP
void CPPModule::setBusy(const bool &busy)
{
m_busy = busy;
emit busyChanged();
}
void CPPModule::InsertIntoDB()
{
setBusy(true);
QThread::msleep(50);
QCoreApplication::processEvents();
/*
very Long Operation
*/
setBusy(false);
}
I have used a TabbedPane in this while clicking i need to call an another qml file here is the code am using but its not working
TabbedPane {
showTabsOnActionBar: tabs
sidebarState: SidebarState.VisibleCompact
Tab {
imageSource: "asset:///msg.png"
Page {
id: page1
actions: [
ActionItem {
onTriggered: {
var page = pageDefinition.createObject();
navigationPane.push(page);
}
attachedObjects: ComponentDefinition {
id: pageDefinition;
source: "x.qml"
}
}
]
actionBarVisibility: ChromeVisibility.Visible
}
}
can anyone send me code how to add Click event inside this function
Thanks
Here is an example for creating tabs for a different screen. You are in main tab to go next qml as follows:
Tab {
title: "Puppies"
NavigationPane {
id: navigationPane
Sample //this is one qml page as like Sample.qml
{
}
}
}
Tab {
title: "Kittens"
NavigationPane
{
id: testpage
Test //this is one qml page as like Test.qml
{
}
}
}
You can access other QML files by setting the id.
I encounter a problem which is that the pop-up window cannot get the focus when it is shown. I tried to use the activefocus function in main window, but it doesn't work. It is supposed that if I press the enter key, the pop-window will be closed. How can I get the focus for the pop-up window? Thanks.
...
GridView {
id:grid_main
anchors.fill: parent
focus: true
currentIndex: 0
model: FileModel{
id: myModel
folder: "c:\\folder"
nameFilters: ["*.mp4","*.jpg"]
}
highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
delegate: Item {
width: 100; height: 100
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
text: fileName
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
}
}
}
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show();
subWindow.forceActiveFocus();
event.accepted = true;
grid_main.focus = false;
}
}
}
Window {
id: subWindow
Keys.onPressed: {
if (event.key == 16777220) {//press enter
subWindow.close();
}
}
}
...
Let's start with some basics:
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show()
...
event.accepted = true
}
}
Not to mention how error-prone it is, just for the sake of readability, please don't hard-code enum values like 16777220. Qt provides Qt.Key_Return and Qt.Key_Enter (typically located on the keypad) and more conveniently, Keys.returnPressed and Keys.enterPressed signal handlers. These convenience handlers even automatically set event.accepted = true, so you can replace the signal handler with a lot simpler version:
Keys.onReturnPressed: {
subWindow.show()
...
}
Now, the next thing is to find the correct methods to call. First of all, the QML Window type does not have such method as forceActiveFocus(). If you pay some attention to the application output, you should see:
TypeError: Property 'forceActiveFocus' of object QQuickWindowQmlImpl(0x1a6253d9c50) is not a function
The documentation contains a list of available methods: Window QML type. You might want to try a combination of show() and requestActivate().
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
Then, you want to handle keys in the sub-window. Currently, you're trying to attach QML Keys to the Window. Again, if you pay attention to the application output, you should see:
Could not attach Keys property to: QQuickWindowQmlImpl(0x1ddb75d7fe0) is not an Item
Maybe it's just the simplified test-case, but you need to get these things right when you give a testcase, to avoid people focusing on wrong errors. Anyway, what you want to do is to create an item, request focus, and handle keys on it:
Window {
id: subWindow
Item {
focus: true
Keys.onReturnPressed: subWindow.close()
}
}
Finally, to put the pieces together, a working minimal testcase would look something like:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
width: 300
height: 300
visible: true
GridView {
focus: true
anchors.fill: parent
// ...
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
}
Window {
id: subWindow
Item {
focus: true
anchors.fill: parent
Keys.onReturnPressed: subWindow.close()
}
}
}
PS. Key events rely on focus being in where you expect it to be. This may not always be true, if the user tab-navigates focus elsewhere, for example. Consider using the Shortcut QML type for a more reliable way to close the popup.