Выбор времени в QML
Мне нужно дать пользователю возможность выбрать дату и время в приложении QML. Для дат выбора есть Calendar
в элементах управления QtQuick.
Я не нашел аналогичного элемента управления, чтобы пользователь мог выбрать время суток.
В Интернете есть несколько примеров, таких как Grog или Harmattan.
Я предполагаю, однако, что они не интегрируются с внешним видом и чувствуют, что другие QtQuick Controls делают.
Есть ли стандартный подход, о котором я не знаю, хорошие альтернативы, с которыми я не сталкивался, или рекомендации по выбору?
Ответы
Ответ 1
Так как Qt 5.5, так называемый Qt Quick Enterprise Controls будет доступен также в издании сообщества Qt под названием Qt Quick Extras. Среди прочих, Tumbler
кажется приемлемым решением для ваших требований: вы можете легко настроить два столбца: один для часов и один для мин.
Если вы по-прежнему интересуетесь круговым выбором (или хотите реализовать свой собственный тумблер), вы можете использовать разные маршруты, например, создать свой собственный компонент, наследующий от QQuickItem
или QQuickPaintedItem
или используя пользовательское представление PathView
. Последний случай я рассмотрю в этом ответе. Для примера о создании пользовательских компонентов обратитесь к приведенным ссылкам.
Ссылаясь на документацию PathView
:
В представлении есть модель, которая определяет отображаемые данные, и делегат, который определяет, как данные должны отображаться. Делегат создается для каждого элемента на пути. Элементы могут перемещаться по пути.
Следовательно, путь определяет способ размещения элементов на экране, даже круговым способом. Путь может быть построен с помощью типа Path
, т.е. Последовательности сегментов пути разного типа. PathArc
- это тот, который нас интересует, поскольку он обеспечивает желаемую округлую форму.
В следующем примере эти элементы используются для определения кругового выбора времени. Каждый путь строится путем использования currentIndex
делегата: целое число используется как модель для PathView
- 12
для представления часов и 6
для представления минут соответственно. Текст делегатов генерируется путем использования прикрепленного свойства index
и манипулирования им для генерации часов и 10-минутных интервальных значений (см. Делегаты Text
). Наконец, текст текущего элемента (т.е. currentItem
) привязан к метке времени в центре окна: при изменении currentIndex
и currentItem
также обновляется метка.
Общий компонент выглядит следующим образом:
![enter image description here]()
highlight
компоненты (синие и зеленые круги) используются для графического представления времени редактирования: при видимом времени можно редактировать время, т.е. можно выбрать другой Item
пути. Переключение между обычным и режимом редактирования происходит путем нажатия метки времени в центре.
В режиме редактирования пользователь может просто навести различные значения часов/минут, чтобы выбрать их. Если щелкнуть вновь выбранный час/минуту, редактирование для этого конкретного PathView
отключено, и соответствующий кружок подсветки исчезнет.
Этот код - это просто пример игрушек, который поможет вам понять, для чего можно использовать PathView
. Можно сделать несколько улучшений, например. анимации, лучшее позиционирование чисел, подробное представление минут, хороший фон и так далее. Однако они выходят за рамки w.r.t. вопрос и не рассматривался.
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
import QtQuick.Layouts 1.1
Window {
visible: true
width: 280; height: 280
RowLayout { // centre time label
anchors.centerIn: parent
Text {
id: h
font.pixelSize: 30
font.bold: true
text: outer.currentItem.text
}
Text {
id: div
font.pixelSize: 30
font.bold: true
text: qsTr(":")
}
Text {
id: m
font.pixelSize: 30
font.bold: true
text: inner.currentItem.text
}
MouseArea {
anchors.fill: parent
onClicked: outer.choiceActive = inner.choiceActive = !outer.choiceActive
}
}
PathView { // hours path
id: outer
property bool pressed: false
model: 12
interactive: false
highlightRangeMode: PathView.NoHighlightRange
property bool choiceActive: false
highlight: Rectangle {
id: rect
width: 30 * 1.5
height: width
radius: width / 2
border.color: "darkgray"
color: "steelblue"
visible: outer.choiceActive
}
delegate: Item {
id: del
width: 30
height: 30
property bool currentItem: PathView.view.currentIndex == index
property alias text : textHou.text
Text {
id: textHou
anchors.centerIn: parent
font.pixelSize: 24
font.bold: currentItem
text: index + 1
color: currentItem ? "black" : "gray"
}
MouseArea {
anchors.fill: parent
enabled: outer.choiceActive
onClicked: outer.choiceActive = false
hoverEnabled: true
onEntered: outer.currentIndex = index
}
}
path: Path {
startX: 200; startY: 40
PathArc {
x: 80; y: 240
radiusX: 110; radiusY: 110
useLargeArc: false
}
PathArc {
x: 200; y: 40
radiusX: 110; radiusY: 110
useLargeArc: false
}
}
}
PathView { // minutes path
id: inner
property bool pressed: false
model: 6
interactive: false
highlightRangeMode: PathView.NoHighlightRange
property bool choiceActive: false
highlight: Rectangle {
width: 30 * 1.5
height: width
radius: width / 2
border.color: "darkgray"
color: "lightgreen"
visible: inner.choiceActive
}
delegate: Item {
width: 30
height: 30
property bool currentItem: PathView.view.currentIndex == index
property alias text : textMin.text
Text {
id: textMin
anchors.centerIn: parent
font.pixelSize: 24
font.bold: currentItem
text: index * 10
color: currentItem ? "black" : "gray"
}
MouseArea {
anchors.fill: parent
enabled: inner.choiceActive
onClicked: inner.choiceActive = false
hoverEnabled: true
onEntered: inner.currentIndex = index
}
}
path: Path {
startX: 140; startY: 60
PathArc {
x: 140; y: 220
radiusX: 40; radiusY: 40
useLargeArc: false
}
PathArc {
x: 140; y: 60
radiusX: 40; radiusY: 40
useLargeArc: false
}
}
}
// to set current time!
onVisibleChanged: {
var d = new Date();
outer.currentIndex = d.getUTCHours() % 12
inner.currentIndex = d.getMinutes() / 10
}
}
Ответ 2
я думаю, что мое собственное средство выбора времени хорошо, вы можете расширить его так, как вам нравится в персидской ориентации, вам нужно немного поменяться местами или использовать какое-либо зеркальное отображение макетов:
UButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
Button {
id:root
Universal.accent: Universal.Cobalt
Universal.foreground: "white"
highlighted: true
font.family: "B Nazanin"
font.pointSize: 12
}
UCard.qml
import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
import QtGraphicalEffects 1.0
Item{
property alias radius : morakhasiRect.radius
property alias color : morakhasiRect.color
implicitWidth: 150
implicitHeight: 150
Rectangle{
anchors.rightMargin: 1
anchors.leftMargin: 1
anchors.bottomMargin: 1
anchors.topMargin: 1
id:morakhasiRect
anchors.fill: parent
color: "#f5f5f5"
}
DropShadow {
anchors.fill: morakhasiRect
radius: 9.0
samples: 17
color: "#80000000"
source: morakhasiRect
}
}
URect.qml
Rectangle{
color: "transparent"
border.color: Universal.color(Universal.Cobalt)
border.width: 1
}
UTumbler.qml
import QtQuick 2.0
import QtQuick.Controls.Universal 2.4
import QtQuick.Controls 2.4
Tumbler{
id:hourSpin
wrap: false
delegate: Text{
font.pointSize: 12
text: modelData
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
opacity: 1.0 - Math.abs(Tumbler.displacement) / (hourSpin.visibleItemCount / 2)
}
Rectangle {
anchors.horizontalCenter: hourSpin.horizontalCenter
y: hourSpin.height * 0.4
width: 40
height: 1
color: Universal.color(Universal.Cobalt)
}
Rectangle {
anchors.horizontalCenter: hourSpin.horizontalCenter
y: hourSpin.height * 0.6
width: 40
height: 1
color: Universal.color(Universal.Cobalt)
}
}
UTimeDialog
import QtQuick 2.0
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
Item{
id:root
property alias hour : hourSpin.currentIndex
property alias minute : minuteSpin.currentIndex
signal open
signal close
signal accepted
signal rejected
visible: element.opened
onOpen: element.open()
onClose: element.close()
implicitWidth: 200
implicitHeight: 200
Dialog {
id: element
modal: true
width: parent.width
height: parent.height
padding: 5
margins: 5
background: Item{
}
onAccepted: {
root.accepted()
}
onRejected: {
root.rejected()
}
contentItem: UCard{
anchors.fill: parent
radius: 10
}
Column{
id: column
spacing: 30
anchors.centerIn: parent
Row{
id: row
spacing: 20
anchors.horizontalCenter: parent.horizontalCenter
Column{
id: column1
spacing: 15
height: 80
width: 50
clip:true
UTumbler{
id:hourSpin
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
model: 24
}
}
Text{
text: ":"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
font.family: "B Nazanin"
}
Column{
id: column2
spacing: 15
height: 80
width: 50
clip:true
UTumbler{
id:minuteSpin
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
model: 60
}
}
}
Row{
anchors.horizontalCenter: parent.horizontalCenter
spacing: 40
UButton{
text:"select"
onClicked: {
element.reject()
}
}
UButton{
text: "cancel"
onClicked: {
element.accept()
}
}
}
}
}
}
UIcoButton.qml
import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
import QtGraphicalEffects 1.0
FocusScope{
id:focusScope
signal clicked
property alias font : icoText.font.family
property alias icon : icoText.text
property alias size : icoText.font.pixelSize
property alias caption : captionTxt.text
property alias spacing : row.spacing
property string colorEnter :Universal.color(Universal.Cobalt)
property string colorExit :"#00171f"
property alias state: root.state
implicitWidth: captionTxt.text!= "" ? 100 : 35
implicitHeight: 40
Rectangle {
id: root
radius: 0
anchors.fill: parent
color: colorExit
state: "default"
focus: true
onFocusChanged: {
if(focus){
root.border.width = 1
root.border.color = Universal.color( Universal.Cobalt)
}
else{
root.border.width = 0
root.border.color = "transparent"
}
}
Row{
id: row
anchors.rightMargin: 5
anchors.leftMargin: 5
anchors.bottomMargin: 5
anchors.topMargin: 5
anchors.fill: parent
layoutDirection: Qt.RightToLeft
spacing: 15
Text {
id: icoText
text: ""
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 25
font.family: "fontawesome"
color: "white"
}
Text{
id:captionTxt
text: ""
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: icoText.font.pixelSize * 55 /100
font.family: "B Nazanin"
color: "white"
visible: text!= ""
}
}
InnerShadow {
id:shadow
anchors.fill: row
radius: 1.0
samples: 17
horizontalOffset: 1
color: colorExit
source: row
visible: false
}
// Glow {
// id:shadow
// anchors.fill: row
// radius: 6
// samples: 25
// color: "white"
// source: row
// visible: false
// }
MouseArea{
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: {
if(root.state == "default")
root.color = colorEnter
else{
icoText.color = colorEnter
captionTxt.color = colorEnter
}
}
onExited: {
if(root.state == "default")
root.color = colorExit
else{
icoText.color = colorExit
captionTxt.color = colorExit
}
}
onPressed: {
shadow.visible = true
}
onReleased: {
shadow.visible = false
}
onClicked: {
focusScope.clicked()
}
}
states: [
State {
name: "transparent"
PropertyChanges {
target: root
color:"transparent"
}
PropertyChanges {
target: icoText
color:colorExit
}
PropertyChanges {
target: captionTxt
color:colorExit
}
},
State{
name: "default"
PropertyChanges {
target: root
color:"#00171f"
}
PropertyChanges {
target: icoText
color:"white"
}
PropertyChanges {
target: captionTxt
color:"white"
}
}
]
}
}
UTimePicker
import QtQuick 2.4
import QtQuick.Controls 2.4
import QtQuick.Controls.Universal 2.4
Item {
id: scope
clip: true
QtObject{
id:variables
property var time: ({hour: 0, minute: 0})
onTimeChanged: {
refreshDialogTime()
}
}
signal changed
property alias caption : captionTxt.text
property size size : Qt.size(30,70)
property string splitter : ":"
property alias spacing : row.spacing
Component.onCompleted: {
var q = new Date()
var curtime = q.toLocaleTimeString().substring(0,5);
if(splitter != ":"){
curtime.replace(':',splitter)
}
var vars = curtime.split(':')
setTime(vars[0],vars[1])
refreshDialogTime()
}
function refreshDialogTime(){
dialog.hour = variables.time.hour
dialog.minute = variables.time.minute
}
function getTime(){
return variables.time;
}
function setTimeString(time){
textArea.text= time
}
function setTime(hour,minute){
var _hour = hour
if(_hour<10){
_hour = "0"+hour.toString()
}
else{
_hour = hour.toString()
}
var _minute = minute
if(_minute <10){
_minute = "0"+minute.toString()
}
else{
_minute = minute.toString()
}
var time = _hour+":"+_minute
textArea.text = time
}
implicitHeight: 50
implicitWidth: 200
Row{
id: row
width: parent.width
height: parent.height
spacing: 25
layoutDirection: Qt.RightToLeft
Text{
font.bold: true
id: captionTxt
font.pointSize: 12
horizontalAlignment: Text.AlignRight
anchors.verticalCenter: parent.verticalCenter
width: scope.size.width * scope.width /100 - scope.spacing/2
verticalAlignment: Text.AlignVCenter
font.family: "B Nazanin"
}
Item{
id: element
anchors.verticalCenter: parent.verticalCenter
height: parent.height
width: scope.size.height * scope.width /100 - scope.spacing/2
Rectangle{
id:backrec
height: parent.height
anchors.verticalCenter: parent.verticalCenter
width: parent.width
border.width: 1
border.color: "black"
TextField{
id:textArea
selectByMouse: true
anchors.verticalCenter: parent.verticalCenter
height: parent.height
rightPadding: 5
bottomPadding: 5
topPadding: 5
padding: 5
verticalAlignment: Text.AlignVCenter
onFocusChanged: {
if(focus){
captionTxt.color = Universal.color( Universal.Cobalt)
backrec.border.color = Universal.color( Universal.Cobalt)
}
else{
captionTxt.color = "black"
backrec.border.color = "black"
}
}
background: URect{
color: "transparent"
border.color: "black"
border.width: 0
}
onTextChanged: {
var _temp = text.split(splitter)
if(_temp.length>0){
variables.time.hour =_temp[0]==""?0: _temp[0]
variables.time.minute = _temp[1]==""?0:_temp[1]
}
changed()
}
placeholderText : "HH:mm"
anchors.right: parent.right
anchors.left: iconBtn.right
font.family: "B Nazanin"
font.pointSize: 12
inputMask: "99:99"
validator: RegExpValidator { regExp: /^([0-1\s]?[0-9\s]|2[0-3\s]):([0-5\s][0-9\s])$ / }
}
IcoButton{
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 2
id:iconBtn
caption: ""
size: 30
icon: "\uf017"
state: "transparent"
onClicked: {
textArea.focus = true
dialog.open()
}
}
}
}
}
UTimeDialog{
id:dialog
x:iconBtn.x
y:iconBtn.y+ scope.height
onAccepted: {
setTime(hour,minute)
}
}
}
пример
UTimePicker{
x: 285
width: 200
spacing: 15
size: Qt.size(35,65)
caption: "time"
onChanged: {
var i = getTime()
console.log(i.hour)
console.log(i.minute)
}
}
который выглядит так:
![ScreenShot2]()
для зеркалирования:
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
если кто-то заинтересовался, я мог бы поделиться библиотекой для этого