I guess you could move the event handling from #PB_Event_DeactivateWindow to #PB_Event_ActivateWindow so when a window is activated it closes any other open windows you want rid of. You'll just have to cycle over the windows you want to display that behaviour, test each one with IsWindow() and if so close it.Blue wrote:Oops ! That’s an unexpected curve. But now that you point it out, it does seem obvious.TI-994A wrote:[...]
Nevertheless, it should be noted that even windows from other processes would be able to dismiss this child window if they were to automatically pop-up or receive focus.
Child window message loop
- the.weavster
- Addict
- Posts: 1577
- Joined: Thu Jul 03, 2003 6:53 pm
- Location: England
Re: Child window message loop
Re: Child window message loop
I've added a small routine that would trigger-launch NotePad five seconds after the child window is opened. Although there are no clicks whatsoever, the child window would be closed once NotePad launches and receives focus.Blue wrote:...try the latest updated code provided by the.weavster, but with the 2 modified procedures I submitted. Can you think of an independent process that would interfere unexpectedly with that child window ?
Code: Select all
Enumeration windows
#frmMain
#frmChild
EndEnumeration
Enumeration gadgets
#frmMain_btnChild
#frmChild_btnOK
#frmMain_txtClock
#frmChild_txtClock
EndEnumeration
Enumeration timers
#frmMain_tmrClock
#frmChild_tmrClock
EndEnumeration
;; modified by Blue
Procedure frmChild_Open()
If OpenWindow(#frmChild,#PB_Ignore,#PB_Ignore,250,150,"child",#PB_Window_SystemMenu)
ButtonGadget(#frmChild_btnOK,10,10,150,30,"Message Box") ; <<< changed
TextGadget(#frmChild_txtClock,20, 110, 210, 30,
FormatDate("%hh:%ii", Date()), #PB_Text_Right)
AddWindowTimer(#frmChild, #frmChild_tmrClock, 1000)
;#############################
SetWindowData(#frmChild,#True) ; deactivating the window will close it ; <<< changed
ResizeWindow(#frmChild,WindowX(#frmMain)+80,WindowY(#frmMain)+80,#PB_Ignore,#PB_Ignore) ;so we can always see it
SetWindowTitle(#frmChild,"Closes automatically") ; <<< changed
;#############################
Global t = ElapsedMilliseconds()
EndIf
EndProcedure
Procedure frmChild_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmChild_btnOK
;##############################
SetWindowData(#frmChild,#False)
SetWindowTitle(#frmChild,"Remains active")
nResponse = MessageRequester("Message","something important... or not !",#PB_MessageRequester_Ok)
SetWindowData(#frmChild,#True)
SetWindowTitle(#frmChild,"Closes automatically") ; <<< changed
;##############################
EndSelect
EndProcedure
; app's main window
Procedure frmMain_Open()
If OpenWindow(#frmMain,#PB_Ignore,#PB_Ignore,400,300,"parent")
ButtonGadget(#frmMain_btnChild,10,10,150,30,"Show Child")
TextGadget(#frmMain_txtClock,280, 20, 100, 30,
FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
AddWindowTimer(#frmMain, #frmMain_tmrClock, 1000)
EndIf
EndProcedure
Procedure frmMain_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmMain_btnChild
frmChild_Open()
EndSelect
EndProcedure
Procedure Events_Timer()
Static minuteUpdatedTime.s, c
nTimer = EventTimer()
Select nTimer
Case #frmMain_tmrClock
;updates every second
SetGadgetText(#frmMain_txtClock,
FormatDate("%hh:%ii:%ss", Date()))
Case #frmChild_tmrClock
;updates every minute
If ElapsedMilliseconds() - t > 1000
SetGadgetText(#frmChild_txtClock, "I'm going bye-bye in " + Str(5 - c) + "...")
t = 0 : c + 1
If c > 5
c = 0
RunProgram("notepad.exe")
EndIf
EndIf
currentTime.s = FormatDate("%hh:%ii", Date())
If minuteUpdatedTime <> currentTime
minuteUpdatedTime = currentTime
SetGadgetText(#frmChild_txtClock, currentTime)
EndIf
EndSelect
EndProcedure
Procedure Events_Gadget()
nGadget = EventGadget()
nEventType = EventType()
nX = 0 : nY = 0
Select GadgetType(nGadget)
Case #PB_GadgetType_Canvas
nX = GetGadgetAttribute(nGadget,#PB_Canvas_MouseX)
nY = GetGadgetAttribute(nGadget,#PB_Canvas_MouseY)
Case #PB_GadgetType_ListIcon
nY = GetGadgetState(nGadget)
; etc...
EndSelect
Select EventWindow()
Case #frmMain
frmMain_onGadget(nGadget,nEventType,nX,nY)
Case #frmChild
frmChild_onGadget(nGadget,nEventType,nX,nY)
EndSelect
EndProcedure
;################################
;Split the handlers
Procedure Events_CloseWindow()
nWin = EventWindow()
Select nWin
Case #frmMain
End ; closing main window quits app
Default
CloseWindow(nWin)
EndSelect
EndProcedure
Procedure Events_DeactivateWindow()
nWin = EventWindow()
Select nWin
Case #frmChild ; list all windows you want this behaviour for
If GetWindowData(nWin) = #True
CloseWindow(nWin)
EndIf
EndSelect
EndProcedure
;###############################
BindEvent(#PB_Event_Gadget,@Events_Gadget())
BindEvent(#PB_Event_Timer,@Events_Timer())
BindEvent(#PB_Event_CloseWindow,@Events_CloseWindow())
;###############################
;point this to the new handler
BindEvent(#PB_Event_DeactivateWindow,@Events_DeactivateWindow())
;###############################
frmMain_Open()
Repeat
nWait = WaitWindowEvent()
ForEver
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel 

Re: Child window message loop
I don't believe that he's referring to your example. IMHO, the object-based callbacks are very clean and structured. It's a good model.the.weavster wrote:To my mind there is only one event loop in my code. Taking gadget events as an example, Events_Gadget() is the only callback bound to gadget events and that simply routes to other procedures based on the associated window id, gadget id and event type. Would you agree with that, Fred?Fred wrote:When starting with PB, it's probably a very good advice to use only one event loop.

Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel 

Re: Child window message loop
You're right; neither the UnbindEvent() nor the CloseWindow() functions seem to release the memory captured by the BindEvent() function. The memory footprint simply keeps growing with every call.User_Russian wrote:...If you call BindEvent several times with identical parameters, a memory leak occurs.Code: Select all
Procedure Child() If OpenWindow(1, #PB_Ignore, #PB_Ignore, 200, 150, "child", #PB_Window_SystemMenu, WindowID(0)) BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1) For i=1 To 1000000 BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1) Next i EndIf EndProcedure
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel 

Re: Child window message loop
Clever demonstration, TI-994A. Much appreciated.TI-994A wrote:...
I've added a small routine that would trigger-launch NotePad five seconds after the child window is opened. Although there are no clicks whatsoever, the child window would be closed once NotePad launches and receives focus.
...
@ the.weavster
As TI-994A pointed out, your way of coding is very good. It uses a single events loops, which is obvious just from reading the code.
Fred's comment could only be directed at my admission that I keep using dedicated events loops for my child windows.
PB Forums : Proof positive that 2 heads (or more...) are better than one 

Re: Child window message loop
@ User_Russian :TI-994A wrote:You're right; neither the UnbindEvent() nor the CloseWindow() functions seem to release the memory captured by the BindEvent() function. The memory footprint simply keeps growing with every call.User_Russian wrote:...If you call BindEvent several times with identical parameters, a memory leak occurs.
[...]
In light of TI-994A's confirmation of your observation concerning the memory leak, you really should post that as a bug, regardless of what Fred had to say about it.
PB Forums : Proof positive that 2 heads (or more...) are better than one 

-
- Enthusiast
- Posts: 334
- Joined: Mon Feb 04, 2013 5:28 pm
Re: Child window message loop
I don't think there is a memory leak, or at least I don't think what shown in the task manager can be used as supporting evidence.Blue wrote: In light of TI-994A's confirmation of your observation concerning the memory leak, you really should post that as a bug, regardless of what Fred had to say about it.
When you free resources, this is not necessary reflected in the task manager in real time.
Some pages of memory results as allocated in the task manager until the memory manager trims pages from the working set to create more available memory. It does that when it thinks it's the right moment to do so.
If you manually force the trim of those pages using the appropriate API call, the task manager reflects that immediately and there is no longer an apparent memory leak.
Re: Child window message loop
That might be right. Not sure if there are any utilities to force this cache-trimming, but simply starting up some gobblers, like PhotoShop, might also do the trick. The memory usage, as reported by Task Manager, shrinks exponentially.DontTalkToMe wrote:... When you free resources, this is not necessary reflected in the task manager in real time.
Some pages of memory results as allocated in the task manager until the memory manager trims pages...
Good info. Thank you.

Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel 

-
- Enthusiast
- Posts: 334
- Joined: Mon Feb 04, 2013 5:28 pm
Re: Child window message loop
TI, you can use EmptyWorkingSet to force the trimming of the pages not actually in use.
https://msdn.microsoft.com/it-it/library/windows/desktop/ms682606(v=vs.85).aspx
A tool you can use is VMMap.
https://docs.microsoft.com/en-us/sysinternals/downloads/vmmap
You can force the trimming using an option under Tools.
About the bind/unbind commands, they seem to work correctly when the same event is bound to different callbacks.
If you bind a single event to the same callback multiple times (why ?) and then try to unbind it, it does so only once, so n-1 bindings are still active.
Preferably this should not be permitted or at least treated appropriately, I think.
https://msdn.microsoft.com/it-it/library/windows/desktop/ms682606(v=vs.85).aspx
A tool you can use is VMMap.
https://docs.microsoft.com/en-us/sysinternals/downloads/vmmap
You can force the trimming using an option under Tools.
About the bind/unbind commands, they seem to work correctly when the same event is bound to different callbacks.
If you bind a single event to the same callback multiple times (why ?) and then try to unbind it, it does so only once, so n-1 bindings are still active.
Preferably this should not be permitted or at least treated appropriately, I think.
Last edited by DontTalkToMe on Wed May 30, 2018 3:19 pm, edited 1 time in total.
-
- Addict
- Posts: 1522
- Joined: Wed Nov 12, 2008 5:01 pm
- Location: Russia
Re: Child window message loop
This will move memory to swap file, but will not free.
Re: Child window message loop
Continuing on the subject of a child window closing when it loses focus
(running PB 5.62 (X64) on Windows 10 Pro x64)
I'm still using the.weavster's sample code, heavily modified by Ti-994A and some more by me. What follows is its latest incarnation, with the addition of a few debug statements to track what happens under certain circumstances :
The code works really well, but something is missing :
Launch the app, minimize PB's IDE, just leaving the debug output window opened. When you select PB's debug output window, it takes on its activated look, while the demo changes to its deactivated look. When you return to the demo, the reverse happens : its window takes on its activated look, while the debug window looks deactivated. All normal. That's the behaviour you expect and it happens correctly with any opened window on the desktop, and with the desktop itself.
However, If you now open a child window in the demo app, and then click into the parent window, the child window closes (as designed by code), but the parent window does not take on its activated look ! Furthermore, at line 144 of the code, the instruction SetActiveWindow(#main_WINDOW), intended to provoke a change of appearance, produces nothing; the debug statements also clearly show that the #PB_Event_ActivateWindow event never fires, even though it should (I think...). The parent window is expected to change its appearance to its activated colors.
Is this a PB bug ?
(running PB 5.62 (X64) on Windows 10 Pro x64)
I'm still using the.weavster's sample code, heavily modified by Ti-994A and some more by me. What follows is its latest incarnation, with the addition of a few debug statements to track what happens under certain circumstances :
Code: Select all
;- :: ************************************
;- :: may 2018
;- :: source : the.weavster
;- :: modified by TI-994A, Blue
;- :: child window 3
; https://www.purebasic.fr/english/viewtopic.php?p=522679#p522679
; demo : how to open an interactive child window WITHOUT using a secondary dedicated events loop
;- :: ************************************
EnableExplicit
; :: constantes
Enumeration windows
#main_WINDOW
#child_WINDOW
EndEnumeration
Enumeration gadgets
#main_btn
#main_clock
#child_btn
#child_clock
EndEnumeration
Enumeration timers
#main_timer
#child_timer
EndEnumeration
;.
Procedure Child_Open()
If OpenWindow(#child_WINDOW,#PB_Ignore,#PB_Ignore,250,150,"child",#PB_Window_SystemMenu)
ButtonGadget(#child_btn,10,10,150,30,"Message Box")
TextGadget(#child_clock,20, 110, 210, 30,
FormatDate("%hh:%ii", Date()), #PB_Text_Right)
AddWindowTimer(#child_WINDOW, #child_timer, 1000)
SetWindowData(#child_WINDOW,#True) ; signals that, when losing focus, the child window must close
ResizeWindow(#child_WINDOW,WindowX(#main_WINDOW)+80,WindowY(#main_WINDOW)+80,#PB_Ignore,#PB_Ignore) ;so it remains visible
SetWindowTitle(#child_WINDOW,"Closes spontaneously !") ; show action in title
#timerTime = 20 ; timer duration
Global timer_t = #timerTime ; timer countdown
EndIf
EndProcedure
Procedure Child_gadgets(gadget, eventType)
Select gadget
Case #child_btn
;##############################
SetWindowData(#child_WINDOW,#False) ; child window must NOT close when losing focus to message box
SetWindowTitle(#child_WINDOW,"Remains active")
MessageRequester("Message", "something real important... or not !",$40)
; reset the window to its auto-close state
SetWindowData(#child_WINDOW,#True) ; will close when losing focus
SetWindowTitle(#child_WINDOW,"Closes spontaneously !")
;##############################
EndSelect
EndProcedure
; app's main window
Procedure Main_opened()
If OpenWindow(#main_WINDOW,#PB_Ignore,#PB_Ignore,400,300,"Main window")
ButtonGadget(#main_btn,10,10,150,30,"Show Child")
TextGadget(#main_clock,280, 20, 100, 30,
FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
AddWindowTimer(#main_WINDOW, #main_timer, 1000)
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure Main_gadgets(gadget, eventType)
Select gadget
Case #main_btn
Child_Open()
EndSelect
EndProcedure
;- ************************************
Procedure Events_Timer()
Static currentMinute
Select EventTimer()
Case #main_timer
;updates every second
SetGadgetText(#main_clock,
FormatDate("%hh:%ii:%ss", Date()))
Case #child_timer
;updates every minute
Define thisMinute = Minute(Date())
If thisMinute <> currentMinute
currentMinute = thisMinute
SetGadgetText(#child_clock, FormatDate("%hh:%ii", Date()))
EndIf
; timer_t previously defined as global
If timer_t <= 10
SetGadgetText(#child_clock, "NotePad opens in " + timer_t + "...")
EndIf
timer_t - 1
Debug "timer_t: "+ timer_t
If timer_t = 0
RunProgram("notepad.exe")
timer_t = #timerTime ; reset timer countdown to original value
SetGadgetText(#child_clock, FormatDate("%hh:%ii", Date())) ; re-display the clock
EndIf
EndSelect
EndProcedure
Procedure Events_Gadget()
Define gadget, eventType
gadget = EventGadget()
eventType = EventType()
Select EventWindow()
Case #main_WINDOW
Main_gadgets(gadget,eventType)
Case #child_WINDOW
Child_gadgets(gadget,eventType)
EndSelect
EndProcedure
Procedure Events_window_closed()
; closing main window quits app
Debug "closing " + EventWindow()
If #main_WINDOW = EventWindow()
Debug " >>> " + #main_WINDOW + " is #main_WINDOW"
Debug "all done !"
End
Debug "never displayed ???"
EndIf
Debug " >>> " + #child_WINDOW + " is #child_WINDOW"
CloseWindow(#child_WINDOW)
EndProcedure
Procedure Events_window_deactivated()
Protected window = EventWindow()
Select window
Case #child_WINDOW
If GetWindowData(window) = #True
CloseWindow(window)
;SetActiveWindow(#main_WINDOW) ; nothing happens !
Debug " >>> has #main_WINDOW changed to activated appearance ???"
EndIf
EndSelect
EndProcedure
Procedure Events_window_activated()
Protected window = EventWindow()
Debug "ActivateWindow " + EventWindow()
Debug " >>> has window changed to activated appearance ???"
EndProcedure
BindEvent(#PB_Event_Gadget,@Events_gadget())
BindEvent(#PB_Event_Timer,@Events_timer())
BindEvent(#PB_Event_CloseWindow,@Events_window_closed())
BindEvent(#PB_Event_ActivateWindow,@Events_window_activated())
BindEvent(#PB_Event_DeactivateWindow,@Events_window_deactivated())
If Main_opened()
Repeat
WaitWindowEvent()
ForEver
EndIf
End
Launch the app, minimize PB's IDE, just leaving the debug output window opened. When you select PB's debug output window, it takes on its activated look, while the demo changes to its deactivated look. When you return to the demo, the reverse happens : its window takes on its activated look, while the debug window looks deactivated. All normal. That's the behaviour you expect and it happens correctly with any opened window on the desktop, and with the desktop itself.
However, If you now open a child window in the demo app, and then click into the parent window, the child window closes (as designed by code), but the parent window does not take on its activated look ! Furthermore, at line 144 of the code, the instruction SetActiveWindow(#main_WINDOW), intended to provoke a change of appearance, produces nothing; the debug statements also clearly show that the #PB_Event_ActivateWindow event never fires, even though it should (I think...). The parent window is expected to change its appearance to its activated colors.
Is this a PB bug ?
PB Forums : Proof positive that 2 heads (or more...) are better than one 

Re: Child window message loop
Remove the deactivation callback altogether, and replace the activation procedure with this one:Blue wrote:... If you now open a child window in the demo app, and then click into the parent window, the child window closes (as designed by code), but the parent window does not take on its activated look !
Code: Select all
Procedure Events_window_deactivated()
EndProcedure
Procedure Events_window_activated()
Debug "ActivateWindow " + EventWindow()
If EventWindow() = #main_WINDOW
If IsWindow(#child_WINDOW) And
GetWindowData(#child_WINDOW) = #True
CloseWindow(#child_WINDOW)
EndIf
EndIf
EndProcedure
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel 

Re: Child window message loop
Thanks, TI-994A; what you propose does the job, no question. But it is exactly, word for word, what I already had in place as a workaround. (well, you know the saying about great minds thinking alike etc.TI-994A wrote:[...]
Remove the deactivation callback altogether, and replace the activation procedure with this one:Code: Select all
[,,,]

So that's not really the question here. I'm not looking for a workaround. I already have that.
The point i want to raise is two-fold :
(1) in the original demo code, PB should have generated a #PB_Event_ActivateWindow event when a click in the parent window returns focus to it . Put another way, shouldn't PB be doing, with the demo code as is, exactly what the workaround code does ?
(2) on line 144 of the demo code, the SetActiveWindow(#main_WINDOW) instruction produces nothing . I certainly was expecting that instruction to force the parent window to show its activated colors. Weren't you ?
That's what I mean when I write that something appears to be missing in PB's output code.
And since PB fails on both points, isn't that a bug ? I don't see how the current behaviour can be the correct one.
PB Forums : Proof positive that 2 heads (or more...) are better than one 

Re: Child window message loop
I only ever have one event loop and
in which nothing more is programmed than to leave the program.
Each gadget gets its own event procedure and each window gets its own event procedures for each event.
Most of the time I use my EventDesigner to create all necessary event procedures.
But with the Runtime-Library I found a new interesting method to bind the event procedures automatically.
Here is the link to the RuntimeEventManager with complete GUI event diagnosis.
Link: viewtopic.php?f=12&t=70119
Translated with www.DeepL.com/Translator
in which nothing more is programmed than to leave the program.
Each gadget gets its own event procedure and each window gets its own event procedures for each event.
Most of the time I use my EventDesigner to create all necessary event procedures.
But with the Runtime-Library I found a new interesting method to bind the event procedures automatically.
Here is the link to the RuntimeEventManager with complete GUI event diagnosis.
Link: viewtopic.php?f=12&t=70119
Translated with www.DeepL.com/Translator
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive