Still no problems closing the widow clicking on the X.
Looking at the logs, now that the program doesn't crash, I noticed that the more tests I performed opening/closing the QDialog, more lines were added at the end of the log.
So I added even more HB_TRACEto hbQt and to composite.prg
The logs revealed that when the program was closing and Harbour GC was shutting down, a lot of requests were done to release objects, a number proportional to the times I opened the QDialog... something is not working as expected...
When we create an object derived from QObject wh silently add an "ancestor":
PHB_ITEM oClass = hbqt_defineClassBegin( "QOBJECT", s_oClass, "HBQTOBJECTHANDLER" );
HBQTOBJECTHANDLER is a Harbour class defined in qtcore/hbqt_misc.prg with some stuff used and some that seems to not be in usebut what interest us now is:
DESTRUCTOR FUNCTION __hbqt_destroy()
The function is defined in qtcore\hbqt_bind.cpp as (simplified)
HB_FUNC( __HBQT_DESTROY )
{
PHB_ITEM pObject = hb_stackSelfItem();
if( pObject )
hbqt_bindDestroyHbObject( pObject );
}
So when a Harbour variable holding an object derived from QObject goes out of scope, or set to a different value, the destroyer gets called and finally the call reaches
hbqt_bindDestroyHbObject( pObject)
When in a .prg we create a
oLabel1 := QLabel( oParent )
oLabel1:setText( "test" )
oLabel1:=NIL
what do you expect ?
Well, since the QLabel() HAS A PARENT when we set the variable to NIL we lose all the references to it by an Harbour variable but the QT object is still alive since we have moved the ownership and the duty of handling its delete to oParent. I don't know how to define this situation with a name, let's call it "Detached from harbour GC", or simply "detached".
When oLabel1 is set to NIL or goes out of scope, it is also removed from the "bind" linked list, whose lenght is the one retrieved by __hbqt_itemsInGlobalList(),
Eache item in this list keeps a reference to the Harbour object, to the Qt object, some flags and ancillary informations, like the type of Qt object.
Ok, so for whatever reason, we ask Harbour to delete oLabel1 and the magic finally happens in hbqt_bindDestroyHbObject( pObject) !
This function retrieves the "bind" item and then tries to understand what to do. It must remove the item from the "bind list (the harbour variable is being deleted) but what to do with the Qt object?
The logic it performs is clear: if the object is derived from QObject AND the object HAS A PARENT, DO NOT delete the Qt object !
Oooooooooooook ....
So, I repeat, if a Qt object has a parent, hbQt would not delete it !!
No references from Harbour are kept ( "bind" is cleaned ) but the Qt object is present.
So, why we have those logs at shutting down with delete requests ? Because when we did the oLabel1 := QLabel( oParent ) hbQt silently added this connection
QObject::connect( obj, SIGNAL( destroyed(QObject*) ), hbqt_bindGetThreadData()->pDestroyer, SLOT( destroyer(QObject*) ) );
to the QLabel and since we did not delete the Qt object, when everything is hutting down and Qt starts to delete QMainWindow, all its children are deleted, and the slots called.
This SIGNAL is only at Qt level hbqt_bindGetThreadData()->pDestroyer references to an instance of c++ HBQDestroyer() that calls hbqt_bindDestroyQtObject
Now, the main question is: why when I open the QDialog with a parent I get no crashes ?
Probably because the QDialog (and its childen? should check...) are never deleted at Qt level, they keep leaking memory but all the references are valid - whatevere those references are !
As soon as I removed the parent, I got my crashes back.
To summarize:
- I was not able to find the real problem
- I added some code to clean the situation when exiting, but it must be revised due to further discoveries
- we need a way to force the delete of a Qt object from a Harbour variable, forse, also if it has a parent() (prabably that :setParent( QWidget()) made the object an orphan
As interesting as it may be, I have to stop now, I have to reorder my ideas....
Francesco