Qt: How to pass variable value betweeen QWizardPages with registerField() - c++

I'm working on Qt 4.8.5. I'm using a QWizard structure with its QWizardPages (lets name them wp1, wp2, wp3,...). I need to pass one value from wp2 to wp4 but every time I try it, I get an empty string :(
The value I need is on a variable (QString sVar;) so not a widget and I've tried some things:
Using RegisterField with the wizardpage itselfs (as its still a type of qwidget) like this: registerField("myField",this); but ofcourse when i go to wp4 and try to qDebug()<< "data: " << field("myField").toString();it is empty.
I've see in some forums ppl saying that you can create a Q_PROPERTY and then use the register field. I've set it as Q_PROPERTY sData READ getData() WRITE setDATA() and then with registerField("myfield, this, ...and here I have a problem because i expect sData to apear but it doesn't.
So... any idea about how can I achieve this using registerField (I know I can also create my own slot and signal, emit it from wp2 and catch it up on wp4 but I would like to avoid it if possible)
Added the solution:
Class A.h:
class ClassA: public QWizardPage
{
Q_OBJECT
Q_PROPERTY(QString sAP READ getAP WRITE setAP)
....
public:
QString getAP() const {return AP;}
void setAP(QString s){AP=s;};
private:
QString AP;
Class A constructor:
registerField("AP_field",this, "sAP", SIGNAL(APChanged()));
Class A ::initializePage() function:
switch(m_iVar)
{
case 0 :...
break;
case 1:
setAP("AP1");
emit APChanged();
break;
}
And then in Class B (Where you need to know that data):
qDebug() << " AP QPROPERTY = " <<field ("AP_Field").toString();

According to the docs:
When we create a field using QWizardPage::registerField(), we pass a
unique field name and a widget. We can also provide a Qt property name
and a "changed" signal (a signal that is emitted when the property
changes) as third and fourth arguments; however, this is not necessary
for the most common Qt widgets, such as QLineEdit, QCheckBox, and
QComboBox, because QWizard knows which properties to look for.
So you still need a signal, but Qt will handle necessary connections for you and will catch your new value as you change it. You have to register like this:
registerField("myField", this, "myProperty", SIGNAL(myPropertyChanged()));
Then you have to remember to emit the signal each time you change your variable, and of course register it as a property.

This works from some but not all widgets that emit a signal. For QDoubleSpinWidgets,
QWizard::setDefaultProperty("QDoubleSpinBox", "value", SIGNAL(valueChanged(double)));
This is because the valueChanged() is for QString and double....does not know which value to take without the parameter specifying:
"Every time the value changes QDoubleSpinBox emits two valueChanged() signals, one taking providing a double and the other a QString. The QString overload provides the value with both prefix() and suffix(). The current value can be fetched with value() and set with setValue()." doc.qt.io/qt-5/qdoublespinbox.html I hope this never troubles others and this complete solution, no warnings, errors nor unpredictable behaviour.

Related

How to transfer parameter from constructor into slot?

I am doing a small exercise with Qt. As I know, slot save_clicked brings only type of parameter. So how can I transfer userid into slot save_clicked in file MyClass.cpp for processing below ?
MyClass.h
class MyClass : public QWidget
{
Q_OBJECT
public:
MyClass(QString userid, QString lastname, QString firstname, QWidget *parent = 0);
~MyClass();
public slots:
void save_clicked(QString userid);
private:
Ui::MyClass ui;
};
#endif // MYCLASS_H
MyClass.cpp
MyClass::MyClass(QString userid, QString lastname, QString firstname, QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
...........
...........
connect(ui.save, SIGNAL(clicked()), this, SLOT(save_clicked(QString)));
}
void MyClass::save_clicked(QString userid)
{
// process userid here
}
First of all, there have already been suggestions that userid doesn't need to be passed to a slot. Declaring it as a member of MyClass and simply using it in MyClass::save_clicked() will do just fine.
If you still want to know how to handle the case where you need to pass arguments to a slot not provided by the signal, there are mutiple ways to achieve that and they depend on the version of Qt you're using.
In general, the following from the Qt5 docs holds for Qt5 and Qt4:
The signature of a signal must match the signature of the receiving
slot. (In fact a slot may have a shorter signature than the signal it
receives because it can ignore extra arguments.)
Given that fact, you have to either use what the signal provides (or don't - depends on if you need it at all) or get your data somewhere else. A slot is just a regular function (a little more constrained in Qt4 but in Qt5, you can basically connect anything that's callable to a signal) and in that function, you can get the data you need from anywhere your design permits. The member variable approach is common, but you can also call another member function of any other object, free function or simply compute whatever you need inside the slot.
Note: Handling a signal doesn't necessarily mean you need any data - it's common, yes, but you could also do something simple as bring up a QMessageBox with some hard-coded text or simply clear some text edit (think something like a button doing Clear Console) or execute some external process or what have you.
If you need your slot to actually receive data when it is invoked, you have multiple options:
use everything or a subset of what the signal provides
use QSignalMapper to establish a mapping with limited control over your slot arguments
use the, IMHO, superior approach of using a function object that stores what you need, i.e. either a functor you provide directly or as a lambda or a function object returned from std::bind
For the latter to work you need C++11support , but by now, that shouldn't be a problem.
In your above example, you could do something like this in the most basic approach (UNTESTED - Please report compile errors so I can fix the answer):
MyClass::MyClass(QString userid, QString lastname, QString firstname, QWidget *parent)
: QWidget(parent)
{
...
connect (
ui.save,
&QPushButton::clicked,
std::bind (&MyClass::save_clicked, this, userid)
);
...
}
I the above case, when clicked() is emitted by ui.save, the function call wrapper returned by std::bind will be call, which in turn will call MyClass::save_clicked on this instance of MyClass and pass userid to save_clicked().
The above is basically what you wanted to achieve initially. However, this is just the tip of the iceberg. You can go much, much further with this and, for instance, bind anything you want.
For instance, suppose you connected to another signal provided by your button, toggled(bool checked). If you wanted to receive both the check status as a bool and a number of arguments you provide yourself, for instance the userid, you can extend the above call to std::bind with an appropriate placeholder:
connect (
ui.save,
&QPushButton::toggled,
std::bind (&MyClass::save_clicked, this, _1, userid /*, ... more args here*/)
);
You would now have the following signature for save_clicked():
save_clicked(bool, QString);
On emission of toggled(bool) by ui.save, the function call wrapper returned by std::bind will then call MyClass::save_clicked with both the bool in place of _1 and the QString you provided as the 4th argument to std::bind.
Better than use signals/slots just use a variable. It's the goal of a class. Add something like :
private:
QString userId;
And in your constructor add :
MyClass::MyClass(QString userid, QString lastname, QString firstname, QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
this.userId=userid;
}
Like this you can use userId like you want and where you want into your class.
EDIT :
add in your .h
private slots:
void on_saveButton_clicked();
and in your .cpp
void MyClass::on_saveButton_clicked()
{
// you can use userId here
}
If you use Qt Creator those functions can add automatically in the UI design interface

signal slot issue in qt 5

I have one c++ class and have one signal in it and want to connect that signal with slot another C++ class. Here is my code
class Data : public QObject
{
Q_OBJECT
public:
static Data *instance(void);
signals:
void sendUserID(const QString& userId);
private:
static Data *s_instance;
};
here is my Slot in another class
void DataDetails::seTUserID(const QString user_id)
{
QAndroidJniObject user_id = QAndroidJniObject::fromString(user_id);
QAndroidJniObject::callStaticMethod<void>("com/user/data/userActivity",
"setUserID",
"(Ljava/lang/String;)V",
user_id.object<jstring>());
}
The idea is to access the value of user_id from Data class to DataDetails class
The connection is trying is
QObject::connect(&s_instance, SIGNAL(sendUserID(uid), this, SIGNAL(setUserID(uid))
any other id to get uid to other class is also fine ..
Generally speaking, when you encounter an issue with QObject::connect...
Make sure you have your declarations in order:
Both classes need the Q_OBJECT macro in their declaration.
Make sure your slot is actually declared as a slot (i.e. is part of a public slot: section).
Because connecting signals and slots just uses character strings evaluated at run-time, it's possible to write absolute nonsense and have it compile. In my experience, errors caused by typos are pretty common.
Always test in an environment where you can see your application's console output. Failed connect calls will usually trigger an error message printed to stderr.
Double-check your method names and signatures. Code will still compile even if you've made a typo!
For debugging, use assertions (e.g. bool c = connect(...); Q_ASSERT(c);) to catch missed connections early.
Alternatively, you can use the QMetaMethod-based version of QObject::connect, introduced in Qt 4.8, to avoid some of these issues.
In your particular case:
You've got a typo in the function declaration: it's called seTUserID but you're using setUserID in the connect call.
You're using variable names, not function signatures, in your signal and slot names. Qt expects to see QObject::connect(&s_instance, SIGNAL(sendUserID(const QString), this, SLOT(setUserID(const QString))
You've got a signal connected to another signal, which is valid but doesn't do what you want (it's usually used to chain stuff like this: SomeChildWidget's signal -> MyClass1's signal -> MyClass2's slot).
Check that seTUserID definition is marked as slot. You are probably calling connect in some Data method (because you are using private member directly). Are you trying to use a ref to a pointer to s_instance O_o? Write sendUserID(const Qstring) (the signature of a signal) rather then sendUserID(uid). The same situation with setUserID. You are trying to connect to this pointer and want to send info to another class!? Use new style of Qt connecting signals and slots (pointers to members), with was introduced in Qt 5. Check for setUserID has written right.

How can I catch the change of a child Widget in Qt?

I have some dynamically added QWidgets and I want to carry out some task when they are changed.
I think I can't use connect() because I also need the name of the QWidget that triggered the change.
How can I also see which QWidget was changed and still catch the value change event with a common handler?
The quick-and-dirty way is to use connect() as usual, and then in your slot method, call sender() to find out which object sent the signal. For example:
// slot method that you've connected all of your widgets' stateChanged(int) signals to
void MyClass :: someWidgetsStateChanged(int newState)
{
const QObject * s = sender();
if (dynamic_cast<const QCheckBox *>(s) == _myFirstCheckbox) printf("First checkbox is now %s\n", newState?"Checked":"unchecked");
else if (dynamic_cast<const QCheckBox *>(s) == _mySecondCheckbox) printf("Second checkbox is now %s\n", newState?"Checked":"unchecked");
[... and so on...]
}
Note that the reason this is considered "dirty" is that it breaks encapsulation. In particular, the someWidgetsStateChanged() method above now behaves differently depending on which object generated the signal, and so if e.g. at some point in the future you connected a QPushButton::clicked() (or whatever) to that same slot, you'd probably have to update the someWidgetsStateChanged() implementation to handle it appropriately. Still, this works and doesn't require a lot of effort to implement.
Use this to catch events before they are passed to QObject subclass instances:
http://qt-project.org/doc/qt-4.8/qobject.html#installEventFilter
After some additional thinking I arrived at saying why not to extend these controllers?
So that I could hook them to the parent object using parent() or using a custom constructor.
It requires potentially though that I define them as friend classes...

How to issue signal each time a row is edited in QListWidget?

class genericTaskList : public QListWidget
{
Q_OBJECT
public:
QListWidgetItem *defaultText;
genericTaskList (QWidget *parentWidget)
{
setParent (parentWidget);
setFixedSize (445, 445);
defaultText = new QListWidgetItem ("Double click here to compose the task");
defaultText->setFlags (defaultText->flags () | Qt :: ItemIsEditable);
insertItem (0, defaultText);
QObject :: connect (this, SIGNAL (currentRowChanged (int)), this, SLOT (addDefaultText (int)));
}
public slots:
void addDefaultText (int rr)
{
std::cout << "\ndsklfjsdklfhsdklhfkjsdf\n";
insertItem (++rr, defaultText);
}
};
This code is supposed to issue a signal each time the row gets edited.
After I call "insertItem" in the constructor, the signal is issued.
But, that's it. It never gets issued after that - no matter how many times I edit the row.
What am I missing?
At first it seems like QListWidget::itemChanged is the way to go, but soon you run into a problem: the signal is sent for everything - inserts, removes, changing colors, checking boxes, etc! So then you end up trying to put in flags and filter everywhere by intercepting various signals to find out if editing was the actual event. It gets very messy.
There is also QAbstractItemModel::dataChanged , which would seem like a good solution. It even has a parameter "const QVector& lstRoles" so you could scan for Qt::EditRole and see if it was really edited. Alas, there's a catch - it gets called for everything just like QListWidget::itemChanged and unfortunately, for QListWidget anyway, the roles parameter is always empty when it's called (I tried it). So much for that idea...
Fortunately, there's still hope... This solution does the trick! :
http://falsinsoft.blogspot.com/2013/11/qlistwidget-and-item-edit-event.html
He uses QAbstractItemDelegate::closeEditor, but I prefer using QAbstractItemDelegate::commitData.
So make a connect like so...
connect(ui.pLstItems->itemDelegate(), &QAbstractItemDelegate::commitData, this, &MyWidget::OnLstItemsCommitData);
Then implement the slot like this...
void MyWidget::OnLstItemsCommitData(QWidget* pLineEdit)
{
QString strNewText = reinterpret_cast<QLineEdit*>(pLineEdit)->text();
int nRow = ui.pLstItems->currentRow();
// do whatever you need here....
}
Now you have a slot that gets called only when the list item's text has been edited!
currentRowChanged indicates the row selection has changed, not the content of the row. Perhaps you want to use currentTextChanged or itemChanged instead.
The reuse of the word current and changed in the QT docs is quite confusing.
Warning: A QListWidgetItem can only be added to a QListWidget once. Adding the same QListWidgetItem multiple times to a QListWidget will result in undefined behavior.
So even if it will emit the signal I think you should better to add newly created Item.
And when do you want the new row to be inserted ? -
as soon as item is double clicked or finishing edit - they differ.

How to use QMetaMethod with QObject::connect

I have two instances of QObject subclasses and two QMetaMethod instances of signal in one of objects and slot in another object. I want to connect this signal and slot with each other.
I've looked through the qobject.h file and find that SIGNAL() and SLOT() macro are just add "1" or "2" character to the beginning of method signature so it looks like it should be possible to add the same character to the beginning of string returned by QMetaMethod::signature() but this approach depends on some undocumented internals of toolkit and may be broken at any time by a new version of Qt.
Does anybody know reliable way to connect signals and slots through their QMetaMethod reflection representation?
Edited:
I've created suggestion in Qt issue tracker:
https://bugreports.qt.io/browse/QTBUG-10637
If anybody also interested in this feature you can vote for this ticket there.
This has been fixed as of Qt 4.8.0:
https://bugreports.qt.io/browse/QTBUG-10637
Suppose we have a QObject* m_subject, and wish to connect the change-notification signal of a property to a propertyChanged() slot:
const QMetaObject* meta = m_subject->metaObject();
QMetaProperty prop = meta->property(meta->indexOfProperty("myProperty"));
if (prop.hasNotifySignal()) {
QMetaMethod signal = prop.notifySignal();
QMetaMethod updateSlot = metaObject()->method(
metaObject()->indexOfSlot("propertyChanged()"));
connect(m_subject, signal, this, updateSlot);
}
I successfully used this to make a QWidget subclass which finds all the properties of any QObject and creates a QLineEdit for each of them, with a connection to keep the QLineEdit updated whenever the corresponding property changes. (Because I didn't find a way to pass a propertyID value to propertyChanged() though, it was necessary to make a subclass of QLineEdit and implement propertyChanged() there. QSignalMapper didn't help, because all the properties are in the same object.)
Thanks to MBack, I now use metamethods to connect my view to my model's properties dynamically for MVVM or MVC pattern.
In order to respect DRY, a boilerplate is required with something like this :
void MyClass::connectSignalToSlot(QObject* sender, std::string signalName, QObject* receiver, std::string slotName)
{
int sigIdx = sender->metaObject()->indexOfSignal(signalName.c_str());
auto signal = sender->metaObject()->method(sigIdx);
int slotIdx = receiver->metaObject()->indexOfSlot(slotName.c_str());
auto slot = receiver->metaObject()->method(slotIdx);
connect(sender,signal,receiver,slot);
}
void MyClass::connectPropertyChangedToSlot(QObject* sender, std::string propName, QObject* receiver, std::string slotName)
{
int sigIdx = sender->metaObject()->indexOfProperty(propName.c_str());
auto signal = sender->metaObject()->property(sigIdx ).notifySignal();
int slotIdx = receiver->metaObject()->indexOfSlot(slotName.c_str());
auto slot = receiver->metaObject()->method(slotIdx);
return connect(sender, signal, receiver, slot);
}
It looks like there is no way to make it work without relying on internal implementation. If I were you, I'd submit feature request to Qt bug tracker, write a code that mimics current behavior SIGNAL/SLOT macros and add unit test that will fail when SIGNAL/SLOT behavior changes.
There might be a simpler solution to the problem you're trying to solve: describe what exactly are you trying to do without any implementation details.
If signature method is public in QMetaMethod then the result shouldn't be broken by trolls and it's safe to use it (documentation says nothing about "dangers" when using QMetaMethod::signature method). I think you can safely use it. Just to be sure, what version of Qt you are using right now ?