Button problem

Just starting out? Need help? Post your questions and find answers here.
collectordave
Addict
Addict
Posts: 1310
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Button problem

Post 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
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Button problem

Post 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
DE AA EB
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Button problem

Post 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
infratec
Always Here
Always Here
Posts: 7576
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Button problem

Post 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
collectordave
Addict
Addict
Posts: 1310
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: Button problem

Post 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
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Button problem

Post 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:
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 :D
User avatar
Demivec
Addict
Addict
Posts: 4259
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Button problem

Post 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.
Last edited by Demivec on Fri Oct 23, 2015 9:18 pm, edited 2 times in total.
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: Button problem

Post 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.
Think Unicode!
User avatar
Demivec
Addict
Addict
Posts: 4259
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Button problem

Post 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 :) ).
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Button problem

Post 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
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 :D
User avatar
Demivec
Addict
Addict
Posts: 4259
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Button problem

Post 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
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Button problem

Post 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:
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 :D
User avatar
Demivec
Addict
Addict
Posts: 4259
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Button problem

Post 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.
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Button problem

Post 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.
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 :D
ElementE
Enthusiast
Enthusiast
Posts: 139
Joined: Sun Feb 22, 2015 2:33 am

Re: Button problem

Post 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
Think Unicode!
Post Reply