Passing signals through hierarchies - qt

I am having some difficulty fully grasping how signals and slots are used in Qt. I am sure it is really basic but I'm just no getting it today.
I have a set of widgets a bit like this:
MainWindow
-->StackedWidget
-->ChildForms
Now the idea is that there are some actions on the Child widgets that will cause the stacked widget to display a different page.
So if I understand it properly I thought the way to connect signals and slots is to use the connect() at the scope that knows about the objects but what I have managed to get working doesn't do it this way. At the moment in my child form I use parentWidget() to access the slot of the StackedWidget but I am not very happy with really because it is giving the child information about the parent which it shouldn't have:
void TaskSelectionForm::setButtonMappings()
{
// Set up a mapping between the buttons and the pages
QSignalMapper *mapper = new QSignalMapper(this);
connect(mapper, SIGNAL(mapped(int)), parentWidget(), SLOT(setCurrentIndex(int)));
mapper->setMapping(ui->utilitiesButton, 2); // Value of the index
connect(ui->utilitiesButton, SIGNAL(clicked()), mapper, SLOT(map()));
}
But I am not really sure how I should do this and connect it up. Do I need to have signals at each level and emit through the tree?

A Bit of Signal-Slot Theory
The signal-slot connections are oblivious to parent-child relationships between QObjects, and any such relationship doesn't matter. You're free to connect objects to their children, to their siblings, to their parents, or even to QObjects that are in a separate hierarchy, or to lone QObjects that have neither parents nor children. It doesn't matter.
A signal-slot connection connects a signal on a particular instance of QObject to slot on another instance of QObject. To use the connect method, you need the pointers to the instance of sender QObject and the instance of receiver QObject. You then use the static QObject::connect(sender, SIGNAL(...), receiver, SLOT(...)). Those connections have nothing to do with any hierarchy there is between the sender and receiver.
You can also connect a signal to a signal, to forward it -- for example from a private UI element to a signal that's part of the API of the class. You cannot connect a slot to a slot, because it'd incur a bit of runtime overhead for a rarely-used case. The overhead would be an extra bool member in QObjectPrivate, plus a failed if (bool) test. If you want to forward slots to slots, there are at least two ways to do it:
Emit a signal in the source slot and connect that signal to the destination slot.
Obtain a list of all signals connected to the source slot, iterate on it and connect them to to the target slot. There's no easy way to maintain such connections when further signals are connected or disconnected from the source slot. Unfortunately, QObject only has a connectNotify(const char*) protected function, but not a signal -- so you can't hook up to it unless you would modify src/corelib/kernel/qobject[.cpp,_p.h,.h] to emit such a signal. If you truly need it, just modify the Qt source, you have access it for a reason, after all. Hacking the vtable without modifying Qt is possible, but discouraged for obvious reasons.
The Answer
Below is a self contained example that shows how to do what you want. Turns out I have answers to quite a few questions from my various experiments I've done in Qt in the past. I'm a packrat when it comes to test code. It's all SSCCE to boot :)
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-slot-hierarchy-10783656
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class Window : public QWidget
{
QSignalMapper m_mapper;
QStackedLayout m_stack{this};
QWidget m_page1, m_page2;
QHBoxLayout m_layout1{&m_page1}, m_layout2{&m_page2};
QLabel m_label1{"Page 1"}, m_label2{"Page 2"};
QPushButton m_button1{"Show Page 2"}, m_button2{"Show Page 1"};
public:
Window(QWidget * parent = {}) : QWidget(parent) {
// the mapper tells the stack which page to switch to
connect(&m_mapper, SIGNAL(mapped(int)), &m_stack, SLOT(setCurrentIndex(int)));
// Page 1
m_layout1.addWidget(&m_label1);
m_layout1.addWidget(&m_button1);
// tell the mapper to map signals coming from this button to integer 1 (index of page 2)
m_mapper.setMapping(&m_button1, 1);
// when the button is clicked, the mapper will do its mapping and emit the mapped() signal
connect(&m_button1, SIGNAL(clicked()), &m_mapper, SLOT(map()));
m_stack.addWidget(&m_page1);
// Page 2
m_layout2.addWidget(&m_label2);
m_layout2.addWidget(&m_button2);
// tell the mapper to map signals coming from this button to integer 0 (index of page 1)
m_mapper.setMapping(&m_button2, 0);
connect(&m_button2, SIGNAL(clicked()), &m_mapper, SLOT(map()));
m_stack.addWidget(&m_page2);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}

Connect(stackedwidget->currentactivewidget,SIGNAL(OnAction()),this,SLOT(PrivateSlot()));
PrivateSlot() is a slot declared privately. So in this function, you can add your code to change the page of stackedwidget corresponding to the action produced by currentactivewidget.
Again if you really want to pass the signal up the heirarchy, emit a publicsignal() at the end of private slot function.
Connect(this,SIGNAL(publicsignal()),Parentwidgetofstackedwidget(here mainwindow),SLOT(mainwindow_slot()));

Related

How may I fix my error prone on_foo_bar slots

I have eight list widgets in a tab widget. They have similar names, and Designer's "Go to slot" mechanism has made links to slots it names (in the "private slots" section of "mainwindow.h") like:
void on_SR0listWidget_itemClicked(QListWidgetItem *item);
I saw warnings that "Slots named on_foo_bar are error-prone," and now I need to change their names in order to discover if that's the cause of the weird behaviour I'm getting.
I tried simply refactoring the names, but that stopped the slot code from working. I used Designer's graphical "Edit Signal/Slot" mechanism and was able to connect a newly added list widget's "itemClicked(QListWidgetItem *item)" signal to a newly added slot, and it looks OK in the graphical representation, but there's no debug message (that I set up in the Slot function) when an item is clicked.
I also use those widgets' "entered" signals, so there will be at least 16 to fix. I would write a script if it could be done by parsing the relevant files.
One example of exactly how to rename one of my replacement slots and connect an "item clicked" or "entered" signal to it (and where it should go) would be a great help.
Signals/slots setup through the designer rely on the names of the widgets involved. This can lead to problems if the widget names are changed. There are times when using the designer method will lead to code that compiles but doesn't actually make the connections you expect. This is why you are getting that warning.
You can get more reliable behavior by connecting the signals and slots programmatically. For example, let's say you have a class header such as:
#include <QMainWindow>
namespace Ui {
class MyWindow;
};
class QListWidgetItem;
class MyWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyWindow(QWidget* parent = nullptr);
~MyWindow() override;
private:
void handleItemClicked(QListWidgetItem* item); // this is your slot
Ui::MyWindow* ui;
};
You can connect the signal/slot together in the cpp file like this:
#include "MyWindow.h"
#include "ui_MyWindow.h"
#include <QDebug>
MyWindow::MyWindow(QWidget* parent)
: QWidget(parent),
ui(new Ui::MyWindow()) {
ui->setupUi(this);
// connect() has many overloads, but in this case we are passing:
// 1. the object emitting the signal
// 2. the signal being emitted
// 3. the object whose slot we want to call
// 4. the slot to connect to
connect(
ui->listWidget, &QListWidget::itemClicked,
this, &MyWindow::handleItemClicked);
}
MyWindow::~MyWindow() {
delete ui;
}
void MyWindow::handleItemClicked(QListWidgetItem* item) {
qDebug() << "item clicked";
}
You can still use the designer to layout your UI - but prefer to manage connections directly in code rather than through the designer.

Using Qt GUI with objects external to application

I'm creating a Qt GUI that will generate some forms for the user, let them fill them in, and then save the forms in a binary search tree that will then be serialized. This is done using Qt (obviously), c++, and boost. The GUI is the only interface with the application at this point. I would like to instantiate my binary search tree object outside of the GUI as creating it inside the main GUI class seems to be a bad design pattern. Is this thought correct?
Here's my main() right now:
int main (int argc, char *argv[])
{
QApplication app(argc, argv);
ChocAnGui *gui = new ChocAnGui;
gui->show();
return app.exec();
}
Is there a way to create a BST object that the ChocAnGui class can use, but that lives outside of the actual GUI? I'd like to do something like:
int main (int argc, char *argv[])
{
MyBST bst = new MyBST;
MyRecord record = new MyRecord;
QApplication app(argc, argv);
ChocAnGui *gui = new ChocAnGui;
gui->show();
return app.exec();
}
I would then like the GUI to be able to call methods on the MyBST and MyRecord classes from within. Because at the start and finish of the application I am serializing and de-serializing data, this seems to be the most abstract approach (vs. serializing and de-serializing from within the GUI code itself). I hope this question is clear... I'm learning all this stuff as we speak. Is there a way to pass those vars through QApplication(argc, argv)? Eventually the record and BST classes will be built into a database and, again, creating the instances outside of the GUI seems most straightforward. Thanks for any input you can provide.
I think it is necessary for you to put your binary search tree outside the GUI thread only when your Binary search takes a long time and is possible to block the GUI's thread.
If this is the case, the what you wanna do is to put the MyBST object under another thread,
In your main application, create a new thread that will handle all BST operations:
QThread * BSTThread = new QThread();
BSTThread->start();
BSTThread is a new thread that has its own event queue.
Then you create you MyBST Object, Make sure MyBST inherits QObject, and you can call the moveToThread method on the object.
MyBST * bst = new MyBST();
bst->moveToThread(BSTThread);
When the main application wants to communicate with the MyBST, you emit a signal from you main application. Connect the signal to a slot in the MyBST class using Qt::QueuedConnection, or just use Qt::AutoConnection, it will automatically be queuedconnection because your bst object lives in a different thread as the main app, and the function in the slot will be executed by the BSTThread.
And when the bst object wants to communicate back to the main app, also emit a signal, this signal is connected to the slot in the main app, which will handle this properly in your mainThread. In this way, your communication is asynchronous, you can not get the result immediately when you send request to the bst object, because you send it as a signal and it is posted in the event queue of the BSTThread waiting to be processed. After the BSTThread processed the request from the main app, it posts back the result to the main thread's event queue, the main thread handles the results and updates the GUI.
I think you need to read this http://doc.qt.digia.com/qt/thread-basics.html on QT if you want to know more details.
Just create your window to declare a MyBST member:
class MyBST;
class ChocAnGui
: public QMainWindow
{
Q_OBJECT
public:
ChocAnGui(MyBST* aBst = 0, QWidget *parent = 0);
MyBST *bst
And then initialize it with your object:
ChocAnGui *gui = new ChocAnGui(bst);
Or you can not make it a member, and make your gui just always take a MyBST pointer:
class MyBST;
class ChocAnGui
: public QMainWindow
{
Q_OBJECT
public:
ChocAnGui(QWidget *parent = 0);
void doSomething(MyBST*);

What's the QMetaCallEvent for and how to access its details?

I have an event filter and I noticed when I click to expand/collapse a tree branch I get QEvent::MetaCall. I was thinking about using this to roll my own expand/collapse code, but I don't know how to get any information, such as the index of the item.
Is there anything available from this MetaCall event type?
I found someone had asked this same question on another site, but without an answer, here:
http://www.qtcentre.org/threads/37525-How-to-filter-QEvent-MetaCall
What is this event typically used for?
The biggest question are: What exactly are you trying to do? What is the Qt class that received those events? As far as I'm concerned, you're trying to do things the hard way, so why bother?
The QMetaCallEvent is the event representing a slot call whenever a queued connection is used to invoke a slot. This might be due to a signal firing that was connected to a slot, or due to the use QMetaObject::invoke or QMetaObject::invokeMethod. The queued connection bit is the important part! Queued connections are not used by default for calls between objects in the same thread, since they have the event queue management overhead, unless either of the two conditions below holds true:
You provide Qt::QueuedConnection argument to QObject::connect or QMetaObject::invoke[Method], or
The receiving object's thread() is different from the thread where the call is originating - at the time of the call.
The QMetaCallEvent event class carries the information needed to invoke a slot. It contains the sender QObject and its signal id (if the call comes from a signal-slot connection), as well as the target slot identifier, and the arguments needed to be passed into the slot.
Thus, you could check if the called slot is the one you wish to intercept, as well as what arguments were passed to it. For example, if you're calling a slot with a single int parameter, then *reinterpret_cast<int*>(metaCallEvent->args()[1]) will give you the value of that integer. The zero-th argument is used for the return value, if any, so the parameters are indexed with base 1.
Disclaimer Since the QMetaCallEvent class is internal to Qt's implementation, you're making your application's binary tied to the particular Qt version in full (entire major.minor version) and you lose the benefits of binary compatibility offered by Qt across the major version. Your code may still compile but cease to work properly when you switch to another minor version of Qt!
The below applies to Qt 5.2.0, I have not looked at any other versions!
So, suppose you want to intercept a call to QLabel::setNum. You'd catch such events as follows:
#include <private/qobject_p.h> // Declaration of QMetaCallEvent
bool Object::eventFilter(QObject * watched, QEvent * event) {
QLabel * label = qobject_cast<QLabel*>(watched);
if (! label || event->type() != QEvent::MetaCall) return false;
QMetaCallEvent * mev = static_cast<QMetaCallEvent*>(event);
static int setNumIdx = QLabel::staticMetaObject.indexOfSlot("setNum(int)");
if (mev->id() != setNumIdx) return false;
int num = *reinterpret_cast<int*>(mev->args()[1]);
// At this point, we can invoke setNum ourselves and discard the event
label->setNum(num);
return true;
}
If you want to see, globally, all slots that are called using the metacall system, you can do that too. Template parametrization of the base class allows flexibility to use any application class - say QCoreApplication, QGuiApplication, QApplication, or a user-derived type.
template <class Base> class MetaCallWatcher : public Base {
MetaCallWatcher(int& argc, char** argv) : Base(argc, argv) {}
bool notify(QObject * receiver, QEvent * event) {
if (event->type() == QEvent::MetaCall) {
QMetaCallEvent * mev = static_cast<QMetaCallEvent*>(event);
QMetaMethod slot = receiver->metaObject()->method(mev->id());
qDebug() << "Metacall:" << receiver << slot.methodSignature();
}
return Base::notify(receiver, event);
}
}
int main(int argc, char ** argv) {
MetaCallWatcher<QApplication> app(argc, argv);
...
}
The QEvent::MetaCall-type event is created whenever a signal has been emitted that is connected to a slot in the receiving QObject. Reacting to this event in a custom filter/event handler seems to circumvent Qt's mightiest feature, the signal-slot architecture. It's probably better to find out which slot is called and if that slot is virtual so you can overload it.
QEvent::MetaCall is used for delivering cross-thread signals.

Help me understand the QThread usage

I have the MainWindow w windows and TestThread testThread as a member of w. I know it i simple, but I cannot run the testThread.foo() method in testThread thread (not in window thread). In another words: I don't understand the QThread behavior.
Please help correct the next test application. There is a QProgressBar *MainWindow::ui::progressBar and QPushButton *MainWindow::ui::startButton (write simply). I want to start (by startButton click) TestThread::foo(int* progress) which will increment int progress each second.
MainWindow:
MainWindow::MainWindow(QWidget *parent) : // ...
{
// ...
ui->progressBar->setRange(0, 5);
progress = 0; // int MainWindow::progress
this->connect(ui->startButton, SIGNAL(clicked()), SLOT(startFoo()));
connect(this, SIGNAL(startFooSignal(int*)), &testThread, SLOT(foo(int*)));
// TestThread MainWindow::testThread
testThread.start();
}
// ...
void MainWindow::timerEvent(QTimerEvent *event)
{
ui->progressBar->setValue(progress);
}
void MainWindow::startFoo() // this is a MainWindow SLOT
{
startTimer(100);
emit startFooSignal(&progress);
// startFooSignal(int*) is a MainWindows SIGNAL
}
TestThread:
void TestThread::foo(int *progress) // this is a TestThread SLOT
{
for (unsigned i = 0; i < 5; ++i) {
sleep(1);
++*progress; // increment MainWindow::progress
}
}
I know, this is simple. I am doing something wrong :)
P.S. I want to run the simpliest (as possible) example to understand the QThread behavior.
Thanks!
The critical issue is to have the object containing the foo()-function be owned by that thread, so that slot calls are dispatched from the right thread's event-loop.
(Note that there's no need to actually have foo() on the TestThread object. You can use separate objects for QThread and WhatEver::foo() function. It might be easier too, I'm not sure..)
IIUC, this is what you have to do:
Use QObject::moveToThread() to assign the object containing the foo-function to TestThread (that means that Qt::AutoConenction (the default) signal/slots calls will run correctly across thread, being dispatched from each thread's own event loop).
By having the object "owned" by the right thread, slots calls will be scheduled on that thread's event loop, rather than executed directly.
Hope it helps. :)
One alternative solution: If you just want to run a function in another thread, and don't insist using QThread, you should check out the QT Concurrent Namespace.
The following example will run the function foo() in separate thread and will not block on the line where calling the function. Of course there are mechanisms to understand when a function ends, to get a result, to wait for it, to control execution.
void foo(int &progress) {...}
int progress;
QtConcurrent::run(foo, progress);
Hope this helps
See QThread::start and QThread::run.

difference between connections in qt

I have some constructor for the class LCDRange:
LCDRange::LCDRange(QWidget *parent)
: QWidget(parent)
{
QLCDNumber *lcd = new QLCDNumber(2);
lcd->setSegmentStyle(QLCDNumber::Filled);
slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 99);
slider->setValue(0);
connect(slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)));
connect(slider, SIGNAL(valueChanged(int)),
this, SIGNAL(valueChanged(int)));
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(lcd);
layout->addWidget(slider);
setLayout(layout);
}
but I can't understand the difference between these lines:
connect(slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)));
connect(slider, SIGNAL(valueChanged(int)),
this, SIGNAL(valueChanged(int)));
thanks in advance
Remember that we are in OO land, so we are operating on objects.
In order to connect a signal to something, those signal/slots have to be apart of an object.
Another way to think about it is you can't have a signal by itself, because a signal has to come from somewhere. Likewise, you can't have a slot all by itself it has to be attached to something.
Now, Qt allows you to connect a signal to a slot, but it also allows you to connect a signal to a signal, which is what is happening in the second connect line. This allows you to chain signals from one object to another, or you could think about it as passing a signal through.
Usually this happens when an Object or Widget has a private internal child that emits a signal, and the parent Object/Widget wants to emit the same signal to whoever is listening to it.
In the case of your example the LCDRange Widget contains a slider, which is an internal implementation detail. The user of LCDRange doesn't need to know about all the different components that make up the LCDRange (slider, lcdnumber, layout, etc), that's a basic requirement of encapsulation which is a huge advantage of OO.
However, the user of LCDRange will want to know when the value of LCDRange changes. And rather then creating a slot in LCDRange, that simply re-emits the signal valueChanged signal, you can forward or passthrough the valueChanged signal from the slider.
The following achieves the same effect of passing the signal through, however it requires much more code.
class LCDRange : public QWidget
{
// constructor and other methods
//...
signals:
void valueChanged(int)
private slots:
void sliderValueChanged(int);
// ...
};
// in the LCDRange constructor
connect(slider, SIGNAL(valueChanged(int)),
this, SLOT(sliderValueChanged(int)));
// elsewhere in LCDRange's definition
void LCDRange::sliderValueChanged( int value )
{
emit valueChanged( value );
}
connect(slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)));
tells, if the value of the slider gets changed then valueChanged signal will be emited and it will be reflected in LCD view. i.e display slots gets called, whose implementation will be present in QLCDNumber
connect(slider, SIGNAL(valueChanged(int)),
this, SIGNAL(valueChanged(int)));
Qt offers, mechanism of connecting one signal to another signal, it implies if slider valueChangedsignal is emited means present class valueChangedsignal is emited.
the connected slot for valueChanged gets called

Resources