QML: contentItem does not show any Image - qml

I am using QML to design a small user interface.
The problem I have is that I need to select an image if a certain conditions happens or not, and nothing happens because I may have something wrong in the contentItem below, I set a simple a if loop that replicates exactly the problem I have:
main.qml
// operations ....
Row {
anchors.fill: parent
anchors.leftMargin: 20
anchors.topMargin: 20
Button {
id: button
width: 90
height: 270
contentItem: Item {
Image {
source: root.selected === 0 ?
source: "qrc:/images/btn-Modes-on.png" :
source: "qrc:/images/btn-modes-normal.png"
}
}
// operations ....
}
I believe the problem is where I set the if loop for the images. I can confirm that the path of the images is correct and double checked.
I also used according to the documentation the proper notation of the images, and the property I am using is source: "path to your image".
However after checking that I still also have no return.
Thanks for pointing in the right direction to solve this problem.

There's a typo in your code. The Image source should look like this:
source: root.selected === 0 ?
"qrc:/images/btn-Modes-on.png" :
"qrc:/images/btn-modes-normal.png"

Related

Qt QML Settings.hasTouchScreen returns false

I am trying to find out why flicking is not working with a TreeView example on my Raspberry Pi3 with touch screen.
Looking at the qml code of TreeView.qml, e.g.
https://github.com/RSATom/Qt/blob/master/qtquickcontrols/src/controls/TreeView.qml:
BasicTableView {
...
__mouseArea: MouseArea {
id: mouseArea
parent: __listView
width: __listView.width
height: __listView.height
z: -1
propagateComposedEvents: true
focus: true
// If there is not a touchscreen, keep the flickable from eating our mouse drags.
// If there is a touchscreen, flicking is possible, but selection can be done only by tapping, not by dragging.
preventStealing: !Settings.hasTouchScreen
...
}
}
By similarly looking at the qml code for BasicTableView.qml, it seems that behavior is controlled by Settings.hasTouchScreen.
According to:
https://code.woboq.org/qt5/qtquickcontrols/src/controls/Private/qquickcontrolsettings.cpp.html
it corresponds to the following method:
bool QQuickControlSettings1::hasTouchScreen() const
{
const auto devices = QTouchDevice::devices();
for (const QTouchDevice *dev : devices)
if (dev->type() == QTouchDevice::TouchScreen)
return true;
return false;
}
However, in my case, Settings.hasTouchScreen returns false; i.e. the touch screen (although working for the rest), is not
correctly detected by the QML environment, which probably explains why the flicking does not work.
According to https://doc.qt.io/qt-5/qtouchdevice.html, my touch device should have been registered somehow by the private QWindowSystemInterface::registerTouchDevice() method, but wasn't.
How can I get this to work?
Thanks!
It seems not to work correctly with tslib, but works by using the evdevtouch plugin which is enabled by adding the following command line arguments when launching the program:
-plugin evdevtouch:/dev/input/eventX
where eventX is the event assigned to the touch input.
With that, QTouchDevice::devices() is no longer empty, and the flicking of the TreeView works.

Querying global mouse position in QML

I'm programming a small PoC in QML. In a couple of places in my code I need to bind to/query global mouse position (say, mouse position in a scene or game window). Even in cases where mouse is outside of MouseAreas that I've defined so far.
Looking around, the only way to do it seems to be having whole screen covered with another MouseArea, most likely with hovering enabled. Then I also need to deal with semi-manually propagating (hover) events to underlying mouseAreas..
Am I missing something here? This seems like a pretty common case - is there a simpler/more elegant way to achieve it?
EDIT:
The most problematic case seems to be while dragging outside a MouseArea. Below is a minimalistic example (it's using V-Play components and a mouse event spy from derM's answer). When I click the image and drag outside the MouseArea, mouse events are not coming anymore so the position cannot be updated unless there is a DropArea below.
The MouseEventSpy is taken from here in response to one of the answers. It is only modified to include the position as parameters to the signal.
import VPlay 2.0
import QtQuick 2.0
import MouseEventSpy 1.0
GameWindow {
id: gameWindow
activeScene: scene
screenWidth: 960
screenHeight: 640
Scene {
id: scene
anchors.fill: parent
Connections {
target: MouseEventSpy
onMouseEventDetected: {
console.log(x)
console.log(y)
}
}
Image {
id: tile
x: 118
y: 190
width: 200
height: 200
source: "../assets/vplay-logo.png"
anchors.centerIn: parent
Drag.active: mausA.drag.active
Drag.dragType: Drag.Automatic
MouseArea {
id: mausA
anchors.fill: parent
drag.target: parent
}
}
}
}
You can install a eventFilter on the QGuiApplication, where all mouse events will pass through.
How to do this is described here
In the linked solution, I drop the information about the mouse position when emitting the signal. You can however easily retrieve the information by casting the QEvent that is passed to the eventFilter(...)-method into a QMouseEvent and add it as parameters to the signal.
In the linked answer I register it as singleton available in QML and C++ so you can connect to the signal where ever needed.
As it is provided in the linked answer, the MouseEventSpy will only handle QMouseEvents of various types. Once you start dragging something, there won't be QMouseEvents but QDragMoveEvents e.t.c. Therefore you need to extend the filter method, to also handle those.
bool MouseEventSpy::eventFilter(QObject* watched, QEvent* event)
{
QEvent::Type t = event->type();
if (t == QEvent::MouseButtonDblClick
|| t == QEvent::MouseButtonPress
|| t == QEvent::MouseButtonRelease
|| t == QEvent::MouseMove) {
QMouseEvent* e = static_cast<QMouseEvent*>(event);
emit mouseEventDetected(e->x(), e->y());
}
if (t == QEvent::DragMove) {
QDragMoveEvent* e = static_cast<QDragMoveEvent*>(event);
emit mouseEventDetected(e->pos().x(), e->pos().y());
}
return QObject::eventFilter(watched, event);
}
You can then translate the coordinates to what ever you need to (Screen, Window, ...)
As you have only a couple of places where you need to query global mouse position, I would suggest you to use mapToGlobal or mapToItem methods.
I believe you can get cursor's coordinates from C++ side. Take a look on answer on this question. The question doesn't related to your problem but the solution works as well.
On my side I managed to get global coordinates by directly calling mousePosProvider.cursorPos() without any MouseArea.

How to show current selection QML TreeView?

I can show current selection QML ListView but similar thing doesn't work in TreeView.
Part of the problem is for TreeView it doesn't recognize index which is passed to delegate in case of ListView. I tried styleData.indexbut that doesn't work either.
rowDelegate: Item {
id: row_delegate
height: 40
Rectangle {
id: rectid
anchors.fill: parent
MouseArea {
id: mouse_area
anchors.fill: parent
onClicked: {
console.log("Row clicked " + rectid.styleData.index)
}
}
}
}
The output is:
qml: Row clicked undefined
As stated by the documentation, you have a set of properties within the namespace styleData that can be used for almost the same purposes from within a delegate.
As an example, you can set the text property of a label that is part of your delegate as it follows:
text: styleData.value
Where styleData.value is (documentation excerpt):
the value or text for this item
Similarly, you have:
styleData.pressed - true when the item is pressed
styleData.index - the QModelIndex of the current item in the model
styleData.hasChildren - true if the model index of the current item has or can have children
And so on... Please, refer to the documentation for the full list.
Be aware also of the note at the end of the documentation:
Note: For performance reasons, created delegates can be recycled across multiple table rows. This implies that when you make use of implicit properties such as styleData.row or model, these values can change after the delegate has been constructed. This means that you should not assume that content is fixed whenComponent.onCompleted is called, but instead rely on bindings to such properties.

How to access delegate properties in ListView using index

I want to access delegate properties in ListView. I've tried with contentItem but sometimes it's undefined.
Here is my code:
ListModel{
id: modeldata
ListElement{
name:"don"
rank:1
}
ListElement{
name:"shan"
rank:2
}
ListElement{
name:"james"
rank:3
}
ListElement{
name:"jeggu"
rank:4
}
}
Component{
id: delegateitem
Row {
property int count: rank
Rectangle{
width: 100
height: 50
Text{
anchors.centerIn: parent
text: name
}
}
}
}
ListView{
id: listview
focus: true
anchors.fill: parent
model: modeldata
delegate: delegateitem
onCurrentIndexChanged: {
console.log("position",currentIndex)
console.log("property",contentItem.children[currentIndex].count);
}
}
Problem invalid output at position 1
qml: position 0
qml: property 1
qml: position 1
qml: property undefined
qml: position 2
qml: property 2
qml: position 3
qml: property 3
#Teimpz didn't really explain it well. Especially since there are bunch of qt project and ubuntu touch qml examples and use cases where you manage dynamically created list elements using javascript, and that is why they have javascript methods and properties
In QML there is more a notion of parent than a child, which is common in html. In bigger projects it is recommended (as you can also see in qt examples and in docs http://doc.qt.io/qt-5/qtqml-javascript-expressions.html#functions-in-imported-javascript-files) to have js logic separate from qml elements so you do access and manage elements from outside rather than pollute you qml elements with js logic, but not in a way of looking for children elements, but rather exposing children elements that you need.
In your case you should just use currentItem, same as you use currentIndex, so currentItem.count will give you what you need.
And if you don't need current item at all, you can access elements from model directly:
modelData.get(currentIndex).count, or listview.model.get(currentIndex).count
As for the hack that is mentioned by #Teimpz that is also one bad example. When you have more complex requirements and wanting specific elements inside delegate, every delegate has ListView.isCurrentItem property which you can attach and check. This would mean you can add property var myTargetItem to listview, and set it from child to whatever element you want if that delegate is current http://doc.qt.io/qt-5/qml-qtquick-listview.html#isCurrentItem-attached-prop
You can of course do that for any kind of event, maybe activeFocus so you could only reference activeFocused item.
This once again give you ability to expose only wanted elements without any advanced logic or lopping. Combining this with signals you can create very complex but clean interfaces without searching through children items.
So in the end maybe less nice but still better than searching for elements would be to add property int currentItemCount: 0 to listview. In delegate (Row element) you then add property bool isCurrentItem: ListView.isCurrentItem
so you get onIsCurrentItemChanged signal inside delegate, where you can do:
onIsCurrentItemChanged: if(isCurrentItem) listview.currentItemCount = count
so you have your current item count always set
The simple way is using itemAtIndex() like intemAt() in Repeater.
First of all: if you are trying to access list elements from outside your list, this is a good indicator that you should rethink your desing.
Now the solution: a listview has more children than only its items. You can filter them out by defining a property "property string type: "myType" " for example. Then find the items by looping over the children and only take those where the type property equals "myType".
Its somewhat of a hack but again you should really not be doing this in the first place.
myListView.itemAtIndex(currentIndex)).function_name()

dojo splitter not resizing properly with dynamic content

I'm creating a seemingly simple dojo 1.8 web page which contains an app layout div containing a tab container and an alarm panel below the tab container. They are separated by a splitter so the user can select how much of the alarms or the tabcontainer they want to see.
Here's the example on jsfiddle:
http://jsfiddle.net/bfW7u/
For the purpose of the demo, there's a timer which grows the table in the alarm panel by an entry every 2 seconds.
The problem(s):
If one doesn't do anything and just lets the table grow, no scroll bar appears in the alarm panel.
If one moves the splitter without having resized the browser window first, the splitter handle ends up in a weird location.
Resizing the browser window makes it behave like I would expect it to begin with.
Questions:
Am I doing something wrong in the way I'm setting things up and that's causing this problem?
How can I catch the splitter has been moved event (name?)
How do I resize the splitter pane to an arbitrary height? I've tried using domStyle.set("alarmPanel", "height", 300) and this indeed sets the height property... but the pane does not resize!
Any help greatly appreciated!
I forked your jsFiddle and made some modifications to it: http://jsfiddle.net/phusick/f7qL6/
Get rid of overflow: hidden in html, body and explicitly set height of alarmPanel:
.claro .demoLayout .edgePanel {
height: 150px;
}
This tricky one. You have two options: to listen to splitter's drag and drop or to listen to ContentPane.resize method invocation. Both via dojo/aspect:
// Drag and Drop
var splitter = registry.byId("appLayout").getSplitter("bottom");
var moveHandle = null;
aspect.after(splitter, "_startDrag", function() {
moveHandle = aspect.after(splitter.domNode, "onmousemove", function() {
var coords = {
x: !splitter.horizontal ? splitter.domNode.style.left : 0,
y: splitter.horizontal ? splitter.domNode.style.top : 0
}
dom.byId("dndOutput").textContent = JSON.stringify(coords);
})
});
aspect.after(splitter, "_stopDrag", function() {
moveHandle && moveHandle.remove();
});
// ContentPane.resize()
aspect.after(registry.byId("alarmPanel"), "resize", function(duno, size) {
dom.byId("resizeOutput").textContent = JSON.stringify(size);
});
Call layout() method after changing the size:
registry.byId("alarmPanel").domNode.style.height = "200px";
registry.byId("appLayout").layout();