I am learning QT and am trying to get my signals and slots working. I am having no luck.
Here is my Main
int main(int argc, char** argv) {
QApplication app(argc, argv);
FilmInput fi;
FilmWriter fw;
QObject::connect (&fi->okButton, SIGNAL( clicked() ), &fi, SLOT( okButtonClicked() )
); //Error received Base operand of '->' has non-pointer type 'FilmInput'
QObject::connect(&fi,SIGNAL(obtainFilmData(QVariant*)),&fw,SLOT(saveFilmData(QVariant*)));
//Error received No matching function for call to 'QObject::connect(Filminput*, const char*, FilmWriter*, const char*)
fi.show();
return app.exec();
}
and here is my sad attempt at signals and slots:
FilmInput.h
public:
FilmInput();
void okButtonClicked();
QPushButton* okButton;
signals:
void obtainFilmData(Film *film);
Here is FilmWriter.h
public slots:
int saveFilm(Film &f);
Here is Film Input.cpp
void FilmInput::okButtonClicked(){
Film *aFilm=new Film();
aFilm->setDirector(this->edtDirector->text());
emit obtainFilmData(aFilm);
}
Here is FilmWriter.cpp
void FilmInput::okButtonClicked(){
Film *aFilm=new Film();
aFilm->setDirector(this->edtDirector->text());
emit obtainFilmData(aFilm);
}
Please assist me in getting the signals and slots to work, I have spent hours but am no closer to getting it working. I have added the errors received in my comments above.
Regards
okButton is already a pointer, then you should remove the ampersand:
QObject::connect(fi.okButton, SIGNAL(clicked()), &fi, SLOT(okButtonClicked()));
(actually, be sure you create the button in FilmInput' constructor...)
Next, your methods signature doesn't match what you say in connect: given your functions, should be
Object::connect(&fi, SIGNAL(obtainFilmData(Film*)), &fw, SLOT(saveFilmData(Film*)));
this will work, because you are exchanging a pointer, and then Qt can make a copy of it. Otherwise, your type should be registered.
The first error is caused by &fi->okButton because -> has a higher precedence than &. See Operator Precedence. Easily fixed with (&fi)->okButton.
The second error is likely caused by one or both objects not inheriting from QObject. Signals and slots can only work between QObjects. Just a quick checklist:
Both sender and receiver must inherit from the QObject class
The Q_OBJECT macro must be used in the definition of each class
You must generate the appropriate meta-object code and compile it into your project. See Meta-Object Compiler
Also, if you're using Qt version 5.0 or higher, you should start passing function pointers to the connect methods instead of using the SIGNAL and SLOT macros. It'll provide compile-time checking of the connections.
Related
Im having trouble figuring out how to use variables across slots. I have read the signals and slots page a few times over and haven't been able to figure out exactly how to use the connect function. I'm trying to have one button to select the directory, and then have it sent over to the other slot for when I hit print, so it can use that directory. Thanks for the help.
void MainWindow::on_pushButton_clicked()
{
QApplication app(int argc, char** argv());
int n = 107;
for (int q = 1; q <= n; q++)
{
QString fileName =(folderName + "/batch_%1.jpg").arg(q);
// QPrinter printer;
//QPrintDialog *dlg = new QPrintDialog(&printer,0);
//if(dlg->exec() == QDialog::Accepted) {
QPrinter printer(QPrinter::HighResolution);
printer.setResolution(300);
printer.setCopyCount(1);
printer.setDoubleSidedPrinting(false);
printer.setDuplex(QPrinter::DuplexNone);
printer.setColorMode(QPrinter::Color);
printer.setPageSize(QPrinter::Letter);
printer.setPaperSize(QPrinter::Letter);
printer.setPaperSource(QPrinter::Auto);
printer.setOrientation(QPrinter::Portrait);
printer.setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Inch);
QString outputFileName = QStringLiteral("/Users/alexdotzler/Desktop/testing/test%1.pdf").arg(q);
printer.setOutputFileName(outputFileName);
printer.setOutputFormat(QPrinter::PdfFormat);
QImage img(fileName);
QPainter painter(&printer);
painter.drawImage(QPoint(0,0),img);
painter.end();
}
//delete dlg;
}
void MainWindow::on_pushButton_2_clicked()
{
QString folderName = QFileDialog::getExistingDirectory(0, ("Select file"), QDir::currentPath());
}
At first, remember about Minimal reproducible example. This is not only about example, but also about minimal. In this case, your printing code is not nessessary.
Following your question, slot is just a function of class. And in your case, you can jist create printing function, that will be called from both pushButton 1 and 2.
Also, don't be await of real magic from sig/slots. It looks like, but not. Connect slot to signal is just virtual call to function B when function A called.
Then let's go a bit deeply into the world of signals and slots.
You can connect (slots or signals) to signals . No other way.
Basically, signals and slots used to pass data between different objects or to inform them about something.
// foo.h
class First {
...
signals:
void newData(QVariant val);
};
// bar.h
class Another {...
public slots:
void getNewData(QVariant val);
};
// main.cpp
Another another1;
First first1;
QObject::connect(first1, &First::newData, another1, &Another::getNewData);
You can also create something can be called "Connections tree", when signal [Another::sig1] connected to [Third::sig2], which connected to signal [Fourth::sig3] and slot [Fifth::slot1], and so on. I'm not sure someone really need it, but be free to play with.
AFAIK, the only reason to use signal/slot connection inside single class, is when you need to pass some data to GUI thread from another thread running in GUI. Please, don't do that in real projects.
Make folderName a member of your MainWindow class so it will be accessible by your pushButton clicked slots. Then you can read/write it as necessary from those slots.
My original code passed a QStringList from the signal to the slot and then returned a QList. Everything worked fine but I needed to change both the QStringList and QList into 2 different subclassed QObjects. Since then I have been receiving errors like "synthesized method first required here" or it simply crashes without any error message.
I understand that qt copies all arguments passed in a queued connection and a qobject cannot be copied. So instead of returning a qobject I thought I would create both qobjects prior to emitting the signal. Then I would pass references to each object, modify one of them in the slot function and void the return value. Unfortunately the app still crashes no matter how I code the signal and slot functions. How can I code the signal/slot functions and connect them to either pass both qobjects as arguments or return a qobject?
MyQObject1 obj1("a","b","c");
MyQObject2 obj2();
emit sendSignal(&obj1, &obj2);
// or
MyQObject2 obj2 = emit sendSignal(&obj1);
connect(someObj, SIGNAL(sendSignal(const QObject&)), this, SLOT(receiveSignal(const QObject&)));
The receiveSignal() function does not directly create or modify any qobject. It has to pass the qobjects to another function first which then either modifies obj2 or creates and returns it. Any code examples would be greatly appreciated.
Usually QObject is passed by pointer, not by reference (note that QObject cannot be copied and cannot be passed by value). QObject* is registered as a meta type by default. So creating a signal and a slot with QObject* argument is enough to get them work:
private slots:
void test_slot(QObject* object);
signals:
void test_signal(QObject* object);
Initialization:
connect(this, SIGNAL(test_signal(QObject*)), this, SLOT(test_slot(QObject*)));
Emitting:
QObject* object = new QObject();
emit test_signal(object);
Of course the signal and the slot could be in different classes.
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()));
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.
In short, I get following error:
QObject::connect: Cannot queue arguments of type 'cv::Mat'
(Make sure 'cv::Mat' is registered using qRegisterMetaType().)
What I'm trying to do is send a signal containing two cv::Mat images from a QThread to the main thread, so that I can display the output. There's no compile time error, but when I run the program, it gets stuck at a breakpoint in qglobal.h (inline void qt_noop() {}).
I've tried to add Q_DECLARE_METATYPE(cv::Mat) to the code, to no avail. I'm quite suck with what to do now.
code
In a QThread class:
signals:
void sndFlow(cv::Mat &leftEye, cv::Mat &rightEye);
void eyesDriver::run()
{
forever
{
flow->draw(leftEye, rightEye);
sndFlow(leftEye, rightEye);
}
}
Capturing in a QObject class:
public slots:
void recFlow(cv::Mat &leftEye, cv::Mat &rightEye);
void myClass::recFlow(cv::Mat &leftEye, cv::Mat &rightEye)
{
cv::imshow("left", leftEye);
cv::imshow("rigth", rightEye);
cv::waitKey(40);
}
In main:
Q_DECLARE_METATYPE(cv::Mat)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qRegisterMetaType< cv::Mat >("cv::Mat");
// create objects from QThread and QObject class
QObject::connect(&qthread, SIGNAL(sndFlow(cv::Mat&,cv::Mat&)),
&qobject, SLOT(recFlow(cv::Mat&,cv::Mat&)));
qthread.start();
return a.exec();
}
Changing the signal-slot variables to QSharedPointer< cv::Mat > does not work either. Gives the same error:
QObject::connect: Cannot queue arguments of type 'QSharedPointer<cv::Mat>'
(Make sure 'QSharedPointer<cv::Mat>' is registered using qRegisterMetaType().)
WORKS
All right, it seems to work. I've move qRegisterMetaType< cv::Mat >("cv::Mat"); right before the QObject::connect call. However I still have to 'F5' past the breakpoint in qglobal.h, it works afterwards.
I might be wrong, but it seems that the location of qRegisterMetaType is not trivial.
You need to call qRegisterMetaType in addition to the macro (or instead of it, depending on your needs). This is necessary for the signals to be able to marshal your data across threads. However, it might be a wiser idea to pass by reference or smart pointer, or raw pointer if you are using the QObject hierarchy to manage the object lifetime.