I am learning qml and i am trying to make an sample application that achieves the below use case.
Use Case : When user presses on button in qml scene
- Icon on button has to be updated.
- Color of the button has to updated (i am trying to use foreground from qml but unable to acheive).
- Count down timer has to be displayed on top of button.
Ex : Let's say when the Cam button is clicked, i wanted to update the icon from small circle to elliptical curve, change the color of Cam button and after color is applied i wanted to display a count down timer with some n value.
All the specified above expectation has to be applied only for Cam button that was rendered through Repeater model.
Attaching the code i tried
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window{
width: 640
height: 480
visible: true
color: "pink"
title: qsTr("Repeater Button Panel")
Body{
}
Test{
}
}
Body.qml
import QtQuick 2.0
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Item {
id:root
width: 640
height: 480
property color bgColor
signal repeaterbuttonpressed(int index,string buttonname)
Rectangle{
width: 250
height: 600
color: "beige"
border.color: "black"
}
ListModel{
id:listModel
ListElement{
name:"G"
}
ListElement{
name:"M"
}
ListElement{
name:"Mu"
}
ListElement{
name:"Cam"
}
ListElement{
name:"Im"
}
ListElement{
name:"Ph"
}
}
Flickable{
x:scrollBar.x
y:250
anchors.fill: parent
contentHeight: 2000
Grid{
id:scrollBar
rows:8
columns: 2
spacing: 10
Repeater{
model:listModel
Button{
id:btn
text:name
onClicked: {
repeaterbuttonpressed(index,name)
}
}
}
}
ScrollBar.vertical:ScrollBar{
id:vbar; active: true
}
}
}
Test.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Item {
Body{
id:bodyId
onRepeaterbuttonpressed: {
//Here i wanted to implement logic to update icon, color, display count down timer for Cam button
console.log("inside the on repeater button pressed",index,buttonname)
}
function myRepeaterbuttonpressed(index,buttonname)
{
console.log("myrepeaterbutton " buttonname)
console.log("myrepeaterbutton " index)
}
Component.onCompleted: {
repeaterbuttonpressed.connect(myRepeaterbuttonpressed)
}
}
}
Attaching button expectation image for better understanding, any suggestions on how to achieve the same would be much appreciated.
import QtQuick
import QtQuick.Controls
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
title: "Button with Timer"
ListModel {
id: fruitModel
ListElement {
name: "Apple"
colorValue: "crimson"
}
ListElement {
name: "Kiwi"
colorValue: "sienna"
}
ListElement {
name: "Banana"
colorValue: "gold"
}
}
Row {
spacing: 10
anchors.centerIn: parent
Repeater {
model: fruitModel
delegate: SpecialButton {
id: delegateRoot
required property string name
required property color colorValue
text: delegateRoot.name
backgroundColor: delegateRoot.colorValue
}
}
}
component SpecialButton: Button {
id: control
property color backgroundColor: "royalblue"
text: qsTr("Button")
Timer {
id: timer
property int count: 5
interval: 1000
running: false
repeat: true
onTriggered: {
--timer.count
if (timer.count === -1) {
timer.stop()
timer.count = 5
}
}
}
contentItem: Item {
anchors.fill: parent
Column {
anchors.centerIn: parent
Item {
width: 40
height: 40
Rectangle {
id: circle
property int xScale: timer.running ? 2 : 1
radius: circle.height
color: "royalblue"
border.color: "navy"
border.width: 1
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
transform: Scale {
origin.x: circle.width / 2
origin.y: circle.height / 2
xScale: circle.xScale
}
}
Text {
anchors.centerIn: parent
text: timer.count
visible: timer.running
color: "white"
}
}
Text {
text: control.text
font: control.font
color: timer.running ? "black" : "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 100
border.color: "navy"
border.width: 1
color: timer.running ? "darkorange" : control.backgroundColor
}
onClicked: timer.start()
}
}
CodePudding user response:
A suggested minimal changes to your code that approximate your criteria:
- added the
delegate
keyword to the Repeater - hard to explain, but it feels right to do so - introduce an inactive
Timer
property inside your Button - use property binding so that
Timer
automatically starts whencountdown === 5
and automatically stops whencountdown === 0
- use the
background
property of your Button to configure a custom layout - I use the
Frame
component to magically wrap my countdownText
component without specifying numbers for margins or padding - approximate an
ellipse
simply by using Frames with a background Rectangle appearance with rounded corners (seeradius: height / 2
) - I also increased the size of your buttons with
width: 100
andheight: 100
so that the background is rendered nicely
Repeater{
model:listModel
delegate: Button {
id: btn
width: 100
height: 100
property int countdown: 0
property Timer countdownTimer: Timer {
interval: 1000
running: countdown > 0
repeat: true
onTriggered: countdown--;
}
text: name
background: Rectangle {
color: countdownTimer.running ? "orange" : "royalblue"
Frame {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
visible: countdownTimer.running
background: Rectangle {
border.color: "black"
color: "royalblue"
radius: height / 2
}
Text {
text: countdown
}
}
}
onClicked: {
countdown = 5;
repeaterbuttonpressed(index,name)
}
}
You can use the Rectangle component to render either an ellipse or a circle. For a perfect circle, you set width === height
. For an ellipse you can set width > height
assuming that your ellipse grows horizontally, you can set radius: height / 2
. If the ellipse can stretch in either direction, you can generalize with radius: Math.min(width, height) / 2
.
Rectangle {
width: 50
height: 50
radius: height / 2
}