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
}
}
Related
I'm trying to create a very simple rich text editor component in QML. The requirements are that the user must be able to quickly format individual words inside a textbox separately. So for example, the user could press "Ctrl+B" in the middle of typing and from that point onward the text becomes bold, when the user press "Ctrl+B" again the subsequently typed text has a normal font weight:
So looking around I found that there is an official example provided by the Qt company to do this. So I extrapolated this to a very basic app:
// DocumentHandler.cpp
#pragma once
#include <QObject>
#include <QQuickTextDocument>
#include <QTextCharFormat>
class DocumentHandler : public QObject {
Q_OBJECT
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged)
Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
public:
QQuickTextDocument *document() const;
void setDocument(QQuickTextDocument *newDocument);
int cursorPosition() const;
void setCursorPosition(int newCursorPosition);
int selectionStart() const;
void setSelectionStart(int newSelectionStart);
int selectionEnd() const;
void setSelectionEnd(int newSelectionEnd);
bool modified() const;
void setModified(bool newModified);
bool bold() const;
void setBold(bool newBold);
signals:
void documentChanged();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void modifiedChanged();
void boldChanged();
private:
void mergeFormatOnWordOrSelection(const QTextCharFormat & format);
void reset();
QTextCursor textCursor() const;
QTextDocument* textDocument() const;
QQuickTextDocument * m_document = nullptr;
int m_cursorPosition = -1;
int m_selectionStart = 0;
int m_selectionEnd = 0;
};
// DocumentHandler.cpp
#include "documenthandler.h"
#include <QTextCursor>
QQuickTextDocument *DocumentHandler::document() const
{
return m_document;
}
void DocumentHandler::setDocument(QQuickTextDocument *newDocument)
{
if (m_document == newDocument)
return;
if(m_document)
m_document->textDocument()->disconnect(this);
m_document = newDocument;
if(m_document){
QObject::connect(m_document->textDocument(), &QTextDocument::modificationChanged,
this, &DocumentHandler::modifiedChanged);
}
emit documentChanged();
}
int DocumentHandler::cursorPosition() const
{
return m_cursorPosition;
}
void DocumentHandler::setCursorPosition(int newCursorPosition)
{
if (m_cursorPosition == newCursorPosition)
return;
m_cursorPosition = newCursorPosition;
reset();
emit cursorPositionChanged();
}
int DocumentHandler::selectionStart() const
{
return m_selectionStart;
}
void DocumentHandler::setSelectionStart(int newSelectionStart)
{
if (m_selectionStart == newSelectionStart)
return;
m_selectionStart = newSelectionStart;
emit selectionStartChanged();
}
int DocumentHandler::selectionEnd() const
{
return m_selectionEnd;
}
void DocumentHandler::setSelectionEnd(int newSelectionEnd)
{
if (m_selectionEnd == newSelectionEnd)
return;
m_selectionEnd = newSelectionEnd;
emit selectionEndChanged();
}
bool DocumentHandler::modified() const
{
return m_document && m_document->textDocument()->isModified();
}
void DocumentHandler::setModified(bool newModified)
{
if (m_document)
m_document->textDocument()->setModified(newModified);
}
void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
}
void DocumentHandler::reset()
{
emit boldChanged();
}
QTextCursor DocumentHandler::textCursor() const
{
QTextDocument* doc = textDocument();
if(!doc)
return QTextCursor();
QTextCursor cursor = QTextCursor(doc);
if(m_selectionStart != m_selectionEnd){
cursor.setPosition(m_selectionStart);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
}
else{
cursor.setPosition(m_cursorPosition);
}
return cursor;
}
QTextDocument *DocumentHandler::textDocument() const
{
return m_document ? m_document->textDocument() : nullptr;
}
bool DocumentHandler::bold() const
{
auto cursor = textCursor();
if(cursor.isNull())
return false;
return cursor.charFormat().fontWeight() == QFont::Bold;
}
void DocumentHandler::setBold(bool newBold)
{
QTextCharFormat format;
format.setFontWeight(newBold ? QFont::Bold : QFont::Normal);
mergeFormatOnWordOrSelection(format);
emit boldChanged();
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import DocumentHandler 1.0
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: "lightpink"
header: ToolBar {
id: appToolbar
ToolButton {
text: "Bold"
checkable: true
checked: documentHandler.bold
onClicked: {
documentHandler.bold = !documentHandler.bold
}
}
}
DocumentHandler {
id: documentHandler
document: textInput.textDocument
cursorPosition: textInput.cursorPosition
selectionStart: textInput.selectionStart
selectionEnd: textInput.selectionEnd
}
TextArea {
id: textInput
anchors.fill: parent
font.pixelSize: 30
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
focus: true
selectByMouse: true
persistentSelection: true
textFormat: Qt.RichText
}
}
If you try to run this app, type something like "hello" and the press the "Bold" button the app correctly makes the "hello" bold. However, there is a problem I'm struggling to get past:
if you click on the button when the textarea is empty, then typing doesn't give you bold text. In fact, it seems that it is not possible to change the font weight of the textarea prior to having some text in it. Also, if you type a word, then press space and then click the button: nothing happens.
Anyone got any clue on what's the root cause of this behaviour?
Right now, all I have is my QML file, with the button.
/*PlasmaComponents.*/ToolButton {
id: shutdownButton
text: i18n("Shutdown")
iconSource: "system-shutdown"
enabled: power.canShutdown
onClicked: doTheThing();
}
From reading about QML, it seems I'll need to add a C++ process. Is this possible with QML4? If not, could QProcess work? What files would need changing if so?
you can write a process executor class like this :
#include <QProcess>
#include <QVariant>
class Process : public QProcess {
Q_OBJECT
public:
Process(QObject *parent = 0) : QProcess(parent) { }
Q_INVOKABLE void start(const QString &program, const QVariantList &arguments) {
QStringList args;
// convert QVariantList from QML to QStringList for QProcess
for (int i = 0; i < arguments.length(); i++)
args << arguments[i].toString();
QProcess::start(program, args);
}
Q_INVOKABLE QByteArray readAll() {
return QProcess::readAll();
}
};
and register them :
#include <QtQml>
#include "process.h"
qmlRegisterType<Process>("Process", 1, 0, "Process");
and finally run your command from QML :
import QtQuick 2.4
import QtQuick.Controls 1.3
import Process 1.0
ApplicationWindow {
width: 800
height: 480
visible: true
Text {
id: text
}
Process {
id: process
onReadyRead: text.text = readAll();
}
Timer {
interval: 1000
repeat: true
triggeredOnStart: true
running: true
onTriggered: process.start("poweroff", [ "-f" ]);
}
-f make forced to power off immediately.
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
}
}
I was following an example on web to populate combobox, but it did not wok for me and I do not know why!. I have two classes stock and DbCon, stock has three private fields along with public accessor and mutators. DbCon has a Q_Property and two public function, one returns a database connection and the other creates and returns stock list as a QList<QObject*>. In main.cpp I have created a contextual property named "data" to access DbCon from QML.
in main.qml I have
....
ComboBox{
model: data.stockModel
textRole: "code"
}
....
in main.cpp
DbCon db;
engine.rootContext()->setContextProperty("data", &db);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
in dbcon.h
class DbCon:public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> stockModel READ stockModel)
public:
explicit DbCon(QObject *parent = 0);
QSqlDatabase db();
QList<QObject*> stockModel();
};
in implementation of QList<QObject*> stockModel() of dbcon.h
QList<QObject*> data;
....
while (query.next()) {
stock *s = new stock();
....
data.append(s);
}
return data;
and in stock.h
class stock : public QObject
{
Q_OBJECT
private:
QString m_name;
QString m_code;
int m_id;
public:
explicit stock(QObject *parent = 0);
QString name();
void setname(QString &name);
QString code();
void setcode(QString &code);
int id();
void setid(int &id);
};
When I run the application I get the following message in application output
QQmlExpression: Expression qrc:/main.qml:16:20 depends on non-NOTIFYable properties:
QQuickComboBox::data
and I do not get anything in combobox!
If I create another contextual property in main.cpp in this way
engine.rootContext()->setContextProperty("myModel", QVariant::fromValue(data));
and set myModel as model for combobox, it works fine. But I want to do it in this way because onCurrentIndexChanged I will call another function that returns another QList<QObject*> for a TableView of another qml file.
EDIT: Entrie qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width:600
height:600
property string contentSource
Column{
spacing:10
ComboBox{
model: data.stockModel
textRole: "code"
}
Loader {
id: content
width: parent.width
height:400
}
Row{
spacing:10
Button{
text: "Page1"
onClicked: content.source = "Page1.qml"
}
Button{
text: "Page2"
onClicked: content.source = "Page2.qml"
}
}
}
}
By changing data to dataStore in main.cpp and data.stockModel to dataStore.stockModel in main.qml I get following error
file:///C:/Qt/Qt5.7.0/5.7/mingw53_32/qml/QtQuick/Controls.2/ComboBox.qml:62:15: Unable to assign [undefined] to QString
You have two issues:
Your stockModel property should be NOTIFYable, which means that you should define the property with e.g. Q_PROPERTY(QList<QObject*> stockModel READ stockModel NOTIFY stockModelChanged) and provide a void stockModelChanged(const QList<QObject *> &) signal in the DbCon class.
stock::name() must be a property too, so you need to declare that with Q_PROPERTY(QString name READ name NOTIFY nameChanged) and provide a void nameChanged(const QString &) signal in the stock class as well.
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 }
}
}