Attachment 'win.py'

Download

   1 #!/usr/bin/python
   2 # coding: utf-8
   3 # vim: expandtab:ts=4:sw=4
   4 '''
   5 Написать «оконную систему», состоящую из классов
   6 
   7         * Экран (контейнер окон, позволяет манипулировать окнами)
   8                     o Список окон, определение окна-получателя события
   9                     o Глубина окон, изменение глубины окон
  10                     o Перемещение и изменение размера окон 
  11         * Окно (позволяет манипулировать своим содержимым)
  12 
  13 win.py: классы
  14 '''
  15 
  16 import pygame, sys
  17 
  18 class descriptor:
  19     '''Window descritor'''
  20     def __init__(self, rect, window, mode=["basic"], **argn):
  21         '''Create a window descriptor object with defaut basic mode'''
  22         self.rect=rect
  23         self.window=window
  24         if type(mode) is str:
  25             self.mode=[mode]
  26         else:
  27             self.mode=mode or ["basic"]
  28 
  29 class basic_screen:
  30     '''Windows container'''
  31     def __init__(self, surface, bg=(0,0,0)):
  32         '''Create a screen using given surface'''
  33         self.surface = surface
  34         self.windows = []
  35         self.immed = []     # events to be handled immediately
  36         self.hanled = []    # events we handle if no child doesn.t
  37         self.bg = bg
  38 
  39     def append(self, rect, mode, creator, *args, **argn):
  40         '''Call creator() with *args, **argn
  41         to produce a window (of mode type) on rect-sized subsurface
  42         and put the window in the top'''
  43         wnd = creator(self.surface.subsurface(rect), *args, **argn)
  44         self.windows.append(descriptor(rect,wnd,mode))
  45         return wnd
  46 
  47     def pop(self, index=-1):
  48         '''Unregister corresponded window and return it'''
  49         if self.windows:
  50             return self.windows.pop(index)
  51         else:
  52             return None
  53 
  54     def clear(self):
  55         '''Draw initial screen's surface without all windows'''
  56         if self.bg:
  57             self.surface.fill(self.bg)
  58 
  59     def redraw(self):
  60         '''Redraw all the contents'''
  61         self.clear()
  62         for wnd in self.windows:
  63             wnd.window.redraw()
  64 
  65     def up(self, index):
  66         '''Put indexed window deeper'''
  67         if len(self.windows) > 1 and index < len(self.windows)-1:
  68             index+=1
  69             self.windows[index-1],self.windows[index]= \
  70             self.windows[index],  self.windows[index-1]
  71         return index
  72 
  73     def down(self, index):
  74         '''Pull indexed window upper'''
  75         if len(self.windows) > 1 and index > 0:
  76             self.windows[index-1],self.windows[index]= \
  77             self.windows[index]  ,self.windows[index-1]
  78             index-=1
  79         return index
  80 
  81     def top(self, index):
  82         '''Bring indexed window to the top'''
  83         if index < len(self.windows):
  84             w=self.windows.pop(index)
  85             self.windows.append(w)
  86 
  87     def locate(self, pos):
  88         '''Locate a window than owns visible pos coordinate.
  89         If not found return -1'''
  90         for i in xrange(len(self.windows)-1, -1, -1):
  91             if self.windows[i].rect.collidepoint(pos):
  92                 return i
  93         return -1
  94 
  95     def handler(self, event, pos):
  96         '''Handle an event that not correspond any window'''
  97         return False
  98 
  99     def proceed(self, event, pos):
 100         '''Check if the event is to be handled by certain window
 101         and let the window do it. If no appropriete windows found 
 102         or window nas no idea how to handle an event, handle it manually.
 103         Return false if no event handler is provided.'''
 104         ret=False
 105         if event in self.immed:
 106             ret=self.handler(event, pos)
 107         if not ret:
 108             index=self.locate(pos)
 109             if index>=0:
 110                 wpos=(pos[0]-self.windows[index].rect.left,pos[1]-self.windows[index].rect.top)
 111                 ret=self.windows[index].window.proceed(event,wpos)
 112         return ret or self.handler(event, pos)
 113 
 114     def renew(self,surface):
 115         '''Stck window to a new surface'''
 116         self.surface = surface
 117         for wnd in self.windows:
 118             wnd.window.renew(surface.subsurface(wnd.rect))
 119 
 120 class simple_screen(basic_screen):
 121     '''Screen with simple window management'''
 122     def __init__(self, *argv, **argn):
 123         '''Additional parameters:
 124         titlebg: title background
 125         titlefg: title foreground
 126         borderwidth: width of border line
 127         bordercolor: base color of border (3D effect applied)
 128         '''
 129         self.titlebg=argn.get('titlebg',(0,32,64))
 130         self.titlefg=argn.get('titlefg',(255,248,128))
 131         self.borderwidth=argn.get('borderwidth',2)
 132         self.bordercolor=argn.get('bordercolor',(128,128,128))
 133         self.titlesize=14
 134         basic_screen.__init__(self, *argv, **argn)
 135         self.immed.extend([pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN])
 136         self.focus=-1
 137         self.min_w,self.min_h=2*self.borderwidth,2*self.borderwidth
 138 
 139     def handler(self, event, pos):
 140         '''
 141 pygame.MOUSEMOTION with button1 down -- move focused window
 142 pygame.MOUSEBUTTONDOWN with button1 -- focus a window
 143                        with buttion4/5 -- raise/lower a window
 144         '''                   
 145         ret=False
 146         if event.type == pygame.MOUSEMOTION:
 147             if self.focus >= 0 and event.buttons[0]:
 148                 nrect = self.windows[self.focus].rect.move(event.rel)
 149                 if self.surface.get_rect().contains(nrect):
 150                     self.windows[self.focus].rect.move_ip(event.rel)
 151                     self.windows[self.focus].window.renew(self.surface.subsurface(self.windows[self.focus].rect))
 152                     ret=True
 153             elif self.focus >= 0 and event.buttons[2]:
 154                 nrect = self.windows[self.focus].rect.inflate(event.rel)
 155                 if self.surface.get_rect().contains(nrect) and nrect.w>self.min_w and nrect.h>self.min_h:
 156                     #print >> sys.stderr, nrect, self.min_w, self.min_h
 157                     self.windows[self.focus].rect.inflate_ip(event.rel)
 158                     self.windows[self.focus].window.renew(self.surface.subsurface(self.windows[self.focus].rect))
 159                     ret=True
 160         elif event.type == pygame.MOUSEBUTTONDOWN:
 161             index = self.locate(pos)
 162             if event.button == 1:
 163                 if index >=0 and index != self.focus:
 164                     self.focus=index
 165                     ret=True
 166             elif event.button == 4:
 167                 self.focus=self.up(index)
 168                 ret=True
 169             elif event.button == 5:
 170                 if index>0 and "top" not in self.windows[index].mode:
 171                     self.focus=self.down(index)
 172                     ret=True
 173         return ret
 174 
 175     def highlight_window(self, index):
 176         '''Draw 4 black-white circles to merk a window'''
 177         for center in [ self.windows[index].rect.topleft, 
 178                         self.windows[index].rect.bottomleft, 
 179                         self.windows[index].rect.topright, 
 180                         self.windows[index].rect.bottomright ]:
 181             pygame.draw.circle(self.surface, (0,0,0), center, 4)
 182             pygame.draw.circle(self.surface, (255,255,255), center, 3)
 183 
 184     def pop(self, index=-1):
 185         w=basic_screen.pop(self,index)
 186         if w:
 187             # TODO not to lose focus if non-focused window is deleted
 188             self.focus=-1
 189         return w
 190 
 191     def redraw(self):
 192         '''Redraw all windows then highlight focused window, if any'''
 193         basic_screen.redraw(self)
 194         if self.focus >=0:
 195             self.highlight_window(self.focus)
 196 
 197 class basic_window:
 198     '''Basic type window'''
 199     min_w, min_h = 10, 10
 200     def __init__(self, surface, bg=(0,0,0)):
 201         '''Create a window based on given surface
 202         if bg is None, make transparent window'''
 203         self.surface = surface
 204         self.bg = bg
 205 
 206     def renew(self,surface):
 207         '''Stck window to a new surface'''
 208         self.surface = surface
 209 
 210     def redraw(self):
 211         if self.bg:
 212             self.surface.fill(self.bg)
 213 
 214     def proceed(self, event, pos):
 215         return False
 216 
 217     def drawable(self):
 218         return self.surface
 219 
 220 class text_window(basic_window):
 221     '''Window with text inside'''
 222     def __init__(self, surface, bg=(0,0,0), fg=(200,200,200), text='', font="dejavusans", size=14):
 223         basic_window.__init__(self, surface, bg)
 224         self.fg=fg
 225         self.text=text
 226         if type(font) is pygame.font.Font:
 227             self.font = font
 228         else:
 229             if not pygame.font.get_init:
 230                 pygame.font.init()
 231             self.font=pygame.font.SysFont(font, size)
 232         # TODO calculate this dynamically!
 233         self.min_w,self.min_h=self.__fsize()
 234 
 235     def __fsize(self):
 236         r,n=self.font.size(self.text),(1,self.font.get_height())
 237         return (max(r[0],n[0]),max(r[1],n[1]))
 238 
 239     def redraw(self):
 240         tx=self.font.render(self.text,True,self.fg)
 241         self.min_w,self.min_h=self.__fsize()
 242         basic_window.redraw(self)
 243         self.surface.blit(tx,(0,0))
 244 
 245 class screen_window(basic_screen):
 246     '''Window with title and frame, screen internal'''
 247     def __init__(self, surface, screen, title):
 248         '''Use parent simple_creen defaults for title and frame'''
 249         basic_screen.__init__(self, surface, screen.bg)
 250         self.bordercolor, self.borderwidth = screen.bordercolor, screen.borderwidth
 251         self.title = title
 252         self.parent = screen
 253         trect=self.__trect()
 254         self.append(trect, "text", text_window, text=title,
 255                 bg=screen.titlebg, fg=screen.titlefg, size=screen.titlesize)
 256         drect=self.__drect()
 257         self.append(drect, "basic", basic_window, bg=screen.bg)
 258         self.min_w=self.windows[0].window.min_w+2*screen.borderwidth
 259         self.min_h=self.windows[0].window.min_h+self.windows[1].window.min_h+3*screen.borderwidth
 260 
 261     def __trect(self, surface=None):
 262         surf = surface or self.surface
 263         return pygame.Rect(self.parent.borderwidth, \
 264                 self.parent.borderwidth, \
 265                 surf.get_width()-2*self.parent.borderwidth, \
 266                 self.parent.titlesize+2*self.parent.borderwidth)
 267 
 268     def __drect(self, surface=None):
 269         surf = surface or self.surface
 270         return pygame.Rect(self.parent.borderwidth, \
 271                 self.__trect(surface).height+2*self.parent.borderwidth, \
 272                 self.__trect(surface).width, \
 273                 surf.get_height()-self.__trect(surface).height-3*self.parent.borderwidth)
 274 
 275     def redraw(self):
 276         basic_screen.redraw(self)
 277         rect=self.surface.get_rect()
 278         x,y,w,h=rect.topleft+rect.size
 279         line=((x,y), (x+w-self.borderwidth,y), (x+w-self.borderwidth,y+h-self.borderwidth), (x,y+h-self.borderwidth))
 280         pygame.draw.lines(self.surface, self.bordercolor, True, line, self.borderwidth)
 281         middle=self.windows[0].rect.height+self.borderwidth
 282         pygame.draw.line(self.surface, self.bordercolor, (self.borderwidth,middle), (rect.width-self.borderwidth,middle), self.borderwidth)
 283 
 284     def renew(self,surface):
 285         '''Stck window to a new surface'''
 286         # TODO cannot deny renew if sizes do not fit :(
 287         self.windows[0].rect=self.__trect(surface)
 288         self.windows[1].rect=self.__drect(surface)
 289         basic_screen.renew(self,surface)
 290 
 291     def drawable(self):
 292         return self.windows[1].window.surface
 293 
 294 class edit_window(text_window):
 295     '''Simple text input'''
 296     def __init__(self, surface, bg=(0,0,0), fg=(200,200,200), text='', font="dejavusans", size=14):
 297         text_window.__init__(self, surface, bg, fg, text, font, size)
 298         self.cur=len(self.text)
 299         self.type="input string"
 300 
 301     def handler(self, event, pos):
 302         if event.type == pygame.KEYDOWN:
 303             if event.unicode and event.unicode in u'\n\r':
 304                 pygame.event.post(pygame.event.Event(pygame,USEREVENT, code=[self.type,self.text]))
 305             elif event.key == 8:
 306                 self.text=self.text[:self.cur-1]+self.text[self.cur:]
 307                 self.cur -=1
 308             elif event.key == 276:
 309                 self.cur = max(0, self.cur - 1)
 310             elif event.key == 275:
 311                 self.cur = min(len(self.text), self.cur + 1)
 312             elif event.unicode and event.key >= 32 and event.key not in [ 127 ]:
 313                 self.text = self.text[:self.cur] + event.unicode + self.text[self.cur:]
 314                 self.cur+=1
 315             else:
 316                 return False
 317                 print >> sys.stderr, event
 318         return True
 319 
 320     def proceed(self, event, pos):
 321         if event.type == pygame.KEYDOWN:
 322             return self.handler(event, pos)
 323         else: return False
 324 
 325     def redraw(self):
 326         text_window.redraw(self)
 327         tx=self.font.size(self.text[:self.cur])
 328         pygame.draw.line(self.surface,(255,255,0),(tx[0]+1,1), (tx[0]+1,tx[1]+1))

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.