Example of OOP GUI App needed.
BlitzMax Forums/BlitzMax Beginners Area/Example of OOP GUI App needed.
I've had to start from scratch with my app and want to go about it using OOP.
Can anyone post some code that shows how to create a window with a canvas but using OOP and being able to check for events?
It may sound like a lot but I kind of don't know where to start. In the mean time I'll start coding.
Thanks :)
Strict
Type TAmon
Field window:TGadget
Field canvas:TGadget
Field button:TGadget
Field clsR:Byte
Field clsG:Byte
Field clsB:Byte
Method ev()
If EventSource()=canvas
If EventID()=EVENT_GADGETPAINT
update
EndIf
If EventID()=EVENT_MOUSEMOVE
clsR=EventX()
clsG=EventY()
update
EndIf
EndIf
If EventSource()=button
clsR=Rnd(255)
clsG=Rnd(255)
clsB=Rnd(255)
update
EndIf
End Method
Method update()
SetGraphics CanvasGraphics(canvas)
SetClsColor clsR,clsG,clsB
Cls
Flip
End Method
End Type
'
' a seperate create function to make it a bit more like the other blitz commands
'
Function CreateAmon:TAmon(x:Int,y:Int,w:Int,h:Int)
Local a:TAmon=New TAmon
a.window=CreateWindow("kamikaze",x,y,w,h)
a.canvas=CreateCanvas(0,0,w-80,h,a.window)
a.button=CreateButton("O_o",w-80,0,70,32,a.window)
SetGadgetLayout a.canvas,1,0,1,0
Return a
End Function
Local a:TAmon=CreateAmon(32,32,512,256)
OnEnd quit
Repeat
WaitEvent()
a.ev
If EventID()=EVENT_WINDOWCLOSE End
Forever
Function quit()
GCCollect()
End
End Function
Note that it still has manual event-checking (a.ev), haven't 100% got yet how to do the eventhook stuff, and how to emit events. It's probably not that hard, but the manual is kinda minimalistic on that..
This is perfect. Thanks dude. :)
Heres a quick demo using an eventhook...
Strict
Type TTestApp
Field Window:TGadget = CreateWindow("",0,0,640,480)
Field Button:TGadget = CreateButton("BUTTON",0,0,100,20,Window)
Field Canvas:TGadget = CreateCanvas(0,20,ClientWidth(window),ClientHeight(window)-20,window,PANEL_BORDER)
Method New()
AddHook EmitEventHook,eventhook,Self
Button.SetLayout(1,0,1,0)
Canvas.SetLayout(1,1,1,1)
EndMethod
Function eventhook:Object(id,data:Object,context:Object)
If TTestApp(context) Then TTestApp(context).OnEvent TEvent(data)
Return data
EndFunction
Method OnEvent(event:TEvent)
Select event.id
Case EVENT_WINDOWCLOSE
Select Event.Source
Case window
End
End Select
Case EVENT_GADGETACTION
Select Event.Source
Case Button
Notify "You pressed the button"
EndSelect
Case EVENT_GADGETPAINT
Select Event.Source
Case Canvas
SetGraphics(CanvasGraphics(canvas))
SetViewport(0,0,ClientWidth(canvas),ClientHeight(canvas))
Cls
DrawText "TEST CANVAS",10,10
Flip 0
EndSelect
EndSelect
EndMethod
EndType
Global App:TTestApp = New TTestApp
While True
WaitEvent()
Wend
Fab! Finally an eventhook example that isn't written for cyborgs! Examples should be minimalistic, lotsa examples are often large pieces o' non-modular code.
Papa^2 !
Now, can you add the same style o' tut to this about own events?
imagine this chunk being added to your mainloop, right under Waitevent()
If EventID()= <New custom ID number here>
If EventSource()=<ID of source here>
Notify "papa!"
EndIf
EndIf
Now for example let a mousedown on that canvase create a new eventID. Meaning I can work with such an object *everywhere* (since EventID(), EventSource() and all the other Event-things are globals)
So in short: mousedown on canvas should trigger the notify 'papa!'
.
I try to keep the main loop empty, thats what events are for ;)
no, globally I meant, not as part of the object, it really has to emit an event.
Imagine I'm making a GUI-object like a spinner orso, I simply create it like any other gadget, and anywhere in any part of the whole source, in any object, I should be able to read it out by checking the globals EventID() etc.
Strict
Global CustomEvent = 1
Type TTestApp
Field Window:TGadget = CreateWindow("",0,0,640,480)
Field Button:TGadget = CreateButton("BUTTON",0,0,100,20,Window)
Field Canvas:TGadget = CreateCanvas(0,20,ClientWidth(window),ClientHeight(window)-20,window,PANEL_BORDER)
Method New()
AddHook EmitEventHook,eventhook,Self
Button.SetLayout(1,0,1,0)
Canvas.SetLayout(1,1,1,1)
EndMethod
Function eventhook:Object(id,data:Object,context:Object)
If TTestApp(context) Then TTestApp(context).OnEvent TEvent(data)
Return data
EndFunction
Method OnEvent(event:TEvent)
Select event.id
Case EVENT_WINDOWCLOSE
Select Event.Source
Case window
End
End Select
Case EVENT_GADGETACTION
Select Event.Source
Case Button
Notify "You pressed the button"
EndSelect
Case EVENT_GADGETPAINT
Select Event.Source
Case Canvas
SetGraphics(CanvasGraphics(canvas))
SetViewport(0,0,ClientWidth(canvas),ClientHeight(canvas))
Cls
DrawText "TEST CANVAS",10,10
Flip 0
EndSelect
Case EVENT_MOUSEDOWN
Select Event.Source
Case Canvas
Local NewEvent:TEvent = New TEvent
NewEvent.ID = CustomEvent
EmitEvent(NewEvent)
EndSelect
EndSelect
EndMethod
EndType
Global App:TTestApp = New TTestApp
While True
WaitEvent()
If EventID() = CustomEvent Then Notify "PAPA!"
WendMind you the custom event could also be caught by the eventhook :)
Nicenice! For completeness and non-globalness I'd say:
Const CustomEvent = 1
(..)
Field NewEvent:TEvent = New TEvent
(..)
and
NewEvent.ID = CustomEvent
in Method New()
Saves globals.. the less globals the better, I always figure..!
Ok, I think I get the hang of it,
just before the emitevent in that method I put this:
NewEvent.source=Self
Et Voila, in my mainloop I can now check on EventSource being App ...
and add this
NewEvent.x=Event.x
before the emitevent means I can readout EventX() in my mainloop ..
Ok, eventhook rocks, Papa next time you should write the bmax manual ^_^
You got it CS, its easier than it seems isn't it :)
Well things being easy depends on who teaches it. As good as the manual for B+ was, as weak is the manual for Bmax. While I can understand that BRL doesn't always have the time to polish manuals, it's still highly required. B+'s manual was really excellent for that matter..
One could ask on the forum, but a *lot* of coders either have their backgrounds in game-programming, which seems to lead to a completely different code-style (probably NOT event-based), or aren't thinking modular enough to come up with the perfect example-code.
I made some GUI-object, need to add some free-up method:
Method Free()
End Method
I figure I can kill the created gadgets easily with:
Method Free()
canvas=null
button=null
gccollect()
End Method
However, how do I get rid of the eventhook for this object? The manual is as zero'ish as it is ever:
Function RemoveHook( id,func:Object( id,data:Object,context:Object ) )
I figure 'id' could be 'self' ? And what is that func stuff/how to use it?
I've Updated the example with a Delete method, which is called automatically by BlitzMax when the object is released. (same as the new method is called when a new object is created)
Strict
Type TTestApp
Field Window:TGadget = CreateWindow("",0,0,640,480)
Field Button:TGadget = CreateButton("BUTTON",0,0,100,20,Window)
Field Canvas:TGadget = CreateCanvas(0,20,ClientWidth(window),ClientHeight(window)-20,window,PANEL_BORDER)
Method New()
AddHook EmitEventHook,eventhook,Self
Button.SetLayout(1,0,1,0)
Canvas.SetLayout(1,1,1,1)
EndMethod
Method Delete()
RemoveHook(EmitEventHook,eventhook)
FreeGadget(Window)
FreeGadget(Button)
FreeGadget(Canvas)
EndMethod
Function eventhook:Object(id,data:Object,context:Object)
If TTestApp(context) Then TTestApp(context).OnEvent TEvent(data)
Return data
EndFunction
Method OnEvent(event:TEvent)
Select event.id
Case EVENT_WINDOWCLOSE
Select Event.Source
Case window
End
End Select
Case EVENT_GADGETACTION
Select Event.Source
Case Button
Notify "You pressed the button"
EndSelect
Case EVENT_GADGETPAINT
Select Event.Source
Case Canvas
SetGraphics(CanvasGraphics(canvas))
SetViewport(0,0,ClientWidth(canvas),ClientHeight(canvas))
Cls
DrawText "TEST CANVAS",10,10
Flip 0
EndSelect
EndSelect
EndMethod
EndType
Global App:TTestApp = New TTestApp
While True
WaitEvent()
Wend
ah, nice.
When/how is an object released actually? When I do:
App=null
?
Or only after App=null and some garbagecollecting?
Or what else? :P
I think the GC calls the Delete method just before it releases the object from memory (when there is no more references to that object).
I just tested it, I put a notify "bla" in the Delete method, but nothing is ever notified when I 'End' or 'App=null'..
My OnEnd function also runs a GCCollect, so plenty o' opportunities to show my those notifications.. sofar: zippo~
Yeah I've just been doing that too :(
Change Delete to Free and call app.free and it works.
Is it a bug BRL?
*EDIT* It works fine if you comment out the hook stuff. Seems that the GC never gets used because there is still a reference to the object... in the hook!
so its not really a bug, should be mentioned in the docs tho ;)
well, bug or not, I figured a manual free-up would work. The primary question was about that RemoveHook anyway. I figure my first-ever-hooked-gadget is kinda ready then, a multimulti-functional spinnergadget. ^_^
One of these days it'll appear in the code-archives. Sofar it works like other gadgets (except the gadget-modifying like Setgadgetshape etc. but I rarely/never use that anyway).
An interesting thread this became, it by now contains a full howto for eventhooks, far better understandable than any other attempts I've seen sofar!
papa to the rescue!
Move the green windows around and observe! ._.
SuperStrict
Type Tbla
Field canvas:TGadget
Field c:Int
Function eventhook:Object(id:Int,data:Object,context:Object)
If Tbla(context) Tbla(context).ev TEvent(data);Return data
EndFunction
Method New()
AddHook EmitEventHook,eventhook,Self
End Method
Method Free()
RemoveHook EmitEventHook,eventhook
GCCollect()
End Method
Method ev(event:TEvent)
If Event.source=canvas
If Event.id=EVENT_GADGETPAINT update
EndIf
End Method
Method update()
SetGraphics CanvasGraphics(canvas)
SetClsColor c,255-c,c;Cls
Flip
End Method
End Type
Function CreateBla:Tbla(parent:TGadget)
Local a:TBla=New TBla
a.canvas=CreateCanvas(0,0,128,128,parent)
a.c=Rnd(255)
Return a
End Function
Local window:TGadget=CreateWindow("o_O",0,0,640,480)
Local w1:TGadget=CreateWindow("1",50,50,128,128,window,1|WINDOW_CHILD)
Local w2:TGadget=CreateWindow("2",90,90,128,128,window,1|WINDOW_CHILD)
Local bla1:TBla=CreateBla(w1)
Local bla2:TBla=CreateBla(w2)
Repeat
WaitEvent()
If EventID()=EVENT_WINDOWCLOSE End
Forever
The WINDOW_CHILD is basically to blame here, without it there's no harm. However I want to make sure it's something I'm doing wrong/could fix, before it goes to the bug-submit orso.
hi with the following code:
Strict
Type TTestApp
Field Window:TGadget = CreateWindow("",0,0,640,480)
Field Button:TGadget = CreateButton("BUTTON",0,0,100,20,Window)
Field Canvas:TGadget = CreateCanvas(0,20,ClientWidth(window),ClientHeight(window)-20,window,PANEL_BORDER)
Method New()
AddHook EmitEventHook,eventhook,Self
Button.SetLayout(1,0,1,0)
Canvas.SetLayout(1,1,1,1)
EndMethod
Function eventhook:Object(id,data:Object,context:Object)
If TTestApp(context) Then TTestApp(context).OnEvent TEvent(data)
Return data
EndFunction
Method OnEvent(event:TEvent)
Select event.id
Case EVENT_WINDOWCLOSE
Select Event.Source
Case window
End
End Select
Case EVENT_GADGETACTION
Select Event.Source
Case Button
Notify "You pressed the button"
EndSelect
Case EVENT_GADGETPAINT
Select Event.Source
Case Canvas
SetGraphics(CanvasGraphics(canvas))
SetViewport(0,0,ClientWidth(canvas),ClientHeight(canvas))
Cls
DrawText "TEST CANVAS",10,10
Flip 0
EndSelect
EndSelect
EndMethod
EndType
Global App:TTestApp = New TTestApp
While True
WaitEvent()
Wend
should i not be able to do
Global App:TTestApp = New TTestApp
Global App2:TTestApp = New TTestApp
...although if i do it throws and error with the line?
Works for me.
What error do you get?
Global App:TTestApp = New TTestApp by itself is ok,
but if i do
Global App:TTestApp = New TTestApp
Global App2:TestApp = New TTestApp
it stops at the event.id with an error about accessing property of a null object?
You said...
global app2:TestApp = New TTestApp
rather than
global app2:TTestApp = New TTestApp
Correcting that on Bmax 1.16 and it works as expected.
sorry just my spelling mistake, it is TTestApp - its just my keyboard at work and the keys stick...lol
will try again at home