# GUIWrap v0.2 # -------------- # # 18/07/05 # -------- # # Notes: # ------ # # * Implemented all base functions including callbacks, helper functions # init and registration functions as well as all globals and constants # * Buttons wrapper implemented and working well, no bugs found yet # * User definable hotkeys implemented. # * Text wrapper implemented ad working well, with selectable position # and fontsize. # * Slider wrapper implmented but not working as expected, selected value # is not persistent, it reverts back to initial value. ----> !!!ON HOLD!!! # * Slider get and set value functions implemented but not working # as expected. I assume this has something to do with the above # error, but it may be unrelated. Set function needs revising anyway # it causes crash if value is min < value > max, however this may # still have something to do with the first bug, since the Get fucntion # always returns 0.0. ----> !!!ON HOLD!!! # # 19/07/05 # -------- # # Notes: # ------ # # * Moved Yes/No exit box from TODO to done. Implemented as # GUIoptionalExit() # * Implemented adaptable menu, GUImenu # * Implemented generalised inputbox, does not work as expected # string input boxes do not show on the screen, int and float boxes # work as expected. Error checking code for this function also # does not perform as expected, it does not raise an error in cases # where type is not a valide type. ----> !!!ON HOLD!!! # * Added GUIimage with selectable autoscaling and persistent drawing # doesn't work at the moment, suspect it has to do with the errors # problem below. ----> !!!ON HOLD!!! # * NO ERRORS - errors are not being thrown or raised as they should be # * Implemented mouse events system including user definable clickable # regions and hover overs. # * Added background colour change function. # * Horizontal rule code is doing very weird things, the exact same # code I had working yesterday is now screwing with the Blender # interface. Have to speak to someone who knows about OGL. ----> !!!ON HOLD!!! # * Disabled mouse over code as it was particularly ineffective, for # each event that occurs whilst the mouse is in the mouseover region # the mouse event is called. Should only be called once when mouse # enters area. # # TODO's: # ------- # # * Implement changable font colour, I assume this will be done with BGL # -> requires further investigation. # * Implement wordwrapping for text, can be done with wordwrap module # -> is it safe to assume anyone using this script would have a full # install of python? # -> distribute with wordwrap module? # -> code word wrap from scratch? # -> SOLVED - sys module required so full python install is required. # * Implement visibilty scheme for UI elements # -> remove objects from draw loop when not visible? # -> use BGL rect's to cover them up? # * Implement remaining UI items. # * Fix the hackish code that hides errors caused by the GUImenu code when # when it returns and throws an unrecognised event. # * Implement a decent mouseover system, mouseclick works fine. # # Ideas: # ------ # # * Multiline text box - # -> Create interface for it using BGL, if mouse cursor is inside interface # then trap all text and print to string which is wordwrapped to fit # interface. Scroll bars pose more of a problem, limit to vertical scroll # only. Could be done with BGL masks to hide unshown text, this could give # a true scroll look. The string would still need to be dynamically # modified otherwise mask would be required to be too big and would hide # other interface elements. ----DOABLE---- # # * Horizontal Rule - # -> More of a todo I guess, implementation should be very simple, just a # couple of BGL commands. Selectable colour and auto length. ----WILL DO---- # # * Radio buttons - # -> Could be done a couple of ways, create the interface itself with BGL # then test for clicks etc and do appropriate event. Or, have groups of # buttons that automatically turnoff if another of the group is selected. # Buttons option is likely much easier to implement. ----DOABLE---- # # * User definable click regions - # -> Another that is probably todo. Should be relatively easy to implement # with very little extra code, just Window module. Could extend to # mouseover/mouseout events too. ----DOABLE---- # # * Tabs - # -> Unsure how this could work since Blender UI elements can't be seperated # into layers. Perhaps some BGL trickery could do the job, but im doubtul. # Possibly to seperate UI's and showing one or the other depending on a # button press. ----INVESTIGATE---- # # * Tickers - # -> An object that outputs a continuous 'tick', or calls a defined function # for every tick, which could be attached to a ui elements input such as # x position to allow for animating. # # timmeh 1959 19.02.05 # ------------------------------------------------------------------------------------ # import Blender import string import sys objects = [] #storage for our ui elements events = [] #events callback lookup keyevents = [] #storage for key events mouseevents = [] #storage for click regions BGLbackCol = [] #back ground colour init = 0 #initisalisation state registered = 0 #registered state TINYFONT = "tiny" #font constants SMALLFONT = "small" MEDIUMFONT = NORMALFONT = DEFAULTFONT = "normal" LARGEFONT = "large" MENUSTRING = MENUSTR = "string" #inputbox constants MENUINT = MENUINT = "integer" MENUFLOAT = MENUFLT = "float" MOUSECLICK = 0 #mouse event type constants MOUSEOVER = 1 LMB = LEFTMOUSE = LMOUSE = Blender.Draw.LEFTMOUSE #blender moouse events RMB = RIGHTMOUSE = RMOUSE = Blender.Draw.RIGHTMOUSE MMB = MIDDLEMOUSE = MMOUSE = Blender.Draw.MIDDLEMOUSE #helper function, appends a list of items to another list---------- def multiappend(list, varlist): for var in varlist: list.append(var) return list #callback for key and mouse events---------------------------------- def GUIevent(event, val): global keyevents global mouseevents mouse = [] for key in keyevents: #event handler for user defined if event == key[0]: #key events key[1]() mouse = Blender.Window.GetMouseCoords() for mevent in mouseevents: #event handler for user defined if event == mevent[7] and mevent[6] == 0: #click regiods. if mouse[0] > mevent[2] and mouse[0] < (mevent[2] + mevent[4]): if mouse[1] > mevent[3] and mouse[1] < (mevent[3] + mevent[5]): mevent[1]() continue #elif event == Blender.Draw.MOUSEX or event == Blender.Draw.MOUSEY: #ineffective mouse over routine #if mevent[6] == 1: #calls event for every event that #if mouse[0] > mevent[2] and mouse[0] < (mevent[2] + mevent[4]): #occurs whilst mouse is over region. #if mouse[1] > mevent[3] and mouse[1] < (mevent[3] + mevent[5]): #mevent[1]() #continue #callback for buttons events---------------------------------------- def GUIbevent(event): global events try: #HACK - find a fix for this, don't like the try..except events[event]() #error catching to get around errors caused by except: #GUImenu creating unknown events. sys.exc_clear() Blender.Draw.Redraw() #callback for rendering UI------------------------------------------ def GUIdraw(): global objects #draw routines for all objects here global BGLbackCol #obj, type, name, callback, data[] if not objects:return for object in objects: if object[1] == "button": object[0] = Blender.Draw.Button(object[4], object[3], object[5], object[6], object[7], object[8], object[9]) elif object [1] == "text": Blender.BGL.glRasterPos2i(object[4], object[5]) object[0] = Blender.Draw.Text(object[3], object[6]) elif object[1] == "slider": object[0] = Blender.Draw.Slider(object[4], object[3], object[5], object[6],object[7], object[8], object[9], object[10], object[11], object[12]) elif object[1] == "image": Blender.Draw.Image(object[0], object[3], object[4], object[5]) elif object[1] == "HR": #GUIdrawHR(object) #produces strange artifacts on the screen pass #not sure why, code works in other scripts Blender.BGL.glClearColor(*BGLbackCol) #set background colour as set by init or usr Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT) #change background colour, uses object interface------------------- def GUIbackgroundCol(r, g, b, a): global BGLbackCol BGLbackCol = [r, g, b, a] def GUIdrawHR(object): #<------- fix this Blender.BGL.glColor3f(object[5], object[6], object[7]) #code produces strange artifacts on the Blender.BGL.glBegin(Blender.BGL.GL_LINE_LOOP) #screen rather than drawing a line as Blender.BGL.glVert2i(object[3], object[4]) #expected Blender.BGL.glVert2i(Blender.Window.GetScreenSize()[0] - object[3], object[4]) Blender.BGL.glEnd() #initialise module - at the moment uses a hackish method to stop--- #event 0 being assigned, could be done automatically on loading---- #the module, however for future proofing where futher init steps--- #may be required, I will leave it here nd make it a requirement---- #to initialise the module before use------------------------------- def GUIinit(): global events global init GUIbackgroundCol(0.5, 0.5, 0.5, 1.0) #background default set to grey if not init: multiappend(events, [1,2,3,4,5,6,7,8,9,10]) init = 1 else: raise RuntimeError, "GUIinit: GUIinit must only be called one" #register callbacks once gui is set up, MUST only be called once--- def GUIregister(): global registered if not registered: Blender.Draw.Register(GUIdraw, GUIevent, GUIbevent) registered = 1 else: raise RuntimeError, "GUIregister: GUIregister must only be called once" #set key events function------------------------------------------------ def GUIregisterKeyEvent(event, callback): global keyevents if not init: raise RuntimeError, "GUIregisterKeyEvent: GUIinit must preceed register functions" key = [] multiappend(key, [event, callback]) keyevents.append(key) #set mouse events function--------------------------------------------- def GUIregisterMouseEvent(name, callback, x, y, width, height, type, mbutton): global mouseevents if not init: raise RuntimeError, "GUIregisterMouseEvent: GUIinit must preceed register functions" key = [] multiappend(key, [name, callback, x, y, width, height, type, mbutton]) mouseevents.append(key) #button object creation function----------------------------------- def GUIcreateButton(name, callback, text, x, y, w, h, tooltip): global objects global events global init if not init: raise RuntimeError, "GUIcreateButton: GUIinit must preceed creation funtions" object = [] events.append(callback) #print "event assigned:", len(events)-1 object = multiappend(object, [Blender.Draw.Create(0.0), "button", name, len(events) -1, text, x, y, w, h, tooltip]) objects.append(object) #text object creation function------------------------------------------- def GUIcreateText(name,text, x, y, fontsize): global objects global init if not init: raise RuntimeError, "GUIcreateText: GUIinit must preceed creation funtions" object = [] multiappend(object, [Blender.Draw.Create(""), "text", name, text, x, y, fontsize]) objects.append(object) #create horizontal rule function---------------------------------------- def GUIcreateHR(name, offset, height, r, g, b): global objects if not init: raise RuntimeError, "GUIcreateHR: GUIinit must precede creation functions" object = [] multiappend(object, ["HR", "HR", name, offset, height, r, g, b]) objects.append(object) #slider object creation function---------------------------------------- def GUIcreateSlider(name, callback, text, x, y, width, height, initial, min, max, realtime, tooltip): global objects global events global init if not init: raise RuntimeError, "GUIcreateSlider: GUIinit must preceed creation functions" object = [] events.append(callback) object = multiappend(object, [Blender.Draw.Create(0.0), "slider", name, len(events) -1, text, x, y, width, height, initial, min, max, realtime, tooltip]) objects.append(object) #slider set value function---------------------------------------------- def GUIsetSlider(name, value): global objects for object in objects: if object[2] == name: if value < object[10] and value > object[9]: object[0].val = value else: raise ValueError, "GUIsetSlider: Slider value must be between min and max" return raise ValueError, string.join(["GUIsetSlider:", name, "is not a valide slider object"]) #slider get value function---------------------------------------------- def GUIgetSlider(name): global objects for object in objects: if object[2] == name: return object[0].val raise ValueError, string.join(["GUIsetSlider:", name, "is not a valid slider object"]) #quit GUI function----------------------------------------------------- def GUIquit(): if registered: Blender.Draw.Exit() else: raise RuntimeError, "GUIquit: no registered UI" #optional quit function------------------------------------------------ def GUIoptionalQuit(): menustr = "OK?%t| Quit" if not registered: raise RuntimeError, "GUIoptionalQuit: no registered UI" if Blender.Draw.PupMenu(menustr) == 1: Blender.Draw.Exit() #menu creation function------------------------------------------------ def GUImenu(title, menuopts): menustr = string.join([title, "%t |"]) if not menuopts: raise ValueError, "GUImenu: No menu options" for item in menuopts: menustr = string.join([menustr, item, "|"]) return Blender.Draw.PupMenu(menustr) #input box creation funtion--------------------------------------------- def GUIinputBox(type, title, default, min=0, max=0, clickstep=10, floatlen=2): type = string.strip(type) #print type if type in ('str', 'string'): return Blender.Draw.PupStrInput(title, default, min) elif type in ('int', 'integer'): return Blender.Draw.PupIntInput(title, default, min, max) elif type in ('flt', 'float'): return Blender.Draw.PupFloatInput(title, default, min, max, clickstep, floatlen) else: raise ValueError, string.join(["GUIinputBox:", type, "is invalid"]) #<------ DOESNT RAISE ERROR #image placement function----------------------------------------------- def GUIimage(filename, name, x=0, y=0, scale=1, autosize=0): global objects object = [] imgsize = [] scrsize = [] sFactor = [] img = Blender.Image.Load(filename) if autosize: imgsize = img.getSize() scrsize = Blender.Window.GetScreenSize() sFactor = [scrsize[i] / imgsize[i] for i in imagesize] if sFactor[0] < sFactor[1]: scale = sFactor[0] else: scale = sFactor[1] object = multiappend([object, img, "image", name, x, y, scale]) print object objects.append(object) ######################################################################### def test(): GUIimage("d:/fire2.jpg", 10, 400,1,0) sdsg def test2(): print "hello" return def test3(): print "hello again" return def move(): print "slider event" def mevent(): rnd = Blender.Mathutils.Rand GUIbackgroundCol(rnd(), rnd(), rnd(), 1) GUIinit() GUIcreateButton("test",test, "Timmeh", 24,16,138,20, "test") GUIcreateSlider("slider", move, "Test", 24, 40, 138, 20, 100, 0, 200, 1, "test") GUIcreateHR("hr1", 24, 100, 0.0, 0.0, 0.0) #GUIcreateButton(test2, "Tim ", 24,40,138,20, "test2") #GUIcreateButton(test3, "Tim2 ", 24,64,138,20, "test3") GUIcreateText("name","timmeh test", 24, 400, NORMALFONT) GUIregisterKeyEvent(Blender.Draw.ESCKEY, GUIoptionalQuit) GUIregisterKeyEvent(Blender.Draw.QKEY, GUIoptionalQuit) GUIregisterMouseEvent("mevent1", mevent, 1,100, 400, 400, MOUSECLICK, LEFTMOUSE) GUIregister()