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) :D

Said

Re: Button problem

Posted: Fri Oct 23, 2015 12:44 pm
by infratec
I'm a bit to slow :wink:

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 :wink: )
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

Code: Select all

   Case #PB_Event_Gadget
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? :lol:

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 :wink: ) 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. :wink:

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. :wink:

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