I`m try to get control under cursor. At my example i can get only red rectangle, but i need get other also.
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
id: mainWindow
title: qsTr("Hello World")
width: 640
height: 480
Item {
id: parentPanel
anchors.fill: parent
MouseArea {
anchors.fill: parent
hoverEnabled: true
onMouseXChanged: moveMouse()
onMouseYChanged: moveMouse()
function moveMouse()
{
currentControl.text = parentPanel.childAt(mouseX, mouseY).color ? parentPanel.childAt(mouseX, mouseY).color : "not colored"
}
}
Rectangle {
id: redRect
anchors {
fill: parent
leftMargin: 50
bottomMargin: 50
}
color: "red"
Rectangle {
id: yellowRect
anchors {
fill: parent
leftMargin: 50
bottomMargin: 50
}
color: "yellow"
Rectangle {
id: greenRect
anchors {
fill: parent
leftMargin: 50
bottomMargin: 50
}
color: "green"
}
}
}
Text {
id: currentControl
anchors.left: parent.left
anchors.bottom: parent.bottom
}
}
}
I have screenshot from running program. Green rect inside yellow, yellow inside red. I need get control ref when mouse cursor over control.
I'm not familiar with QML, so I don't know the exact syntax for this, but it seems like you want to loop until you find the inner-most control and get the color of that. Here's some C++ish pseudo-code
auto control = parentPanel.childAt(mouseX, mouseY);
while (control)
{
currentControl.text = control.color ? control.color : "not colored";
control = control.childAt(mouseX, mouseY);
}
Of course, this code assumes that the X and Y passed are absolute, not relative. If the are relative, you would need to decrement them by the location of control in each consecutive loop.
Related
I'm developping an app using QT 5.15 LTS (5.15.2). I have the following QML item that I use to handle virtual keyboard interactions :
//InputScreen.qml
import QtQuick 2.3
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.2
import QtQuick.VirtualKeyboard 2.3
Rectangle
{
id: inputScreen
property var target: undefined
width: app.width
height: app.height
color: "#44000000"
z: 200
onVisibleChanged:
{
if (inputScreen.visible == true)
{
fld.text = target.text;
inputPanel.forceActiveFocus();
fld.forceActiveFocus();
fld.clicked(); //Tried to force a click here. Explanation below
}
}
Rectangle
{
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
height: 40
width: 600
z: 201
TextField
{
id: fld
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 400
height: 40
font.pixelSize: 24
focus: true
}
Button
{
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 200
height: 40
font.pixelSize: 24
text: "Valider"
onClicked:
{
if (target != undefined)
{
target.text = fld.text;
inputScreen.visible = false;
}
}
}
}
InputPanel
{
id: inputPanel
height: 400
width: app.width - 40
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
focus: true
z: 201
}
MouseArea
{
anchors.fill: parent
onClicked:
{
target.text = fld.text;
inputScreen.visible = false;
}
}
}
When my item becomes visible, the textField gets the focus correctly and I can start typing through the InputPanel virtual keyboard. There's however one single problem : the shift key does not work !
In order to make that shift key enable itself, I need to click on the TextField, which beats the purpose of forcing the active focus on it to begin with.
Is there any way to fix that problem ? Or at the very least, is there any kind of workaround I can use to enable that shift key ?
I have already tried to force a click inside my TextField (as seen in the code above), but it didn't work.
For context : here's the content of main.qml as well, in order to give out a working example :
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.4
import QtQuick.VirtualKeyboard 2.3
Window
{
id: app
visible: true
width: 640
height: 480
title: qsTr("Example")
InputScreen
{
id: inputScreen
visible: false
}
Rectangle
{
id: rect
height: parent.height * 0.8
width: parent.width * 0.8
anchors.centerIn: parent
border.color: "#FF0000"
z: 1
TextField
{
id: myField
anchors.centerIn: parent
width: 200
height: 24
MouseArea
{
anchors.fill: parent
onClicked:
{
inputScreen.target = myField;
inputScreen.visible = true;
}
}
}
}
}
I finally managed to find a trick that enabled that shift key without requiring to click on my already focused TextField, and it's actually very simple.
I simply added the following line in the onClicked event of the MouseArea inside the "myField" TextField :
MouseArea
{
anchors.fill: parent
onClicked:
{
parent.focus = true; //This line solved the issue !
//...
}
}
I have no idea why, but somehow this was enough to actually unlock the shift key from my InputPanel virtual keyboard without requiring me to click a second time on the TextField from my InputScreen item. I suppose it has probably something to do with the fact that my MouseArea is hijacking the focus of the "myField" TextField, which triggers some sort of odd behaviour on the InputPanel item despite having another TextField item focused.
In any case, I hope this will help someone.
I have 3 files. main.qml, Guide.qml and ChannelViewer.qml
my main class contains 2 components and a loader here is the code
main.qml
import QtQuick 2.0
Rectangle {
id:loader
color: "black"
property string channelName
property string channelURL
Component{
id:tv
ChannelViewer{}
}
Component{
id:guide
Guide{}
}
Loader
{
id: pageLoader
anchors.fill:parent
focus:true
sourceComponent: tv
}
Connections{
target:pageLoader.item
onChangeChannel:{
channelName=name
channelURL=url
}
}
Keys.onPressed: {
event.accepted = true;
if (event.key === Qt.Key_I) {
pageLoader.sourceComponent = tv;
}
else if(event.key === Qt.Key_G) {
pageLoader.sourceComponent = guide;
}
}
}
Now if I press "G" I would be moved to the guide file without any issues In my guide page I am able to send signal to main.qml and update the name property in main.
Guide.qml
Item {
signal changeChannel(string url, string name)
Loader {
id: pageLoader
anchors.fill:parent
sourceComponent: guide
focus:true
}
Keys.onPressed: {
if(event.key === Qt.Key_Escape) {
pageLoader.source = "main.qml";
}
event.accepted = true;
}
Component {
id:guide
Rectangle {
color:"lightblue"
Keys.onPressed: {
if(event.key === Qt.Key_Return) {
changeChannel(menuContent.currentItem.ch_url, menuContent.currentItem.ch_name)
pageLoader.source = "main.qml";
}
event.accepted = true;
}
}
}
}
However now when I press "Return" in my Guide.qml i will be taken back to main.qml (Channelname and ChannelURL will be updated successfully), and my main.qml will now take me to ChannelViewer.qml and here is the problem my ChannelViewer.qml will not receive the updated channelName and channelURL. And I am not sure what am I doing wrong.
ChannelViewer.qml
import QtQuick 2.0
import VLCQt 1.0
Rectangle {
id:root
width: 640
height: 480
color: "black"
focus:true
Loader
{
id: pageLoader
anchors.fill:parent
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log(channelURL)
}
}
Keys.onPressed: {
if (event.key === Qt.Key_I) {
event.accepted = true;
if(channelInfo.visible === true) {
channelInfo.visible=false;
}
else {
channelInfo.visible=true;
}
}
}
VlcVideoPlayer {
id: vidwidget
anchors.fill: parent
url:channelURL
ChannelInfo{
id:channelInfo
anchors.bottom: parent.bottom
anchors.bottomMargin: ((parent.height*5)/100)
anchors.horizontalCenter: parent.horizontalCenter
width:parent.width - ((parent.width*10)/100)
height: (parent.height*20)/100
backgroundOpacity: 0.7
radius:10
channelNameProp: channelName
channelNumberProp: "1"
headerIcon: "imgs/television_32x32.png"
}
}
}
EDIT:
Code for my ChannelInfo.qml
import QtQuick 2.0
Item {
id:channelinfo
property color backgroundColor: "blue"
property color headerBackgroundColor: "lightblue"
property color headerNameColor: "black"
property color borderColor: "black"
property color channelNameColor: "white"
property color channelNumberColor: "white"
property real borderWidth:0
property real radius:0
property real backgroundOpacity: 0.5
property string menuTitle : "TV Channels"
property string channelNameProp
property string channelNumberProp
property url headerIcon: "imgs/television.png"
visible:false
Rectangle{
id:root
width:channelinfo.width
height:channelinfo.height
color:channelinfo.backgroundColor
border.color:channelinfo.borderColor
border.width: channelinfo.borderWidth
radius:channelinfo.radius
opacity:channelinfo.backgroundOpacity
visible: parent.visible
Rectangle{
id:header
anchors.top:parent.top
// width:(parent.width*40)/100
width: parent.width
height: (parent.height*30)/100
radius: channelinfo.radius
color:channelinfo.headerBackgroundColor
Image{
source:channelinfo.headerIcon
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -4
}
Text{
id:headerTitle
anchors.left: parent.left
anchors.leftMargin: 50
anchors.verticalCenter: parent.verticalCenter
width:parent.width
wrapMode: Text.WordWrap
color:channelinfo.headerNameColor
text:menuTitle
font.pixelSize: Math.round(parent.height/2)
font.bold: true
}
}
Rectangle{
id:content
anchors.bottom: parent.bottom
width:parent.width
height:parent.height-header.height
color:"transparent"
Text{
id:channelName
anchors.left: parent.left
anchors.leftMargin: 50
anchors.verticalCenter: parent.verticalCenter
color:channelinfo.channelNameColor
text:channelNameProp
font.pixelSize: Math.round(parent.height/4)
font.bold: true
}
Text{
id:channelNumber
anchors.right: parent.right
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
color:channelinfo.channelNumberColor
text:channelNumberProp
font.pixelSize: Math.round(parent.height/4)
font.bold: true
}
}
}
}
Github Page for VLCPlayer
https://github.com/vlc-qt/
If you are going to have such a fixed structure, why even bother with the signal, you can simply:
Keys.onPressed: {
if(event.key === Qt.Key_Return) {
channelName = menuContent.currentItem.ch_name
channelURL = menuContent.currentItem.ch_url
pageLoader.source = "main.qml";
}
event.accepted = true;
}
And then remove the unnecessary part:
Connections{
target:pageLoader.item
onChangeChannel:{
channelName=name
channelURL=url
}
}
Since channelName and channelURL are declared in the root object of the qml file, they should be accessible from within objects that are nested further up the tree because of dynamic scoping.
So after you posted the relevant code, you have an:
Text{
id:channelName
in your ChannelInfo object, which is shadowing the channelName property, declared in main.qml. It is a good idea to develop the habit of consistent naming conventions. For example, since this is an id, I personally would have used id: _cName, this way you minimize the odds of getting such collisions.
Update:
The only other reason I can think why it doesn't work is that you are somewhere breaking the channelNameProp: channelName binding by doing something like channelNameProp = something.
Here is a quick example to illustrate that dynamic scoping just works (as long as you don't shadow anything), even in situations that involve dynamically changing Loader items:
// main.qml
ApplicationWindow {
id: _cName
visible: true
width: 640
height: 480
property int value: 0
Loader {
id: loader
source: "Obj.qml"
}
}
// Rect.qml
Rectangle {
id: rectangle
width: 50; height: 100
color: "red"
Text {
anchors.centerIn: parent
text: value
}
MouseArea {
anchors.fill: parent
onClicked: {
loader.source = "Obj.qml"
}
}
}
// Obj.qml
Rectangle {
id: rectangle
width: 50; height: 100
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
value++
loader.source = "Rect.qml"
}
}
}
As the properties
property string channelName
property string channelURL
have change signals and therefore support property binding, I think the easiest way would be to change line 9-17 to
Component{
id:tv
ChannelViewer {
id: channelViewer
channelName: loader.channelName
channelURL: loader.channelURL
}
}
Component{
id:guide
Guide {
id: guide
channelName: loader.channelName
channelURL: loader.channelURL
}
}
If Guide changes the channelName you need to make sure that you change it in the loader. You might use Binding-objects to make the bindings survive assignments (=).
So this works, you need to create the properties channelName and channelURL in the root nodes of your Guide.qml abd your ChannelViewer.qml. Then, in each place inside those files, you use fully qualified identifiers: id.propertyName, which would be e.g channelinfo.channelName in the ChannelInfo.qml, root.channelName in the ChannelViewer.qml and an id you will need to set (e.g. root again) in your Guid.qml -> root.channelName.
Usage of fully qualified identifiers for bindings, which always include the idOfTheObject.propertyName helps to avoid problems. In some cases (positioning, anchoring, sizing) parent is ok, but you might not know what exactly is the parent)
Dynamic scoping is a blessing if you know exactly how and where the code will be used, e.g. if it is essentially a partial definition of a larger object, and will never be used in another context. But here you need to know that if the parent file changes the internal api, you need to adapt the child file accordingly. If you think, the file might be for reuse, avoid dynamic scoping and only reference what is defined inside the file.
I have a TextArea and popup or another item which overlaps it. But when I pointing at popup, cursor shape doesn't change. I need the cursor to become default when I pointing to overlapped items.
Code:
import QtQuick 2.7
import QtQuick.Controls 2.1
ApplicationWindow {
id: root
visible: true
width: 800
height: 600
Component.onCompleted: pop.open()
TextArea {
width: 800
height: 600
}
Popup {
id: pop
Rectangle {
color: "red"
width: 100
height: 100
}
MouseArea {
anchors.fill: parent
}
}
}
The TextArea contains a MouseArea that sets a different cursor shape.
The cursor shape is always defined by the top-most MouseArea. Therefore the solution is, to add a MouseArea to the overlapping Item to reset the cursor shape for this area.
import QtQuick 2.7
import QtQuick.Controls 1.4
ApplicationWindow {
id: root
visible: true
width: 800
height: 600
TextArea {
width: 800
height: 600
}
Rectangle {
color: 'red'
width: 100
height: 100
x: 100
y: 50
MouseArea { // This resets the cursor shape, if the cursor hovers over the Rectangle
anchors.fill: parent
}
}
}
The issue has been fixed in Qt 5.9.
Thanks to jpnurmi
I want to do something very similar to this image in Qt where I can click in any square and change the color of it.
It's pretty simple to do this with QML. Look at the code below:
import QtQuick 2.1
import QtQuick.Window 2.0
Window {
id: root
visible: true
width: 360
height: 500
Column{
Repeater{
model: getRowsNumber(root.height)
delegate: Row{
property int externalIdx: index
Repeater{
model: getColumnsNumber(root.width)
delegate: Rectangle{
property bool selected: false
property color originalColor: (index + externalIdx) % 2 == 0 ? "black" : "white"
width:20
height: 20
color: selected ? "red" : originalColor
border.width: 1
border.color: "black"
MouseArea{
anchors.fill: parent
onClicked: parent.selected = !parent.selected
}
}
}
}
}
}
function getColumnsNumber(width){
return width/20;
}
function getRowsNumber(height){
return height/20;
}
}
That's all you need to have a rectangular chess-like grid where each cell changes its color when it is clicked on. Of course, you will need to adapt it to your needs but that should be enough for you to start.
If I have the following:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 800
height: 700
visible: true
property var myArray: [1, 2, 3, 4, 5, 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Open")
onTriggered: messageDialog.show(qsTr("Open action triggered"));
}
MenuItem {
text: qsTr("E&xit")
onTriggered: Qt.quit();
}
}
}
Rectangle {
id: myButton
anchors.top: parent.top
anchors.topMargin: 5
color: "yellow"
width: 100
height: 25
radius: 3
anchors.horizontalCenter: parent.horizontalCenter
Text {
text: "Clear Selection"
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
myListView.currentIndex = -1
}
}
}
ListView {
id: myListView
width: 300
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: myButton.bottom
anchors.topMargin: 5
anchors.bottom: parent.bottom
currentIndex: -1
//highlightFollowsCurrentItem: false
highlight: Rectangle {
color: "pink"
radius: 3
width: parent.width - 10
height: 25
//y: myListView.currentItem.y
anchors.horizontalCenter: parent.horizontalCenter
}
clip: true
model: myArray
delegate: Rectangle {
width: parent.width - 10
height: 25
color: "transparent"
border.color: "cyan"
anchors.horizontalCenter: parent.horizontalCenter
Text {
text: myArray[index]
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: myListView.currentIndex = index
}
}
}
MessageDialog {
id: messageDialog
title: qsTr("May I have your attention, please?")
function show(caption) {
messageDialog.text = caption;
messageDialog.open();
}
}
}
When clicking the Clear Selection button I receive the following:
qrc:/main.qml:67: TypeError: Cannot read property of null
qrc:/main.qml:64: TypeError: Cannot read property of null
How can I clear the selection without getting the error? It doesn't appear to crash the application but I have a list view that changes based on another list view selection and the error occurs several times, cluttering up the debug output in Qt Creator. I have noticed this in Qt 5.4 and 5.5
The documentation for ListView says:
An instance of the highlight component is created for each list. The geometry of the resulting component instance is managed by the list so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
So you don't need to try to manage the position of the highlight item yourself. If you want to position the highlight, create an intermediate parent item instead:
highlight: Item {
Rectangle {
color: "pink"
radius: 3
width: parent.width - 10
height: 25
anchors.horizontalCenter: parent.horizontalCenter
}
}
As for why it happens, it's likely because the highlight item is reparented, leaving it in a state where its parent property is null. You can test this out with the following code:
anchors.horizontalCenter: { print(parent); parent.horizontalCenter }
The problem in general is that if you have a foo, which is supposed to have a bar, then you reference it as foo.bar, however, if foo was not properly initialized, then it cannot have a bar, since it does not exist (yet). In your case it seems that parent is not properly initialized, so it does not have a width and horizontalCenter (in the delegate, maybe), respectively. The solution is to initialize properly the object whose members are to be used, in our case, parent.
I had asked this question over on the Qt forum (https://forum.qt.io/topic/62328/clearing-a-qml-listview-selection) as well, but stack was quicker with a response. Checking the parent value works:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
ApplicationWindow {
title: qsTr("Hello World")
width: 800
height: 700
visible: true
property int myMargin: 5
menuBar: MenuBar {
Menu {
title: qsTr("&File")
MenuItem {
text: qsTr("&Open")
onTriggered: messageDialog.show(qsTr("Open action triggered"));
}
MenuItem {
text: qsTr("E&xit")
onTriggered: Qt.quit();
}
}
}
Rectangle {
id: myButton
anchors.top: parent.top
anchors.topMargin: myMargin
color: "yellow"
width: 100
height: 25
radius: 3
anchors.horizontalCenter: parent.horizontalCenter
Text {
text: "Clear Selection"
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors.fill: parent
onClicked: {
myListView.currentIndex = -1
}
}
}
Rectangle {
width: 300
anchors.top: myButton.bottom
anchors.topMargin: myMargin
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
ListView {
id: myListView
anchors.fill: parent
currentIndex: -1
spacing: 3
highlightMoveDuration: 25
highlight: Rectangle {
width: parent ? parent.width - 10 : 0
height: parent ? 25 : 0
color: "pink"
radius: 3
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
}
clip: true
model: ListModel {
id: myArray
Component.onCompleted: {
for (var i = 1; i < 46; i++)
append({number: i})
}
}
delegate: Rectangle {
width: parent ? parent.width - 10 : 0
height: parent ? 25 : 0
color: "transparent"
border.color: "cyan"
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
Text {
text: number
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
myListView.currentIndex = index
}
}
}
}
}
MessageDialog {
id: messageDialog
title: qsTr("May I have your attention, please?")
function show(caption) {
messageDialog.text = caption;
messageDialog.open();
}
}
}