So I have FINALLY gotten to the point where I can select multiple items on a ListView:
ListView {
id: lv_stuffs
horizontalAlignment: HorizontalAlignment.Fill
dataModel: _app.personDataModel //REFERENCE 1
multiSelectAction: MultiSelectActionItem {
}
multiSelectHandler {
actions: [
// Add the actions that should appear on the context menu
// when multiple selection mode is enabled
ActionItem {
title: "Search for stuffs"
onTriggered: {
_app.search(lv_stuffs.selectionList());
}
...
And I am sending this selection list through to my search method:
void ApplicationUI::search(const QVariantList &list)
{
alert(QString("%1 items selected").arg(list.length()));
alert(((Person)list.at(0)).firstName);//<---- THIS IS THE PROBLEM
}
I am trying to get the "Person" object out of the GroupedDataModel that originally bound to the item... and I have to say I am more than a little stumped. The person is being added to the personDataModel via a simple insert method in a database class:
personDataModel->insert(person);
and the items are then bound to the ListView in the QML (REFERENCE 1 above). The binding is all fine and the items are visible in the list. What I can't figure out is how to now extract these "Person" objects out of the QVariantList I am sent via the MultiSelectionMethod.
My person class:
Person::Person(QObject *parent) : QObject(parent){}
Person::Person(const QString &id, const QString &firstname, const QString &lastname, QObject *parent)
: QObject(parent)
, m_id(id)
, m_firstName(firstname)
, m_lastName(lastname)
{
}
QString Person::customerID() const
{
return m_id;
}
QString Person::firstName() const
{
return m_firstName;
}
QString Person::lastName() const
{
return m_lastName;
}
void Person::setCustomerID(const QString &newId)
{
if (newId != m_id) {
m_id = newId;
emit customerIDChanged(newId);
}
}
void Person::setFirstName(const QString &newName)
{
if (newName != m_firstName) {
m_firstName = newName;
emit firstNameChanged(newName);
}
}
void Person::setLastName(const QString &newName)
{
if (newName != m_lastName) {
m_lastName = newName;
emit lastNameChanged(newName);
}
}
I have been PAINFULLY following this tutorial here, https://developer.blackberry.com/cascades/documentation/ui/lists/list_view_selection.html, which conveniently stops right where my question begins.
Are you perhaps looking for the value function?
void ApplicationUI::search(const QVariantList &list)
{
alert(QString("%1 items selected").arg(list.length()));
alert(((Person)list.value(0)).firstName);
}
(syntax of the extraction of the firstName value may not be right there, depends on your implementation)
Your Person class will be stored in a QVariant. To accomplish this, Qt has to generate some code specific to your class. It can be done by adding this right after your class definition, in the header file for example: Q_DECLARE_METATYPE(Person). You can read more about this here: http://qt-project.org/doc/qt-4.8/qmetatype.html#Q_DECLARE_METATYPE
Now, to extract the value as a Person object, you can use QVariant::value<T>() (http://qt-project.org/doc/qt-4.8/qvariant.html#value):
alert(list.at(0).value<Person>().firstName());
Related
I tried to define a function that returns std::shared_ptr<OrderModel> as follows:
Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
namespace tradeclient {
class OrderModel : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString marketId READ marketId CONSTANT)
Q_PROPERTY(quint64 id READ id CONSTANT)
...
};
using OrderPtr = std::shared_ptr<OrderModel>;
class MarketModel : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE tradeclient::OrderPtr createLimitOrder()
{
return m_orders.front();
}
private:
//In my app I initialize OrderModel-s and add them so some container.
std::vector<OrderPtr> m_orders;
};
} //namespace tradeclient
Q_DECLARE_METATYPE(tradeclient::OrderPtr)
qRegisterMetaType<tradeclient::OrderPtr>();
and then call createLimitOrder() from QML:
var order = market.createLimitOrder()
console.log("Limit order type: %1, value: %2, id: %3".arg(typeof(order)).arg(JSON.stringify(order)).arg(order.id))
but with no success, because I can't access order.id and the console output is:
Limit order type: object, value: "", id: undefined
I also tried to define functions like this:
Q_INVOKABLE QVariant createLimitOrder()
{
return QVariant::fromValue(m_orders.front());
}
Q_INVOKABLE QSharedPointer<tradeclient::OrderModel> createLimitOrder()
{
return QSharedPointer<tradeclient::OrderModel>(new OrderModel());
}
but nothing changed.
But at least, QML treats order as a non-empty value and the following QML code:
if (order)
console.log("The order is defined.");
else
console.log("The order is not defined.");
prints:
"The order is defined."
So order is defined, but its properties are not. What did I miss?
Buy the way, the above QML code works correctly if I return naked pointer:
Q_INVOKABLE tradeclient::OrderModel* createLimitOrder()
{
return m_orders.front().get();
}
it prints
Limit order type: object, value: {"objectName":"","marketId":"BTCUSDT","id":0,"side":1,"type":0,"status":0,"price":{"precision":2},"stopPrice":{".....
Does QVariant that wraps std::shared_ptr expose object properties to QML?
EDIT1
As far as I understand, Q_DECLARE_SMART_POINTER_METATYPE is a relatively simple thing, it requires the class template to have operator -> (see ./qtbase/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp):
namespace MyNS {
template<typename T>
class SmartPointer
{
T* pointer;
public:
typedef T element_type;
explicit SmartPointer(T* t = nullptr)
: pointer(t)
{
}
T* operator->() const { return pointer; }
};
}
Q_DECLARE_SMART_POINTER_METATYPE(MyNS::SmartPointer)
so it is reasonable to expect that it behaves like a naked pointer in QML. However, I have concerns that this is one of those cases where they simply did not implement this simple thing well enough.
I have a function which calls emitResult after loading data from file.
bool IpResolver::ResolvedInfo::load(QTextStream &in)
{
ResolvedInfo rf;
while (!in.atEnd())
{
QString line = in.readLine();
QStringList list = line.split(' ');
list[0] = rf.country;
list[1] = rf.ip;
if (rf.ip.isEmpty() == false)
{
emitResult(rf);
}
}
}
So here is declaration of emitResult:
private:
void emitResult(const ResolvedInfo &data);
And it gives me this error:
a nonstatic member reference must be relative to a specific object
No idea what should I do.
emitResult is a non-static member function of IpResolver, I presume. Yet you're calling it without any instance, from a subclass IpResolver::ResolvedInfo. Remember that just because the ResolvedInfo is a subclass, doesn't make it special in any other way. Specifically, if it doesn't hold a reference to the instance of the parent class, it won't work the way you expect it to.
There are two general ways to fix your issue:
You can pass a reference to IpResolver to the ResolvedInfo constructor, and retain the reference in the ResolvedInfo instance:
class IpResolver {
class ResolvedInfo {
IpResolver & q;
public:
ResolvedInfo(IpResolver & q) : q(q) { ... }
static bool load(QTextStream &in) {
ResolvedInfo rf;
while (!in.atEnd())
{
QString line = in.readLine();
QStringList list = line.split(' ');
list[0] = rf.country;
list[1] = rf.ip;
if (!rf.ip.isEmpty())
q.emitResult(rf);
}
}
};
void emitResult(const ResolvedInfo &);
...
};
Or you can make the emitResult a static method:
class IpResolver {
...
static void emitResult(const ResolvedInfo &);
};
In the example, I would like to filter the data contained in rsModel by making a selection in sectionComboBox. In sectionComboBox, I display the NAME of a section, and would like to filter rsModel to entries with a matching SECTION_ID
ComboBox {
id: sectionComboBox
model: sectionModel
textRole: "NAME"
onCurrentTextChanged: rsModel.setQLFilter("SECTION_ID=" + ??)
}
QLTableModel subclasses QSqlRelationalTableModel and implements setQLFilter
void QLSqlTableModel::setQLFilter(const QString filter){
setFilter(filter);
}
Here's code that seems to be working. Please suggest any improvements you might see.
QLSqlTableModel.cpp (derived from QSqlRelationTableModel)
void QLSqlTableModel::setQLFilter(const QString & field, int value){
QString tempString = field + QString::number(value);
setFilter(tempString);
}
QVariant QLSqlTableModel::data(int row, int role) const {
QModelIndex qmi;
qmi = index(row, role, qmi);
return data(qmi, role);
}
main.qml
ComboBox {
id: sectionComboBox
model: sectionModel
textRole: "NAME"
onCurrentIndexChanged: {
rsModel.setQLFilter("SECTION_ID=", sectionModel.data(currentIndex, 0))
}
}
Quick googling returned no results on how to properly use QDjango's Foreign Keys - there are functions
QDjangoModel::foreignKey and QDjangoModel::setForeignKey - but I couldn't find any good examples on how to use them in practice.
Its quite old, maybe will help someone else.. I searched for FKs in QDjango for eons and found something in google group, there is also nice documentation with some basic examples(but no FK examples)
I create table models using QDjango in this way (also foreign keys):
class Role : public QDjangoModel { ... };
class User : public QDjangoModel
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QString password READ password WRITE setPassword)
Q_PROPERTY(Role *role READ role WRITE setRole)
Q_CLASSINFO("name", "max_length=255")
Q_CLASSINFO("password", "max_length=255")
Q_CLASSINFO("role", "on_delete=cascade")
QString mName;
QString mPassword;
public:
User(QObject *parent = 0);
QString name() const { return mName; }
void setName(QString name) { mName = name; }
QString password() const { return mPassword; }
void setPassword(QString pass) { mPassword = pass; }
Role *role() const;
void setRole(Role *role);
};
You can use Q_CLASSINFO to set some attributes to columns.
The foreign keys are bound in constructor and filled in role functions.
User::User(QObject *parent)
: QDjangoModel(parent)
{
setForeignKey("role", new Role(this));
}
Role *User::role() const
{
return qobject_cast<Role *>(foreignKey("role"));
}
void User::setRole(Role *role)
{
setForeignKey("role", role);
}
When you want to select using fields from fk dependant table use two undescores and of course use selectRelated() (which just sets some flag which is later used to produce join select) like:
QDjangoQuerySet<User> matched;
matched = matched.selectRelated()
.filter(QDjangoWhere("role__rolename", QDjangoWhere::Equals, "admin"));
In QT, a created lineEdit shows a text using the setText() method.
But the cursor is movable for the default text. I want the cursor should not be movable for the default text.
My lineEdit type has been set as password. Hence the default text('Password') is also displayed as '********'. Whenever user types the type has to be changed as password and when there is no text or until the user have not typed any text, the lineEdit should display the plain text 'password'
Any idea to fix the above two issues?
In the constructor put
ui->lineEdit->setPlaceholderText("password");
ui->lineEdit->setReadOnly(1);
And in on_lineEdit_selectionChanged() SLOT, put
ui->lineEdit->setText("");
ui->lineEdit->setEchoMode(QLineEdit::Password);
ui->lineEdit->setReadOnly(0);
I noticed this question has tag pyqt so I'll put an actual answer related to that tag for those actually looking for a python way instead of c++.
self.searchEditText = QtGui.QLineEdit()
self.searchEditText.setPlaceholderText("Search for word")
I managed to do what you want by deriving a class from QLineEdit as per following..
Constructor..
QCustomLineEdit::QCustomLineEdit(QWidget *parent) :
QLineEdit(parent)
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(onTextChanged(QString)));
connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int)));
setEchoMode(QLineEdit::Password); // Echo mode in your case..
m_echoMode = echoMode(); // Member variable to store original echo mode..
m_placeHolderText = "Password"; // Member variable..
m_isPlaceHolderActive = true; // Member varible..
// Default case..
setPlaceholderText("");
setStyleSheet("QCustomLineEdit{color: gray;}");
setEchoMode(QLineEdit::Normal);
setText(__placeHolderText);
}
Override keyPressEvent..
void QCustomLineEdit::keyPressEvent(QKeyEvent *e)
{
if(m_isPlaceHolderActive)
{
if(e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
e->accept();
else
QLineEdit::keyPressEvent(e);
return;
}
QLineEdit::keyPressEvent(e);
}
Cursor position change event..
void QCustomLineEdit::onCursorPositionChanged(int /*oldPos*/, int newPos)
{
if(m_isPlaceHolderActive)
{
if(newPos != 0)
setCursorPosition(0);
}
}
Text change event..
void QCustomLineEdit::onTextChanged(const QString &text)
{
if(m_isPlaceHolderActive)
{
if(text.compare(m_placeHolderText) != 0)
{
m_isPlaceHolderActive = false;
// Remove the 'placeHolderText' from 'text' itself..
QString temp = text;
temp = temp.mid(0, text.lastIndexOf(m_placeHolderText));
setStyleSheet("QCustomLineEdit{color: black;}");
setEchoMode(m_echoMode);
setText(temp);
}
else
{
setEchoMode(QLineEdit::Normal);
setText(m_placeHolderText);
setStyleSheet("QCustomLineEdit{color: gray;}");
setCursorPosition(0);
}
}
else
{
if(text.isEmpty())
{
m_isPlaceHolderActive = true;
setStyleSheet("QCustomLineEdit{color: gray;}");
setEchoMode(QLineEdit::Normal);
setText(m_placeHolderText);
}
}
}
I have written it very hastily to just show you. Test it yourself and feel free to point any mistake(s) or optimization(s). Hope this helps.
For question 1, in Qt 5.0 and higher, setPlaceholderText does what you want. https://codereview.qt-project.org/#change,45326