X-Git-Url: https://scm.cri.ensmp.fr/git/minwii.git/blobdiff_plain/346a9b8e1fcfe30629f0d1ee4675e9e8f89890cf..4c4732c6ed8cb0aaa70fb2d4c6e5a958868c5349:/src/pgu/gui/theme.py diff --git a/src/pgu/gui/theme.py b/src/pgu/gui/theme.py deleted file mode 100644 index 283c287..0000000 --- a/src/pgu/gui/theme.py +++ /dev/null @@ -1,485 +0,0 @@ -# theme.py - -""" -""" -import os, re -import pygame - -from const import * -import widget -import surface -from basic import parse_color, is_color - -__file__ = os.path.abspath(__file__) - -def _list_themes(dir): - d = {} - for entry in os.listdir(dir): - if os.path.exists(os.path.join(dir, entry, 'config.txt')): - d[entry] = os.path.join(dir, entry) - return d - -class Theme: - """Theme interface. - -

If you wish to create your own theme, create a class with this interface, and - pass it to gui.App via gui.App(theme=MyTheme()).

- - Default Theme - -
Theme(dirs='default')
-
-
dirs
Name of the theme dir to load a theme from. May be an absolute path to a theme, if pgu is not installed, or if you created your own theme. May include several dirs in a list if data is spread across several themes. -
- - Example - - - theme = gui.Theme("default") - theme = gui.Theme(["mytheme","mytheme2"]) - - """ - def __init__(self,dirs='default'): - self.config = {} - self.dict = {} - self._loaded = [] - self.cache = {} - self._preload(dirs) - pygame.font.init() - - def _preload(self,ds): - if not isinstance(ds, list): - ds = [ds] - for d in ds: - if d not in self._loaded: - self._load(d) - self._loaded.append(d) - - def _load(self, name): - #theme_dir = themes[name] - - #try to load the local dir, or absolute path - dnames = [name] - - #if the package isn't installed and people are just - #trying out the scripts or examples - dnames.append(os.path.join(os.path.dirname(__file__),"..","..","data","themes",name)) - - #if the package is installed, and the package is installed - #in /usr/lib/python2.3/site-packages/pgu/ - #or c:\python23\lib\site-packages\pgu\ - #the data is in ... lib/../share/ ... - dnames.append(os.path.join(os.path.dirname(__file__),"..","..","..","..","share","pgu","themes",name)) - dnames.append(os.path.join(os.path.dirname(__file__),"..","..","..","..","..","share","pgu","themes",name)) - dnames.append(os.path.join(os.path.dirname(__file__),"..","..","share","pgu","themes",name)) - for dname in dnames: - if os.path.isdir(dname): break - if not os.path.isdir(dname): - raise 'could not find theme '+name - - fname = os.path.join(dname,"config.txt") - if os.path.isfile(fname): - try: - f = open(fname) - for line in f.readlines(): - vals = line.strip().split() - if len(vals) < 3: continue - cls = vals[0] - del vals[0] - pcls = "" - if cls.find(":")>=0: - cls,pcls = cls.split(":") - attr = vals[0] - del vals[0] - self.config[cls+":"+pcls+" "+attr] = (dname, vals) - finally: - f.close() - fname = os.path.join(dname,"style.ini") - if os.path.isfile(fname): - import ConfigParser - cfg = ConfigParser.ConfigParser() - f = open(fname,'r') - cfg.readfp(f) - for section in cfg.sections(): - cls = section - pcls = '' - if cls.find(":")>=0: - cls,pcls = cls.split(":") - for attr in cfg.options(section): - vals = cfg.get(section,attr).strip().split() - self.config[cls+':'+pcls+' '+attr] = (dname,vals) - - is_image = re.compile('\.(gif|jpg|bmp|png|tga)$', re.I) - def _get(self,key): - if not key in self.config: return - if key in self.dict: return self.dict[key] - dvals = self.config[key] - dname, vals = dvals - #theme_dir = themes[name] - v0 = vals[0] - if v0[0] == '#': - v = parse_color(v0) - #if (len(v0) == 7): - # # Due to a bug in pygame 1.8 (?) we need to explicitly - # # specify the alpha value (otherwise it defaults to zero) - # v0 += "FF" - #v = pygame.color.Color(v0) - elif v0.endswith(".ttf") or v0.endswith(".TTF"): - v = pygame.font.Font(os.path.join(dname, v0),int(vals[1])) - elif self.is_image.search(v0) is not None: - v = pygame.image.load(os.path.join(dname, v0)) - else: - try: v = int(v0) - except: v = pygame.font.SysFont(v0, int(vals[1])) - self.dict[key] = v - return v - - def get(self,cls,pcls,attr): - """Interface method -- get the value of a style attribute. - -
Theme.get(cls,pcls,attr): return value
- -
-
cls
class, for example "checkbox", "button", etc. -
pcls
pseudo class, for example "hover", "down", etc. -
attr
attribute, for example "image", "background", "font", "color", etc. -
- -

returns the value of the attribute.

- -

This method is called from [[gui-style]].

- """ - - if not self._loaded: self._preload("default") - - o = cls+":"+pcls+" "+attr - - #if not hasattr(self,'_count'): - # self._count = {} - #if o not in self._count: self._count[o] = 0 - #self._count[o] += 1 - - if o in self.cache: - return self.cache[o] - - v = self._get(cls+":"+pcls+" "+attr) - if v: - self.cache[o] = v - return v - - pcls = "" - v = self._get(cls+":"+pcls+" "+attr) - if v: - self.cache[o] = v - return v - - cls = "default" - v = self._get(cls+":"+pcls+" "+attr) - if v: - self.cache[o] = v - return v - - v = 0 - self.cache[o] = v - return v - - def box(self,w,s): - style = w.style - - c = (0,0,0) - if style.border_color != 0: c = style.border_color - w,h = s.get_width(),s.get_height() - - s.fill(c,(0,0,w,style.border_top)) - s.fill(c,(0,h-style.border_bottom,w,style.border_bottom)) - s.fill(c,(0,0,style.border_left,h)) - s.fill(c,(w-style.border_right,0,style.border_right,h)) - - - def getspacing(self,w): - # return the top, right, bottom, left spacing around the widget - if not hasattr(w,'_spacing'): #HACK: assume spacing doesn't change re pcls - s = w.style - xt = s.margin_top+s.border_top+s.padding_top - xr = s.padding_right+s.border_right+s.margin_right - xb = s.padding_bottom+s.border_bottom+s.margin_bottom - xl = s.margin_left+s.border_left+s.padding_left - w._spacing = xt,xr,xb,xl - return w._spacing - - - def resize(self,w,m): - # Returns the rectangle expanded in each direction - def expand_rect(rect, left, top, right, bottom): - return pygame.Rect(rect.x - left, - rect.y - top, - rect.w + left + right, - rect.h + top + bottom) - - def func(width=None,height=None): - s = w.style - - pt,pr,pb,pl = s.padding_top,s.padding_right,s.padding_bottom,s.padding_left - bt,br,bb,bl = s.border_top,s.border_right,s.border_bottom,s.border_left - mt,mr,mb,ml = s.margin_top,s.margin_right,s.margin_bottom,s.margin_left - # Calculate the total space on each side - top = pt+bt+mt - right = pr+br+mr - bottom = pb+bb+mb - left = pl+bl+ml - ttw = left+right - tth = top+bottom - - ww,hh = None,None - if width != None: ww = width-ttw - if height != None: hh = height-tth - ww,hh = m(ww,hh) - - if width == None: width = ww - if height == None: height = hh - - #if the widget hasn't respected the style.width, - #style height, we'll add in the space for it... - width = max(width-ttw, ww, w.style.width) - height = max(height-tth, hh, w.style.height) - - #width = max(ww,w.style.width-tw) - #height = max(hh,w.style.height-th) - - r = pygame.Rect(left,top,width,height) - - w._rect_padding = expand_rect(r, pl, pt, pr, pb) - w._rect_border = expand_rect(w._rect_padding, bl, bt, br, bb) - w._rect_margin = expand_rect(w._rect_border, ml, mt, mr, mb) - - #w._rect_padding = pygame.Rect(r.x-pl,r.y-pt,r.w+pl+pr,r.h+pt+pb) - #r = w._rect_padding - #w._rect_border = pygame.Rect(r.x-bl,r.y-bt,r.w+bl+br,r.h+bt+bb) - #r = w._rect_border - #w._rect_margin = pygame.Rect(r.x-ml,r.y-mt,r.w+ml+mr,r.h+mt+mb) - - # align it within it's zone of power. - rect = pygame.Rect(left, top, ww, hh) - dx = width-rect.w - dy = height-rect.h - rect.x += (w.style.align+1)*dx/2 - rect.y += (w.style.valign+1)*dy/2 - - w._rect_content = rect - - return (w._rect_margin.w, w._rect_margin.h) - return func - - - def paint(self,w,m): - def func(s): -# if w.disabled: -# if not hasattr(w,'_disabled_bkgr'): -# w._disabled_bkgr = s.convert() -# orig = s -# s = w._disabled_bkgr.convert() - -# if not hasattr(w,'_theme_paint_bkgr'): -# w._theme_paint_bkgr = s.convert() -# else: -# s.blit(w._theme_paint_bkgr,(0,0)) -# -# if w.disabled: -# orig = s -# s = w._theme_paint_bkgr.convert() - - if w.disabled: - if (not (hasattr(w,'_theme_bkgr') and - w._theme_bkgr.get_width() == s.get_width() and - w._theme_bkgr.get_height() == s.get_height())): - w._theme_bkgr = s.copy() - orig = s - s = w._theme_bkgr - s.fill((0,0,0,0)) - s.blit(orig,(0,0)) - - if hasattr(w,'background'): - w.background.paint(surface.subsurface(s,w._rect_border)) - self.box(w,surface.subsurface(s,w._rect_border)) - r = m(surface.subsurface(s,w._rect_content)) - - if w.disabled: - s.set_alpha(128) - orig.blit(s,(0,0)) - -# if w.disabled: -# orig.blit(w._disabled_bkgr,(0,0)) -# s.set_alpha(128) -# orig.blit(s,(0,0)) - - w._painted = True - return r - return func - - def event(self,w,m): - def func(e): - rect = w._rect_content - if e.type == MOUSEBUTTONUP or e.type == MOUSEBUTTONDOWN: - sub = pygame.event.Event(e.type,{ - 'button':e.button, - 'pos':(e.pos[0]-rect.x,e.pos[1]-rect.y)}) - elif e.type == CLICK: - sub = pygame.event.Event(e.type,{ - 'button':e.button, - 'pos':(e.pos[0]-rect.x,e.pos[1]-rect.y)}) - elif e.type == MOUSEMOTION: - sub = pygame.event.Event(e.type,{ - 'buttons':e.buttons, - 'pos':(e.pos[0]-rect.x,e.pos[1]-rect.y), - 'rel':e.rel}) - else: - sub = e - r = m(sub) - return r - return func - - def update(self,w,m): - def func(s): - if w.disabled: return [] - r = m(surface.subsurface(s,w._rect_content)) - if type(r) == list: - dx,dy = w._rect_content.topleft - for rr in r: - rr.x,rr.y = rr.x+dx,rr.y+dy - return r - return func - - def open(self,w,m): - def func(widget=None,x=None,y=None): - if not hasattr(w,'_rect_content'): w.rect.w,w.rect.h = w.resize() #HACK: so that container.open won't resize again! - rect = w._rect_content - ##print w.__class__.__name__, rect - if x != None: x += rect.x - if y != None: y += rect.y - return m(widget,x,y) - return func - - #def open(self,w,m): - # def func(widget=None): - # return m(widget) - # return func - - def decorate(self,widget,level): - """Interface method -- decorate a widget. - -

The theme system is given the opportunity to decorate a widget methods at the - end of the Widget initializer.

- -
Theme.decorate(widget,level)
- -
-
widget
the widget to be decorated -
level
the amount of decoration to do, False for none, True for normal amount, 'app' for special treatment of App objects. -
- """ - - w = widget - if level == False: return - - if type(w.style.background) != int: - w.background = Background(w,self) - - if level == 'app': return - - for k,v in w.style.__dict__.items(): - if k in ('border','margin','padding'): - for kk in ('top','bottom','left','right'): - setattr(w.style,'%s_%s'%(k,kk),v) - - w.paint = self.paint(w,w.paint) - w.event = self.event(w,w.event) - w.update = self.update(w,w.update) - w.resize = self.resize(w,w.resize) - w.open = self.open(w,w.open) - - def render(self,s,box,r): - """Interface method - render a special widget feature. - -
Theme.render(s,box,r)
- -
-
s
pygame.Surface -
box
box data, a value returned from Theme.get, typically a pygame.Surface -
r
pygame.Rect with the size that the box data should be rendered -
- - """ - - if box == 0: return - - if is_color(box): - s.fill(box,r) - return - - x,y,w,h=r.x,r.y,r.w,r.h - ww,hh=box.get_width()/3,box.get_height()/3 - xx,yy=x+w,y+h - src = pygame.rect.Rect(0,0,ww,hh) - dest = pygame.rect.Rect(0,0,ww,hh) - - s.set_clip(pygame.Rect(x+ww,y+hh,w-ww*2,h-hh*2)) - src.x,src.y = ww,hh - for dest.y in xrange(y+hh,yy-hh,hh): - for dest.x in xrange(x+ww,xx-ww,ww): s.blit(box,dest,src) - - s.set_clip(pygame.Rect(x+ww,y,w-ww*3,hh)) - src.x,src.y,dest.y = ww,0,y - for dest.x in xrange(x+ww,xx-ww*2,ww): s.blit(box,dest,src) - dest.x = xx-ww*2 - s.set_clip(pygame.Rect(x+ww,y,w-ww*2,hh)) - s.blit(box,dest,src) - - s.set_clip(pygame.Rect(x+ww,yy-hh,w-ww*3,hh)) - src.x,src.y,dest.y = ww,hh*2,yy-hh - for dest.x in xrange(x+ww,xx-ww*2,ww): s.blit(box,dest,src) - dest.x = xx-ww*2 - s.set_clip(pygame.Rect(x+ww,yy-hh,w-ww*2,hh)) - s.blit(box,dest,src) - - s.set_clip(pygame.Rect(x,y+hh,xx,h-hh*3)) - src.y,src.x,dest.x = hh,0,x - for dest.y in xrange(y+hh,yy-hh*2,hh): s.blit(box,dest,src) - dest.y = yy-hh*2 - s.set_clip(pygame.Rect(x,y+hh,xx,h-hh*2)) - s.blit(box,dest,src) - - s.set_clip(pygame.Rect(xx-ww,y+hh,xx,h-hh*3)) - src.y,src.x,dest.x=hh,ww*2,xx-ww - for dest.y in xrange(y+hh,yy-hh*2,hh): s.blit(box,dest,src) - dest.y = yy-hh*2 - s.set_clip(pygame.Rect(xx-ww,y+hh,xx,h-hh*2)) - s.blit(box,dest,src) - - s.set_clip() - src.x,src.y,dest.x,dest.y = 0,0,x,y - s.blit(box,dest,src) - - src.x,src.y,dest.x,dest.y = ww*2,0,xx-ww,y - s.blit(box,dest,src) - - src.x,src.y,dest.x,dest.y = 0,hh*2,x,yy-hh - s.blit(box,dest,src) - - src.x,src.y,dest.x,dest.y = ww*2,hh*2,xx-ww,yy-hh - s.blit(box,dest,src) - - -class Background(widget.Widget): - def __init__(self,value,theme,**params): - params['decorate'] = False - widget.Widget.__init__(self,**params) - self.value = value - self.theme = theme - - def paint(self,s): - r = pygame.Rect(0,0,s.get_width(),s.get_height()) - v = self.value.style.background - if is_color(v): - s.fill(v) - else: - self.theme.render(s,v,r)