QML - How to use QStringList as a model of ListView? - qt

I try to make TelnetClient.I use FileIO for read Telnet.There is no problem read or write and also create a string list but I need to show QStringList to ListView but I m getting error: "m_model is not defined".
I create QStringList:
QStringList FileIO::read() {
if (m_source.isEmpty()) {
emit error("source is empty");
return QStringList();
}
QFile file(m_source);
QString fileContent;
QString line;
QStringList list;
if ( file.open(QIODevice::ReadWrite) ) {
QTextStream t( &file );
line = t.readAll();
fileContent += line;
list.append(line.split("\r\n"));
foreach (QString item, list) {
if (item[0].isNumber()) {
list2.append(item);
}
}
QQmlContext *ctxt;
ctxt->setContextProperty("m_model", QVariant::fromValue(list2));
qDebug() << "\r\n\r\nlist2 =" << list2;
line = t.readAll();
qDebug() << "SOURCE" << m_source;
file.close();
}
else {
emit error("Unable to open the file");
return QStringList();
}
return list2;
This can make a new QStringList successfully and also I assign my string list as a model; m_model.
ListView {
id: listView1
x: 0
y: 0
model: m_model
delegate: Rectangle{
Text {text: modelData }
}
}
and here is my ListView. When I try like this, I m getting error. How can I solve this problem. If I can use "list2" in main.cpp I can solve the problem but I don't know how can I use it in main.cpp because it exist in another class.
Thank you!

You can try to set the context property with an instance of the class. That way, you can instantiate the class in main, and then pass it's address to set the context property. If the data of the model is subject to change while the program is running, I would suggest implementing the QStringList as a Q_Property.
//main.cpp
FileIO fileIO;
QQmlApplicationEngine engine;
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("fileio", &fileIO);
engine.load(/* Path to your qml */);
//qml
ListView {
id: listView1
x: 0
y: 0
model: fileio.m_model
delegate: Rectangle{
Text {text: modelData }
}
}

To elaborate on Francisco's answer: if the data of the model is subject to change while the program is running, it is indeed the cleanest solution to implement the QStringList as a Q_PROPERTY, because that will send signals to QML objects when the model data changes. A good example is here.
According to the Qt Manual there is also another option:
Note: There is no way for the view to know that the contents of a QStringList have changed. If the QStringList changes, it will be necessary to reset the model by calling QQmlContext::setContextProperty() again.
An (untested) example of this technique is demonstrated below:
main.cpp:
QStringList updateStringList() {
// Your code from "I create QStringList" goes here.
}
QStringList myStringList = updateStringList();
QQmlApplicationEngine engine;
QQmlContext* ctx = engine.rootContext();
ctx->setContextProperty("m_model", &myStringList);
engine.load("ListView.qml");
// ... more code ...
myStringList = updateStringList();
ctx->setContextProperty("m_model", &myStringList);
ListView.qml:
ListView {
id: listView1
x: 0
y: 0
model: fileio.m_model
delegate: Rectangle{
Text {text: modelData }
}
}

Related

How to refresh a combo-box in qml after sending a signal

Basically, I have a combo-box in qml that I populate using a QStringList. However, I'm not able to refresh the combo-box (reload) to show the list has changed. I looked into doing that using the Loader but I couldn't figure it out. Can someone guide me in how to do it.
network.qml
Popup{
contentItem: Rectangle{
LabelValueList {
id: list1
row1: LabelValue {
id: row1
row2: LabelValue {
id: row2
value: ComboBox {
id: combobox
model: ListModel {
id: comboModel
Component.onCompleted: {
//..
}
}
}
}
}
}
}
}
network.h
class Network : public QObject{
Q_OBJECT
Q_PROPERTY(QStringList listOfNetworks READ m_listOfNetworks NOTIFY updateNetworks)
private:
QStringList m_listOfNetworks;
public:
explicit Network(QObject *parent = 0);
QStringList listOfNetworks() const;
public slots:
void slot_scanNetworks();
signals:
void updateNetworks();
};
network.cpp
Network::Network(QObject *parent) : QObject (parent){
}
void Network::slot_scanNetworks(){
QFile SSIDsFile("/home/root/networking/listOfWifiNetworks.txt");
m_listOfNetworks.clear();
if (!SSIDsFile.open(QIODevice::ReadOnly | QIODevice::Text)){
//
}
else{
QTextStream scanNetworkStream(&SSIDsFile);
while (!scanNetworkStream.atEnd()){
QString line = scanNetworkStream.readLine();
if (line.size() != 0){
QStringList lineSplit = line.split(' ');
m_listOfNetworks.append(lineSplit[1]);
}
}
}
SSIDsFile.close();
emit updateNetworks();
}
How do I reload the combo-box of row2 to refresh the list? It only get's the list at the beginning but i want to update the drop-down (combo-box) when I emit the signal updateNetworks(). I tried using the Loader and setting the source.Component of it to the id of row2 but I kept getting the error "Error: Cannot assign QObject* to QQmlComponent". Any help?
I am not pro but maybe help u this post .
you can use a timer on ur application and set 1s to refresh this and make a variable and when call signal call this variable
example :
// make a variable type of bool by value = false
property bool refreshMyBoxNow : false
//add this timer to ur project
Timer
{
id: timeToRefreshBox
//interval = time to tick . (1000) = 1sec
interval: 1; running: true; repeat: true
onTriggered:
{
//your code here for refresh box
}
}

Qt QML How to manually select word in TextArea by double click

double click selects words, but i'd like to customise it to, for example, select the $1000 term.
Example:
import QtQuick 2.7
import QtQuick.Controls 1.4
ApplicationWindow
{
visible: true
width: 640
height: 480
TextArea
{
anchors.fill: parent
text: "hello, try to select the $1000 prize!"
font.pointSize: 14
}
}
For example, by somehow overriding selectWord or by overriding onDoubleClicked or by somehow adding my own MouseArea that doesn't break the existing TextArea functionality.
Not sure how to do this.
Thanks for any help.
update
I tried adding a MouseArea but it didnt work. Example;
ApplicationWindow
{
visible: true
width: 640
height: 480
TextArea
{
anchors.fill: parent
text: "hello, try to select the $1000 prize!"
font.pointSize: 14
MouseArea
{
anchors.fill: parent
propagateComposedEvents: true
onClicked:
{
// attempt to get an event on click without
// affecting the TextArea. But it breaks selection.
console.log("clicked some text")
mouse.accepted = false
}
}
}
}
Update 2
I think this problem is a version of the long-running Qt problem that you can't have some kind of "event observer" whose job is to check events but not stop them from continuing their normal operation.
If i had an event observer, i could make it "observe" the TextArea and do something on click or double click.
So, here goes trying to make one....
toucharea.h
#pragma once
#include <QGuiApplication>
#include <QQuickItem>
#include <QTime>
class TouchArea : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
public:
QTime _lastMousePress;
int _clickThresholdMS = 300;
bool eventFilter(QObject*, QEvent *event) override
{
// if false this will allow the event to continue as normal
// if true it will stop the event propagating
bool handled = false;
// https://doc.qt.io/qt-5/qevent.html#Type-enum
QEvent::Type t = event->type();
switch (t)
{
case QEvent::TouchUpdate:
break;
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug("key press %d", keyEvent->key());
}
break;
case QEvent::MouseButtonPress:
{
qDebug() << "mouse press";
_lastMousePress.start();
break;
}
case QEvent::MouseButtonRelease:
{
qDebug() << "mouse release";
int dt = _lastMousePress.elapsed();
if (dt < _clickThresholdMS)
{
qDebug() << "mouse click";
emit clicked();
}
break;
}
}
return handled;
}
QQuickItem *target() const { return _target; }
void setTarget(QQuickItem *target)
{
qDebug() << "set target";
if (_target == target) return;
if (_target)
_target->removeEventFilter(this);
_target = target;
if (_target)
_target->installEventFilter(this);
emit targetChanged();
}
signals:
void targetChanged();
void clicked();
private:
QQuickItem* _target = 0;
};
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include "toucharea.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<TouchArea>("App", 1, 0, "TouchArea");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
main.qml
import QtQuick 2.12
import QtQuick.Controls 1.4
import App 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
TextArea
{
id: tarea
anchors.fill: parent
text: "hello, try to select the $1000 prize!"
font.pointSize: 14
MouseArea
{
enabled: false
anchors.fill: parent
TouchArea
{
target: parent
onClicked: console.log("captured a click")
}
}
}
}
Well it nearly worked. I can only capture my synthetic click if we handled the event in eventFilter. When not handled the filter does not see the MouseButtonRelease.
Why is this. I was expecting this to be the first handler encountered before the other QtQuick items get to see it.
Any help?
Figured out a way;
Step 1, define an Observer class for events
observer.h
#pragma once
#include <QGuiApplication>
#include <QQuickItem>
#include <QTime>
#include <QMouseEvent>
class Observer : public QQuickItem
{
Q_OBJECT
public:
QTime _lastMousePress;
int _clickThresholdMS = 300;
Observer()
{
setFiltersChildMouseEvents(true);
}
bool childMouseEventFilter(QQuickItem*, QEvent *event) override
{
// if false this will allow the event to continue as normal
// if true it will stop the event propagating
bool handled = false;
// https://doc.qt.io/qt-5/qevent.html#Type-enum
QEvent::Type t = event->type();
switch (t)
{
case QEvent::TouchUpdate:
break;
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug("key press %d", keyEvent->key());
}
break;
case QEvent::MouseButtonPress:
{
//qDebug() << "mouse press";
_lastMousePress.start();
}
break;
case QEvent::MouseButtonRelease:
{
//qDebug() << "mouse release";
int dt = _lastMousePress.elapsed();
if (dt < _clickThresholdMS)
{
//qDebug() << "mouse click";
emit clicked();
}
}
break;
case QEvent::MouseButtonDblClick:
{
//QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
//qDebug() << "mouse double click";
emit doubleClicked();
handled = true;
}
break;
}
return handled;
}
signals:
void clicked();
void doubleClicked();
};
Step 2, put this in main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include "observer.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<Observer>("App", 1, 0, "Observer");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Step 3, use Observer to detect event
Detect whichever event you want, then make it do what you need, for example, for double click to select a wider class of characters in TextArea;
import QtQuick 2.12
import QtQuick.Controls 1.4
import App 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
Observer
{
anchors.fill: parent
onDoubleClicked:
{
tarea.selectWord();
var s = tarea.selectionStart
var e = tarea.selectionEnd
function allowed(c)
{
if (c == "$" || c == "#") return true;
if (c >= "0" && c <= "9") return true;
if (c.toUpperCase() != c.toLowerCase()) return true;
return false;
}
while (allowed(tarea.getText(s-1, s))) tarea.select(--s, e);
while (allowed(tarea.getText(e, e+1))) tarea.select(s, ++e);
}
TextArea
{
id: tarea
anchors.fill: parent
text: "hello, try to select the #$$$1000###$foo prize!"
font.pointSize: 14
}
}
}
Looks like you can do this with TapHandler:
import QtQuick 2.15
import QtQuick.Controls 2.15
TextField {
width: parent.width
selectByMouse: true
TapHandler {
grabPermissions: PointerHandler.TakeOverForbidden
onDoubleTapped: (eventPoint) => {
print("taptap");
eventPoint.accepted = true;
}
}
}
Note that I experienced both that the double-click = select string on TextField did and did not work after the above. Click+drag to select always worked though.
I cannot think of any solution that would preserve the TextArea behavior, just on the QML side. I would instead try to wrap, inherit or reimplement the component on the C++ side, to add what you want.
Inheriting may be tricky, as few Qt classes are meant to be inherited directly, looking at QQuickTextArea and QQuickTextEdit source, it might be challenging
Reimplementing can be some work, but that would probably be the option I would go for, while keeping the logic and code there to a minimum. QQuickTextArea seems a good base/reference as smaller/simpler than QQuickTextArea (which in turn uses TextArea internally)
Two key elements to that:
Events management (mouse, selection, ...)
Rendering of the component, based on Qt OpenGL wrapper classes (QSG* Scene graph classes). It is a bit obscure, but there are some tutorials and presentations out there about it. And you may be able to just copy-paste the original code.
For both, I would recommend digging into the source code:
QQuickTextEdit: https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/quick/items/qquicktextedit.cpp
QQuickTextArea: https://code.qt.io/cgit/qt/qtquickcontrols2.git/tree/src/quicktemplates2/qquicktextarea.cpp

Setting context property for multiple qml files

I have a main.qml and inside that i have two banners(titlebanner and bottombanner) as shown in below.
Window {
id: main
visible: true
Image {
source: "qrc:/banner.png"
anchors.fill: parent
}
TitleBanner {
x:0
y:10
}
BottomBanner {
x:0
y:420
}
}
In TitleBanner.qml, i have below code
Item {
Rectangle {
id : id_title_banner
property real value : titlebanner.title
Image {
source: "qrc:/banner_title.png";
}
//todo:: some animation stuffs on 'value' later
}
}
In BottomBanner.qml, i have below code
Item {
Rectangle {
id : id_bottom_banner
property real value : bottombanner.title
Image {
source: "qrc:/banner_bottom.png";
}
//todo:: some animation stuffs on 'value' later
}
}
In C++ side, I am keeping two separate objects(for later flexibility) for both title banner and bottom banner. I set the root context property to expose the title banner objects from C++ to qml as below
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//create title banner object
CTitleBanner *objTitleBanner = new CTitleBanner();
//create bottom banner object
CBottomBanner *objBottomBanner = new CBottomBanner();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("titlebanner", objTitleBanner);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
My question is, how can we set context property for both titlebanner and bottombanner qml files separately instead of setting it to root context ? My concern is at later point if more banner comes, I set it to rootContext. Is it a proper way of doing ? How can we create seperate context for each banner ?
Your assumption:
it replaces above context property.
is wrong:
// in main.cpp
QObject* obj1 = new QObject();
QObject* obj2 = new QObject();
engine.rootContext()->setContextProperty("obj1", obj1);
engine.rootContext()->setContextProperty("obj2", obj2);
qDebug() << "obj1" << obj1 << engine.rootContext()->contextProperty("obj1").value<QObject*>();
qDebug() << "obj2" << obj2 << engine.rootContext()->contextProperty("obj2").value<QObject*>();
// in main.qml
Component.onCompleted: console.log(obj1, obj2)
// output:
obj1 QObject(0x22d8c768) QObject(0x22d8c768)
obj2 QObject(0x22d8c778) QObject(0x22d8c778)
qml: QObject(0x22d8c768) QObject(0x22d8c778)
Both objects are there, with the right identifier.
Personally I don't like contextProperties since they are easily to be shadowed e.t.c. - I rather register my CPP models as singletons so I can import them at the places where I really want to have access to them. Though there are downsides of those singletons. (e.g. qmlscene will fail to import them)

how to change textbox data remotely by tcp socket in QT?

i would like to code a program by Qt tcp socket and QML together.
i am going to change a textbox variable that created by QML to the new data that get by remote client by tcp socket?
my snippet code is :
void Server::startRead()
{
client->waitForReadyRead(3000);
QString buf= client->readAll();
qDebug() << buf;
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
QObject *rootObject = engine.rootObjects().first();
QObject *qmlObject = rootObject->findChild<QObject*>("ttt");
qDebug() << "before change"
qDebug() << qmlObject->property("text");
qmlObject->setProperty("text", "new data");
qDebug() << "after change"
qDebug() << qmlObject->property("text");
}
main.qml
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
Window{
width:400
height:400
visible:true
Item {
id: rootItem
property string text
Text {
id: message
text: rootItem.text
}
}
}
I really appreciate it.
There are many ways to do that. Next examples demonstrate how to change qml element properties from cpp. If you need to change visibility just use bool properties instead of string that i used in examples.
1. This is good solution i think.
You could create qml adapter
class ServerQMLAdapter : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QString data READ data WRITE setData NOTIFY dataChanged)
public:
explicit ServerQMLAdapter(QQuickItem *parent = nullptr) : QQuickItem(parent){}
QString data() const {
return mData;
}
void setData(const QString &data) {
if(mData == data)
return;
mData = data;
emit dataChanged();
}
private slots:
/**
this slots called on data read
*/
void onDataRead(const QString &data) {
setData(data);
}
private:
QString mData = QString();
};
in application class you sould register your adapter
qmlRegisterType<ServerQMLAdapter>("ServerQMLAdapter", 1, 0, "ServerQMLAdapter");
After that you can use it in your qml file
import ServerQMLAdapter 1.0
Item {
ServerQMLAdapter {
id: myAdapter
}
Text {
id: message
text: myAdapter.data
}
}
2. You can change properties from cpp.
Just like you do in your snippet
auto rootObject = engine.rootObjects().first();
rootObject->setProperty("text", "new data");
and add this propetry to your qml file
Item {
id: rootItem
property string text
Text {
id: message
text: rootItem.text
}
}
3. Use invoked methods
From you cpp file
auto rootObject = engine.rootObjects().first();
QMetaObject::invokeMethod(rootObject, "setText",
Q_ARG(QVariant, QVariant::fromValue(data)));
and add function to your qml file
Item {
id: rootItem
function setText(data) {
message.text = data;
}
Text {
id: message
}
}

QSettings & unicode

I have some buttons in a qml file and I want to make them editable from a settings file.
Therefore I read my settings.ini with QSettings and pass the data to qml.
All works just fine until I try to read icons in unicode format.
I found this question but the answer doesn't work for me.
The settings.ini has UTF-8 format and since "QString icon2 = QString::fromUtf8("\uf00c");" works, the problem has to be QSettings.
Any ideas?
main.cpp
----------
int main(int argc, char *argv[])
{
// locale code doesnt help
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QGuiApplication app(argc, argv);
QList<QObject*> dataList;
QSettings *settings = new QSettings("settings.ini", QSettings::IniFormat);
// settings code doesnt change anything
settings->setIniCodec(QTextCodec::codecForName("UTF-8"));
settings->beginGroup("icons");
// read settings
QString icon1 = settings->value("icon1").toString();
// test
QString icon2 = QString::fromUtf8("\uf00c");
settings->endGroup();
qDebug() << "icon1:" << icon1;
qDebug() << "icon2:" << icon2;
// to qml
MyData *data1 = new MyData();
data1->setIcon(icon1);
MyData *data2 = new MyData();
data2->setIcon(icon2);
dataList.append(data1);
dataList.append(data2);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("mydata", QVariant::fromValue(dataList));
engine.load(QUrl("qrc:/main.qml"));
return app.exec();
}
qml
----------
FontLoader { id: fontAwesome; source: "qrc:/fontawesome-webfont.ttf" }
Repeater {
model: mydata
Grid {
rows: 1
spacing: 5
Text {
text: index
color: "black"
}
Text {
font: fontAwesome.name
text: model.icon
color: "black"
}
}
}
settings.ini
[icons]
icon1=\uF00C
Output:
icon1: "F00C"
icon2: "\uF00C"
qml output
Looks like I was just on the totally wrong track..
INI file escaping works just fine.
\x....

Resources