Page 1 of 2
Button problem
Posted: Fri Oct 23, 2015 10:36 am
by collectordave
Simply trying to count the number of times a button is pressed so created this code
Code: Select all
Global MyButton.l
Global btnCount.i
Global String_0
OpenWindow(0, 0, 0, 600, 220, "MainWindow", #PB_Window_SystemMenu)
MyButton = ButtonGadget(#PB_Any, 150, 20, 50, 50, "Click")
String_0 = StringGadget(#PB_Any, 100, 100, 110, 30, "")
Procedure.i CheckEvent(event)
Select event
Case #PB_Event_Menu ,
#PB_Event_Gadget ,
#PB_Event_SysTray ,
#PB_Event_Timer ,
#PB_Event_CloseWindow ,
#PB_Event_Repaint ,
#PB_Event_SizeWindow ,
#PB_Event_MoveWindow ,
#PB_Event_MinimizeWindow ,
#PB_Event_MaximizeWindow ,
#PB_Event_RestoreWindow ,
#PB_Event_ActivateWindow ,
#PB_Event_DeactivateWindow,
#PB_Event_WindowDrop ,
#PB_Event_GadgetDrop ,
#PB_Event_RightClick ,
#PB_Event_LeftClick ,
#PB_Event_LeftDoubleClick
ProcedureReturn #True
Default
ProcedureReturn #False
EndSelect
EndProcedure
Repeat
event = WaitWindowEvent()
;If CheckEvent(event) = #True
Select EventGadget()
Case MyButton
btnCount = btnCount + 1
SetGadgetText(String_0, Str(btnCount))
EndSelect
;EndIf
Until event = #PB_Event_CloseWindow
When the button is pressed it counts up in threes as shown in the string gadget.
Now I am told that checking for a valid event is not the best or most efficient way to write a PB programme but when i uncomment the ;If CheckEvent(event) = #True and the ;EndIf statements it works correctly. Any Ideas?
__________________________________________________
Thread moved
Bugs - Windows>Coding Questions
23.10.2015
RSBasic
Re: Button problem
Posted: Fri Oct 23, 2015 10:48 am
by davido
@
collectordave,
Couldn't you just, simply check for an EventType Left-Click?
See example below:
Code: Select all
Global MyButton.l
Global btnCount.i
Global String_0
OpenWindow(0, 0, 0, 600, 220, "MainWindow", #PB_Window_SystemMenu)
MyButton = ButtonGadget(#PB_Any, 150, 20, 50, 50, "Click")
String_0 = StringGadget(#PB_Any, 100, 100, 110, 30, "")
Procedure.i CheckEvent(event)
Select event
Case #PB_Event_Menu ,
#PB_Event_Gadget ,
#PB_Event_SysTray ,
#PB_Event_Timer ,
#PB_Event_CloseWindow ,
#PB_Event_Repaint ,
#PB_Event_SizeWindow ,
#PB_Event_MoveWindow ,
#PB_Event_MinimizeWindow ,
#PB_Event_MaximizeWindow ,
#PB_Event_RestoreWindow ,
#PB_Event_ActivateWindow ,
#PB_Event_DeactivateWindow,
#PB_Event_WindowDrop ,
#PB_Event_GadgetDrop ,
#PB_Event_RightClick ,
#PB_Event_LeftClick ,
#PB_Event_LeftDoubleClick
ProcedureReturn #True
Default
ProcedureReturn #False
EndSelect
EndProcedure
Repeat
event = WaitWindowEvent()
;If CheckEvent(event) = #True
Select EventGadget()
Case MyButton
If EventType() = #PB_EventType_LeftClick
btnCount + 1
SetGadgetText(String_0, Str(btnCount))
EndIf
EndSelect
;EndIf
Until event = #PB_Event_CloseWindow
Re: Button problem
Posted: Fri Oct 23, 2015 12:44 pm
by said
Hi,
EventGadget() should/need be checked only after the PB-event #PB_Event_Gadget (you can double check the doc, it is clear about this one!), you seem to be checking for it after any valid PB-event!
The sequence of checking for events i personally use is this - which seems to work just fine (i believe this is the right way to deal with PB event loop):
; level 1
WaitWindowEvent() : we have a valid event we carry on
; level 2
EventWindow() : only needed in multi-windows program
; level 3 - gadget/object
EventGadget() : to be consulted after the event #PB_Event_Gadget
EventMenu() : to be consulted after the event #PB_Event_Menu
EventTimer() : to be consulted after the event #PB_Event_Timer
; level 4 - detailed event-type after EventGadget()
EventType() : to be consulted for each gadget separately
Re-writing your code using that sequence (added some comments):
Code: Select all
Global MyButton.l
Global btnCount.i, btnCount_r.i
Global String_0
OpenWindow(0, 0, 0, 600, 220, "MainWindow", #PB_Window_SystemMenu)
MyButton = ButtonGadget(#PB_Any, 150, 20, 50, 50, "Click")
String_0 = StringGadget(#PB_Any, 100, 100, 110, 30, "") ; counts left-click
String_1 = StringGadget(#PB_Any, 220, 100, 110, 30, "") ; try to count right-click !
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget ; plays the same role as CheckEvent() - we have a valid event: #PB_Event_Gadget,we carry on
Select EventGadget() ; checking which gadget got affected
Case MyButton
Select EventType() ; checking the event-type we are interested in (each gadget has its own set of event types)
; a button gadget has only one event-type that is #PB_EventType_LeftClick
; so it wont re-act to a right-click! see that string_1 is not updated at all!!
Case #PB_EventType_LeftClick
btnCount = btnCount + 1
SetGadgetText(String_0, Str(btnCount))
Case #PB_EventType_RightClick ; since this event-type is not supported by a button gadget so nothing will happen here ...
btnCount_r = btnCount_r + 1
SetGadgetText(String_1, Str(btnCount_r))
EndSelect
EndSelect
EndSelect
Until event = #PB_Event_CloseWindow
I hope this helps ... i am sure you will soon master the PB event-pump (quite unique on its own but just works)
Said
Re: Button problem
Posted: Fri Oct 23, 2015 12:44 pm
by infratec
I'm a bit to slow
The problem comes from an other side:
The ButtonGadget() returns only one event, so a check for left button is not needed.
But what is needed is a check if it is a gadget event:
Code: Select all
EnableExplicit
Define.i MyButton, btnCount, String_0, Event, Exit
OpenWindow(0, 0, 0, 300, 220, "MainWindow", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
MyButton = ButtonGadget(#PB_Any, 150, 20, 50, 50, "Click")
String_0 = StringGadget(#PB_Any, 100, 100, 110, 30, "")
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case MyButton
btnCount = btnCount + 1
SetGadgetText(String_0, Str(btnCount))
EndSelect
Case #PB_Event_CloseWindow
Exit = #True
EndSelect
Until Exit
And, (again) as hint, use .i for the normal variables, else you are running in trouble if you compile the code on x64.
Globals should only be used wen they are really needed.
Procedures inside normal program code ??? (looks horrible for me, maybe I'm to old for new coding styles

)
Oh..., also EnableExplicit is a good thing to avoid trouble with typos.
Bernd
Re: Button problem
Posted: Fri Oct 23, 2015 1:32 pm
by collectordave
Excellent
Code: Select all
Select event
Case #PB_Event_Gadget
Select EventGadget()
works straight out of the box.
Just to make sure i understand
which is checking for a specific event type is the bit that rejects the other events causing the button event to appear to run three times?
Thanks to both for the examples. Just glad it is more specific than eventwindow().
__________________________________________________
Code-Tags added
23.10.2015
RSBasic
Re: Button problem
Posted: Fri Oct 23, 2015 1:38 pm
by TI-994A
collectordave wrote:Code: Select all
Select event
Case #PB_Event_Menu ,
#PB_Event_Gadget ,
#PB_Event_SysTray ,
#PB_Event_Timer ,
#PB_Event_CloseWindow ,
#PB_Event_Repaint ,
#PB_Event_SizeWindow ,
#PB_Event_MoveWindow ,
#PB_Event_MinimizeWindow ,
#PB_Event_MaximizeWindow ,
#PB_Event_RestoreWindow ,
#PB_Event_ActivateWindow ,
#PB_Event_DeactivateWindow,
#PB_Event_WindowDrop ,
#PB_Event_GadgetDrop ,
#PB_Event_RightClick ,
#PB_Event_LeftClick ,
#PB_Event_LeftDoubleClick
So, I assume that you're done with this?

Re: Button problem
Posted: Fri Oct 23, 2015 3:45 pm
by Demivec
The example event loop which I posted in
your other thread details the order in which you should handle the events. It also mentions the existence of 'special' events (that don't have a specific event type) for gadgets that generate a single event (value 0) to signal interaction with them (i.e. checkbox, button, option).
If you make use of that sample code, it will properly skip over all events that aren't needed. The demonstration loop can be simplified by simply deleting the events you don't need to react to (i.e. like drag n' drop events).
In any case the proper procedure for processing events is to go from a general level to a specific level.
Here is a more pictorial (ascii

) summary of the example loop I presented in your other thread. It is organized like a tree. The functions at each level are valid only after the functions at a higher level of the branch have been checked properly and each of the functions at the same level are of equal importance (with no intrinsic order). An event loop does not have to handle each event but the events that are handled should follow a hierarchy similar to this.
Another alternative to this is to use the functions BindEvent(), BindGadgetEvent(), or BindMenuEvent() to route specific events to a procedure instead of the general message loop.
Code: Select all
+event = WindowEvent() ;or WaitWindowEvent()
|
+#PB_Event_Menu
| |
| +EventMenu()
| |
| +EventWindow()
|
+#PB_Event_Gadget
| |
| +EventGadget()
| |
| +EventType()
| | |
| | +Events are neither reported nor handled by every gadget type,
| | check gadget documentation for specific events that are possible.
| | The CanvasGadget(), WebGadget() and OpenGLGadget() each also
| | support a special set of additional events.
| |
| +EventWindow()
|
+#PB_Event_SysTray
| |
| +EventGadget()
| |
| +EventType()
|
+#PB_Event_SysTray
| |
| +EventGadget()
| |
| +EventType()
|
+#PB_Event_Timer
| |
| +EventTimer()
| |
| +EventWindow()
|
+#PB_Event_WindowDrop
| |
| +EventWindow()
| |
| +EventDropType()
| | |
| | +EventDropText()
| | |
| | +EventDropImage()
| | |
| | +EventDropFiles()
| | |
| | +EventDropPrivate()
| |
| +EventDropAction()
| |
| +EventDropBuffer()
| |
| +EventDropSize()
| |
| +EventDropX()
| |
| +EventDropY()
|
+#PB_Event_GadgetDrop
| |
| +EventGadget()
| |
| +EventDropType()
| | |
| | +EventDropText()
| | |
| | +EventDropImage()
| | |
| | +EventDropFiles()
| | |
| | +EventDropPrivate()
| |
| +EventDropAction()
| |
| +EventDropBuffer()
| |
| +EventDropSize()
| |
| +EventDropX()
| |
| +EventDropY()
|
+#PB_Event_CloseWindow
| |
| +EventWindow()
|
+#PB_Event_Repaint
| |
| +EventWindow()
|
+#PB_Event_SizeWindow
| |
| +EventWindow()
|
+#PB_Event_MinimizeWindow
| |
| +EventWindow()
|
+#PB_Event_MaximizeWindow
| |
| +EventWindow()
|
+#PB_Event_RestoreWindow
| |
| +EventWindow()
|
+#PB_Event_ActivateWindow
| |
| +EventWindow()
|
+#PB_Event_DeactivateWindow
| |
| +EventWindow()
|
+#PB_Event_RightClick
| |
| +EventWindow()
|
+#PB_Event_LeftClick
| |
| +EventWindow()
|
+#PB_Event_LeftDoubleClick
| |
| +EventWindow()
|
+any custom events posted using PostEvent()
| |
| +EventWindow()
| |
| +EventGadget()
| |
| +EventType()
| |
| +EventData()
+#Null ;no more events
@Edit: added the other Bind Event functions.
Re: Button problem
Posted: Fri Oct 23, 2015 8:26 pm
by ElementE
Another alternative to this is to use the function BindEvent() to route specific events to a procedure instead of the general message loop.
This implies that "BindEvent" should be used if an immediate response is required and when the latency in "polling" for the Event in a loop would be too great.
Re: Button problem
Posted: Fri Oct 23, 2015 10:09 pm
by Demivec
ElementE wrote:Another alternative to this is to use the function BindEvent() to route specific events to a procedure instead of the general message loop.
This implies that "BindEvent" should be used if an immediate response is required and when the latency in "polling" for the Event in a loop would be too great.
IMHO that wouldn't be true.
Here is a great and informative quote from freak (in response to a concern) that gives additional details on the 'BindEvent' functions:
freak wrote:> But the big advantage of the event driven system gets lost, those events fire in the middle of my other code and I have to secure anything.
This not true: The BindEvent procedures are only called while you are inside of a WaitWindowEvent() or WindowEvent() call. They are sometimes also called from functions that change the GUI (if this change directly generates an event). However, they are never called "in the middle of your code". There is no multi-threading going on here, so there is no need to secure anything in the event callbacks.
Some of their usefulness is simplicity. Others are that they can be considered dynamic by switching them on or off instead of putting those complexities into a message loop. Another is that they are needed to know the scrollbar position during an active scrollbar drag event, otherwise only the scrollbar position at the end of the event can be known.
They are also needed because of changes in PB v5.30:
Fred wrote:- Changed: #PB_Event_SizeWindow and #PB_Event_MoveWindow are no more realtime on Windows, use BindEvent() to get real time update. It should fixes ugly flickering when realtime resizing on Windows.
These changes meant that the message loop only gets the SizeWindow and MoveWindow events after those operations are completed and not when they are actively occurring during a mouse-drag (I think I just made a new word

).
Re: Button problem
Posted: Sat Oct 24, 2015 6:01 am
by TI-994A
Demivec wrote:Another alternative to this is to use the functions BindEvent(), BindGadgetEvent(), or BindMenuEvent() to route specific events to a procedure instead of the general message loop.
Not exactly. The bound events would be routed to the specified callbacks
in addition to the PureBasic message loop;
not instead of.
Even when events have been bound, they are still returned and processed by the
WindowEvent() and
WaitWindowEvent() functions.
A simple illustration:
Code: Select all
Procedure Callback()
Debug "bound callback..."
EndProcedure
OpenWindow(0, 200, 200, 200, 80, "Bind Events")
ButtonGadget(0, 10, 10, 180, 50, "Click Me!")
BindEvent(#PB_Event_Gadget, @Callback())
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
appQuit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case 0
Debug "message loop..."
EndSelect
EndSelect
Until appQuit = 1
Re: Button problem
Posted: Sat Oct 24, 2015 7:09 pm
by Demivec
TI-994A wrote:Not exactly. The bound events would be routed to the specified callbacks in addition to the PureBasic message loop; not instead of.
Even when events have been bound, they are still returned and processed by the WindowEvent() and WaitWindowEvent() functions.
I am glad you brought that up. I wasn't aware of that.
Hmm, that would mean that if double handling wasn't wanted that we would simply eliminate the handling in the message loop if we are binding the event to a callback.
So something like this in your example:
Code: Select all
Procedure Callback()
Debug "bound callback..."
EndProcedure
OpenWindow(0, 200, 200, 200, 80, "Bind Events")
ButtonGadget(0, 10, 10, 180, 50, "Click Me!")
BindEvent(#PB_Event_Gadget, @Callback())
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
appQuit = 1
EndSelect
Until appQuit = 1
Re: Button problem
Posted: Sun Oct 25, 2015 3:51 pm
by TI-994A
Demivec wrote:...if double handling wasn't wanted that we would simply eliminate the handling in the message loop if we are binding the event to a callback.
Well, that depends. Since such events are not exclusive to any particular object, their use might still be warranted in the main event loop, and thus should not be summarily delegated to just the bound callback.
Case in point:
Code: Select all
Procedure Callback()
If EventGadget() = 0
Debug "Button 1"
EndIf
EndProcedure
OpenWindow(0, 200, 200, 200, 80, "Bind Events")
ButtonGadget(0, 10, 10, 180, 25, "Button 1")
ButtonGadget(1, 10, 40, 180, 25, "Button 2")
BindEvent(#PB_Event_Gadget, @Callback())
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
appQuit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case 1
Debug "Button 2"
EndSelect
EndSelect
Until appQuit = 1
Bears noting.

Re: Button problem
Posted: Mon Oct 26, 2015 12:02 am
by Demivec
TI-994A wrote:Bears noting.
True, but in that example you would have probably used BindGadgetEvent() instead of BindEvent() if you wanted the callback to handle only a single gadget. At least that would be the direction I would probably choose.
As a point of trivia, according to what freak said (and also your example code) the 'bound' event is called first, then the event is processed in the message loop.
Re: Button problem
Posted: Mon Oct 26, 2015 4:47 am
by TI-994A
Demivec wrote:TI-994A wrote:Bears noting.
True, but in that example you would have probably used BindGadgetEvent() instead of BindEvent() if you wanted the callback to handle only a single gadget.
Again, it all depends.
Demivec wrote:...the 'bound' event is called first, then the event is processed in the message loop.
Appears so, although not necessarily faster. But it is triggered in real-time for some events.
Re: Button problem
Posted: Mon Oct 26, 2015 5:40 am
by ElementE
I modified collectordave's code and created a free running infinite loop!
This shows the importance of checking if the event equals #PB_Event_Gadget.
Code: Select all
OpenWindow(0, 0, 0, 600, 220, "MainWindow", #PB_Window_SystemMenu)
ButtonGadget(0, 150, 20, 50, 50, "Click")
StringGadget(1, 100, 100, 110, 30, "")
btnCount.i=0
Repeat
event = WaitWindowEvent()
Debug event
;If event = #PB_Event_Gadget
evGadget.i=EventGadget()
Debug "evGadget ="+Str(evGadget)
Select evGadget
Case 0
Debug "MyButton "+ Str(1)
btnCount = btnCount + 1
SetGadgetText(1, Str(btnCount))
EndSelect
;EndIf
Until event = #PB_Event_CloseWindow