I'm trying to get a displayed number to change when a button is clicked. How would I do that?
Here is my QML code
Button {
id:button
x:232
y:250
width:18
height:18
// Makes button have a transparent background
palette {
button: "transparent"
}
Image {
anchors.fill: Button
source:"Images/image.png"
}
// Moves rectangle down, on button click
onClicked: rectangle.y-=10
}
Text{
text: qsTr("12.0")
}
I want the number 12 to increase each time the button is clicked
You have to declare a property of type int, visible for both Text and Button items.
So an example code can be:
import QtQuick 2.0
import QtQuick.Controls 2.0
Window {
id: rootWindow
visible: true
width: 300; height: 300
property int displayValue: 12
Text {
id: displayTextId
anchors.left: addOneButtonId.right
text: displayValue.toString() //more clear if you explicit the parent rootWindow.displayValue.toString()
}
Button {
id: addOneButtonId
text: "Add 1"
onClicked: {
rootWindow.displayValue += 1
}
}
}
Alternatively, the property can be declared local to the Text element (so defined inside it), but pay attention, because a property is visible only for its child.
By the way, your code is full of error. If you want to create a button, that contains an image and a text, the best way is that you create a Rectangle object and define a Mouse area inside it.
The code structure should be like:
Rectangle {
id: root
property int number: 12
width: 100; height: 50
color: "transparent"
border.width: 1
border.color: "black"
Image { id: imageId }
Text { id: textId; text: root.number.toString() }
MouseArea {
anchors.fill: parent
onClicked: {
root.y += 10 // shift the y position down
root.number += 1
}
}
}
Related
I have a Component for an sddm theme. At the moment I use the theme dark sugar as the base theme. The component looks like the following:
Item {
id: hexagon
property color color:"yellow"
property int radius: 30
//layer.enabled: true
//layer.samples: 8
Shape {
//... Here some Positioning and other Stuff
ShapePath {
//... Here some Options and Pathlines
}
}
}
This works fine, but as soon as I uncomment both layer settings the component disappears. Does this happen, because I load the component like this:
Pane {
...
Item {
...
MyComponent {
z: 1
}
}
}
Nor the Pane or the Item use layer but most Components in the Item use the z: 1 property.
As iam_peter says, the default width and height properties of any Item are 0, and layer.enabled sets the size of the offscreen texture to the item size. By default, the scene graph doesn't do any clipping: a child item can populate scene graph nodes outside its parent's bounds. But when you confine the children's rendering to a specific offscreen texture, anything that doesn't fit is clipped. Here's a more interactive example to play with this:
import QtQuick
import QtQuick.Controls
Rectangle {
width: 640
height: 480
Column {
CheckBox {
id: cbLE
text: "layer enabled"
}
Row {
spacing: 6
TextField {
id: widthField
text: layerItem.width
onEditingFinished: layerItem.width = text
}
Label {
text: "x"
anchors.verticalCenter: parent.verticalCenter
}
TextField {
id: heightField
text: layerItem.height
onEditingFinished: layerItem.height = text
}
}
}
Rectangle {
id: layerItem
x: 100; y: 100
border.color: "black"; border.width: 2
layer.enabled: cbLE.checked
Rectangle {
width: 100
height: 100
color: "tomato"
opacity: 0.5
}
Text {
text: "this text will get clipped even when layer size is defined"
}
}
}
You can use renderdoc to see how the rendering is done; for example you can see the texture that is created by enabling the layer.
This is a small reproducible example:
import QtQuick
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Item {
//width: 200
//height: 200
//layer.enabled: true
Rectangle {
width: 100
height: 100
color: "red"
}
}
}
I suspect that if you don't set a size on the Item on which you want to enable the layer (layer.enabled: true), it will have a size of 0. Hence the offscreen buffer has a size of 0.
As a side note, this works without layer, because the clip property of an Item by default is set to false. So it won't clip to the bounds of its parent.
I have a QML Controls 2 SplitView and a redefined handle, which works well, but I want detect mouse release event on the handler, so I could collapse the SplitView under a certain threshold of width. Adding a MouseArea on top of the existing handle will absorb drag events, so I'm unable to move the handlebar. Any idea how could I gather the mouse release event, or any other solution which solves this problem?
Alright, I have created an example application. As you can see in this example, my MouseArea is marked with yellow and collapses the right view programmatically when double clicked, which is nice, but I also want to drag the handlebar and upon mouse release under a certain width threshold I want to collapse the view as well. The black part of the handlebar where my MouseArea is not covering the handlebar, responds to drag, but since there is no signal I can gather from it, the width threshold already set shouldCollapse boolean property, so the view won't update. Probably I could solve this issue with a timer, but I need a more sophisticated solution.
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 800
height: 400
visible: true
SplitView {
id: splitView
anchors.fill: parent
orientation: Qt.Horizontal
function toggleCollapse() { collapsibleRect.shouldCollapse = !collapsibleRect.shouldCollapse }
handle: Rectangle {
implicitWidth: 20
implicitHeight: 20
color: "black"
MouseArea {
anchors.centerIn: parent
width: parent.width
height: parent.height / 2
onDoubleClicked: splitView.toggleCollapse()
Rectangle {
anchors.fill: parent
color: "yellow"
Text {
anchors.centerIn: parent
text: "Double click to collapse"
rotation: 90
}
}
}
}
Rectangle {
id: mainRect
color: "green"
SplitView.fillWidth: true
Text {
anchors.centerIn: parent
font.pixelSize: 24
text: "Main scene"
}
}
Rectangle {
id: collapsibleRect
property bool shouldCollapse: false
SplitView.preferredWidth: shouldCollapse ? 0 : 300
color: "purple"
clip: true
onWidthChanged: {
if(width < 200) shouldCollapse = true
else shouldCollapse = false
}
Text {
anchors.centerIn: parent
rotation: parent.shouldCollapse ? 90 : 0
font.pixelSize: 24
text: parent.shouldCollapse ? "SHOULD BE COLLAPSED" : "NOT COLLAPSED"
Behavior on rotation { NumberAnimation { duration: 100 } }
}
}
}
}
I had a similar problem and was able to solve it thanks to the hint of #Ponzifex that the SplitView's resizing property will be set to true as soon as the handle is clicked. Using a Timer I managed to detect whether the handle was quickly pressed twice in a row.
SplitView {
id: view
...
handle: Rectangle {
...
}
//============================================================
// double click behavior
Timer {
id: doubleClickTimer
interval: 300 // number of ms between clicks that should be considered a double click
}
property bool doubleClicked: false
// `resizing` will be set to true even if the handle is just pressed
onResizingChanged: {
if (view.resizing) {
if (!doubleClickTimer.running) {
doubleClickTimer.start();
return;
}
view.doubleClicked = true;
} else {
if (view.doubleClicked) {
// do any manual resizing in here
view.doubleClicked = false;
}
}
}
}
It is important to note, however, that it is only possible to resize the contents of a SplitView when resizing is false. That's why I need to have the doubleClicked helper property.
Add this to MouseArea:
onPressed: {
mouse.accepted = (mouse.flags & Qt.MouseEventCreatedDoubleClick);
}
propagateComposedEvents: true
cursorShape: Qt.SplitHCursor
I have a row for a listview delegate with buttons on it. On click of a button, i need a dialog to open just below that button. I tried mapToItem property and partially succeeded but this listview is scrollable and on scrolling the dialog stays in its initial position. Unsure of how to get it working. Also, new to posting questions. Kindly ignore if I am being vague and help me out.
The dialog i want to open is placed outside of this delegate. I have provided a short outline of my code.
Listview{
delegate: Row{
Button1{
}
Button2{
id: button2Id
onCheckedChanged{
var coords = button2Id.mapToItem(null,0,0)
dialogId.x = coords.x
dialogId.y= coords.y
dialogId.visible = true
}
}
}
}
//dialog rect outside of my listview
Rectangle{
id: dialogId
}
You could add the dialog to the highlight item of the list. I have modified your example a little so that I could test it. I encapsulated your Rectangle in an Item because ListView controls the size and position of the root object of the highlight. The Rectangle then just has to be anchored to the bottom of that Item.
ListView {
id: lv
width: 200
height: parent.height
model: 50
spacing: 1
currentIndex: -1
delegate: Row {
spacing: 1
height: 40
Button {
text: index
}
Button {
id: button2Id
text: ">"
onClicked: {
lv.currentIndex = index;
}
}
}
highlight: Item { // ListView controls the size/pos of this Item
z: 1
Rectangle {
id: dialogId
anchors.top: parent.bottom // Anchor to bottom of parent
width: 200
height: 100
color: "red"
}
}
}
UPDATE:
Here is a way to keep the dialog directly under the button without calculating margins. I put it in a Loader so that each item in the list doesn't always carry the whole dialog around with it. It might make a performance difference.
The ugly part of this solution is the z-ordering. Each item in the list is drawn after the one that comes sequentially before it. (I'm not actually sure if that's even guaranteed.) That means the dialog gets drawn underneath any item that comes after it in the list. I was able to get around that by changing the z value of each item in the list to be less than the item before it.
ListView {
id: lv
width: 200
height: parent.height
model: 50
spacing: 1
currentIndex: -1
delegate: Row {
z: lv.count - index // <<- z-value fix
spacing: 1
height: 40
Button {
text: index
}
Button {
id: button2Id
text: ">"
onClicked: {
lv.currentIndex = index;
}
Loader {
anchors.top: parent.bottom
asynchronous: true
sourceComponent: (index === lv.currentIndex) ? dialogComp : null
}
}
}
}
Component {
id: dialogComp
Rectangle {
id: dialogId
width: 200
height: 100
color: "red"
}
}
I am using a ComboBox in QML and when populated with a lot of data it exceeds my main windows bottom boarder. From googling I have learned that the drop-down list of a ComboBox is put on top of the current application window and therefore it does not respect its boundaries.
Ideally I would want the ComboBox to never exceed the main applications boundary, but I can not find any property in the documentation.
A different approach would be to limit the number of visible items of the drop-down list so that it do not exceed the window limits for a given window geometry. I was not able to find this in the documentation either and I have run out of ideas.
Take a look to the ComboBox source code, the popup is of a Menu type and it doesn't have any property to limit its size. Moreover, the z property of the Menu is infinite, i.e. it's always on top.
If you Find no way but to use the ComboBox of Qt you can create two models one for visual purpose, I will call it visual model, you will show it in your ComboBox and the complete one , it will be the reference model. Items count in your VisualModel wil be equal to some int property maximumComboBoxItemsCount that you declare . you'll need o find a way that onHovered find the index under the mouse in the visualmodel if it's === to maximumComboBoxIemsCount you do visualModel.remove(0) et visualModel.add(referenceModel.get(maximum.. + 1) and you'll need another property minimumComboBoxIemsCount, same logic but for Scroll Up , I dont know if it will work. but it's an idea
I think there is no solution using the built-in component and you should create your own comboBox. You can start from the following code.
ComboBox.qml
import QtQuick 2.0
Item {
id: comboBox
property string initialText
property int maxHeight
property int selectedItem:0
property variant listModel
signal expanded
signal closed
// signal sgnSelectedChoice(var choice)
width: 100
height: 40
ComboBoxButton {
id: comboBoxButton
width: comboBox.width
height: 40
borderColor: "#fff"
radius: 10
margin: 5
borderWidth: 2
text: initialText
textSize: 12
onClicked: {
if (listView.height == 0)
{
listView.height = Math.min(maxHeight, listModel.count*comboBoxButton.height)
comboBox.expanded()
source = "qrc:/Images/iconUp.png"
}
else
{
listView.height = 0
comboBox.closed()
source = "qrc:/Images/iconDown.png"
}
}
}
Component {
id: comboBoxDelegate
Rectangle {
id: delegateRectangle
width: comboBoxButton.width
height: comboBoxButton.height
color: "#00000000"
radius: comboBoxButton.radius
border.width: comboBoxButton.borderWidth
border.color: comboBoxButton.borderColor
Text {
color: index == listView.currentIndex ? "#ffff00" : "#ffffff"
anchors.centerIn: parent
anchors.margins: 3
font.pixelSize: 12
text: value
font.bold: true
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.height = 0
listView.currentIndex = index
comboBox.selectedItem = index
tools.writePersistence(index,5)
comboBoxButton.text = value
comboBox.closed()
}
}
}
}
ListView {
id: listView
anchors.top: comboBoxButton.bottom
anchors.left: comboBoxButton.left
width: parent.width
height: 0
clip: true
model: listModel
delegate: comboBoxDelegate
currentIndex: selectedItem
}
onClosed: comboBoxButton.source = "qrc:/Images/iconDown.png"
Component.onCompleted: {
var cacheChoice = tools.getPersistence(5);
listView.currentIndex = tools.toInt(cacheChoice)
selectedItem = listView.currentIndex
comboBoxButton.text = cacheModel.get(selectedItem).value
}
}
ComboBoxButton.qml
import QtQuick 2.0
Item {
id: container
signal clicked
property string text
property alias source : iconDownUp.source
property string color: "#ffffff"
property int textSize: 12
property string borderColor: "#00000000"
property int borderWidth: 0
property int radius: 0
property int margin: 0
Rectangle {
id: buttonRectangle
anchors.fill: parent
color: "#00000000"
radius: container.radius
border.width: container.borderWidth
border.color: container.borderColor
Image {
id: image
anchors.fill: parent
source: "qrc:/Images/buttonBackground.png"
Image {
id: iconDownUp
source: "qrc:/Images/iconDown.png"
sourceSize.height:20
sourceSize.width: 20
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
id:label
color: container.color
anchors.centerIn: parent
font.pixelSize: 10
text: container.text
font.bold: true
}
MouseArea {
id: mouseArea;
anchors.fill: parent
onClicked: {
container.clicked()
buttonRectangle.state = "pressed"
startTimer.start()
}
}
Timer{
id:startTimer
interval: 200
running: false;
repeat: false
onTriggered: buttonRectangle.state = ""
}
states: State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges { target: image; scale: 0.7 }
PropertyChanges { target: label; scale: 0.7 }
}
transitions: Transition {
NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
}
}
}
I've used it in some software of mine, hence it is possible that It could not work "out of the box". I use it like this:
ComboBox{
id:cacheChoice
initialText: "None"
anchors.top: baseContainer.top
anchors.topMargin: 2
anchors.right: baseContainer.right
maxHeight: 500
listModel: cacheModel
onExpanded: {
cacheChoice.height = 500
}
onClosed: {
cacheChoice.height = 20
}
}
In case you are working with ComboBox from Qt Quick Controls 2, here's the source code for it:
https://github.com/qt/qtquickcontrols2/blob/5.12/src/imports/controls/ComboBox.qml
Based on that, this override of the behavior works to limit the height to something reasonable:
myComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
return Math.min(250, myComboBox.popup.contentItem.contentHeight);
});
It is possible to access the hidden MenuStyle within the ComboBoxStyle component. There you can use all the things and hidden things you have within a MenuStyle, including its maximum height.
The thing looks roughly like this.
Not pretty but it works well enough.
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
ComboBox {
id: comboBox
style: ComboBoxStyle {
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: 400
__menuItemType: "comboboxitem" //not 100% sure if this is needed
}
}
As it came up resonantly in our team, here is a updated version of the idea shown above. The new version restricts the size automatically to the size of your application.
ComboBox {
id: root
style: ComboBoxStyle {
id: comboBoxStyle
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: Math.max(55, //min value to keep it to a functional size even if it would not look nice
Math.min(400,
//limit the max size so the menu is inside the application bounds
comboBoxStyle.control.Window.height
- mapFromItem(comboBoxStyle.control, 0,0).y
- comboBoxStyle.control.height))
__menuItemType: "comboboxitem" //not 100% sure if this is needed
} //Component __dropDownStyle: MenuStyle
} //style: ComboBoxStyle
} //ComboBox
I have QtQuick 1.0
I use the following code:
Rectangle {
Component {
id: appDelegate
MouseArea{
id:myMouseArea
hoverEnabled: true
onClicked:{
onClicked: load.source = page;
}
}
Loader {
id: load
}
}
GridView {
id: view
// I am unable to access myMouseArea here.
highlight: myMouseArea.containsMouse ? appHighlight : !appHighlight
delegate: appDelegate
}
}
It gives me the following error:
ReferenceError: Can't find variable: myMouseArea
/usr/lib/i386-linux-gnu/qt4/bin/qmlviewer exited with code 0
I don't know if the details I provided are sufficient, please let me know if theres anything else I am missing.
I am using this code as an example:
http://docs.knobbits.org/qt4/declarative-modelviews-gridview-qml-gridview-example-gridview-example-qml.html
You cannot access myMouseArea because it's created inside delegate context. You cannot access delegate other then currentItem. But you can freely access view inside the context of delegate, to set currentIndex to attached property index.
This is a corrected code:
Rectangle {
width: 360
height: 360
Component { // It must be a component, if we want use it as delegate
id: appDelegate
// its not possible to have more than one element inside component
Rectangle
{
// need to set size of item, anchors wont work here
// could use view.cellWidth and view.cellHeight to keep it DRY
width: 96
height: 66
color: "green" // color only to see the place of MouseArea
MouseArea {
id:myMouseArea
anchors.fill: parent // this setup the size to whole rectangle
// it this item have the size 0,0 it will simple do not work
hoverEnabled: true
onEntered: {
// we know the mouse is inside this region
// setting this value will show the highlight rectangle
view.currentIndex = index;
}
onClicked:{
onClicked: load.source = page;
}
}
Loader {
// this is not needed but it's wise to not keep zero size
anchors.fill: parent
id: load
}
}
}
GridView {
id: view
// the size of GridView must be set,
// as otherwise no delegate will not show
anchors.fill: parent
anchors.margins: 5
cellWidth: 100
cellHeight: 70
// Rectangle will act as a border.
// Size and position is set by GridView
// to the size and position of currentItem.
// This is no a item, this makes a Component
// as highlight property needs one.
// You can create a Component like appDelegate.
highlight : Rectangle {
border.width: 2
border.color: "blue"
}
// some ListModel to setup the page variable inside delegate context
model: ListModel {
ListElement { page: "test1.qml"; }
ListElement { page: "test2.qml"; }
ListElement { page: "test3.qml"; }
}
delegate: appDelegate
}
}