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

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
}
}

Related

Set character format for a QML TextArea using QTextCursor

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?

Making window previews in qt using QSCreen::grabWindow() function

I want to make a screen caster application that captures a window or a screen directly to QQuickImage. To do so I made this header which has a thread object which frequently updates screen shots when signal received (sorry file was written in a poor language)
class OtherThread : public QThread
{
Q_OBJECT
QString oldWritingFile = "filename.png";
bool loaded = true;
public:
int winId = 0;
OtherThread(QObject *parent = nullptr):QThread(parent){};
~OtherThread(){
if(isRunning()){
requestInterruption();
wait();
}
};
void setParams(int id){
winId = id;
}
void knowLoaded(){
loaded =true;
}
signals:
void fileCacheChanged(QString);
protected:
void run() override{
while (!isInterruptionRequested()) {
if(winId != 0 && loaded){
bool success;
loaded = false;
if(oldWritingFile == QString::number(winId)+".png"){
if(qApp->primaryScreen()->grabWindow(winId).save(QString::number(winId)+"file.png")){
oldWritingFile = QString::number(winId)+"file.png";
success = true;
}else{success=false;}
}else{
if(qApp->primaryScreen()->grabWindow(winId).save(QString::number(winId)+".png")){
oldWritingFile = QString::number(winId)+".png";
success = true;
}else{success = false;}
}
emit fileCacheChanged(oldWritingFile);
}
}
};
};
class Controller : public QObject
{
Q_OBJECT
QString oldWritingFile;
public:
Controller(QObject *parent = nullptr):QObject(parent) {}
virtual ~Controller() {}
void changeFile(QString message){
oldWritingFile = message;
emit newFile(message);
};
public slots:
void startThread(){
OtherThread *thread =new OtherThread;
connect(thread, &OtherThread::fileCacheChanged, this, &Controller::changeFile);
connect(this, &Controller::stop, thread, &OtherThread::requestInterruption);
connect(this, &Controller::changePrint, thread, &OtherThread::setParams);
connect(this, &Controller::loaded, thread, &OtherThread::knowLoaded);
thread->start();
}
QString getLoadedFile(){
if(oldWritingFile != NULL){
return "file:./"+oldWritingFile;
}else{
return "file:./index.png";
}
}
signals:
void stop();
void changePrint(int id);
void newFile(QString);
void loaded();
};
and with my qml image i did this
Image {
id: image
anchors.fill: parent
source: "images/kalaripayattu.svg"
fillMode: Image.PreserveAspectFit
cache:false
Component.onCompleted: {
justThread.newFile.connect(updateFunction)
}
function updateFunction(filename){
source = "file:./"+filename;
justThread.loaded()
}
}
JustThread{
signal stopThread
id:justThread
onStopThread: {
stop()
}
Component.onCompleted: {
startThread()
changePrint(id)
}
}
Component.onCompleted:{
applicationWindow.closing.connect(justThread.stopThread)
}
but it has a really horrible fps.
can anything be done to increase fps?
Is there any easy way to do this?
Image is not really made for displaying frequently changing images. What you want is VideoOutput.
Here's what I would do :
You need to create a QObject derived class instance and set it as the source of the VideoOutput : http://doc.qt.io/qt-5/qml-qtmultimedia-videooutput.html#source-prop
Your class needs to have a Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface NOTIFY videoSurfaceChanged)
In your setVideoSurface method, you need to call the start method of QAbstractVideoSurface with a correct format (your size and the pixelformat, pixelformat should be QVideoFrame::Format_ARGB32 for desktop I guess).
And then when you want to update the VideoOutput (via a QTimer for example), you call the present method of QAbstractVideoSurface with a QVideoFrame you constructed from the QPixmap you got in QScreen::grabWindow.
For that, you could convert the QPixmap to QImage with toImage and then convert it to QVideoFrame with theQVideoFrame::QVideoFrame(const QImage &image) constructor.

QSignalBlocker in Qml/QtQuick2?

I want to set the currentIndex of a ComboBox dynamically without triggering my attached signal.
I am currently using a bool property to set a flag that gets cleared when my signal gets called.
Item {
property bool ignoreNextChanged: false
CustomCppItem {
id: customItem
onSomethingHappened: {
ignoreNextChanged = true
comboBox.currentIndex = 42
}
}
ComboBox {
id: comboBox
currentIndex: -1
onCurrentIndexChanged: {
if(ignoreNextChanged) {
ignoreNextChanged = false
return
}
// Removed code here
}
}
}
Coming from Qt/C++, I would like to use some sort of QSignalBlocker here instead of my own property.
Is there some equivalent in Qml/QtQuick2?
For this I would implement an attached type in c++ :
signalblockerattachedtype.h :
#ifndef SIGNALBLOCKERATTACHEDTYPE_H
#define SIGNALBLOCKERATTACHEDTYPE_H
#include <QObject>
#include <qqml.h>
class SignalBlockerAttachedType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
public:
explicit SignalBlockerAttachedType(QObject *object = nullptr) : QObject(object), m_object(object)
{
}
~SignalBlockerAttachedType() {
if (m_object)
m_object->blockSignals(false);
}
bool enabled() const
{
return m_enabled;
}
void setEnabled(bool enabled)
{
if (m_enabled == enabled)
return;
if (m_object)
m_object->blockSignals(enabled);
m_enabled = enabled;
emit enabledChanged();
}
static SignalBlockerAttachedType *qmlAttachedProperties(QObject *object) {
return new SignalBlockerAttachedType(object);
}
signals:
void enabledChanged();
private:
bool m_enabled = false;
QObject *m_object;
};
QML_DECLARE_TYPEINFO(SignalBlockerAttachedType, QML_HAS_ATTACHED_PROPERTIES)
#endif // SIGNALBLOCKERATTACHEDTYPE_H
main.cpp :
#include "signalblockerattachedtype.h"
// ...
qmlRegisterUncreatableType<SignalBlockerAttachedType>("fr.grecko.SignalBlocker", 1, 0, "SignalBlocker", "SignalBlocker is only available as an attached type.");
// ...
engine.load(QUrl(QLatin1String("qrc:/usage.qml")));
usage.qml :
import fr.grecko.SignalBlocker 1.0
// ...
Item {
id: rootItem
property alias currentIndex: comboBox.currentIndex
onCurrentIndexChanged: {
// this won't get called from a change in onSomethingHappened
}
CustomCppItem {
id: customItem
onSomethingHappened: {
rootItem.SignalBlocker.enabled = true;
rootItem.currentIndex = 42
rootItem.SignalBlocker.enabled = false;
}
}
ComboBox {
id: comboBox
currentIndex: -1
}
}
Even though the SignalBlocker works on the ComboBox, you don't want to put it on the ComboBox. Doing so will prevent it to update its display when the index changes.
In your situation, I don't think this solution is better than using a simple flag.

How to detect dataChanged() and resourcesChanged() from QML

The codes below:
Item{
onDataChanged: console.log("Data changed")
}
Item{
onResourcesChanged: console.log("Resources changed")
}
throw Cannot assign to non-existent property "onDataChanged" and Cannot assign to non-existent property "onResourcesChanged" respectively.
This is not the case with the childrenChanged() signal. The reason for this is that in qtdeclarative/src/quick/items/qquickitem.h, children property is declared with:
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQmlListProperty<QQuickItem> children READ children NOTIFY childrenChanged DESIGNABLE false)
but this is not the case for data or resources. They are declared with:
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQmlListProperty<QObject> data READ data DESIGNABLE false)
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQmlListProperty<QObject> resources READ resources DESIGNABLE false)
with no changed() signal. Why is this design choice to particularly hide the change on non-visible children made? Moreover, how can the change on data be detected from QML?
Why do you need this ?
One possible workaround is to listen for child events. I wrote a quick attached type PoC :
ChildListener.h :
#ifndef CHILDLISTENER_H
#define CHILDLISTENER_H
#include <QObject>
#include <QtQml>
class ChildListener : public QObject {
Q_OBJECT
public:
ChildListener(QObject *object) : QObject(object) {
if (object)
object->installEventFilter(this);
}
static ChildListener *qmlAttachedProperties(QObject *object) {
return new ChildListener(object);
}
signals:
void childAdded(QObject* child);
void childRemoved(QObject* child);
protected:
bool eventFilter(QObject *obj, QEvent *event) override {
Q_UNUSED(obj)
if (QChildEvent *childEvent = dynamic_cast<QChildEvent*>(event)) {
if (childEvent->added())
emit childAdded(childEvent->child());
if (childEvent->removed())
emit childRemoved(childEvent->child());
}
return false;
}
};
QML_DECLARE_TYPEINFO(ChildListener, QML_HAS_ATTACHED_PROPERTIES)
#endif // CHILDLISTENER_H
Register it with qmlRegisterUncreatableType<ChildListener>("fr.grecko.ChildListener", 1, 0, "ChildListener", "ChildListener can only be accessed as an attached type."); and you can now use it like so :
import fr.grecko.ChildListener 1.0
/* ... */
Timer {
id: foo
objectName: "My name is foo"
}
Item {
ChildListener.onChildAdded: print("child added : ", child)
data: [foo];
}
This outputs : qml: child added : QQmlTimer(0x7ffe22f538e0, "My name is foo") in the console

Populate ComboBox from QList<QObject*>

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.

Resources