Carlos, I still have some problems trying to focus on this problem. I "feel" there is something not really clear...
You want to be able to delete a HMG4 object and automatically delete the underlaying hbQt and expecially Qt objects. In every message/thread/sample I find on the web, this is NOT POSSIBLE in Qt. Memory management is done by Qt with its own rules. It took more than one year to understand what happens and how to interface this in hbQt ! Endless sleepless nights...
The only way to ask Qt to delete the object is using the Qt::WA_DeleteOnClose. And you are just asking Qt to "please, can you delete the object when you want to do it..., really thanks...."
That said, your sample code looks strange to me..... let me explain
(I separate with letters so that we can differentiate the discussion)
A) Parenting
I don't understand why you are setting the parents in this way.... are these parenting useful in a application ?
:parent has good code, for examply it correctly handles the removal from the parent both as DATA member and in ::aControls array, something :release() in the various forms doesn't do correctly.
What :parent() doesn't do is Qt parenting !!!!!
Have a look at Label:New(). Creating the QLabel we set a Qt parenting. ::parent() doesn't update this parenting.
So after the line
LBL3:Parent := LBL2
the HMG parenting is one, the Qt parenting is another one and LBL3 is still a child of the window....
You may change the code to
Code: Select all
With Object LBL3 := label():New( "LBL3", LBL2 )
but the label won't show... a label inside a label ?!?!?!?
B) why qt object doesn't get deleted
Code: Select all
// Remove from the parent pool control
FOR i := 1 TO Len( ::oParent:aControls )
IF hb_IsObject( ::oParent:aControls[ i ] )
IF ::oParent:aControls[ i ]:Name == ::Name
::oParent:aControls[ i ] := Nil
// ::oParent:oQTObject := NIL // => GPF
ENDIF
ENDIF
NEXT
Doesn't the ::oParent:aControls[ i ] := Nil cuts the rope we are climbing ? Well, actually it cuts one of the THREE ropes: we have one HMG4 object that is pointed by 3 variables, one is the LOCAL variable, the other is in the aControls array, the last is the DATA member.
Code: Select all
METHOD NEW CLASS CONTROL
....
IF hb_isobject( ::oParent ) == .T.
AADD( ::oParent:aControls , Self )
::oParent:AddData( ::cName , Self )
ENDIF
LBL1 is accessible by the LOCAL LBL1, by Win_1:lbl1 and ::oParent:aControls[ <index of lbl1> ] (BTW, why don't we switch to a hash???)
So to REALLY permit Qt to DELETE a Qt object we should set THREE NILs...
The code for two of them in in :parent(), the third is just a lbl1 := NIL
Now the GC should be able to take care of the situation....
C) main and child windows
Now that we should have found why the Qt objects doesn't get deleted in this specific situation, I'd like to understand WHY you should want to delete them
in the main window.
hbide at startup creates all the windows, panes, tabs and just switches them on/off - is is slower at startup but it results in a quicker UI experience for the user at runtime. Skype uses Qt. It has one "main form" (the list of contacts, with at couple of tabs, etc, etc) there are created at startup and never deleted and when necessary (for chats, calls etc) a new window is opened and then closed.
The main form is built to stay "on" for all the life of the program and anyway in skype and hbide the objects are not "parented" by the main form but by layouts, panes, tables etc. There should ALWAYS be a "central widget" in a QMainWindow if you don't want to have problems with the layout !!!!
If you want to use Qt setParent (and we saw we didn't call it in point A) read what the Qt docs says:
Code: Select all
Warning: It is very unlikely that you will ever need this function. If you have a widget that changes its content dynamically, it is far easier to use QStackedWidget.
That said I will concentrate the attention on the Button1Click() function since in a normal business program you will use a lot of "child" windows, can they be QDialog with tons of hmg objects inside...
The real question we should ask is: is the memory and the Qt objects freed when we call functions like Button1Click and they return the control to the calling function (the oStd LOCAL variable goes out of scope) ?
It doesn't happen with Button1Click because.... please read following point before doing tests
D) how to close a window
From QWidget:close() description: (close() is called by window:release() )
Code: Select all
First it sends the widget a QCloseEvent. The widget is hidden if it accepts the close event.
This means that if we add the msginfo line in your demo
Code: Select all
oStd:Activate()
msginfo( "button1click about to return" )
oStd:Release()
we will NEVER see that msginfo !!!
The window gets hidden, memory is NOT released (hmg/hbQt/Qt) and when we press Win_1:oBtn a NEW window is created and shown !
Is it possible to solve this ? Of course
But unfortunately window.prg code is flawed...
In your sample code the oStd window is created of type WND_STANDARD. In this way you may open dozens of oStd indipendent windows. When windows type is not WND_MAIN the windows is run by hmg using QEventLoop:exec( 0 ) and this means that we must CLOSE (and not hide) the window with :exit()
Please note how the situation is handled in OnRelease method for WND_MAIN e WND_MODAL window types, where we force an exit() or quit().
Now look at the Window:Release() method
Code: Select all
METHOD Release class WINDOW
IF ::nType == WND_MODAL
::oEventLoop:Exit( 0 )
ENDIF
Super:Release() // it calls :close()
RETURN NIL
When window type is WND_STANDARD it doesn't stop the eventloop....
Since WND_MAIN is the only window type run in a different way (we should check all the windows types) I changed the if in:
Code: Select all
IF ::nType != WND_MAIN
::oEventLoop:Exit( 0 )
ENDIF
and now everything works, the window is closed, the msginfo shown and when oStd goes out of scope used memory (hmg/hbQt/Qt) should be correctly recovered.