QSignalBlocker in Qml/QtQuick2? - qt

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.

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?

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

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

Loading multiple images on a page for Blackberry 10

I am working on BB10 cascades. I am trying to load multiple images from network. I have referred following example and I have modified it to load a single image. Sample code is as follows:
main.qml
import bb.cascades 1.2
Page {
Container {
id: outer
Container {
preferredHeight: 500
preferredWidth: 768
layout: DockLayout {}
onCreationCompleted: {}
// The ActivityIndicator that is only active and visible while the image is loading
ActivityIndicator {
id: activity
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
preferredHeight: 300
visible: _loader.loading
running: _loader.loading
}
// The ImageView that shows the loaded image after loading has finished without error
ImageView {
id: image
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
image: _loader.image
visible: !_loader.loading && _loader.label == ""
}
// The Label that shows a possible error message after loading has finished
Label {
id: lable
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
preferredWidth: 500
visible: !_loader.loading && !_loader.label == ""
text: _loader.label
multiline: true
}
}
Button {
text: "load Image"
onClicked: {
_loader.load();
console.log("loading:::"+_loader.loading);
}
}
}
}
applicatioui.hpp
#ifndef ApplicationUI_HPP_
#define ApplicationUI_HPP_
#include "imageloader.hpp"
#include
namespace bb
{
namespace cascades
{
class LocaleHandler;
}
}
class QTranslator;
class ApplicationUI : public QObject
{
Q_OBJECT
public:
ApplicationUI();
virtual ~ApplicationUI() {}
Q_INVOKABLE void prepareImage();
Q_INVOKABLE void loadImage();
Q_INVOKABLE ImageLoader* getImageloadderInstance();
private slots:
void onSystemLanguageChanged();
private:
ImageLoader* image;
QTranslator* m_pTranslator;
bb::cascades::LocaleHandler* m_pLocaleHandler;
};
#endif /* ApplicationUI_HPP_ */
applicatioui.cpp
#include "applicationui.hpp"
#include
#include
#include
#include
using namespace bb::cascades;
ApplicationUI::ApplicationUI() :
QObject()
{
// prepare the localization
m_pTranslator = new QTranslator(this);
m_pLocaleHandler = new LocaleHandler(this);
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
bool res = QObject::connect(m_pLocaleHandler, SIGNAL(systemLanguageChanged()), this, SLOT(onSystemLanguageChanged()));
// This is only available in Debug builds
Q_ASSERT(res);
// Since the variable is not used in the app, this is added to avoid a
// compiler warning
Q_UNUSED(res);
// initial load
onSystemLanguageChanged();
// Create scene document from main.qml asset, the parent is set
// to ensure the document gets destroyed properly at shut down.
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);
qml->setContextProperty("_loader",this);
// Create root object for the UI
AbstractPane *root = qml->createRootObject();
if(root)
{
}
// Set created root object as the application scene
Application::instance()->setScene(root);
}
void ApplicationUI::onSystemLanguageChanged()
{
QCoreApplication::instance()->removeTranslator(m_pTranslator);
// Initiate, load and install the application translation files.
QString locale_string = QLocale().name();
QString file_name = QString("loading_Image_%1").arg(locale_string);
if (m_pTranslator->load(file_name, "app/native/qm")) {
QCoreApplication::instance()->installTranslator(m_pTranslator);
}
}
ImageLoader* ApplicationUI::getImageloadderInstance()
{
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
return (image);
}
void ApplicationUI::prepareImage()
{
image =new ImageLoader("http://uat2.thomascook.in/bpmapp-upload/download/fstore/7f00000105a3d7bf_eb1af9_1485f184f7b_-52f0/GIT_banner.jpg",this);
}
void ApplicationUI::loadImage()
{
image->load();
}
Now I want to load multiple images. I tried to create QList<QObject*>* image;
and add instances of the ImageLoader class, but I don't know how to access it in main.qml.
Any ideas how to do so?
https://github.com/RileyGB/BlackBerry10-Samples/tree/master/WebImageViewSample
follow this link there is a control called "WebImageView". you just have to set the url of image and its done. int main.qml coder have shown user friendly UI.
thanks to #Bojan Kogoj

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