Minimum required to emit a signal
I've been using signals and slots with classes derived from classes created in Qt Designer. In addition to inheriting from the class created with Designer, I inherit from QDialog or QWidget (monkey see monkey do).
I dutifully add the Q_OBJECT in the derived class header file, and everything seems work. I can connect signals of built-in widgets to my own custom slots and emit (and connect) my own custom signals. I must confess I don't understand what's going on "under the covers".
I have some utility classes that really aren't GUI oriented. But, I can see it would be handy for some of the methods in these clases to emit signals that could be connected to new widgets I'd create for displaying some diagnostic information.
I think I need the Q_OBJECT in these class declarations to have a signals: keyword
But, what what do should I inherit from? When I tried to public inherit only from QDialog or QWidget, the compiler complains about not being able to access private data in the base class. And that doesn't seem clean, given my derived class is only similar to a QDialog or QWidget in that it would emit a signal.
So, what do I need just to create a signal?
Thanks,
Dave Thomas
Re: Minimum required to emit a signal
Quote:
Originally Posted by
davethomaspilot
But, what what do should I inherit from?
Inherit QObject.
Re: Minimum required to emit a signal
Thanks for the quick reply.
I get similar error messages when inheriting from QObject:
1>c:\users\dave\my software\stats helper\heat_message.h(127): error C2248: 'QObject::operator =' : cannot access private member declared in class 'QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(333) : see declaration of 'QObject::operator ='
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(112) : see declaration of 'QObject'
1> This diagnostic occurred in the compiler generated function 'HeatMessage &HeatMessage::operator =(const HeatMessage &)'
1> main_window.cpp
1>c:\users\dave\my software\stats helper\heat_message.h(127): error C2248: 'QObject::QObject' : cannot access private member declared in class 'QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(333) : see declaration of 'QObject::QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(112) : see declaration of 'QObject'
1> This diagnostic occurred in the compiler generated function 'HeatMessage::HeatMessage(const HeatMessage &)'
1> moc_collect_stats.cpp
1>c:\users\dave\my software\stats helper\heat_message.h(127): error C2248: 'QObject::QObject' : cannot access private member declared in class 'QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(333) : see declaration of 'QObject::QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(112) : see declaration of 'QObject'
1> This diagnostic occurred in the compiler generated function 'HeatMessage::HeatMessage(const HeatMessage &)'
1> moc_main_window.cpp
1>c:\users\dave\my software\stats helper\heat_message.h(127): error C2248: 'QObject::QObject' : cannot access private member declared in class 'QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(333) : see declaration of 'QObject::QObject'
1> c:\opencv\dep\qt\qt-everywhere-opensource-src-4.8.2\include\qtcore\../../src/corelib/kernel/qobject.h(112) : see declaration of 'QObject'
1> This diagnostic occurred in the compiler generated function 'HeatMessage::HeatMessage(const HeatMessage &)'
1> Generating Code...
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:07.10
It compiles fine without the Q_OBJECT and inheritance from QObject.
Do I need a different constructor that passes 0 to the Bo
Here's the relevant part of header file:
Code:
#ifndef HEAT_MESSAGE_H
#define HEAT_MESSAGE_H
#include <QtCore/QCoreApplication>
#include <QRegExp>
#include <QMap>
#include <iostream>
#include <fstream>
#include <QTextStream>
#include <QWidget>
{
Q_OBJECT
public:
HeatMessage(void);
QList<HeatRecord> heat_records;
float get_time(char lane, char id);
float get_sum_time(char lane);
char get_result(char lane);
int get_race(void) {return heat_records.begin()->get_race();};
int get_heat(void) {return heat_records.begin()->get_heat();};
int num_dogs(char lane);
};
#endif
Re: Minimum required to emit a signal
The QObject copy and assignment constructors are declared private. You cannot copy a QObject and will get these sort of errors if you attempt to do that.
Re: Minimum required to emit a signal
You can't create copies or assignments to QObject instances. You probably have an assignment operator in your class (or the compiler tries to create one for you based on your code). This is not possible.
Re: Minimum required to emit a signal
Hmm, I don't see anthing like that, unless the following fragment (which is called from a method in a different QWidget class)
Code:
HeatMessage hm(msg);
emit new_heat(hm);
Do you have a suggestion on how to find the offending code?
Thanks,
Dave Thomas
Re: Minimum required to emit a signal
What is in heat_message.h file in line 127 (and around)?
Re: Minimum required to emit a signal
That's the last line in the header file (the };)
Not very useful.
[code]
void print(char lane, QTextStream &);
void print(QTextStream &);
line 127: };
Re: Minimum required to emit a signal
Please post the whole file as an attachment.
1 Attachment(s)
Re: Minimum required to emit a signal
Here it is:
Added after 27 minutes:
A HeatMessage object is passed as the argument emitted signals in a couple of places in other files. If I eliminate those calls, the complaints about referencing a private assignment operator in the base class is eliminated. However, I get some unresolved externals at link time:
1>heat_message.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall HeatMessage::metaObject(void)const " (?metaObject@HeatMessage@@UBEPBUQMetaObject@@XZ)
1>heat_message.obj : error LNK2001: unresolved external symbol "public: virtual void * __thiscall HeatMessage::qt_metacast(char const *)" (?qt_metacast@HeatMessage@@UAEPAXPBD@Z)
1>heat_message.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall HeatMessage::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@HeatMessage@@UAEHW4Call@QMetaObject@ @HPAPAX@Z)
1>debug\\Stats Helper.exe : fatal error LNK1120: 3 unresolved externals
1>
Maybe those are result of my quick hack to eliminate code that perhaps caused an implicit copy of a HeatMessage object.
But, now I'm thinking this isn't such a good idea, if I can't pass QObjects as arguments of emitted signals. Is that, in fact, a limitation I need to deal with. Would passing by reference avoid the issue?
Thanks,
Re: Minimum required to emit a signal
How are you using both these classes? Can you show us some code that creates instances of them, etc.?
One thing that I can immediately see that it is incorrect is this:
Code:
QList<HeatRecord> heat_records;
This requires HeatRecord to be copyable, which it is not. You can only have this:
Code:
QList<HeatRecord*> heat_records;
Re: Minimum required to emit a signal
Changing the signal prototypes to use HeatMessage & instead of HeatMessage (and one of my methods with a HeatMessage arg) also eliminated the private data compile errors. However, i still get the unresolved externals. Chasing that now.
heat_message.obj : error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall HeatMessage::metaObject(void)const " (?metaObject@HeatMessage@@UBEPBUQMetaObject@@XZ)
1>heat_message.obj : error LNK2001: unresolved external symbol "public: virtual void * __thiscall HeatMessage::qt_metacast(char const *)" (?qt_metacast@HeatMessage@@UAEPAXPBD@Z)
1>heat_message.obj : error LNK2001: unresolved external symbol "public: virtual int __thiscall HeatMessage::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@HeatMessage@@UAEHW4Call@QMetaObject@ @HPAPAX@Z)
1>debug\\Stats Helper.exe : fatal error LNK1120: 3 unresolved externals
Re: Minimum required to emit a signal
Always pass pointers to QObjects in signals, never references nor copies.
The errors you posted are related to not running qmake after adding Q_OBJECT macro to a class.
Re: Minimum required to emit a signal
Example usage:
connect(this,SIGNAL(new_heat(HeatMessage &)),collect_stats,SLOT(new_heat(HeatMessage &)));
Signal emitted after constructing the HeatMessage like this:
Code:
if (msg[len - 7] == 'X')
i
{
HeatMessage hm(msg);
emit new_heat(hm);
fd = fopen("console_data.txt","a");
fprintf(fd,"%s\n", msg.data());
fclose(fd);
msg.clear();
}
That's after changing the signal to use a HeatMessage & instead of a HeatMessage. Compiles now, doesn't link. And, I think it would segfault anyway, since hm will go out of scope before it's used in the slot.
Quote:
Always pass pointers to QObjects in signals, never references nor copies.
Our posts crossed. I'll try that, and
DOH! of course. I'll redo the qmake.
Thanks,
Dave Thomas
So, I guess I'd have to do a "new" to create a HeatMessage, pass the resulting pointer in the signal, then delete in the slot?
Or, will some other memory management mechanism try to handle it since it's a Q_OBJECT?
Re: Minimum required to emit a signal
Quote:
Originally Posted by
davethomaspilot
So, I guess I'd have to do a "new" to create a HeatMessage, pass the resulting pointer in the signal, then delete in the slot?
It depends on the object. HeatMessage doesn't need to inherit QObject as far as your code is concerned. It doesn't have any signals or slots. Same goes for HeatRecord. Maybe we misunderstood each other. I understood that you wanted some objects of yours to be able to emit signals but now I see that you wanted them to be emitted as arguments to signals. Is that correct?
Re: Minimum required to emit a signal
No, I want methods in the HeatRecord and/or HeatMessage to emit signals in addition to objects of these types to be passed as arguments.
Those classes parse text data and understand a specific text data format. I'm adding some new syntax to include additional diagnostics about the RF channel integrity and locating the new code that recognizes the new packet signatures seem to make the most sense.
Rather than just printing to QDebug(), I was going to add a widget with slots to display that data.
So, yes I DO want to send signals from methods in a HeatMessage and/or HeatRecord class. But, maybe it's not worth it, if doing that implies more complicated memory management, since I now know signals can contain only pointers to QObjects, not references or copies.
Thanks,
Dave Thomas
Sorry for the confusion--I just deleted the signals: from the header file until I got a successful build with the Q_Object.
The method I'd add would simply send a HeatRecord to a slot in a different class if the content matched a specified RegExp.
Re: Minimum required to emit a signal
Quote:
Originally Posted by
davethomaspilot
No, I want methods in the HeatRecord and/or HeatMessage to emit signals in addition to objects of these types to be passed as arguments.
So what sense does it make to create an object just to emit it in a signal and destroy it right away? What signals could it possibly emit? I think you have a serious design problem... Maybe instead of inheritance you should use composition? I mean have some persistent object that emits signals "on behalf" of value-based HeatRecord and HeatMessage objects.
Re: Minimum required to emit a signal
I don't create an object just to emit a signal. The object consists of data and methods to parse the data to get interesting data.
The entire object is passed to slots. Not just the data, but the methods to access the data in convenient ways are encapsulated with the data--standard object oriented design. The objects don't exist for long, but so what? You're suggesting that they shouldn't be objects because of this?
But maybe that's a consideration for QObjects? And for that reason, don't create them in situations like this?
I think you're saying it doesn't make sense to inherit from QObject just to send a signal. Fair enough.
Thanks,
Dave Thomas
Re: Minimum required to emit a signal
Quote:
Originally Posted by
davethomaspilot
I don't create an object just to emit a signal. The object consists of data and methods to parse the data to get interesting data.
You said yourself:
Quote:
So, I guess I'd have to do a "new" to create a HeatMessage, pass the resulting pointer in the signal, then delete in the slot?
Which means you want to create an object, emit it and destroy it. So the only two objects that could possibly connect to a signal emitted by this short-lived object are the sender and the receiver. And both of them can query the state of the object directly, without signals.
Quote:
The objects don't exist for long, but so what? You're suggesting that they shouldn't be objects because of this?
I'm suggesting they shouldn't be emitting any signals.
Quote:
I think you're saying it doesn't make sense to inherit from QObject just to send a signal. Fair enough.
No, that's not what I'm saying. I'm saying it doesn't make sense to emit a signal that nobody can use.
Re: Minimum required to emit a signal
The constructor for the HeatMessage looks at the passed QByteArray to see if it looks like a set of valid HeatRecords This includes making sure it has an integral number of 16 bytes, and finding record boundaries for creating HeatRecords.
HeatRecords are constructed from data passed to the HeatMessage constructor and added to a list of all the HeatRecords found in the HeatMessage.
The HeatRecord constructor parses the bytes passed and saves data in private data members. Methods of the HeatRecord class allow encapsulate access of things like race number, heat number, win/loss--insulating code that wants that information from having to know the byte layout of a HeatRecord or HeatMessage. Sure, code that creates the HeatMessage could dive right into the data, but knowledge of the data format should be encapsulted in the HeatMessage class. That's the motivation for creating an object, not for simply creating a signal.
However, I'm now adding a new record to a communication protocol, that's sort of like "out of band" data. It contains things like "number of retries", RF signal strength, "number of dropped pakets" It isn't a valid HeatRecord (which is a data format I can't modify), and I'd like to show its data in another Widget (to be developed). I'm considering using a signal, so a connect can be used to send these new type records to a widget that extracts data from and displays these new type of records.
So, the motivation for creating a signal is to allow the data parsed in the HeatMessage/HeatRecord constructor to be consumed by other widgets as defined in the widgets that create the HeatMessages.
So, yes, the object would get created, used in the slot code, then discarded. But much more is going on than simply sending the signal. Performance is not an issue-- the HeatMessages are only a few hundred bytes and get created no more frequently than a few minutes apart.
Why shouldn't I be emitting any signals? Isn't this what they are designed fo? The signal indicates "bytes matching this signature were found", and provides the object which has this data along with the surrounding records and accessor methods for display by unspecified widget. Association with a widget(s) is done not by the HeatMessage/HeatRecord class, but in classes that create them.
Thanks,
Added after 19 minutes:
"No, that's not what I'm saying. I'm saying it doesn't make sense to emit a signal that nobody can use."
Thinking about it some more--I guess the problem is the connect, since it would need to be done before the object got created for signals emitted during construction to be handled. But, I'd need the pointer to the newly created object to do the connect.
Obviously, I can get it done without using a signal. Just thought that was a "slick" way of doing it. Not so much now.
Thanks,
Dave Thomas