Time input field in QML - qt

I need to implement a TimeEdit(HH:MM:SS) field in QML similar to QTimeEdit in QT C++. In QML I didn't find TimeEdit and I have implemented the control similar to TimeEdit using TextField and if I add inputMask then the Regular expression is not at all validated, Is there any way that I can achieve this? Following is the code.
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Time Edit")
TextField{
id:textEditTD
text : ""
inputMethodHints: Qt.ImhDigitsOnly
inputMask: "dd:dd:dd; "
validator: RegExpValidator { regExp: /^([0-1]?[0-9]|2[0-3]):([0-5][0-9]):[0-5][0-9]$ / }
width:100
height:50
background:Rectangle{
color:"transparent"
border.color: "red"
border.width:2
radius:(width * 0.05)
}
}
}

I use the following combination of "round-down" QValidator-derived custom validator of time input (it is simple to add seconds filed):
#pragma once
#include <QTime>
#include <QValidator>
// almost equiv to RegExpValidator { regExp: /(([01][0-9])|(2[0-3])):([0-5][0-9])/ }
class TimeValidator
: public QValidator
{
Q_OBJECT
public :
explicit TimeValidator(QObject * const parent = Q_NULLPTR)
: QValidator{parent}
{ ; }
virtual
State validate(QString & input, int & pos) const Q_DECL_OVERRIDE
{
Q_UNUSED(pos);
const auto parts = input.splitRef(':');
if (parts.length() != 2) {
input = QStringLiteral("00:00");
} else {
const int hours = qBound(0, parts.first().toInt(), 23);
const int minutes = qBound(0, parts.last().toInt(), 59);
const QTime time{hours, minutes};
Q_ASSERT(time.isValid());
input = time.toString("hh:mm");
}
return Acceptable;
}
};
template< typename T >
int qmlRegisterClass(int versionMajor = 1, int versionMinor = 0)
{
const auto className = T::staticMetaObject.className();
return ::qmlRegisterType< T >(className, versionMajor, versionMinor, className);
}
// ...
qmlRegisterClass< TimeValidator >();
And TextFiled with inputMask:
import TimeValidator 1.0
TextField {
id: timePicker
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignHCenter
text: "00:00"
inputMask: "00:00;_"
validator: TimeValidator {}
inputMethodHints: Qt.ImhDigitsOnly
// placeholderText: "00:00" // for implicitWidth
// Layout.minimumWidth: implicitWidth
// Layout.fillWidth: true
}

I found two ways to implement this :
1) I made changes to Orient answer to meet my requirement and following is the change which works when backspace is pressed:
virtual
State validate(QString & input, int & pos) const Q_DECL_OVERRIDE
{
const QStringList parts = input.split(":");
if (parts.length() != 3) {
input = QStringLiteral("00:00:00");
}
else
{
int hours = 0;
int minutes = 0;
int seconds = 0;
//hours
if(parts[0].toInt() > 23){
hours = 23;
pos +=1; //Increment the position
}
else{
QString str = parts[0];
if(str.contains(" ")){
str.replace(" ","0");
}
hours = str.toInt();
}
// Minutes
if(parts[1].toInt() > 59){
minutes = 59;
pos +=1; //Increment the position
}
else{
QString str = parts[1];
if(str.contains(" ")){
str.replace(" ","0");
}
minutes = str.toInt();
}
//Seconds
if(parts[2].toInt() > 59){
seconds = 59;
pos +=1; //Increment the position
}
else{
QString str = parts[2];
if(str.contains(" ")){
str.replace(" ","0");
}
seconds = str.toInt();
}
const QTime time{hours, minutes,seconds};
Q_ASSERT(time.isValid());
input = time.toString("hh:mm:ss");
}
return Acceptable;
}
2) By just changing the inputMask and RegExp which is very easy:
inputMask: "99:99:99"
validator: RegExpValidator { regExp: /^([0-1\s]?[0-9\s]|2[0-3\s]):([0-5\s][0-9\s]):([0-5\s][0-9\s])$ / }

Related

"Unable to open file"

This is one of my first projects using QML: I started making a simple game character and a few rooms, and to make the walls'hitboxes I stored them all into a file and included a FileIO class I saw in this site, adapted to return the entire file instead of just one line.
The program, though, tells me it isn't able to open the file (I have put it in the project's directory)
//main.qml (ignore line 5)
import QtQuick 2.15
import QtQuick.Shapes 1.12
import FileIO 1.0
// Whats this? UwU a byzantiuwm?!?!!? UwU i wove byzantiuwm, especiwwawwy in the #memes channew wewe byzantiuwm is the tawk of ze town! OwO
Rectangle {
width: 639
height: 480
id: sus // I hate myself
color: "#00ff00"
Shape {
property string d: "S"
property int room: 1
id: player
x: xPos
y: yPos
z: 1
visible: true
property int xPos: 297
property int yPos: 219
parent: sus
width: 45
height: 45
focus: true
Keys.onPressed:
{
if(event.key === Qt.Key_Down){
yPos+=3; d="S"
}
if(event.key === Qt.Key_Up){
yPos-=3; d="N"
}
if(event.key === Qt.Key_Left){
xPos-=3; d="W"
}
if(event.key === Qt.Key_Right){
xPos+=3; d="E"
}
event.accepted = true;
}
ShapePath{
strokeColor: "transparent"
fillColor: "transparent"
PathLine{x:45 ;y: 0}
PathLine{x:45 ;y:45}
PathLine{x: 0 ;y:45}
}
Image {
id: sprite
parent: player
width: parent.width
height: parent.height
smooth: false
source: "Player/Player_" + player.d + ".png"
}
}
Image {
id: background
x: 0
y: 0
z: 0
width: 639
height: 480
visible: true
smooth: false
parent: sus
source: "Rooms/" + player.room + ".png"
fillMode: Image.Stretch
}
Shape {
id: wallHitBox
x: 0
y: 0
z: 50
width: sus.width
height: sus.height
//visible: false
FileIO{
id: fileReader
source: "Rooms/Hitboxes_or_something.txt"
onError: {console.log(msg)}
}
property var the: fileReader.read()
ShapePath{
strokeColor: "transparent"
fillColor: "blue" //"transparent"
PathSvg{
path: wallHitBox.the[player.room];
}
}
}
}
//fileio.cpp
#include "fileio.h"
#include <QFile>
#include <QTextStream>
FileIO::FileIO(QObject *parent) :
QObject(parent)
{
}
QVector<QString> FileIO::read()
{
if (mSource.isEmpty()){
emit error("source is empty");
return QVector<QString>();
}
QFile file(mSource);
QVector<QString> fileContent;
if ( file.open(QIODevice::ReadOnly) ) {
QString line;
QTextStream t( &file );
do {
line = t.readLine();
fileContent.resize(fileContent.size()+1);
fileContent[fileContent.size()] = line;
} while (!line.isNull());
file.close();
} else {
emit error("Unable to open the file");
return QVector<QString>();
}
return fileContent;
}
bool FileIO::write(const QString& data)
{
if (mSource.isEmpty())
return false;
QFile file(mSource);
if (!file.open(QFile::WriteOnly | QFile::Truncate))
return false;
QTextStream out(&file);
out << data;
file.close();
return true;
}
#define FILEIO_H
#include <QObject>
class FileIO : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString source
READ source
WRITE setSource
NOTIFY sourceChanged)
explicit FileIO(QObject *parent = 0);
Q_INVOKABLE QVector<QString> read();
Q_INVOKABLE bool write(const QString& data);
QString source() { return mSource; };
public slots:
void setSource(const QString& source) { mSource = source; };
signals:
void sourceChanged(const QString& source);
void error(const QString& msg);
private:
QString mSource;
};
#endif // FILEIO_H
Can someone please help me?

How can I use a MouseArea on a ShapePath in QML?

I'm currently learning how to use the Shapes in QML to draw more advanced components. I'm trying to create a button which looks like this :
When I try to apply a MouseArea over the Shape component, the MouseArea does not seem to be able to catch the events on the Shape. Here is my code :
import QtQuick 2.13
import QtQuick.Shapes 1.13
Item
{
Shape
{
id: myShape
ShapePath {
id: myButton
strokeWidth:3.114000082015991
strokeColor: "#000"
miterLimit:7
fillColor: "#ccc"
capStyle:ShapePath.RoundCap
PathSvg {
path: "M392.4,205.9a132.34,132.34,0,0,1,31.7,49.2H575.6a289.67,289.67,0,0,0-12.9-49.2Z"
}
}
}
MouseArea
{
id: myMouseArea
anchors.fill: myShape
enabled: true
hoverEnabled: true
onEntered: myButton.fillColor = "yellow"
onExited: myButton.fillColor = "green"
}
}
So my question is : is it possible to make a Shape/ShapePath clickable in the first place ? And if yes, how to do so ?
A similar question was asked here, but they just wanted a simple circle. Still, the non-accepted answer describes a masked mouse area that could be useful to you. It uses an image to define a masked area. It originally comes from a Qt example program.
maskedmousearea.cpp
MaskedMouseArea::MaskedMouseArea(QQuickItem *parent)
: QQuickItem(parent),
m_pressed(false),
m_alphaThreshold(0.0),
m_containsMouse(false)
{
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
void MaskedMouseArea::setPressed(bool pressed)
{
if (m_pressed != pressed) {
m_pressed = pressed;
emit pressedChanged();
}
}
void MaskedMouseArea::setContainsMouse(bool containsMouse)
{
if (m_containsMouse != containsMouse) {
m_containsMouse = containsMouse;
emit containsMouseChanged();
}
}
void MaskedMouseArea::setMaskSource(const QUrl &source)
{
if (m_maskSource != source) {
m_maskSource = source;
m_maskImage = QImage(QQmlFile::urlToLocalFileOrQrc(source));
emit maskSourceChanged();
}
}
void MaskedMouseArea::setAlphaThreshold(qreal threshold)
{
if (m_alphaThreshold != threshold) {
m_alphaThreshold = threshold;
emit alphaThresholdChanged();
}
}
bool MaskedMouseArea::contains(const QPointF &point) const
{
if (!QQuickItem::contains(point) || m_maskImage.isNull())
return false;
QPoint p = point.toPoint();
if (p.x() < 0 || p.x() >= m_maskImage.width() ||
p.y() < 0 || p.y() >= m_maskImage.height())
return false;
qreal r = qBound<int>(0, m_alphaThreshold * 255, 255);
return qAlpha(m_maskImage.pixel(p)) > r;
}
void MaskedMouseArea::mousePressEvent(QMouseEvent *event)
{
setPressed(true);
m_pressPoint = event->pos();
emit pressed();
}
void MaskedMouseArea::mouseReleaseEvent(QMouseEvent *event)
{
setPressed(false);
emit released();
const int threshold = qApp->styleHints()->startDragDistance();
const bool isClick = (threshold >= qAbs(event->x() - m_pressPoint.x()) &&
threshold >= qAbs(event->y() - m_pressPoint.y()));
if (isClick)
emit clicked();
}
void MaskedMouseArea::mouseUngrabEvent()
{
setPressed(false);
emit canceled();
}
void MaskedMouseArea::hoverEnterEvent(QHoverEvent *event)
{
Q_UNUSED(event);
setContainsMouse(true);
}
void MaskedMouseArea::hoverLeaveEvent(QHoverEvent *event)
{
Q_UNUSED(event);
setContainsMouse(false);
}
Usage in QML:
import Example 1.0
MaskedMouseArea {
id: moonArea
anchors.fill: parent
alphaThreshold: 0.4
maskSource: moon.source
}
Register the custom item:
qmlRegisterType<MaskedMouseArea>("Example", 1, 0, "MaskedMouseArea");

QSqlQueryModel TableView custom delegate

I have a QSqlQueryModel and a TableView to display the data from the model. The code and output data results are fine. But, I just want to display an image in front of each row in the TableView. However, with my current QML code, the image is repeated along with the elements in my table columns. I have added example screenshots for reference
Current Output (Screenshot)
What I want
My Code is as below
Test.qml
import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
Page {
id : somepageid
TableView{
id: testTable
model: QueryModel
height: 500
width: 400
delegate:
Row{
Image {
id: statusImg
height: 18
width: 18
source: "../../../Images/icons/tick.png"
}
Text {
text: display
}
}
}
}
QueryModel.cpp
#include "querymodel.h"
QueryModel::QueryModel(QObject *parent): QSqlQueryModel(parent)
{
}
void QueryModel::setQuery(const QString &query, const QSqlDatabase &db)
{
QSqlQueryModel::setQuery(query, db);
generateRoleNames();
}
void QueryModel::setQuery(const QSqlQuery &query)
{
QSqlQueryModel::setQuery(query);
generateRoleNames();
}
QVariant QueryModel::data(const QModelIndex &index, int role) const
{
QVariant value;
if(role < Qt::UserRole) {
value = QSqlQueryModel::data(index, role);
}
else {
int columnIdx = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
return value;
}
QHash<int, QByteArray> QueryModel::roleNames() const
{
return {{Qt::DisplayRole, "display"}};
}
void QueryModel::callSql()
{
QSqlDatabase dbMysql = QSqlDatabase::database();
this->setQuery(this->tmpSql(), dbMysql);
}
QString QueryModel::tmpSql() const
{
return m_tmpSql;
}
void QueryModel::setTmpSql(QString tmpSql)
{
if (m_tmpSql == tmpSql)
return;
m_tmpSql = tmpSql;
emit tmpSqlChanged(m_tmpSql);
}
void QueryModel::generateRoleNames()
{
m_roleNames.clear();
for( int i = 0; i < record().count(); i ++) {
m_roleNames.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
}
}
A possible solution is to use a Loader:
// ...
delegate: Row{
Loader{
active: model.column === 0
sourceComponent: Image {
id: statusImg
height: 18
width: 18
source: "../../../Images/icons/tick.png"
}
}
Text {
text: model.display
}
}
// ...

Animating MapQuickItem in QML on position updates

I have an QAbstractListModel object that maintains a list of items to show on a map. The position of these items changes every few seconds and it is easy to calculate a pretty accurate position 60 seconds in the future. What I am trying to do is to set the item's position when it gets a new position (that part works well) and then to immediately move the item toward the calculated future position.
The code without animation looks like this and it works fine:
Component {
id: drawTarget
MapQuickItem {
id: marker
coordinate: data.coords
sourceItem: Item {
id: item
...
The data object has a property which returns the estimated position of the item 60 seconds in the future, so I tried this:
Component {
id: drawTarget
MapQuickItem {
id: marker
coordinate: data.coords
CoordinateAnimation {
id:anim
property: "coordinate"
}
onCoordinateChanged: {
anim.stop()
anim.from = data.coords
anim.to = data.coordsIn60sec
anim.duration = 60000
anim.start()
}
sourceItem: Item {
id: item
...
But although the object's position is updated properly at each position update, the animation toward the future estimated position doesn't work at all.
How would one go about doing something like this?
In its code, it makes a binding coordinate: data.coords that states that "coordinate" takes the value of "coords" but at the same time says that "coordinate" depends on the animation, isn't it contradictory? Well, it is contradictory.
The idea is not to do the binding coordinate: data.coords but to update the property only through the animation.
The following code is a workable example:
main.qml
import QtQuick 2.14
import QtQuick.Window 2.14
import QtLocation 5.6
import QtPositioning 5.6
Window {
visible: true
width: 640
height: 480
Plugin {
id: mapPlugin
name: "osm"
}
Map {
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 10
MapItemView{
model: datamodel
delegate: MapQuickItem{
id: item
// begin configuration
property var position: model.position
property var nextposition: model.nextposition
onPositionChanged: restart();
onNextpositionChanged: restart();
function restart(){
anim.stop()
anim.from = position
anim.to = nextposition
anim.start()
}
CoordinateAnimation {
id: anim
target: item
duration: 60 * 1000
property: "coordinate"
}
// end of configuration
anchorPoint.x: rect.width/2
anchorPoint.y: rect.height/2
sourceItem: Rectangle{
id: rect
color: "green"
width: 10
height: 10
}
}
}
}
}
datamodel.h
#ifndef DATAMODEL_H
#define DATAMODEL_H
#include <QAbstractListModel>
#include <QGeoCoordinate>
#include <QTimer>
#include <random>
#include <QDebug>
struct Data{
QGeoCoordinate position;
QGeoCoordinate nextposition;
};
static QGeoCoordinate osloposition(59.91, 10.75); // Oslo;
class DataModel : public QAbstractListModel
{
Q_OBJECT
QList<Data> m_datas;
public:
enum PositionRoles {
PositionRole = Qt::UserRole + 1,
NextPositionRole
};
explicit DataModel(QObject *parent = nullptr)
: QAbstractListModel(parent)
{
init();
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override{
return parent.isValid() ? 0: m_datas.count();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override{
if (!index.isValid() || index.row() < 0 || index.row() >= m_datas.count())
return QVariant();
const Data &data = m_datas[index.row()];
if (role == PositionRole)
return QVariant::fromValue(data.position);
else if (role == NextPositionRole)
return QVariant::fromValue(data.nextposition);
return QVariant();
}
QHash<int, QByteArray> roleNames() const override{
QHash<int, QByteArray> roles;
roles[PositionRole] = "position";
roles[NextPositionRole] = "nextposition";
return roles;
}
private:
void init(){
for(int i=0; i< 10; ++i){
Data data;
data.position = osloposition;;
data.nextposition = data.position;
m_datas << data;
}
QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &DataModel::updateData);
timer->start(60 * 1000);
updateData();
}
void updateData(){
qDebug() << __PRETTY_FUNCTION__;
static std::default_random_engine e;
static std::uniform_real_distribution<> dis(-.1, .1);
for(int i=0; i < m_datas.count(); ++i){
Data & data = m_datas[i];
QModelIndex ix = index(i);
data.position = data.nextposition;
data.nextposition = QGeoCoordinate(osloposition.latitude() + dis(e),
osloposition.longitude() + dis(e));
Q_EMIT dataChanged(ix, ix, {PositionRole, NextPositionRole});
}
}
};
#endif // DATAMODEL_H
In the following link is the complete example

QT QML import ListModel from C++ to QML

How can i change the model of a PathView with c++ code ?
i add an objectName to my pathView to find it, then i change the property like this, but when i do that, my list is empty :
QDeclarativeItem *listSynergie = myClass.itemMain->findChild<QDeclarativeItem*> ("PathViewInscription");
listSynergie->setProperty("model", QVariant::fromValue(dataList));
My data list is fill like this :
QList<QObject*> dataList;
dataList.append(new synergieListObject("Item 1", "1",false));
dataList.append(new synergieListObject("Item 2", "2",true));
dataList.append(new synergieListObject("Item 3", "3",false));
dataList.append(new synergieListObject("Item 4", "4",false));
This is the code of my PathView :
PathView {
objectName: "PathViewInscription"
Keys.onRightPressed: if (!moving) { incrementCurrentIndex(); console.log(moving) }
Keys.onLeftPressed: if (!moving) decrementCurrentIndex()
id: view
anchors.fill: parent
highlight: Image { source: "../spinner_selecter.png"; width: view.width; height: itemHeight+4; opacity:0.3}
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
dragMargin: view.width/2
pathItemCount: height/itemHeight
path: Path {
startX: view.width/2; startY: -itemHeight/2
PathLine { x: view.width/2; y: view.pathItemCount*itemHeight + itemHeight }
}
}
and the code of ListModel :
ListModel {
id: appModel
ListElement { label: "syn1"; value: "1"; selected:false}
ListElement { label: "syn2"; value: "2" ; selected:false}
ListElement { label: "syn3"; value: "3" ; selected:false}
}
what's wrong ?
Thanks !
EDIT :
the code of the appDelegate :
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors { horizontalCenter: parent.horizontalCenter }
text: label
smooth: true
}
MouseArea {
anchors.fill: parent
onClicked: view.currentIndex = index
}
}
}
the code of my object :
#ifndef SYNERGIELISTOBJECT_H
#define SYNERGIELISTOBJECT_H
#include <QObject>
class synergieListObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
public:
synergieListObject(QObject *parent=0);
synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent=0);
QString label() const;
void setLabel(const QString &label);
QString value() const;
void setValue(const QString &value);
bool selected() const;
void setSelected(const bool &selected);
signals:
void labelChanged();
void valueChanged();
void selectedChanged();
private:
QString m_label;
QString m_value;
bool m_selected;
};
#endif // SYNERGIELISTOBJECT_H
c++ my object :
#include "synergielistobject.h"
synergieListObject::synergieListObject(QObject *parent): QObject(parent)
{
}
synergieListObject::synergieListObject(const QString &label,const QString &value,bool selected, QObject *parent): QObject(parent), m_label(label), m_value(value), m_selected(selected)
{
}
QString synergieListObject::label() const
{
return m_label;
}
void synergieListObject::setLabel(const QString &label)
{
if (label != m_label) {
m_label = label;
emit labelChanged();
}
}
QString synergieListObject::value() const
{
return m_value;
}
void synergieListObject::setValue(const QString &value)
{
if (value != m_value) {
m_value = value;
emit valueChanged();
}
}
bool synergieListObject::selected() const
{
return m_selected;
}
void synergieListObject::setSelected(const bool &selected)
{
if (selected != m_selected) {
m_selected = selected;
emit selectedChanged();
}
}
I have never used QdeclarativeItem to set model in QML . Try this instead
QDeclarativeContext *ctxt = view.rootContext(); ctxt->setContextProperty("model", QVariant::fromValue(dataList));
Declare the model as a property of the root. This way you can set model.Or add a function that takes model as argument and sets the model for the view. Then you can call this function from c++.

Resources