Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
Developer
Posts: 404
Registered: ‎04-04-2012
My Device: BlackBerry Z30/Z10/Z10LE/Q10/PlayBook

Progress Indicator updated from Class

[ Edited ]

I have a class that reads the address book. I want to give the user some feedback as to how far along the process is in reading the book. I created the class with a couple of public signals. One signal is emited with the total number of contacts to set the to value. The other is a signal with the next contact is read. Then in the QML, I read the signals and update the progressIndicator. It doesn't work, except at the end when it makes the progress indicator full.

 

Here is my QML

 

import bb.cascades 1.0
import com.example.addressbook 1.0

Page {
    titleBar: TitleBar {
        id: titleBar
        visibility: ChromeVisibility.Visible
        title: qsTr("Contacts Search")
    }
    Container {
        layout: StackLayout {
            orientation: LayoutOrientation.TopToBottom
        }
        topPadding: 10.0
        Label {
            text: qsTr("Searching your contacts.")
            multiline: true
            horizontalAlignment: HorizontalAlignment.Center
        }
        ProgressIndicator {
            id: progressIndicator
            horizontalAlignment: HorizontalAlignment.Center
            fromValue: 0.0
            toValue: 100.0
        }
        ListView {
            dataModel: addressBook.model
            listItemComponents: ListItemComponent {
                type: "item"
                StandardListItem {
                    title: qsTr("%1, %2").arg(ListItemData.lastName).arg(ListItemData.firstName)
                }
            }
            onTriggered: {
                clearSelection()
//               select(indexPath)
//               AddressBook.setCurrentContact(indexPath)
//                AddressBook.viewContact();
//                navigationPane.push(contactViewer.createObject())
            }
        }
    }
    
    onCreationCompleted: {
        timer.start();
    }

    attachedObjects: [
        QTimer {
            id: timer
            interval: 0
            onTimeout: {
                addressBook.searchContacts();
                timer.stop();
            }
        },
        AddressBook {
        	id: addressBook
        	onContactsCounted: {
        	    progressIndicator.toValue = count;
        	}
        	onContactCountChanged: {
                progressIndicator.value = count;
            }
        }
    ]
}



 and the class header

 

#ifndef AddressBook_HPP_
#define AddressBook_HPP_

#include <QtCore/QObject>

#include <bb/cascades/GroupDataModel>
#include <bb/pim/contacts/ContactService>

#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>

class AddressBook : public QObject
{
    Q_OBJECT

    // The model that provides the filtered list of contacts
    Q_PROPERTY(bb::cascades::GroupDataModel *model READ model CONSTANT);

public:

    AddressBook(QObject *parent = 0);
    virtual ~AddressBook();
    Q_INVOKABLE void searchContacts();

    signals:
    void contactsCounted(float count);
    void contactCountChanged(float count);

private:

    bb::cascades::GroupDataModel* model() const;

    // The central object to access the contacts service
    bb::pim::contacts::ContactService* m_contactService;

    // The property values
    bb::cascades::GroupDataModel* m_model;
    QString m_filter;
    float m_contactCount;

    // The ID of the current contact
    bb::pim::contacts::ContactId m_currentContactId;

	// Logging
	void log(const QString &msg);

};

#endif // AddressBook_HPP_

 Any ideas on why it isn't getting the updated values in the onContactCountChanged signal?

Developer
Posts: 6,152
Registered: ‎07-05-2012
My Device: Playbook, Dev Alpha C, Z10 LE, Z30
My Carrier: Orange

Re: Progress Indicator updated from Class

In al likelihood you are blocking the UI so there are no updates.

 

You will either need to return a few entries at a time, make a 'yield' call within your search function or run the search function as a concuurent process/thread.

 

If you do this already then please post your search code and we will look at why it's not working if you are not then take a look at which option might be best for you here...

 

<Typical, struggling to find a page I know is there somewhere will update when found>


If you've been helped click on Like Button, if you've been saved buy the app. Smiley Happy

Developer of stokLocker, Sympatico and Super Sentences.
Developer
Posts: 404
Registered: ‎04-04-2012
My Device: BlackBerry Z30/Z10/Z10LE/Q10/PlayBook

Re: Progress Indicator updated from Class

I tried a few different ways, but was unsuccessful. Can someone assist me in getting this Class to update the progress indicator? This probably would make a good sample app.

 

QML

 

import bb.cascades 1.0
import com.example.addressbook 1.0

Page {
    Container {
        Label {
            id: searchLabel
            text: "Search Contacts"
            horizontalAlignment: HorizontalAlignment.Center
        }
        ProgressIndicator {
            id: progressIndicator
            horizontalAlignment: HorizontalAlignment.Center
        }
        ListView {
            dataModel: addressBook.model
            listItemComponents: ListItemComponent {
                type: "item"
                StandardListItem {
                    title: qsTr("%1, %2").arg(ListItemData.lastName).arg(ListItemData.firstName)
                }
            }
        }
    }

    onCreationCompleted: {
        timer.start();
    }

    attachedObjects: [
        QTimer {
            id: timer
            interval: 0
            onTimeout: {
                timer.stop();
                addressBook.search();
            }
        },
        AddressBook {
            id: addressBook
        }
    ]
}


 HPP

 

#ifndef AddressBook_HPP_
#define AddressBook_HPP_

#include <QtCore/QObject>

#include <bb/cascades/GroupDataModel>
#include <bb/pim/contacts/ContactService>
#include <bb/pim/contacts/Contact>
#include <bb/pim/contacts/ContactListFilters>

class AddressBook : public QObject
{
    Q_OBJECT

    // The model that provides the filtered list of contacts
    Q_PROPERTY(bb::cascades::GroupDataModel *model READ model CONSTANT);

public:

    AddressBook(QObject *parent = 0);
    virtual ~AddressBook();

    Q_INVOKABLE void search();

private:

    // The accessor methods of the properties
    bb::cascades::GroupDataModel* model() const;

    // The central object to access the contacts service
    bb::pim::contacts::ContactService* m_contactService;

    // The property values
    bb::cascades::GroupDataModel* m_model;

    int m_contactCount;

    // Logging
	void log(const QString &msg);

};

#endif // AddressBook_HPP_

 CPP

 

#include "AddressBook.hpp"

#include <iostream>

using namespace bb::cascades;
using namespace bb::pim::contacts;

AddressBook::AddressBook(QObject *parent)
    : QObject(parent)
    , m_contactService(new ContactService(this))
    , m_model(new GroupDataModel(this))
{
    // Disable grouping in data model
    m_model->setGrouping(ItemGrouping::None);
}

AddressBook::~AddressBook()
{

}

bb::cascades::GroupDataModel* AddressBook::model() const
{
    return m_model;
}

void AddressBook::search()
{

	log("Search");

	QList<Contact> contacts;
	ContactListFilters filter;

	filter.setLimit(0);
	m_contactCount = m_contactService->count(filter);

	log("found " + QString::number(m_contactCount) + " contacts");

    const int maxLimit = 5;
	filter.setLimit(maxLimit);
	m_model->clear();
	do
	{
		contacts = m_contactService->contacts(filter);
		foreach (const Contact &contact, contacts) {

			log("next group");

			const Contact contactDetails = m_contactService->contactDetails(contact.id());
			const QList<ContactAttribute> emails = contactDetails.emails();
			if (!emails.isEmpty()) {
				QVariantMap entry;
				entry["contactId"] = contactDetails.id();
				entry["firstName"] = contactDetails.firstName();
				entry["lastName"] = contactDetails.lastName();
			  	foreach(const ContactAttribute &email, emails) {
			   		log(email.value());
			   		entry["email"] = email.value();
			   	}
				m_model->insert(entry);
			}
		}
		if (contacts.size() == maxLimit)
		{
			filter.setAnchorId(contacts[maxLimit-1].id());
		} else {
			break;
		}
	} while (true);

	log("finished search");
}

void AddressBook::log(const QString &msg) {
	std::cout << msg.toStdString() << std::endl;
}

 

Developer
Posts: 404
Registered: ‎04-04-2012
My Device: BlackBerry Z30/Z10/Z10LE/Q10/PlayBook

Re: Progress Indicator updated from Class

Tonight I thought that maybe if I wrote a custom control with everything inside that it would update to the screen. Still no luck.

 

QML

 

import bb.cascades 1.0
import com.example.contactsearch 1.0

Page {
    Container {
        Label {
            id: searchLabel
            text: "Search Contacts"
            horizontalAlignment: HorizontalAlignment.Center
        }
        ContactSearch {
            id: contactSearch
        }
    }
    
    onCreationCompleted: {
        timer.start();
    }

    attachedObjects: [
        QTimer {
            id: timer
            interval: 0
            onTimeout: {
                timer.stop();
                contactSearch.start();
            }
        }
    ]
}


 HPP

 

#ifndef CONTACTSEARCH_H_
#define CONTACTSEARCH_H_

#include <QtCore/QObject>

#include <bb/cascades/CustomControl>
#include <bb/pim/contacts/ContactService>
#include <bb/pim/contacts/Contact>
#include <bb/pim/contacts/ContactListFilters>

using namespace bb::cascades;

namespace bb {
	namespace cascades {
		class ProgressIndicator;
		class ListView;
	}
}

class ContactSearch : public bb::cascades::CustomControl
{
	Q_OBJECT


public:

	ContactSearch(Container *parent = 0);
	virtual ~ContactSearch();

	Q_INVOKABLE void start();

public slots:

	void onValueChanged(float value);

private:

	// Screen Controls
	ProgressIndicator *m_progressIndicator;
	ListView *m_listView;

    // The central object to access the contacts service
    bb::pim::contacts::ContactService* m_contactService;

    // Logging
	void log(const QString &msg);
};

#endif /* CONTACTSEARCH_H_ */

 CPP

 

#include <iostream>

#include "ContactSearch.hpp"

#include <bb/cascades/Container>
#include <bb/cascades/StackLayout>
#include <bb/cascades/ProgressIndicator>

using namespace bb::cascades;
using namespace bb::pim::contacts;

ContactSearch::ContactSearch(Container *parent)
	: CustomControl(parent)
{
	log("ContactSearch Constructor");
	// Root Container
	Container *rootContainer = Container::create();
	StackLayout* rootContainerStack = StackLayout::create();
	rootContainerStack->setOrientation(LayoutOrientation::TopToBottom);
	rootContainer->setLayout(rootContainerStack);

	// ProgressIndicator
	m_progressIndicator = new ProgressIndicator();
	m_progressIndicator->setHorizontalAlignment(HorizontalAlignment::Center);
	m_progressIndicator->setFromValue(0);
	connect(m_progressIndicator, SIGNAL(valueChanged(float)), this, SLOT(onValueChanged(float)));

	rootContainer->add(m_progressIndicator);
	setRoot(rootContainer);
//	ContactSearch::start();
}

ContactSearch::~ContactSearch()
{
}

void ContactSearch::onValueChanged(float value)
{
	if (value == 0) {
		m_progressIndicator->setState(ProgressIndicatorState::Indeterminate);
	}
}

void ContactSearch::start()
{
	log("Search");

	QList<Contact> contacts;
	ContactListFilters filter;

	m_contactService = new ContactService();
	filter.setLimit(0);
	m_progressIndicator->setToValue(m_contactService->count(filter));

	float progressValue;
	const int maxLimit = 5;
	filter.setLimit(maxLimit);
	do
	{
		contacts = m_contactService->contacts(filter);
		foreach (const Contact &contact, contacts) {

			log("next group");

			const Contact contactDetails = m_contactService->contactDetails(contact.id());
			const QList<ContactAttribute> emails = contactDetails.emails();
			if (!emails.isEmpty()) {
				QVariantMap entry;
				entry["contactId"] = contactDetails.id();
				entry["firstName"] = contactDetails.firstName();
				entry["lastName"] = contactDetails.lastName();
				foreach(const ContactAttribute &email, emails) {
					log(email.value());
					entry["email"] = email.value();
				}
			}
			progressValue += maxLimit;
			m_progressIndicator->setValue(progressValue);
		}
		if (contacts.size() == maxLimit)
		{
			filter.setAnchorId(contacts[maxLimit-1].id());
		} else {
			break;
		}
	} while (true);

	log("finished search");
}

void ContactSearch::log(const QString &msg) {
	std::cout << msg.toStdString() << std::endl;
}

 

Developer
Posts: 6,152
Registered: ‎07-05-2012
My Device: Playbook, Dev Alpha C, Z10 LE, Z30
My Carrier: Orange

Re: Progress Indicator updated from Class

I can't see anywhere where you are releasing the thread to update the ui, the first chance it gets to do this is at the end of the search.

Did you look at the subjects I mentioned in an earlier post?

Sorry I couldn't find the article I had read previously.

If you've been helped click on Like Button, if you've been saved buy the app. Smiley Happy

Developer of stokLocker, Sympatico and Super Sentences.
Developer
Posts: 404
Registered: ‎04-04-2012
My Device: BlackBerry Z30/Z10/Z10LE/Q10/PlayBook

Re: Progress Indicator updated from Class

I have been trying to look at examples and documentation about what you suggested. But no lightbulbs are going off in my head yet. I work better with examples and snippets rather than worded documentation. I think I saw the same link that you posted, but it was no longer available. I guess I don't know exactly where to start. It would be nice if Blackberry had an article that said "If you want to release a thread to the UI, here is what you do". Smiley Wink

Highlighted
Developer
Posts: 6,152
Registered: ‎07-05-2012
My Device: Playbook, Dev Alpha C, Z10 LE, Z30
My Carrier: Orange

Re: Progress Indicator updated from Class

I understand, the code I have in my app is pretty convoluted and linked to XML import/export and SQL lookup and so not easy to chop down otherwise I would have posted by now.

If I get the time I will try and write a sample but I doubt it will be anytime soon.

It really is as simple as running a concurrent thread though either using QTconcurrent or QThread or one of the many other higher level ways such as AsyncWorker, etc. etc.

Hopefully someone can beat me to the punch with an example for you.

If you've been helped click on Like Button, if you've been saved buy the app. Smiley Happy

Developer of stokLocker, Sympatico and Super Sentences.
Retired
Posts: 749
Registered: ‎12-16-2008
My Device: BlackBerry Z30
My Carrier: Bell

Re: Progress Indicator updated from Class

Here is some information on how to use QThread, which is a very good thing to use when trying to play around with the Contact Service.

 

http://supportforums.blackberry.com/t5/Native-Development-Knowledge/The-Recommended-Way-to-Use-QThre...

Paul Bernhardt
Application Development Consultant
BlackBerry
@PBernhardt

Did this answer your question? Please accept this post as the solution.
Found a bug? Report it to the Developer Issue Tracker
Developer
Posts: 404
Registered: ‎04-04-2012
My Device: BlackBerry Z30/Z10/Z10LE/Q10/PlayBook

Re: Progress Indicator updated from Class

Paul Thanks. I have already looked at this. I have found all kinds of docs and snippets of code, but no real simple examples of how to impliment it with what I am trying. There really should be some example by Blackberry to display a progress bar (unlock the UI) when a process is locking the UI.

Retired
Posts: 749
Registered: ‎12-16-2008
My Device: BlackBerry Z30
My Carrier: Bell

Re: Progress Indicator updated from Class

Is the UI actually blocked? It never should be, really. An unblocked UI is one of the most important parts to building a responsive application.

Paul Bernhardt
Application Development Consultant
BlackBerry
@PBernhardt

Did this answer your question? Please accept this post as the solution.
Found a bug? Report it to the Developer Issue Tracker