# 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 Blenderimport stringimport sysobjects = [] #storage for our ui elementsevents = [] #events callback lookupkeyevents = [] #storage for key eventsmouseevents = [] #storage for click regionsBGLbackCol = [] #back ground colourinit = 0 #initisalisation stateregistered = 0 #registered stateTINYFONT = "tiny" #font constantsSMALLFONT = "small"MEDIUMFONT = NORMALFONT = DEFAULTFONT = "normal"LARGEFONT = "large"MENUSTRING = MENUSTR = "string" #inputbox constantsMENUINT = MENUINT = "integer"MENUFLOAT = MENUFLT = "float"MOUSECLICK = 0 #mouse event type constantsMOUSEOVER = 1LMB = LEFTMOUSE = LMOUSE = Blender.Draw.LEFTMOUSE #blender moouse eventsRMB = RIGHTMOUSE = RMOUSE = Blender.Draw.RIGHTMOUSEMMB = 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) sdsgdef test2(): print "hello" returndef test3(): print "hello again" returndef 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()