Page MenuHome

implicit_goo.py

implicit_goo.py

''' This module contains some simple classes to make it easier to program a GUI.
The main classes are :
guispace - collects the values of the OpenGL script window.
clicktracker - tracks the left mouse clicks for the tabs class.
panel - draws a coloured panel.
scaffold - creates a grid of values that can be used for placing buttons, panels etc.
tabs - creates a tab widget with a user defined number of tabs.
sidetabs - same as above, but the tabs run down the right side instead of the top.
'''
import Blender
from Blender import BGL, Draw
class guispace:
''' Class to find and store the xstart, ystart,width and height values of the OpenGL window in which the script runs.
If you create an instance at the start of your gui drawing function, then pass the height and width values to
your major gui elements, the gui will resize automatically when the window resizes. '''
def __init__(self, background):
## Basic background colour.
BGL.glClearColor(background[0],background[1],background[2],background[3])
## Clear the OpenGL script window to background colour.
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
## Create a buffer space for the values we're after.
scissorbox=BGL.Buffer(BGL.GL_FLOAT,4)
## Get the openGL window size values.
BGL.glGetFloatv(BGL.GL_SCISSOR_BOX,scissorbox)
## The scissorbox values are floats, so we change them to integers. This prevents protests from Python about "int expected".
## The left and base values are mainly used to calculate the position of the mouse x and y values.
# Left side of the OpenGL script window. Value is in pixels, from the left side of the main Blender window.
self.left=int(scissorbox[0])
# Bottom of the OpenGL script window.Value is in pixels, up from the bottom of the main Blender window.
self.base=int(scissorbox[1])
# Width of the OpenGL script window.
self.width=int(scissorbox[2])
# Height of the OpenGL script window.
self.height=int(scissorbox[3])
class clicktracker:
''' If you are going to use tabs in your GUI, you must create one of these. It checks if any leftmouse clicks are on a tab.
Once it is set up, everything is dealt with automatically, so that you can concentrate on the buttons and sliders.'''
def __init__(self):
## This list that contains the tab panels that are currently on screen.
self.targets=[]
def clickcheck(self,x,y):
## x and y are the screen coordinates of the mouse at the time of the left mouse click.
## Go through our list of targets (tab panels).
for target in self.targets:
## Check the target.box attribute. Do we have a hit?
if (y>target.box[0] and y<target.box[1])and(x>target.box[2] and x<target.box[3]):
## Is it a sidetabs object? If it is we need to search up and down with the y coordinate.
if target.orientation:
along=y
## Go through the tabs (target.divs values), locate the one that was clicked on.
for i in range(len(target.divs)-1):
## Found it.
if along<target.divs[i] and along>target.divs[i+1]:
## target.pageid.val is the pageid of the tab. Change it to the current value.
## Next time the screen is redrawn, this tab's page will be displayed.
target.pageid.val=i
## It's a normal, horizontal tab panel. Search along the x coordinates.
else:
along=x
## Go through the tabs (target.divs values), locate the one that was clicked on.
for i in range(len(target.divs)-1):
## Found it.
if along>target.divs[i] and along<target.divs[i+1]+2:
## target.pageid.val is the pageid of the tab. Change it to the current value.
## Next time the screen is redrawn, this tab's page will be displayed.
target.pageid.val=i
class panel:
''' This class is the basic building block of the tabs, but can also be used on its own -
I use it to surround related button groups with a different background.
The corners, sides and caption arguements are optional. If the corners argument is not entered, the default
is a square cornered panel. A 1 in the list makes a rounded corner.Different patterns of 1's and 0's in the
corners list make the different panels that make up the tabs.
The sides list selectively rubs out border lines.
The caption arguement takea a list with a string and a colour rgb triplet - e.g ["Print this", [0.0,0.0,0.0]].
If you name all your colours at the start of your program, stick in the name - e.g ["Print this", black].'''
def __init__(self,xstart,ystart,width,height,colour,bordercolour, corners=[0,0,0,0],sides=[0,0,0,0],caption=0):
## Most of these should be self explanatory
self.left=xstart
self.right=xstart+width
self.top=ystart+height
self.base=ystart
self.width=width
self.height=height
self.col=colour
self.bcol=bordercolour
self.corners=corners
self.sides=sides
self.caption=caption
## Draw the main background piece.
self.drawpanel()
## Draw in the corners. Default is square edged.
self.docorners()
## Do we want some of the border lines erased, say for making a tab for a tab panel?
for test in self.sides:
## We do?
if test:
## Rub 'em out.
self.coversides()
## Do we want a label, title, exclamation, proclamation etc. in our panel.
if self.caption:
self.text=self.caption[0]
self.textcolour=self.caption[1]
## Start writing.
self.dotext()
def drawpanel(self):
## Draw the main part of the panel.
BGL.glColor3f(self.col[0],self.col[1],self.col[2])
BGL.glRecti(self.left+5,self.base,self.right-5,self.top) ## Large middle bit.
BGL.glRecti(self.left,self.base+4,self.left+8,self.top-4) ## Left small side strip.
BGL.glRecti(self.right-8,self.base+4,self.right,self.top-4) ## Right small side strip.
## Draw the borders.
BGL.glColor3f(self.bcol[0],self.bcol[1],self.bcol[2]) ## Change to border colour.
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.left+6,self.base) ## Base border.
BGL.glVertex2i(self.right-6,self.base)
BGL.glVertex2i(self.left+6,self.top) ## Top border.
BGL.glVertex2i(self.right-6,self.top)
BGL.glVertex2i(self.left,self.base+6) ## Left border.
BGL.glVertex2i(self.left,self.top-6)
BGL.glVertex2i(self.right,self.base+6) ## Right border.
BGL.glVertex2i(self.right,self.top-6)
BGL.glEnd()
def docorners(self):
## Draw the corners.
if self.corners[0]: ## Top left corner.
corner(self.left,self.top,self.col,self.bcol,1)
else:
lcorner(self.left,self.top,self.col,self.bcol,1)
if self.corners[1]: ## Top right corner.
corner(self.right,self.top,self.col,self.bcol,2)
else:
lcorner(self.right,self.top,self.col,self.bcol,2)
if self.corners[2]: ## Bottom right corner.
corner(self.right,self.base,self.col,self.bcol,3)
else:
lcorner(self.right,self.base,self.col,self.bcol,3)
if self.corners[3]: ## Bottom left corner.
corner(self.left,self.base,self.col,self.bcol,4)
else:
lcorner(self.left,self.base,self.col,self.bcol,4)
def coversides(self):
## This function draws lines to cover up border lines designated by the sides arguement.
BGL.glColor3f(self.col[0],self.col[1],self.col[2])
if self.sides[0]: # left border
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.left,self.base+1)
BGL.glVertex2i(self.left,self.top)
BGL.glEnd()
if self.sides[1]: # top border
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.left+1,self.top)
BGL.glVertex2i(self.right,self.top)
BGL.glEnd()
if self.sides[2]: # right border
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.right,self.base+1)
BGL.glVertex2i(self.right,self.top)
BGL.glEnd()
if self.sides[3]: # base border
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.left+1,self.base)
BGL.glVertex2i(self.right,self.base)
BGL.glEnd()
def dotext(self):
## By default this function prints the desired text, in the desired colour centrally in the panel.
## Possibly there will be more options in a leter release.
## Find the vertical centreline of the panel.
centreline=self.left+(self.width/2)
## Find the horizontal centreline of the panel and subtract about half the height of the font (roughly).
textline=self.base+(self.height/2-5)
## Find the length of the string that was passed in.
stringlength=Draw.GetStringWidth(self.text)
## Halve it.
offset=stringlength/2
## Calculate the start point for the text.
titlestart=centreline-offset
## Change to the text colour.
BGL.glColor3f(self.textcolour[0],self.textcolour[1],self.textcolour[2])
## Position raster.
BGL.glRasterPos2i(titlestart,textline)
## Print it.
Draw.Text(self.text)
class scaffold:
''' Creates an invisible grid, making it easier to place buttons and panels in groups.
The grid consists of a list of rows and lists of columns to use as button xstart,ystart coordinates and
a list that can be used for button height sizes.
The widths of buttons are given names like "full", "half", "twothird", etc'''
def __init__(self, xstart, ystart, width, height, numrows,topgap=0,sidegap=0 ):
self.left=xstart
self.right=xstart+width
self.top=ystart+height
self.base=ystart
self.width=width+sidegap
self.height=height+topgap
## Gap between the rows.
self.topgap=topgap
## Gap between the columns.
self.sidegap=sidegap
## Number of rows required.
self.numrows=numrows
## Height of a row.
self.slot=self.height/self.numrows
## Height of a button
self.bheight=[self.slot-self.topgap]
## List to contain the y coordinates of the rows
self.row=[]
## Following lists contain the x coordinates for the columns.
self.col1=[self.left] ## This is full width.
self.col2=[] ## Two columns.
self.col3=[] ## Three columns.
self.col4=[] ## Four col.....ahhh, you get the idea.
self.col5=[]
self.col6=[]
## Calculate the y coordinates and fit them into the row list.
for i in range(0,self.numrows):
level=self.slot*i
self.row.append(self.base+level)
self.bheight.append(level+self.slot-self.topgap)
## They go in from bottom to top.
## We reverse them to give a more natural top down configuration.
self.row.reverse()
## Sizes of the possible button widths. Given fairly simple names.
self.full=self.width-self.sidegap
self.half=self.width/2-self.sidegap
self.third=self.width/3-self.sidegap
self.twothird=(self.width/3)*2-self.sidegap
self.quarter=self.width/4
self.threequarter=self.quarter*3-self.sidegap
self.fifth=self.width/5
self.twofifth=(self.fifth*2)-self.sidegap
self.threefifth=(self.fifth*3)-self.sidegap
self.fourfifth=(self.fifth*4)-self.sidegap
self.sixth=self.width/6
self.fivesixth=(self.sixth*5)-self.sidegap
## Fill in those x coordinates.
for i in range(4):
self.col4.append(self.left+(self.quarter*i))
for i in range(5):
self.col5.append(self.left+(self.fifth*i))
for i in range(6):
self.col6.append(self.left+(self.sixth*i))
## Finish off the button widths, this time shrunk by the gap distance.
self.quarter=self.quarter-self.sidegap
self.fifth=self.fifth-self.sidegap
self.sixth=self.sixth-self.sidegap
## And finish off the columns.
self.col2=[self.left,self.col4[2]]
self.col3=[self.left,self.col6[2],self.col6[4]]
class tabs:
''' Class that creates a tabbed panel. You decide the number of tabs, the title labels and
the colour of each tabpage.'''
def __init__(self,numtabs,titles,colours,bordercolour,textcolour, pageid):
## Number of tabs, titles and colours should be equal
self.numtabs=numtabs
self.titles=titles
self.colours=colours
## Border colour and text colour are separate for flexibility.
self.bordercolour=bordercolour
self.textcolour=textcolour
## This is the current tab, starting at 0, going up to numtabs.
self.pageid=pageid
def drawtabs(self,xstart,ystart,width,height,tabheight,clicktrack):
self.left=xstart
self.right=xstart+width
self.top=ystart+height
self.tabline=self.top-tabheight
self.base=ystart
self.tabheight=tabheight
self.pheight=height-self.tabheight
self.width=width
self.height=height
self.tabwidth=self.width/self.numtabs
## Flag to tell clicktrack that these tabs run horizontally.
self.orientation=0
## This will contain the xstart, xend, ystart, yend target coordinates for the clicktrack object.
self.box=[]
## List of the x coordinates of the tab sides.
self.divs=[self.left]
## This panel is the big space, the page where you will put your buttons,sliders and whatnot.
self.pane=panel(self.left,self.base,self.width,self.pheight,self.colours[self.pageid.val],self.bordercolour,[0,0,1,1])
## Accurate tabwidth, in floating point form/
wid=1.0/float(self.numtabs)
## Create the list of divisions(x coordinates) that delineate the side of each tab.
for i in range(self.numtabs+1):
## We already have the first one, self.left.
if i==0:
pass
## Extreme right hand side
elif i==self.numtabs:
division=self.right
self.divs.append(division)
else:
## Calculate the tabwidth accurately using float values, then convert back to integers.
twidth=int(self.width*wid*i)
## Anchor the width value to the left side of the tabpanel.
division=twidth+self.left
self.divs.append(division)
## Create the tabs and their labels.
for i in range(self.numtabs):
## Width of the tab. There may be slight variations due to int rounding.
width=self.divs[i+1]-self.divs[i]
## This is the active tab, so we draw a tab with the bottom border
## missing. It will join to self.pane seamlessly.
if self.pageid.val==i:
panel(self.divs[i],self.tabline,width,self.tabheight,self.colours[i],self.bordercolour,corners=[1,1,0,0],sides=[0,0,0,1], caption=[self.titles[i],self.textcolour])
## This is a background tab.
else:
panel(self.divs[i],self.tabline,width,self.tabheight,self.colours[i],self.bordercolour,corners=[1,1,0,0], caption=[self.titles[i],self.textcolour])
## Delineate the upper and lower bounds, extreme left and right of the tabs.
self.box=[self.tabline-1,self.top,self.divs[0],self.divs[-1]]
## Pass the target values to clicktrack.
clicktrack.targets.append(self)
## This function for future use.
def addtab(self,title,colour):
self.numtabs=self.numtabs+1
self.titles.append(title)
self.colours.append(colour)
class sidetabs(tabs):
''' Pretty much the same as tabs but creates a panel with tabs on the right hand side. '''
def __init__(self,numtabs,titles,colours,bordercolour,textcolour, pageid):
tabs.__init__(self,numtabs,titles,colours,bordercolour,textcolour, pageid)
def drawtabs(self,xstart,ystart,width,height,tabwidth,clicktrack):
self.left=xstart
self.right=xstart+width
self.top=ystart+height
self.tabline=self.right-tabwidth
self.base=ystart
self.tabwidth=tabwidth
self.pwidth=width-self.tabwidth
self.width=width
self.height=height
self.tabheight=self.height/self.numtabs
## Flag to tell clicktrack that these tabs run vertically.
self.orientation=1
## This will contain the xstart, xend, ystart, yend target coordinates for the clicktrack object.
self.box=[]
## These tabs go up and down, start at the bottom.
self.divs=[self.base]
## Main pane.
self.pane=panel(self.left,self.base,self.pwidth,self.height,self.colours[self.pageid.val],self.bordercolour,[1,0,0,1])
## Check that the titles aren't too wide for the tabs.
for text in self.titles:
## Length of title string in pixels.
stringlength=Draw.GetStringWidth(text)
## If it's wider than the tabwidth value, widen it to accomodate..
if stringlength>self.tabwidth:
self.tabwidth=stringlength+4
self.tabline=self.right-self.tabwidth
## Mostly the same as tabs from here.
wid=1.0/float(self.numtabs)
for i in range(self.numtabs+1):
if i==0:
pass
elif i==self.numtabs:
division=self.top
self.divs.append(division)
else:
twidth=int(self.height*wid*i)
division=twidth+self.base
self.divs.append(division)
## The division values run from the bottom up. We want them top down. Sooooo....
self.divs.reverse()
for i in range(self.numtabs):
height=self.divs[i]-self.divs[i+1]
if self.pageid.val==i:
panel(self.tabline,self.divs[i+1],self.tabwidth,height,self.colours[i],self.bordercolour,[0,1,1,0],[1,0,0,0], caption=[self.titles[i],self.textcolour])
else:
panel(self.tabline,self.divs[i+1],self.tabwidth,height,self.colours[i],self.bordercolour,[0,1,1,0], caption=[self.titles[i],self.textcolour])
self.box=[self.divs[-1],self.divs[0],self.tabline-1,self.right]
clicktrack.targets.append(self)
class corner:
''' Draws rounded corners on panels. That's about it really.'''
def __init__(self,x,y,colour,bordercolour,quadrant):
self.x=x
self.y=y
self.col=colour
self.bcol=bordercolour
if quadrant==1:
self.xflip=1
self.yflip=-1
elif quadrant==2:
self.xflip=-1
self.yflip=-1
elif quadrant==3:
self.xflip=-1
self.yflip=1
elif quadrant==4:
self.xflip=1
self.yflip=1
self.drawcorner()
def drawcorner(self):
BGL.glBegin(BGL.GL_LINES)
BGL.glColor3f(self.col[0],self.col[1],self.col[2])
BGL.glVertex2i(self.x+4*self.xflip,self.y+1*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+1*self.yflip)
BGL.glVertex2i(self.x+2*self.xflip,self.y+2*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+2*self.yflip)
BGL.glVertex2i(self.x+1*self.xflip,self.y+3*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+3*self.yflip)
BGL.glVertex2i(self.x+1*self.xflip,self.y+4*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+4*self.yflip)
# change colour for border, if needed
BGL.glColor3f(self.bcol[0],self.bcol[1],self.bcol[2])
BGL.glVertex2i(self.x+5*self.xflip,self.y)
BGL.glVertex2i(self.x+8*self.xflip,self.y)
BGL.glVertex2i(self.x+3*self.xflip,self.y+1*self.yflip)
BGL.glVertex2i(self.x+5*self.xflip,self.y+1*self.yflip)
BGL.glVertex2i(self.x+2*self.xflip,self.y+2*self.yflip)
BGL.glVertex2i(self.x+3*self.xflip,self.y+2*self.yflip)
BGL.glVertex2i(self.x+1*self.xflip,self.y+3*self.yflip)
BGL.glVertex2i(self.x+2*self.xflip,self.y+3*self.yflip)
BGL.glVertex2i(self.x+1*self.xflip,self.y+4*self.yflip)
BGL.glVertex2i(self.x+2*self.xflip,self.y+4*self.yflip)
BGL.glVertex2i(self.x,self.y+5*self.yflip)
BGL.glVertex2i(self.x,self.y+8*self.yflip)
BGL.glEnd()
class lcorner(corner):
''' Draws square corners on a panel. The default setting.'''
def drawcorner(self):
BGL.glColor3f(self.col[0],self.col[1],self.col[2])
for i in range(8):
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.x,self.y+i*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+i*self.yflip)
BGL.glEnd()
# change colour for border, if needed
BGL.glColor3f(self.bcol[0],self.bcol[1],self.bcol[2])
BGL.glBegin(BGL.GL_LINES)
BGL.glVertex2i(self.x,self.y)
BGL.glVertex2i(self.x+9*self.xflip,self.y)
BGL.glVertex2i(self.x,self.y)
BGL.glVertex2i(self.x,self.y+9*self.yflip)
BGL.glEnd()
class scorner(corner):
''' A diagonal corner style. Not in use yet, if ever.'''
def drawcorner(self):
BGL.glBegin(BGL.GL_LINES)
BGL.glColor3f(self.col[0],self.col[1],self.col[2])
BGL.glVertex2i(self.x+4*self.xflip,self.y+1*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+1*self.yflip)
BGL.glVertex2i(self.x+3*self.xflip,self.y+2*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+2*self.yflip)
BGL.glVertex2i(self.x+2*self.xflip,self.y+3*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+3*self.yflip)
BGL.glVertex2i(self.x+1*self.xflip,self.y+4*self.yflip)
BGL.glVertex2i(self.x+8*self.xflip,self.y+4*self.yflip)
# change colour for border, if needed
BGL.glColor3f(self.bcol[0],self.bcol[1],self.bcol[2])
BGL.glVertex2i(self.x+5*self.xflip,self.y)
BGL.glVertex2i(self.x+8*self.xflip,self.y)
BGL.glVertex2i(self.x+5*self.xflip,self.y)
BGL.glVertex2i(self.x,self.y+5*self.yflip)
BGL.glVertex2i(self.x,self.y+5*self.yflip)
BGL.glVertex2i(self.x,self.y+8*self.yflip)
BGL.glEnd()

File Metadata

Mime Type
text/x-python
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
70/b0/827ba19a9f4b2de9a4424ea76bfe

Event Timeline