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

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

Related

Senders objectName is absent | QT & Cpp

I have created a little UI with a simple toolbar. I am initing the toolbar within the initToolbarfunction. This function is within a Class inheriting from QMainWindow.
void Main_Frame::initToolBar() {
rectangle.setText(rectangle_text);
circle.setText(circle_text);
triangle.setText(triangle_text);
triangle.setObjectName(triangle_id);
circle.setObjectName(circle_id);
rectangle.setObjectName(rectangle_id);
toolBar.addAction(&triangle);
toolBar.addAction(&circle);
toolBar.addAction(&rectangle);
connect(
&toolBar, &QToolBar::actionTriggered, this, [=]() {
process_toolbar_ac_evt(*sender());
});
}
I want any tool bar events to be processed through process_toolbar_ac_ect(QObject obj). Within that method, I want to decide what action (within the toolbar) has been triggered.I want to do this by the objectname. Therefore I have given any action an object name. But when I call sender().objectName() I get an empty QString.
My suggestion is, that sender returns a pointer to on of my actions that I put to the toolbar. If this is the case, why I get an empty QString on the sender()?
void Main_Frame::process_toolbar_ac_evt(QObject &evt) {
if (evt.objectName() == circle_id) {
// If circle action has clicked, to this ...
}
}
As you are connecting to one of QToolBar's signals the sender() will be your tool bar object.
Simply use the QAction argument that is passed in the QToolBar::actionTriggered signal. That's what it is for.
NB: Avoid QObject::sender() wherever possible. It virtually breaks the desired decoupling achieved by signals and slots.

Determine which Shortcut is pressed in Qt

I have a QAction which I've assigned multiple shortcuts to it
test = new QAction();
this->addAction(test);
QList<QKeySequence> shortcuts;
shortcuts << QKeySequence(Qt::Key_N) << QKeySequence(Qt::Key_T);
test->setShortcuts(shortcuts);
connect(test,SIGNAL(triggered()),this,SLOT(SomeFucntion()))
In SomeFucntion I need to know which shortcut was pressed....Is there anyway of knowing that ?
You could try a more elaborate pattern with QSignalMapper that avoids the need to define as many actions as many shortcut you need, but requires c++11 (at least this implementation).
In the constructor of your window use the following code to declare your QShortcut objects and a QSignalMapper:
QSignalMapper* signalMapper = new QSignalMapper(this);
QShortcut* sc1 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_N), this);
QShortcut* sc2 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_T), this);
connect(sc1, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
connect(sc2, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
signalMapper->setMapping(sc1, sc1);
signalMapper->setMapping(sc2, sc2);
QAction* action = new QAction();
connect(signalMapper, static_cast<void (QSignalMapper::*)(QObject*)>(&QSignalMapper::mapped),
[action](QObject *object){
QShortcut* sc = qobject_cast<QShortcut*>(object);
if (sc)
{
action->setData(sc->key().toString());
action->trigger();
}
});
connect(action, &QAction::triggered, this, &MainWindow::doStuff);
The 3rd connection is required because of the way QSignalMapper works: when a shortcut is activated, it will be notified to the QSignalMapper thanks to the 1st and 2nd connections, that will trigger the map() slot.
The QSignalMapper::map() slot will scan its mappings, made with the setMapping() API, whose first argument is the mapped object, and the second one is the parameter that will be used to emit the mapped() slot of the QSignalMapper, once the emitting object is identified. To do so, it uses the sender() method and simply compares the pointer returned to the mapped QObject pointers you provided as mappings.
Once the QObject is identified, QSignalMapper will emit the QSignalMapper::mapped(QObject*) signal, whose argument is the second argument given to setMapping, and in this case it the same as the first one, that is again a pointer to the QShortcut that was activated.
I used a lambda to catch this signal, and inside this lambda I simply check that the parameter given is a QShortcut pointer, and store its key sequence inside the data member of the QAction before triggering the action itself. The QAction::trigger() slot will then emit the QAction::triggered() signal that will in turn invoke your custom slot, in this case doStuff(). There you can retrieve the key sequence and do what you want with it.
So your slot implementation should look similar to this one:
void MainWindow::doStuff()
{
// use sender() to fetch data from action
QAction* act = qobject_cast<QAction*>(sender());
if (act)
{
QString sequence = act->data().toString();
// debug output will show you the triggering key sequence
qDebug() << sequence;
// use sequence string to determine which shortcut was used
// On Mike hint: better to reset data after use :)
act.setData(QVariant());
}
}
Note that I'm using a mapping based on QObject pointers. In this way you can reuse the signalMapper instance to connect events from other kind of QObjects (e.g. QPushButtons) and identify them in your custom slot as well setting a proper value for the QAction data member, that can store a generic QVariant istance.
Also when using QShortcut, be aware of their contex, that is when they are active, as it could be at widget or a window scope.
Unfortunately this pattern violates pure oop principles, but could be better than managing many actions (icon, text, tooltip etc...) for the same purpose.
EDIT: to answer comments
First of all, let me clarify that you can of course skip the use of QSignalMapper at all. This is just a possible solution (not the better, maybe an overkill... but not really worse in terms of performance).
A simpler way, as pointed by Mike in the comments consists in using lambdas for each QShotcut::activated signal, but this will result in copy/paste code, that I always try to avoid.
You can instead define a custom slot inside the MainWindow and use sender() to catch the QShortcut and prepare the action before triggering it.
Anyway, QSignalMapper IMHO, better explains what you are doing (from a semantic point of view) and is more flexible in case you need to expand the connection to other QObjects, supporting also other type of mappings.
Furthermore, but this is related to my personal taste, I like the idea to have fragments of code that are logically tied condensed into small snippets, instead of have it sparse among several slot/functions because it makes it easier to read and to trace back when I need to change it, of course only if this does not hurt the quality of code itself.
You should create a separate QAction for each shortcut and group them using a QSignalMapper.

Refer to the Sender Object in Qt

I'm having some trouble, I'm fairly new to Qt and C++ and was testing the waters so to say. Ill try to describe my problem as follows. I have a LineEdit QLineEdit and this edit has a connection which looks like this:
connect(my_lineedit, SIGNAL (textEdited(QString)),this, SLOT (handleEdits()));
The handleEdits() method gets called and does the following:
Disconnect the previous Signal from my_lineedit
Create a new QLineEdit which gets a new signal and calls handleAddedEdits()
Adds the newely created Edit to my layout.
The stated above works fine Im just telling you this so you get the picture.
Now in the new method which I called handleAddedEdits() I want kinda the same procedure to happen again.
Disconnect the Signal which calls handleAddedEdits() from the Edit which invoked this method in the first place.
Create a fresh QLineEdit
Add this to my layout.
The problem is: in the first case my_lineedit is declared in my class so I can freely refer to it and and remove the signal as I wish. In the second case I have a QLineEdit which was created dynamically in the handleEdits() method and is the "Sender". My Question is, how can I refer to the "Sender Object" ro remove the Signal from the dynamically created edit?
You need to use QObject::sender() method in your receiver's slot:
For cases where you may require information on the sender of the
signal, Qt provides the QObject::sender() function, which returns a
pointer to the object that sent the signal.
handleAddedEdits()
{
QObject* obj = sender();
disconnect( obj, SIGNAL(textEdited(QString)), 0, 0 );
//...
}

Qt can I connect signals/slots to self in constructor?

EDIT: Not related to signals/slots/connect. Problem was constructor calling constructor.
There might be a better way to do this - I'd be interested in hearing those...
I have MyClass that is derived from a QLabel. I want to pass more data about the derived class back in the signal than what the base signal does. So I made a slot to intercept the customContextMenuRequested signal and emit a revised one that has more data.
When I try to connect up this signal in the constructor, then my slot never gets called. But if I move the Policy and connect lines out to the parent widget(not class hierarchy parent) so they execute after MyClass is fully constructed, then my slot will get called. But I always want that to be connected for this class and it seems like something I would want in it's constructor rather than counting on the parent class to remember to do it.
Is there something I'm doing wrong? Or a better way to add data to a signal?
MyClass::MyClass() : QLabel()
{
QFont currFont = font();
currFont.setPointSize(15);
setFont(currFont);
setBackgroundRole(QPalette::Mid);
std::cout << "connecting customContextMenuRequested" << std::endl;
/** PROBLEM START */
setContextMenuPolicy(Qt::CustomContextMenu);
// Is there anything wrong with connecting from "this" to "this" in a constructor?
QObject::connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(addCellDataToMenuContextRequest(const QPoint&)));
/* PROBLEM END **/
}
MyClass::MyClass(QString &cellString, int row, int col)
: QLabel(cellString)
{
MyClass();
setRow(row);
setCol(col);
}
// This one is a slot
void MyClass::addCellDataToMenuContextRequest(const QPoint& pos)
{
// This doesn't get printed if I connect in my constructor,
// but it does print if I do the same connect from a parent widget.
std::cout << "called addCellDataToMenuContextRequest" << std::endl;
emit customContextMenuRequestedForCell(pos, _row, _col);
}
So I would like the parent widget to just look for customContextMenuRequestedForCell but right now, the parent widget seems to need to be responsible for customContextMenuRequested as well.
Actually, you CAN call (sort of) another constructor if you are using C++11. It's called delegating constructor. But I don't think that will make the problem go away. Your issue seems to be that meta object is not fully constructed when connect() is called. Also, you'll probably need to move to Qt 5 for C++11 to work.
The solution is to delay the connection until the object is fully constructed. You can start a timer with an interval of zero. It will trigger on the next event loop processing which will certainly be after your object is fully constructed.
Then in your timerEvent, make the connection and kill the timer.
EDIT: Didn't see your edit. Looks like you find the solution. Ignore this then. :)
BTW. You didn't call another constructor. You created a temporary MyClass object.
A way to make this "cleaner" would be to reimplement QWidget::mouseReleaseEvent() within MyClass. The way to implement it would be, if the QMouseEvent type passed to mouseReleaseEvent is not a right-click mouse release, call QLabel::mouseReleaseEvent(event). If it is a right-click mouse release event, you can emit your custom signal. This gives the benefit of using the existing mouse button release handling code given by QLabel/QWidget, while allowing you to intercept the one case where you want to emit the custom signal.
EDIT Oh, and be sure to call event->accept() after your mouseReleaseEvent handles the custom case.

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 ?