Get notification when contacts are added/deleted/updated - c++

On BlackBerry 10, how can my Qt application be notified when a contact is added, deleted, or updated? Is there a contact API?

QContactManager has these 3 signals:
void QContactManager::contactsAdded ( const QList<QContactLocalId> & contactIds );
void QContactManager::contactsChanged ( const QList<QContactLocalId> & contactIds );
void QContactManager::contactsRemoved ( const QList<QContactLocalId> & contactIds );
Connect your slots to them in order to receive the notifications.

---hpp file------
API : bb::pim::contacts
bb/pim/contacts/ContactService
bb/pim/contacts/Contact
bb/pim/contacts/ContactAttributeBuilder
bb/pim/contacts/ContactBuilder..
see folwing example
#include <bb/pim/contacts/ContactService>
#include <QtCore/QObject>
class ContactEditor : public QObject
{
Q_OBJECT
// The data properties of the contact that is created or updated
Q_PROPERTY(QString firstName READ firstName WRITE setFirstName NOTIFY firstNameChanged)
Q_PROPERTY(QString lastName READ lastName WRITE setLastName NOTIFY lastNameChanged)
Q_PROPERTY(QDateTime birthday READ birthday WRITE setBirthday NOTIFY birthdayChanged)
Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
// Defines whether the editor is in 'create' or 'edit' mode
Q_PROPERTY(Mode mode READ mode WRITE setMode NOTIFY modeChanged)
Q_ENUMS(Mode)
public:
/**
* Describes the mode of the contact editor.
* The mode information are used to adapt the behavior of the editor and
* provide hints to the UI.
*/
enum Mode {
CreateMode,
EditMode
};
ContactEditor(bb::pim::contacts::ContactService *service, QObject *parent = 0);
void setMode(Mode mode);
Mode mode() const;
public Q_SLOTS:
/**
* Loads the contact with the given ID.
*/
void loadContact(bb::pim::contacts::ContactId contactId);
/**
* Save the currently loaded contact if in 'edit' mode or creates a new one
* if in 'create' mode.
*/
void saveContact();
/**
* Resets all fields of the contact editor.
*/
void reset();
Q_SIGNALS:
// The change notification signals of the properties
void firstNameChanged();
void lastNameChanged();
void birthdayChanged();
void emailChanged();
void modeChanged();
private:
// The accessor methods of the properties
void setFirstName(const QString &firstName);
QString firstName() const;
void setLastName(const QString &lastName);
QString lastName() const;
void setBirthday(const QDateTime &birthday);
QDateTime birthday() const;
void setEmail(const QString &email);
QString email() const;
// The central object to access the contact service
bb::pim::contacts::ContactService *m_contactService;
// The ID of the currently loaded contact (if in 'edit' mode)
bb::pim::contacts::ContactId m_contactId;
// The property values
QString m_firstName;
QString m_lastName;
QDateTime m_birthday;
QString m_email;
Mode m_mode;
};
//! [0]
#endif
-----cpp file -----
using namespace bb::pim::contacts;
//! [0]
/**
* A helper method to update a single attribute on a Contact object.
* It first deletes the old attribute (if it exists) and adds the attribute with the
* new value afterwards.
*/
template <typename T>
static void updateContactAttribute(ContactBuilder &builder, const Contact &contact,
AttributeKind::Type kind, AttributeSubKind::Type subKind,
const T &value)
{
// Delete previous instance of the attribute
QList<ContactAttribute> attributes = contact.filteredAttributes(kind);
foreach (const ContactAttribute &attribute, attributes) {
if (attribute.subKind() == subKind)
builder.deleteAttribute(attribute);
}
// Add new instance of the attribute with new value
builder.addAttribute(ContactAttributeBuilder()
.setKind(kind)
.setSubKind(subKind)
.setValue(value));
}
//! [0]
//! [1]
ContactEditor::ContactEditor(ContactService *service, QObject *parent)
: QObject(parent)
, m_contactService(service)
, m_contactId(-1)
, m_birthday(QDateTime::currentDateTime())
, m_mode(CreateMode)
{
}
//! [1]
//! [2]
void ContactEditor::loadContact(ContactId contactId)
{
m_contactId = contactId;
// Load the contact from the persistent storage
const Contact contact = m_contactService->contactDetails(m_contactId);
// Update the properties with the data from the contact
m_firstName = contact.firstName();
m_lastName = contact.lastName();
m_birthday = QDateTime::currentDateTime();
const QList<ContactAttribute> dateAttributes = contact.filteredAttributes(AttributeKind::Date);
foreach (const ContactAttribute &dateAttribute, dateAttributes) {
if (dateAttribute.subKind() == AttributeSubKind::DateBirthday)
m_birthday = dateAttribute.valueAsDateTime();
}
m_email.clear();
const QList<ContactAttribute> emails = contact.emails();
if (!emails.isEmpty())
m_email = emails.first().value();
// Emit the change notifications
emit firstNameChanged();
emit lastNameChanged();
emit birthdayChanged();
emit emailChanged();
}
//! [2]
//! [3]
void ContactEditor::saveContact()
{
if (m_mode == CreateMode) {
// Create a builder to assemble the new contact
ContactBuilder builder;
// Set the first name
builder.addAttribute(ContactAttributeBuilder()
.setKind(AttributeKind::Name)
.setSubKind(AttributeSubKind::NameGiven)
.setValue(m_firstName));
// Set the last name
builder.addAttribute(ContactAttributeBuilder()
.setKind(AttributeKind::Name)
.setSubKind(AttributeSubKind::NameSurname)
.setValue(m_lastName));
// Set the birthday
builder.addAttribute(ContactAttributeBuilder()
.setKind(AttributeKind::Date)
.setSubKind(AttributeSubKind::DateBirthday)
.setValue(m_birthday));
// Set the email address
builder.addAttribute(ContactAttributeBuilder()
.setKind(AttributeKind::Email)
.setSubKind(AttributeSubKind::Other)
.setValue(m_email));
// Save the contact to persistent storage
m_contactService->createContact(builder, false);
} else if (m_mode == EditMode) {
// Load the contact from persistent storage
Contact contact = m_contactService->contactDetails(m_contactId);
if (contact.id()) {
// Create a builder to modify the contact
ContactBuilder builder = contact.edit();
// Update the single attributes
updateContactAttribute<QString>(builder, contact, AttributeKind::Name, AttributeSubKind::NameGiven, m_firstName);
updateContactAttribute<QString>(builder, contact, AttributeKind::Name, AttributeSubKind::NameSurname, m_lastName);
updateContactAttribute<QDateTime>(builder, contact, AttributeKind::Date, AttributeSubKind::DateBirthday, m_birthday);
updateContactAttribute<QString>(builder, contact, AttributeKind::Email, AttributeSubKind::Other, m_email);
// Save the updated contact back to persistent storage
m_contactService->updateContact(builder);
}
}
}
//! [3]
//! [4]
void ContactEditor::reset()
{
// Reset all properties
m_firstName.clear();
m_lastName.clear();
m_birthday = QDateTime::currentDateTime();
m_email.clear();
// Emit the change notifications
emit firstNameChanged();
emit lastNameChanged();
emit birthdayChanged();
emit emailChanged();
}
//! [4]
void ContactEditor::setFirstName(const QString &firstName)
{
if (m_firstName == firstName)
return;
m_firstName = firstName;
emit firstNameChanged();
}
QString ContactEditor::firstName() const
{
return m_firstName;
}
void ContactEditor::setLastName(const QString &lastName)
{
if (m_lastName == lastName)
return;
m_lastName = lastName;
emit lastNameChanged();
}
QString ContactEditor::lastName() const
{
return m_lastName;
}
void ContactEditor::setBirthday(const QDateTime &birthday)
{
if (m_birthday.date() == birthday.date())
return;
m_birthday = birthday;
emit birthdayChanged();
}
QDateTime ContactEditor::birthday() const
{
return m_birthday;
}
void ContactEditor::setEmail(const QString &email)
{
if (m_email == email)
return;
m_email = email;
emit emailChanged();
}
QString ContactEditor::email() const
{
return m_email;
}
void ContactEditor::setMode(Mode mode)
{
if (m_mode == mode)
return;
m_mode = mode;
emit modeChanged();
}
ContactEditor::Mode ContactEditor::mode() const
{
return m_mode;
}

use alert like this -->
alert(tr("contact saved"));
alert(tr("contact added"));
alert(tr("contact deleted"));
refer following sample
-----------qml--------------
Button {
horizontalAlignment: HorizontalAlignment.Center
text: qsTr("Update")
onClicked: {
_app.updateRecord(idUpdateTextField.text, firstNameUpdateTextField.text, lastNameUpdateTextField.text);
}
}
-----------------cpp file-------------------
bool App::updateRecord(const QString &customerID, const QString &firstName, const QString &lastName)
{
bool intConversionGood = false;
const int customerIDKey = customerID.toInt(&intConversionGood);
if (!intConversionGood) {
alert(tr("You must provide valid integer key."));
return false;
}
QSqlDatabase database = QSqlDatabase::database();
QSqlQuery query(database);
const QString sqlCommand = "UPDATE customers "
" SET firstName = :firstName, lastName = :lastName"
" WHERE customerID = :customerID";
query.prepare(sqlCommand);
query.bindValue(":firstName", firstName);
query.bindValue(":lastName", lastName);
query.bindValue(":customerID", customerIDKey);
bool updated = false;
if (query.exec()) {
if (query.numRowsAffected() > 0) {
alert(tr("Customer with id=%1 was updated.").arg(customerID));
updated = true;
} else {
alert(tr("Customer with id=%1 was not found.").arg(customerID));
}

Related

SQL Parameter count mismatch on QTableView

I have a GUI composed of:
N.1 GraphicsView
N.1 QTableView
N.1 dialog that opens up as needed by the user after doubleclicking on each row of the QTableView
I can capture features on the QGraphicsView with the mouse click. After the feature is drawn I right click and open up a dialog like the one in the figure:
After hitting accept: I register the feature as the row index of the QTableView as shown below:
If I doubleclick on each row I am able to open again the same dialog with the information previously saved. I do that because I may need to change the name of the image and call it differently.
The problem I currently have is that I ma receiving a weird Parameter count mismatch from the compiler and I don't understand why and it should be due to QSQLITE
Below is what I am trying to do along with the most important part of the code:
parameters.h
typedef struct Param
{
int mId;
QString mName;
QByteArray mSave;
} Param;
class Parameters
{
public:
Parameters() = default;
Parameters(Param newdata);
Parameters(int id, const QString &name, const QByteArray &save);
int id() const { return data.mId; }
QString name() const {return data.mName; }
QByteArray save() const {return data.mSave; }
Param getData() const { return data; }
void setData(Param ndat) { data = ndat; }
private:
Param data;
};
parameters.cpp
#include "parameters.h"
Parameters::Parameters(Param newdata) {
data = newdata;
}
Parameters::Parameters(int id, const QString &name,
const QByteArray &save) {
data.mId = id;
data.mSave = save;
data.mName = name;
}
the database is set in the following dataleftcamera.h part:
public:
explicit dataLeftCamera(QObject *parent = nullptr);
bool addItem(Parameters* mParameters);
bool updateItem(int itemId, Parameters* mParameters);
QSqlDatabase getDatabase();
private:
QString mError;
QSqlDatabase mDatabaseLeft;
The table is initiated here on dataleftcamera.cpp and here is where the compiler is giving the Parameter count mismatch error specifically on the function updateItem
#define CREATE_TABLE \
" CREATE TABLE IF NOT EXISTS leftCamTable" \
" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" \
", name TEXT NOT NULL" \
", save BLOB NOT NULL)"
dataLeftCamera::dataLeftCamera(QObject *parent) : QObject (parent)
{}
bool dataLeftCamera::addItem(Parameters *mParameters)
{
QSqlQuery qry;
qry.prepare("INSERT INTO leftCamTable (name, save)"\
" VALUES (?,?)");
qry.addBindValue(mParameters->name());
qry.addBindValue(mParameters->save());
bool ok = qry.exec();
if(!ok) {
mError = qry.lastError().text();
qDebug() << mError;
}
}
bool dataLeftCamera::updateItem(int itemId, Parameters *mParameters)
{
QSqlQuery qry;
qry.prepare(" UPDATE lefCamTable SET " \
" name = ?," \
" save = ?" \
" WHERE id = ?");
qry.addBindValue(mParameters->name());
qry.addBindValue(mParameters->save());
qry.addBindValue(itemId);
bool ok = qry.exec();
if(!ok) {
mError = qry.lastError().text();
qDebug() << mError;
}
}
On the mainwindow.cpp is the part where I update the item after I doubleclick on the row to change the name of the image and accept again the modification:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDatabaseLeftCamera = new dataLeftCamera(this);
mModelLeftCamera = nullptr;
mModelLeftCamera = new QSqlTableModel(this);
ui->tableView->setModel(mModelLeftCamera);
connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)),
this, SLOT(onTableClick(QModelIndex)));
// temporary folder
temporaryFolder = "/home/name/Desktop/tempDBFolder/tmp.db";
QFile dbRem(temporaryFolder);
dbRem.remove();
mDatabaseLeftCamera->inizializationDatabaseLeftCamera(temporaryFolder);
mDatabaseLeftCamera->configurationDatabaseLeftCamera();
mModelLeftCamera = new QSqlTableModel(this, mDatabaseLeftCamera->getDatabase());
mModelLeftCamera->setTable("leftCamTable");
mModelLeftCamera->select();
ui->tableView->setModel(mModelLeftCamera);
ui->tableView->showColumn(true);
}
// opening the dialog for the first time after capturing the image
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
// operations
Param result = d.getData();
Parameters* param = new Parameters(result);
mDatabaseLeftCamera->addItem(param);
mModelLeftCamera->select();
ui->tableView->show();
}
// This is the doubleclick that re-opens the small dialog to change the name of the feature
void MainWindow::onTableClick(const QModelIndex &index)
{
int row = index.row();
Param currentData;
int ItemId = index.sibling(row, 0).data().toInt();
currentData.mName = index.sibling(row, 1).data().toString();
currentData.mSave = index.sibling(row, 2).data().toByteArray();
QPixmap iconPix;
if(!iconPix.loadFromData(index.sibling(row, 2).data().toByteArray())) {
}
clipSceneDialog d(this);
d.show();
d.setData(currentData);
d.setImage(iconPix.toImage());
if(d.exec() == QDialog::Rejected) {
return;
} else {
//
}
Param result = d.getData();
Parameters* param = new Parameters(result);
mDatabaseLeftCamera->updateItem(ItemId,param);
mModelLeftCamera->select();
ui->tableView->show();
}
For completeness see the error the compiler is giving if that helps:
I am sorry if this is trivial but I checked the updateItem(ItemId,param)
and thanks for shedding light on this.
Try and add some debug printing. Especially for the parameters that you add to the prepared statement. qry.addBindValue converts your value to a QVariant. Based on the documentation QVariants become NULL when they contain no data:
QVariant x(QString());
// x.isNull() == true
In case there is a problem retrieving your parameters, this could explain the violation of the NOT NULL constraints.

Qt ListView doesn't show C++ model content

I'm creating a QList<> in C++ using a QML ListView to display it. The application runs without errors but the ListView stubbornly remains empty.
The QML will show a rectangle for the presence of each list item.
I checked the UI code by creating a list in QML.
It displays correctly for the QML created list.
Here's my QML:
import Processes 1.0
...
ListView {
id: qInterfaceList
height: parent.height;
width: parent.width;
model: myModel
orientation: ListView.Vertical
delegate:
Rectangle {
height: 30;
width: 120;
border.color: "red"
border.width: 3
}
The C++ code that creates and registers the list object:
// Register C++ classes as a QML type named Processes (version 1.0)
qmlRegisterType<Process>("Processes", 1, 0, "Process");
QQmlApplicationEngine engine;
// read the configuration file
Config conf;
if ( conf.read() )
{
QQmlContext* ctxt = engine.rootContext();
if ( ctxt )
{
qDebug()
<< "--- conf.Interfaces: "
<< conf.Interfaces.length()
;
ConfigInterface c;
QVariant v = QVariant::fromValue( conf.Interfaces );
qDebug()
<< "--- ConfigInterface: "
<< v
<< "--- typeName: "
<< v.typeName()
;
ctxt->setContextProperty("myModel", QVariant::fromValue( conf.Interfaces ));
}
}
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
To debug I output information about list from C++ and QML:
In C++ the count of list items is correct.
In C++ the conversion to QVariant is working.
In QML it sees the defined list.
The debugging output:
Debugging starts
--- conf.Interfaces: 65
--- ConfigInterface: QVariant(QList<ConfigInterface*>, ) --- typeName: QList<ConfigInterface*>
qml: myModel: QVariant(QList<ConfigInterface*>)
Debugging has finished
Any ideas what's wrong or how to debug this?
Thanks
EDIT: Here's the class being used as a list item
Class declaration:
class ConfigInterface : public QObject
{
Q_OBJECT
Q_PROPERTY(QString sql READ getTag WRITE setTag NOTIFY tagChanged)
Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged)
public:
/*explicit*/ ConfigInterface();
/*explicit*/ ConfigInterface(QObject *parent);
~ConfigInterface();
// Copy constructor needed because these are copied when added to a QList
ConfigInterface(const ConfigInterface &p2) {_tag = p2._tag; _description = p2._description; }
QString getDescription() const;
void setDescription(QString&);
QString getTag() const;
void setTag(QString&);
signals:
void tagChanged(QString);
void descriptionChanged(QString);
public:
QString _tag;
QString _description;
QString QueryTemplate;
QString ConnectString;
QString MinimumId;
};
Q_DECLARE_METATYPE(ConfigInterface*)
C++ code:
ConfigInterface::ConfigInterface()
: QObject( nullptr )
{
}
ConfigInterface::ConfigInterface(QObject* parent)
: QObject(parent)
{
}
ConfigInterface::~ConfigInterface()
{
}
QString ConfigInterface::getTag() const
{
return _tag;
}
void ConfigInterface::setTag(QString& str)
{
_tag = str;
emit tagChanged(_tag);
}
The main problem is caused because it has a list of ConfigInterface *, according to the examples provided in the documentation should be a list of QObject *:
class Config{
[...]
public:
QList<QObject *> Interfaces;
[...]
};
In addition to this you should get the following warning:
/..../configinterface.h:17: warning: base class ‘class QObject’ should be explicitly initialized in the copy constructor [-Wextra]
ConfigInterface(const ConfigInterface &p2) {_tag = p2._tag; _description = p2._description; }
^~~~~~~~~~~~~~~
This is caused because QObject and its derived classes must not have a copy constructor or assignment operator, For more information read the following:
http://doc.qt.io/qt-5/qobject.html#no-copy-constructor-or-assignment-operator
Another improvement is that both constructors can be united in only one, in the end their class could have the following structure:
configinterface.h
#ifndef CONFIGINTERFACE_H
#define CONFIGINTERFACE_H
#include <QObject>
class ConfigInterface : public QObject
{
Q_OBJECT
Q_PROPERTY(QString sql READ getTag WRITE setTag NOTIFY tagChanged)
Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged)
public:
ConfigInterface(QObject *parent=Q_NULLPTR);
~ConfigInterface();
QString getTag() const;
void setTag(const QString &tag);
QString getDescription() const;
void setDescription(const QString &description);
signals:
void tagChanged(QString);
void descriptionChanged(QString);
private:
QString _tag;
QString _description;
QString QueryTemplate;
QString ConnectString;
QString MinimumId;
};
#endif // CONFIGINTERFACE_H
configinterface.cpp
#include "configinterface.h"
ConfigInterface::ConfigInterface(QObject* parent)
: QObject(parent)
{
}
ConfigInterface::~ConfigInterface()
{
}
QString ConfigInterface::getDescription() const
{
return _description;
}
void ConfigInterface::setDescription(const QString &description)
{
if(_description == description)
return;
emit descriptionChanged(description);
_description = description;
}
QString ConfigInterface::getTag() const
{
return _tag;
}
void ConfigInterface::setTag(const QString &tag)
{
if(tag == _tag)
return;
emit tagChanged(tag);
_tag = tag;
}

QML C++ integration: View Models for dynamic classes containing arrays of other classes

I'm coming from c++ and I'm having difficulties making my classes work the way I want them to. The program I am building is rather simple. The user creates an album or two and can fill that album with cards. Those albums are created dynamically on run time.
Here is my initial class diagram for the back-end c++ stuff:
class diagram
And here is how the UI looks like:
QML UI
The crux of the problem:
A set of changes to the classes are necessary to make them available to QML.
Inheritance: Classes need to inherit from QObject or QAbstractListModel depending on their role.
Containers: Dynamically instantiated objects within an object, through pointers or not, need to be placed in a QList or something similar.
Root context property: The model needs to be registered so that it can be made available in a Listview for example.
I have had some success setting an instance of an Album class, with some changes made to it, as a root context property. The model worked correctly and the cards in the album displayed correctly in a ListView.
The problem here is that I need to step out of the picture another level, I should set a class that contains the Albums as the root context property and let the user create, and add, Albums to it during run time. I will also need to also display the albums and the cards inside them through different views.
I am wondering if I should implement a table/tree data structure for the albums and the cards and somehow pass the columns and rows to the models or If there is a tidier approach that I am not aware of.
Here are my classes so far:
album.h
#ifndef ALBUM_H
#define ALBUM_H
#include <QObject>
#include <QString>
#include <QAbstractListModel>
#include <QQmlListProperty>
#include "card.h"
class Album : public QAbstractListModel
{
Q_OBJECT
public:
enum AlbumRoles {
CardIDRole = Qt::UserRole + 1,
NameRole,
ImageURLRole,
SubtypeRole,
SupertypeRole,
NumberRole,
ArtistRole,
RarityRole,
SeriesRole,
SetRole,
SetCodeRole,
ConditionRole,
StatusRole
};
Album(QObject *parent = 0);
QString name() const;
void addCard(const Card &card);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
protected:
QHash<int, QByteArray> roleNames() const;
private:
QList<Card> m_cards;
QString m_name;
};
album.cpp
#include "album.h"
Album::Album(QObject *parent)
: QAbstractListModel(parent)
{
}
QString Album::name() const
{
return m_name;
}
void Album::addCard(const Card &card)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_cards << card;
endInsertRows();
}
int Album::rowCount(const QModelIndex & parent) const {
Q_UNUSED(parent);
return m_cards.count();
}
QVariant Album::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= m_cards.count())
return QVariant();
const Card &card = m_cards[index.row()];
if (role == CardIDRole)
return card.cardID();
else if (role == NameRole)
return card.name();
else if (role == ImageURLRole)
return card.imageURL();
else if (role == SubtypeRole)
return card.subtype();
else if (role == SupertypeRole)
return card.supertype();
else if (role == NumberRole)
return card.number();
else if (role == ArtistRole)
return card.artist();
else if (role == RarityRole)
return card.rarity();
else if (role == SeriesRole)
return card.series();
else if (role == SetRole)
return card.set();
else if (role == SetCodeRole)
return card.setCode();
else if (role == ConditionRole)
return card.condition();
else if (role == StatusRole)
return card.status();
return QVariant();
}
QHash<int, QByteArray> Album::roleNames() const {
QHash<int, QByteArray> roles;
roles[CardIDRole] = "cardID";
roles[NameRole] = "name";
roles[ImageURLRole] = "imageURL";
roles[SubtypeRole] = "subtype";
roles[SupertypeRole] = "supertype";
roles[NumberRole] = "number";
roles[ArtistRole] = "artist";
roles[RarityRole] = "rarity";
roles[SeriesRole] = "series";
roles[SetRole] = "set";
roles[SetCodeRole] = "setCode";
roles[ConditionRole] = "condition";
roles[StatusRole] = "status";
return roles;
}
card.h
#ifndef CARD_H
#define CARD_H
#include <QString>
class Card
{
public:
// Standard Qt constructor with parent for memory management
Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
QString cardID() const;
QString name() const;
QString imageURL() const;
QString subtype() const;
QString supertype() const;
int number() const;
QString artist() const;
QString rarity() const;
QString series() const;
QString set() const;
QString setCode() const;
QString condition() const;
QString status() const;
private:
// private members
QString m_cardID;
QString m_name;
QString m_imageURL;
QString m_subtype;
QString m_supertype;
int m_number;
QString m_artist;
QString m_rarity;
QString m_series;
QString m_set;
QString m_setCode;
QString m_condition;
QString m_status;
};
#endif //CARD_H
card.cpp
#include "card.h"
// Standard Qt constructor with parent for memory management
Card::Card(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status): m_cardID(cardID), m_name(name), m_imageURL(imageURL), m_subtype(subtype), m_supertype(supertype), m_number(number), m_artist(artist), m_rarity(rarity), m_series(series), m_set(set), m_setCode(setCode), m_condition(condition), m_status(status)
{
}
QString Card::cardID() const
{
return m_cardID;
}
QString Card::name() const
{
return m_name;
}
QString Card::imageURL() const
{
return m_imageURL;
}
QString Card::subtype() const
{
return m_subtype;
}
QString Card::supertype() const
{
return m_supertype;
}
int Card::number() const
{
return m_number;
}
QString Card::artist() const
{
return m_artist;
}
QString Card::rarity() const
{
return m_rarity;
}
QString Card::series() const
{
return m_series;
}
QString Card::set() const
{
return m_set;
}
QString Card::setCode() const
{
return m_setCode;
}
QString Card::condition() const
{
return m_condition;
}
QString Card::status() const
{
return m_status;
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "app.h"
#include "album.h"
#include "card.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Album>("Classes.PokemonApp", 1, 0, "Album");
qmlRegisterType<Card>("Classes.PokemonApp", 1, 0, "Card");
App pokeApp;
Album myAlbumModel;
Card cardOne("xy7-2","Gloom","http://s3.amazonaws.com/pokemontcg/xy7/2.png","Stage 1","Pokémon",2,"Masakazu Fukuda","Uncommon","XY","Ancient Origins","xy7","Mint","In my collection");
Card cardTwo("xy7-7","Sceptile-EX","https://s3.amazonaws.com/pokemontcg/xy7/7.png","EX","Pokémon",7,"Eske Yoshinob","Rare Holo EX","XY","Ancient Origins","xy7","Used","Duplicate");
myAlbumModel.addCard(cardOne);
myAlbumModel.addCard(cardTwo);
pokeApp.addAlbum(myAlbumModel);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("pokeApp", &pokeApp);
return app.exec();
}
CardsView.qml
import QtQuick 2.0
import Classes.PokemonApp 1.0
// Cards Deligate
ListView {
width: 200; height: 250
model: myAlbumModel
delegate: Text { text: "Card:"
+ "\n" + "ID: " + cardID
+ "\n" + "Name: " + name
+ "\n" + "Image URL: " + imageURL
+ "\n" + "Subtype: " + subtype
+ "\n" + "Supertype: " + supertype
+ "\n" + "Number: " + number
+ "\n" + "Artist: " + artist
+ "\n" + "Rarity: " + rarity
+ "\n" + "Series: " + series
+ "\n" + "Set: " + set
+ "\n" + "Set code: " + setCode
+ "\n" + "Condition: " + condition
+ "\n" + "Status: " + status }
}
So to be clear, this is what I need help with:
Give the user the ability to create albums during run time, let the user add cards to albums and have models display the albums and the cards in them in different views.
Assuming that your App class has a list of Album objects, accessible as a property, adding an album would simply be a slot or invokable method in that class.
Something like
public slots:
void addAlbum(const QString &name);
That would create an instance of Album, put it in the list and emit the notify signal for the property.
For adding a Card you could add a similar slot to the Album class, something like
pulic slots:
void addCard(const QString &cardID, const QString &name, const QString &imageURL, const QString &subtype, const QString &supertype, const int &number, const QString &artist, const QString &rarity, const QString &series, const QString &set, const QString &setCode, const QString &condition, const QString &status);
which would simply create a Card add then call the addCard overload.
I am now finished with the project and I have accomplished all the goals I had and overcome the problems that I have asked for help here and a few other ones that showed up later on.
For those who might find this page while searching for solutions for similar problems they have, you can find all the source code for my complete project on my Github page here: https://github.com/Nizars/PokeApp
I would like to point out a few changes that I have made:
I have switched to an SQL method of storing the cards, the albums and the card images as well.
I have used different models for different views. A proxy model for relational queries used by the table view and two sql models for other functionalities.
I also use a custom made image provider for checking for images requested from the qml side in the database first before requesting the images from an API through https and storing it in the database if it wasn't found there at first.
I have also switched to a borderless window to get rid of the windows default window border and created my own close, minimize and maximize buttons. I have also used a mouse position provider to make dragging the window around possible and seamless.
I have also added a working RSS feed to the homepage and used a page swipe view for the different pages in the app, I used connections to signal messages between the different qml documents so that data can be sent between them.
Final note: You need to include your own OpenSSL library includes for the network requests to work and create the sql tables in your local server as well as connect the app to it in pokeapp.cpp.
Feel free to ask me any questions if you need any help.

How to make QNetworkReply to return custom data?

I am going to use QNetworkAccessManager to make requests to HTTP server by my mobile app to the server. The question is, how do you link custom data to each request ? I tried to subclass QNetworkReply but I found out that I have to implement virtual methods close() and isSequential() but I don't know what those should return so I am afraid I am going to break network request functionality.
For example, when my app does the log in procedure, it has to store the email address of the account:
class MyApp : public QObject
{
Q_OBJECT
private:
QNetworkRequest request;
QNetworkReply *reply;
QNetworkAccessManager *manager;
...
}
void MyApp::do_log_in(QString email, QString password) {
QString s;
someobject.email=email; // <-- I have to store email address before sending request to server, but where do I store it?
s.append("http://myapp.com/do-auth.php?email=");
s.append(QUrl::toPercentEncoding(email));
s.append("&password=");
s.append(QUrl::toPercentEncoding(password));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(login_finished(QNetworkReply*)));
request.setUrl(QUrl(s));
manager->get(request);
}
void MyApp::login_finished(QNetworkReply *rep) {
DepservReply *reply;
QString email;
....
email= ...... // <-- I need to get the email address from QNetworkReply object somehow
///my code here handling server reply
....
}
So, how do I implement storage and retrieval of email in my case, what classes should I subclass and what methods should I re-implement ?
You can leverage the dynamic property system available in each QObject and hold the data in the reply:
// https://github.com/KubaO/stackoverflown/tree/master/questions/network-reply-tracking-40707025
#include <QtNetwork>
class MyCtl : public QObject
{
Q_OBJECT
QNetworkAccessManager manager{this};
// ...
void reply_finished(QNetworkReply *reply);
public:
MyCtl(QObject *parent = nullptr);
void do_log_in(const QString &email, const QString &password);
};
static const char kAuthGetSalt[] = "req_auth-get-salt";
static const char kDoAuth[] = "req_do-auth";
static const char kEmail[] = "req_email";
static const char kPassword[] = "req_password";
static const auto authGetSaltUrl = QStringLiteral("https://myapp.com/auth-get-salt.php?email=%1");
static const auto doAuthUrl = QStringLiteral("https://myapp.com/do-auth.php?email=%1&passwordHash=%2");
MyCtl::MyCtl(QObject *parent) : QObject{parent}
{
connect(&manager, &QNetworkAccessManager::finished, this, &MyCtl::reply_finished);
}
void MyCtl::do_log_in(const QString &email, const QString &password) {
auto url = authGetSaltUrl.arg(email);
auto reply = manager.get(QNetworkRequest{url});
reply->setProperty(kAuthGetSalt, true);
reply->setProperty(kEmail, email);
reply->setProperty(kPassword, password);
}
void MyCtl::reply_finished(QNetworkReply *reply) {
if (!reply->property(kAuthGetSalt).isNull()) {
reply->deleteLater(); // let's not leak the reply
if (reply->error() == QNetworkReply::NoError) {
auto salt = reply->readAll();
auto email = reply->property(kEmail).toString();
auto password = reply->property(kPassword).toString();
Q_ASSERT(!password.isEmpty() && !email.isEmpty());
QCryptographicHash hasher{QCryptographicHash::Sha1};
hasher.addData(salt); // the server must hash the same way
hasher.addData("----");
hasher.addData(password.toUtf8());
auto hash = hasher.result().toBase64(QByteArray::Base64UrlEncoding);
auto url = doAuthUrl.arg(email).arg(QString::fromLatin1(hash));
auto reply = manager.get(QNetworkRequest{url});
reply->setProperty(kDoAuth, true);
reply->setProperty(kEmail, email);
}
}
else if (!reply->property(kDoAuth).isNull()) {
if (reply->error() == QNetworkReply::NoError) {
auto email = reply->property(kEmail).toString();
// ...
}
}
}
Use a constant for a property name to avoid typos by letting the compiler check that you're using a valid identifier.
The example above rectifies the following critical safety issues in your code:
Sending security credentials over a clear connection: use https://, not http://.
Sending a password in cleartext: instead, send a salted hash of it. Your server should generate a random salt for each account when the accounts are created. Existing accounts can be left unsalted, but they should acquire a salt as soon as the user changes the password.
Also note that a QString to QUrl conversion will automatically percent-encode the string, so doing it explicitly is unnecessary.
In this case email is part of the request's URL so you could just extract it from there (the QNetworkReply has access to the QNetworkRequest it is handling, see QNetworkReply::request()).
You an also store more or less any kind of data as a dynamic property because QNetworkReply is a QObject derived class, see QObject::setProperty().
You can subclass QNAM to get more control of it.
network.h
class QNAMNetwork : public QNetworkAccessManager
{
Q_OBJECT
public:
explicit QNAMNetwork(QObject *parent = 0);
~QNAMNetwork();
inline void insertUserValue(const QString & key, const QString & value){this->m_user_values.insert(key,value);}
inline QString getUserValue(const QString & key){return this->m_user_values.value(key);}
signals:
void requestFinished(ExNetwork *, QNetworkReply *);
private slots:
void _sslErrors(QNetworkReply *, const QList<QSslError> &);
void _finished(QNetworkReply *);
private:
QMap<QString, QString> m_user_values;
};
network.cpp
QNAMNetwork::QNAMNetwork(QObject *parent):QNetworkAccessManager(parent)
{
connect(this, &QNAMNetwork::sslErrors, this, &QNAMNetwork::_sslErrors);
connect(this, &QNAMNetwork::finished, this, &QNAMNetwork::_finished);
}
QNAMNetwork::~QNAMNetwork()
{
//qDebug() << __FUNCTION__ << QString::number((long)this,16);
}
void QNAMNetwork::_sslErrors(QNetworkReply * reply, const QList<QSslError> & errors)
{
reply->ignoreSslErrors(errors);
}
void QNAMNetwork::_finished(QNetworkReply * reply)
{
emit requestFinished(this, reply);
}
usecase:
QNAMNetwork * network = new QNAMNetwork(this);
network->insertUserValue("email","yourmail#mail.com");
connect(network, SIGNAL(requestFinished(QNAMNetwork*,QNetworkReply*)), this, SLOT(requestFinished(QNAMNetwork*,QNetworkReply*)));
QNetworkRequest req(QUrl::fromUserInput(query)); //get url
network->get(req);
...
void YourClass::requestFinished(QNAMNetwork * net, QNetworkReply * rep)
{
QString email = net->getUserValue("email");
net->deleteLater();
rep->deleteLater();
}

How to validate email address using qregexp

I need to validate that the text entered in a qlineedit has the form of the regexp, I tried to use this:
void MainWindow::checkReg( QLineEdit& mail, const QCheckBox& skip, string type )
{
if(type == "mail")
{
if( skip.checkState() != 2)
{
QRegExp mailREX("\\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
mailREX.setCaseSensitivity(Qt::CaseInsensitive);
mailREX.setPatternSyntax(QRegExp::Wildcard);
bool regMat = mailREX.exactMatch(mail.text());
if(regMat == false)
{
QMessageBox *message = new QMessageBox(this);
message->setWindowModality(Qt::NonModal);
message->setText("Inserte los datos en el formato correcto");
message->setStandardButtons(QMessageBox::Ok);
message->setWindowTitle("MainWindow");
message->setIcon(QMessageBox::Information);
message->exec();
this->ok = 0;
mail.clear();
}
else
this->ok = 1;
}
}
}
but every mail mail I entered like me#me.com, the error message appear. I also tried using
int regMat = mailREX.indexIn(mail.text());
and it didnt work.
Thanks in advance
Why did you set pattern syntax to wildcard? Your code works (assuming you understand, that your regexp itself is simplified) with RegExp pattern syntax:
QRegExp mailREX("\\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
mailREX.setCaseSensitivity(Qt::CaseInsensitive);
mailREX.setPatternSyntax(QRegExp::RegExp);
qDebug() << mailREX.exactMatch("me#me.com");
prints true
If you want to create a real QValidator for reusing in e.g. QLineEdits, you also have to check for Intermediate email adress strings, else nothing will be accepted unless you COPY&PASTE an email address into the edit.
Here is an example for an EmailValidator:
The emailvalidator.h file:
#ifndef EMAILVALIDATOR_H
#define EMAILVALIDATOR_H
#include <QValidator>
QT_BEGIN_NAMESPACE
class QRegExp;
QT_END_NAMESPACE
class EmailValidator : public QValidator
{
Q_OBJECT
public:
explicit EmailValidator(QObject *parent = 0);
State validate(QString &text, int &pos) const;
void fixup(QString &text) const;
private:
const QRegExp m_validMailRegExp;
const QRegExp m_intermediateMailRegExp;
};
#endif // EMAILVALIDATOR_H
And the emailvalidator.cpp file:
#include "emailvalidator.h"
EmailValidator::EmailValidator(QObject *parent) :
QValidator(parent),
m_validMailRegExp("[a-z0-9._%+-]+#[a-z0-9.-]+\\.[a-z]{2,4}"),
m_intermediateMailRegExp("[a-z0-9._%+-]*#?[a-z0-9.-]*\\.?[a-z]*")
{
}
QValidator::State EmailValidator::validate(QString &text, int &pos) const
{
Q_UNUSED(pos)
fixup(text);
if (m_validMailRegExp.exactMatch(text))
return Acceptable;
if (m_intermediateMailRegExp.exactMatch(text))
return Intermediate;
return Invalid;
}
void EmailValidator::fixup(QString &text) const
{
text = text.trimmed().toLower();
}