Difficulty opening window in a thread PB5.11
Difficulty opening window in a thread PB5.11
Good morning,
I have a project in which I open a Window with it's associated event handler in a thread.
(The code manages the download and display of a Google Map)
This is code I have used for some time and it runs under 5.11 when compiled directly, including showing the map/s.
When I compile it with the debugger the code runs OK up to the point where the thread attempts to open a window and I get the message:
<<OpenWindow() can only be called from the main thread>>
I cannot imagine this is an overlooked bug going back for a long time because I have compiled the program many times under earlier versions of PB and I must have used the debugger on numerous occassions.
I have only recently made a move from 4.6 to 5.11 and cannot easily go back to check due to issues with PurePDF that I now need to use as an IncludeFile with 5.11
Yes... I'm Thread safe!
Any suggestions would be appreciated.
RichardL
I have a project in which I open a Window with it's associated event handler in a thread.
(The code manages the download and display of a Google Map)
This is code I have used for some time and it runs under 5.11 when compiled directly, including showing the map/s.
When I compile it with the debugger the code runs OK up to the point where the thread attempts to open a window and I get the message:
<<OpenWindow() can only be called from the main thread>>
I cannot imagine this is an overlooked bug going back for a long time because I have compiled the program many times under earlier versions of PB and I must have used the debugger on numerous occassions.
I have only recently made a move from 4.6 to 5.11 and cannot easily go back to check due to issues with PurePDF that I now need to use as an IncludeFile with 5.11
Yes... I'm Thread safe!
Any suggestions would be appreciated.
RichardL
Last edited by RichardL on Sat Jun 08, 2013 8:56 am, edited 1 time in total.
Re: Difficulty opening window in a thread PB5.11
There has been a lengthy discussion about the reason behind that change in PB 5.10. You can use this workaround to make PB work as before:
...Just put this on top of your code. No other changes are needed.
Code: Select all
CompilerIf #PB_Compiler_Version >= 510 And #PB_Compiler_Debugger
Procedure _OpenWindow(Window, x, y, InnerWidth, InnerHeight, Title$, Flags=0, ParentID=0)
Protected Result = 0
DisableDebugger
Result = OpenWindow(Window, x, y, InnerWidth, InnerHeight, Title$, Flags, ParentID)
EnableDebugger
ProcedureReturn Result
EndProcedure
Macro OpenWindow(Window, x, y, InnerWidth, InnerHeight, Title, Flags=0, ParentID=0)
_OpenWindow(Window, x, y, InnerWidth, InnerHeight, Title, Flags, ParentID)
EndMacro
Procedure _WaitWindowEvent(Timeout=#PB_Default)
Protected Result = 0
DisableDebugger
Result = WaitWindowEvent(Timeout)
EnableDebugger
ProcedureReturn Result
EndProcedure
Macro WaitWindowEvent(Timeout=#PB_Default)
_WaitWindowEvent(Timeout)
EndMacro
Procedure _WindowEvent()
Protected Result = 0
DisableDebugger
Result = WindowEvent()
EnableDebugger
ProcedureReturn Result
EndProcedure
Macro WindowEvent()
_WindowEvent()
EndMacro
CompilerEndIf
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
Re: Difficulty opening window in a thread PB5.11
Hi,
since there is a reason for that warning, I rewritten my programs to use PostEvent().
You can send events to the main loop which opens and close the window for you.
Bernd
since there is a reason for that warning, I rewritten my programs to use PostEvent().
You can send events to the main loop which opens and close the window for you.
Bernd
Re: Difficulty opening window in a thread PB5.11
That's the way to go, and will work everywhere, not only on Windows.infratec wrote:Hi,
since there is a reason for that warning, I rewritten my programs to use PostEvent().
You can send events to the main loop which opens and close the window for you.
Bernd
- doctorized
- Addict
- Posts: 856
- Joined: Fri Mar 27, 2009 9:41 am
- Location: Athens, Greece
Re: Difficulty opening window in a thread PB5.11
I have a thread and sometimes it is needed to show a window to get some data from the user.
Which is the best way to open the window? PostEvent() is good only when we have the window open.
In my case the window is not open as is it not sure that we will need it. Is the code from c4s the best for me?
Which is the best way to open the window? PostEvent() is good only when we have the window open.
In my case the window is not open as is it not sure that we will need it. Is the code from c4s the best for me?
Re: Difficulty opening window in a thread PB5.11
Better use PostEvent() with a custom event to actually open your window.
- doctorized
- Addict
- Posts: 856
- Joined: Fri Mar 27, 2009 9:41 am
- Location: Athens, Greece
Re: Difficulty opening window in a thread PB5.11
How can I do it when the window is not present? WaitWindowEvent() cannot be used, so? A little help?Fred wrote:Better use PostEvent() with a custom event to actually open your window.
Re: Difficulty opening window in a thread PB5.11
You can just have one invisible window somewhere at program start
Re: Difficulty opening window in a thread PB5.11
Sorry for bringing up this old thread but I'm actually out of ideas.
Having the same issue.
RichardL:
How did you solve the communication back to the Thread out of the main thread?
Having the same issue.
RichardL:
How did you solve the communication back to the Thread out of the main thread?
Tranquil
Re: Difficulty opening window in a thread PB5.11
I did not found a good solution for that issue as well. PostEvent() does not really help (or it just helps in few cases and only in one direction).
If you need some new thread that is similar to a new process as if you click on "something.exe" in windows-explorer and the only information that have to be shared regarding mainthread and the subthread is some memory at start-time of the thread then PostEvent() is the wrong direction and even does not really help to make the code slimmer.
Example which worked in the past:
Example after the change regarding Window handling:
The code is much much longer, much more failure-sensitive and still does not handle everything as in the past. There is no advantage just a big step backwards. So I kept the code and ended with something like this (ugly):
If you need some new thread that is similar to a new process as if you click on "something.exe" in windows-explorer and the only information that have to be shared regarding mainthread and the subthread is some memory at start-time of the thread then PostEvent() is the wrong direction and even does not really help to make the code slimmer.
Example which worked in the past:
Code: Select all
; MainThread
Repeat
Select WindowEvent()
Case #PB_Event_Gadget
If EventGadget() = SomeListGadget
*Copy_Data_From_List_Gadget = GetSomeData(SomeListGadget)
OpenThread(@MyThread(),*Copy_Data_From_List_Gadget)
EndIf
EndSelect
Forever
; Thread that does not care regarding Mainthread anymore
Procedure MyThread(*Copy_Data_From_List_Gadget)
Init_and_UseData(*Copy_Data_From_List_Gadget)
FreeData(*Copy_Data_From_List_Gadget)
window = OpenWindow(#PB_Any, ...)
individual_gadget = SomeGadget(#PB_Any, ...)
outstanding_gadget = SomeGadget(#PB_Any, ...)
Repeat
; Do something
Select WindowEvent()
Case #PB_Event_Gadget
Select EventGadget()
Case individual_gadget
*furtherstuff = OpenFurtherWindow_1(window) ; Lock window of this thread (only) until user chooses something from further window
Case outstanding_gadget
DoSomething()
EndSelect
Case #PB_Event_CloseWindow
StopMe()
EndSelect
Forever
EndProcedure
Code: Select all
; MainThread - and Thread for threads
Repeat
window_event = WindowEvent()
If window_event
event_window = EventWindow()
If event_window = MainWindow
If window_event = #PB_Event_Gadget
If EventGadget() = SomeListGadget
*various.stuff = AllocateMemory(SizeOf(stuff))
InitialzieStructure(*various,stuff)
*various\Copy_Data_From_List_Gadget = GetSomeData(SomeListGadget)
*various\mutex = CreateMutex()
*various\EventQueuePtr = AllocateMemory(somesize)
AddElement(DynamicNumberOfWindows())
DynamicNumberOfWindows()\variousPtr = *various
DynamicNumberOfWindows()\window = OpenWindow(#PB_Any, ...)
*various\window = DynamicNumberOfWindows()\window
DynamicNumberOfWindows()\unnamed_gadget1 = SomeGadget(#PB_Any, ...)
DynamicNumberOfWindows()\unnamed_gadget2 = SomeGadget(#PB_Any, ...)
DynamicNumberOfWindows()\type = DependsOnClick
; ... and lots more
OpenThread(@MyThread(),*various)
EndIf
EndIf
Else ; Nightmare
ForEach DynamicNumberOfWindows()
If DynamicNumberOfWindows()\window = event_window
Select DynamicNumberOfWindows()\type
Case #Window_Type1
If window_event = #PB_Event_Gadget
Select EventGadget()
Case DynamicNumberOfWindows()\unnamed_gadget1
; Blocking now - everything!
LockMutex(DynamicNumberOfWindows()\mutex)
DynamicNumberOfWindows()\variousPtr\furtherstuffPtr = OpenFurtherWindow_1(DynamicNumberOfWindows()\window)
AddToQueue(DynamicNumberOfWindows()\EventQueuePtr,#Look_at_your_pointer_Event)
UnlockMutex(DynamicNumberOfWindows()\mutex)
Case DynamicNumberOfWindows()\unnamed_gadget2
LockMutex(DynamicNumberOfWindows()\mutex)
AddToQueue(DynamicNumberOfWindows()\EventQueuePtr,#DoSomething_Window_Whatever_Event)
UnlockMutex(DynamicNumberOfWindows()\mutex)
EndSelect
ElseIf window_event = #PB_Event_CloseWindow
LockMutex(DynamicNumberOfWindows()\mutex)
AddToQueue(DynamicNumberOfWindows()\EventQueuePtr,#CloseEvent)
UnlockMutex(DynamicNumberOfWindows()\mutex)
ElseIf window_event = #PostClose
FreeMutex(DynamicNumberOfWindows()\mutex)
ClearStructure(*various,stuff)
FreeMemory(*various)
CloseWindow(DynamicNumberOfWindows()\window)
EndIf
Case #Window_Type2
; Increase spagetti-code with 30 further gadgets of window 2 again...
EndSelect
EndIf
Next
EndIf
EndIf
Forever
; Thread that have to care regarding mainthread unwillingless
Procedure MyThread(*various.stuff)
Init_and_UseData(various\Copy_Data_From_List_Gadget)
Repeat
; Do something
LockMutex(*various\mutex)
event = ReadQueue(*various\EventQueuePtr)
UnlockMutex(*various\mutex)
Select event
Case 0
Continue
Case #Look_at_your_pointer_Event
LockMutex(*various\mutex)
*furtherstuff = CopyStuff(*various\furtherstuffPtr)
FreeStuff(*various\furtherstuffPtr)
UnlockMutex(*various\mutex)
Case #DoSomething_Window_Whatever_Event
DoSomething()
Case #CloseEvent
CleanUp()
LockMutex(*various\mutex)
PostEvent(#PostClose,*various\window)
ProcedureReturn(0)
EndSelect
ForEver
EndProcedure
The code is much much longer, much more failure-sensitive and still does not handle everything as in the past. There is no advantage just a big step backwards. So I kept the code and ended with something like this (ugly):
Code: Select all
;{ Bähhh :P
Procedure __OpenWindow(WinID,x,y,InnerWidth,InnerHeight,Title.s,Flags=0,ParentID=0)
Protected retval.i
DisableDebugger
retval = OpenWindow(WinID,x,y,InnerWidth,InnerHeight,Title.s,Flags,ParentID)
EnableDebugger
ProcedureReturn(retval)
EndProcedure
Procedure __WindowEvent()
Protected retval.i
DisableDebugger
retval = WindowEvent()
EnableDebugger
ProcedureReturn(retval)
EndProcedure
Procedure __WaitWindowEvent(Timeout=#PB_Default)
Protected retval.i
DisableDebugger
retval = WaitWindowEvent(Timeout)
EnableDebugger
ProcedureReturn(retval)
EndProcedure
;}
Re: Difficulty opening window in a thread PB5.11
I agree. This non-window events inside threads is terrible. My app lets the user create any number of custom windows that they desire, and each window must respond to their own events. Each such window is created inside a thread at runtime, whenever the user wants.auser wrote:The code is much much longer, much more failure-sensitive and still does not handle everything as in the past. There is no advantage just a big step backwards.
But, this thread issue totally stops this type of functionality from being possible. Yes, I know it'll work with the final exe, but it means I can't debug my app because when the window event commands are reached, the compiler quits with an error. Very annoying.
Yes, I know I can wrap all window stuff with DisableDebugger and EnableDebugger, but frankly, that's an unnecessary workaround and totally impractical when there's so many events to wrap; even more so if you're including files that have windows with events inside them (such as custom input boxes).
No, I can't create all the windows invisibly first and then just show them when needed, because as I said, there's X number of them with different styles. You can't guess what type of window the user is going to create, or how many of them. It could be 1 window, or 1000, and each must have their own event loop and act independently of others.
So, I respectfully ask that either: (a) window opening and events be allowed inside threads again on Microsoft Windows, or if that's impossible, then (b) the compiler internally Disable/Enable the debugger for these commands when the OS is Windows and the exe is being debugged from the IDE.
Thank you.
Re: Difficulty opening window in a thread PB5.11
Hi everyone,
here's an example how i unterstood window threading should working; the following is only a quick example but should be threadsafe and also safe to close any subwindow even in the threaded event loop. Hopefully this is a proper native PureBasic solution and this is helpful for someone.
Bye, Harry.
here's an example how i unterstood window threading should working; the following is only a quick example but should be threadsafe and also safe to close any subwindow even in the threaded event loop. Hopefully this is a proper native PureBasic solution and this is helpful for someone.
Bye, Harry.
Code: Select all
EnableExplicit
; Creating structure for subwindow info
Structure wininfo
num.i
gd1.i
gd2.i
gd3.i
EndStructure
; Setting global and local variables
Global NewMap subwin.wininfo()
Global globalend.i, main.i, thread.i, syncer
Define events.i, evewin.i, evegad.i, evtype.i, evdata.i, newwin.i, title.s, wincnt.i, gd1.i, gd2.i, gd3.i, t.i
; Creating mutex for use of the global map
syncer = CreateMutex()
; Creating own PostEvents
Enumeration #PB_Event_FirstCustomValue
#EventNewWindow
#EventCloseWindow
#EventAddItem
EndEnumeration
; Creating own PostEventTypes
Enumeration #PB_EventType_FirstCustomValue
#EventItemCount
EndEnumeration
; Thread procedure with stuff unrelated from the main run with events
Procedure.i somestuff(beginn.i)
Define.i cycles
While globalend = 0
Select cycles
Case 5000 ; after about 5 seconds window opens
PostEvent (#EventNewWindow)
Case 10000 ; after about 10 seconds window opens
PostEvent (#EventNewWindow)
Case 12000 ; after about 12 seconds post a value to a specific window
LockMutex(syncer)
If Not FindMapElement(subwin(), "Test - subwindow 2") = 0
PostEvent (#EventAddItem, subwin()\num, subwin()\gd2, #EventItemCount, 2)
EndIf
UnlockMutex(syncer)
Case 15000 ; after about 15 seconds first window closes
LockMutex(syncer)
If Not FindMapElement(subwin(), "Test - subwindow 1") = 0
PostEvent (#EventCloseWindow, subwin()\num, 0)
EndIf
UnlockMutex(syncer)
Case 20000 ; after about 20 seconds second window closes
LockMutex(syncer)
If Not FindMapElement(subwin(), "Test - subwindow 2") = 0
PostEvent (#EventCloseWindow, subwin()\num, 0)
EndIf
UnlockMutex(syncer)
EndSelect
; Silly way for adding timers in thread
cycles + 1
Delay(1)
; After timed loop thread is beeing continued until main window closes
Wend
EndProcedure
; Main window creation
main = OpenWindow(#PB_Any, 0, 0, 400, 300, "Test - main window", #PB_Window_SystemMenu)
; Thread is starting
thread = CreateThread(@somestuff(), 0)
; Main Event loop for all windows
While globalend = 0
events = WaitWindowEvent(2000) ; Waits for all events, also for Thread-Events
evewin = EventWindow()
Select events
; Create new window - event from thread
Case #EventNewWindow
wincnt + 1
title = "Test - subwindow " + Str(wincnt)
newwin = OpenWindow(#PB_Any, 50 * wincnt, 50 * wincnt, 700, 300, title, #PB_Window_SystemMenu, WindowID(main))
If Not newwin = 0
gd1 = ListViewGadget(#PB_Any, 10, 10, 200, 100)
gd2 = ListViewGadget(#PB_Any, 220, 10, 200, 100)
gd3 = ListViewGadget(#PB_Any, 430, 10, 200, 100)
LockMutex(syncer)
AddMapElement(subwin(), title)
subwin()\num = newwin : subwin()\gd1 = gd1 : subwin()\gd2 = gd2 : subwin()\gd3 = gd3
UnlockMutex(syncer)
EndIf
; Close window - event from thread
Case #EventCloseWindow
If IsWindow(evewin) : CloseWindow(evewin) : EndIf
LockMutex(syncer)
If subwin()\num = evewin
DeleteMapElement(subwin())
Else
ForEach subwin() : If subwin()\num = evewin : DeleteMapElement(subwin()) : Break : EndIf : Next
EndIf
UnlockMutex(syncer)
; Add gadget items - event from thread
Case #EventAddItem
evegad = EventGadget()
evtype = EventType()
evdata = EventData()
Select evtype
Case #EventItemCount
If IsGadget(evegad) : For t = 1 To evdata : AddGadgetItem(evegad, -1, "Item " + Str(CountGadgetItems(evegad))) : Next : EndIf
EndSelect
; If window has been closed
Case #PB_Event_CloseWindow
If IsWindow(evewin) : CloseWindow(evewin) : EndIf
; If main window has been closed; set ending paramter
If evewin = main : globalend = 1 : EndIf
; If sub window has been closed; remove from subwindow map
LockMutex(syncer)
ForEach subwin() : If subwin()\num = evewin : DeleteMapElement(subwin()) : Break : EndIf : Next
UnlockMutex(syncer)
EndSelect
Wend
; Thread will be ended automatically - we should wait 5 seconds
If IsThread(thread)
WaitThread(thread, 5000)
If IsThread(thread) : KillThread(thread) : EndIf
EndIf
FreeMutex(syncer)
End
- Sherlock Holmes - "When you have eliminated the impossible, whatever remains, however improbable, must be the truth."
In my opinion, he must have been a programmer.
In my opinion, he must have been a programmer.