Прикреплённый файл «pyginput.py»

Загрузка

   1 #!/usr/bin/env python
   2 # coding: utf
   3 '''
   4 Simple input box for pygame
   5 
   6 Usage:
   7     box=Input(…)                                    # declare an input box
   8     while <main loop>:
   9 
  10         if box.is_active() and <event goes to box>: # currently in input state
  11             box.edit(event)                         # pass event to the box
  12         else:
  13             <parse eventsd normally>
  14 
  15         if box.is_done():                           # input is finished …
  16             if box.is_cancelled():                  # …with cancel
  17                 <"no input is needed" action>
  18             elif box.is_failed():                   # …unsuccessfully
  19                 <unsuccessfull input action>
  20             else:                                   # …succsessfuly
  21                 <succsessful input action>
  22             box.deactivate()                        # hide box and resore state
  23 
  24         if <input is needed>:                       # we need to input something in program
  25             box.activate(…)                         # store old state and turn on the box
  26 
  27         if box.is_active(): box.draw(surface,pos)   # show box after all surface changes
  28         pygame.display.flip()                       # display the picture
  29         if box.is_active(): box.undraw()            # hide box before further surface changes
  30 '''
  31 
  32 import pygame
  33 __VERSION__=0.06
  34 
  35 ACTIVE,DONE,FAILED,CANCEL,SHOWN,FIRST=(1<<i for i in xrange(6))
  36 
  37 class Input:
  38     # Default values for some properties
  39     BorderWidth=3
  40     Status=0
  41     CursorWidth=2
  42     Margin=0
  43     TextColor=pygame.Color("Black")
  44     PromptColor=pygame.Color("grey50")
  45     CursorColor=pygame.Color("grey75")
  46     OverColor=pygame.Color("tomato")
  47     PaperColor=pygame.Color("ivory")
  48     Prompt=u""
  49     DefaultText=u""
  50     SetType=unicode
  51     RetryIncorrect=True
  52     TextLength=8
  53     FontID=None
  54     Font=None
  55     FontSize=24
  56     PromptGap=None
  57     Size=None
  58     FixedSize=False
  59     BackingStore=None
  60     RepeatStore=None
  61     RepeatDefault=(500,100)
  62 
  63     def __update__(self, *pargs, **nargs):
  64         '''Update box properties.
  65         Positional parameters: [Prompt, [DefaultText]].
  66         Named parameters: all class data fields.
  67         
  68         - Size supercedes FontSize, default FontSize is 24
  69         - setting DefaultText also sets SetType (so use 0., not "0" for float input)
  70         '''
  71         if len(pargs)>0:
  72             self.Prompt=pargs[0]
  73         if len(pargs)>1:
  74             self.DefaultText=unicode(pargs[1])
  75             self.SetType=type(pargs[1])
  76         for prop in nargs:
  77             if hasattr(self,prop):
  78                 setattr(self,prop,nargs[prop])
  79         if not self.FontID:
  80             self.FontID=pygame.font.match_font("sans")
  81         if self.PromptGap is None and self.Prompt:
  82             self.PromptGap=" "
  83         if "SetType" not in nargs and "DefaultText" in nargs:
  84             self.SetType=type(nargs["DefaultText"])
  85         if "TextLength" not in nargs and self.DefaultText:
  86             self.TextLength=0
  87         if "Size" in nargs:
  88             self.FixedSize=True
  89             self.FontSize=self.Size[1]-2*self.BorderWidth
  90             self.Font=pygame.font.Font(self.FontID, self.FontSize)
  91         elif not self.Size or not self.FixedSize:
  92             self.Font=pygame.font.Font(self.FontID, self.FontSize)
  93             self.Size=self.Font.size(self.Prompt+self.PromptGap+"W"*max(self.TextLength,len(self.DefaultText)+1,2))
  94             self.Size=self.Size[0]+2*self.BorderWidth,self.Size[1]+2*self.BorderWidth
  95         self.Paper=pygame.Surface(self.Size)
  96         # TODO background image/transparency for Paper
  97         self.Paper.fill(self.PaperColor)
  98         pr=self.Font.render(self.Prompt, True, self.PromptColor)
  99         self.Paper.blit(pr, (self.BorderWidth,(self.Size[1]-pr.get_height())/2))
 100         self.Text=unicode(self.DefaultText)
 101         self.Cursor=len(self.Text)
 102 
 103     def __init__(self, *pargs, **nargs):
 104         '''Create a text input entity. Call __update__() next.'''
 105         self.__update__(*pargs, **nargs)
 106 
 107     def __sawtoothed__(self, block, side, mult=3):
 108         '''Create a sawtoothed mark for left (False) or right (True) side'''
 109         w,h=block.get_size()
 110         nw=mult*self.BorderWidth
 111         n=(h/nw)|1
 112         x,d=side and (w-1,-nw) or (0,nw)
 113         return [(x+d*(i%2),h*i/n) for i in xrange(n)]
 114 
 115     def value(self):
 116         '''Check if input is correct and return it, return None if it is not'''
 117         try:
 118             return self.SetType(self.Text)
 119         except:
 120             try:
 121                 return self.SetType(str(self.Text))
 122             except:
 123                 return None
 124 
 125     def render(self):
 126         '''Return paper surface with current prompt and text printed on'''
 127         ret=self.Paper.copy()
 128         wl=self.Font.size(self.Prompt+self.PromptGap)[0]
 129         ib=ret.subsurface((wl,0,ret.get_width()-wl,ret.get_height()))
 130         ia=ret.subsurface((wl+self.BorderWidth,self.BorderWidth,ret.get_width()-wl-2*self.BorderWidth,ret.get_height()-2*self.BorderWidth))
 131         pr=self.Font.render(self.Text, True, self.TextColor)
 132         w=self.Font.size(self.Text[:self.Cursor])[0]
 133         while self.Margin and w-self.Font.size(self.Text[:self.Margin])[0]<self.CursorWidth and self.Margin>0:
 134             self.Margin-=1
 135         while w-self.Font.size(self.Text[:self.Margin])[0]>ia.get_width()-self.CursorWidth and self.Margin<len(self.Text):
 136             self.Margin+=1
 137         Offset=-self.Font.size(self.Text[:self.Margin])[0]
 138         ia.blit(pr,(Offset,(ia.get_height()-pr.get_height())/2))
 139         pygame.draw.line(ia, self.CursorColor, (w+Offset,2), (w+Offset,ia.get_height()-2),self.CursorWidth)
 140         if Offset<0:
 141             pygame.draw.polygon(ib, self.OverColor, self.__sawtoothed__(ib, False))
 142         if Offset+pr.get_width()>ia.get_width()-self.CursorWidth:
 143             pygame.draw.polygon(ib, self.OverColor, self.__sawtoothed__(ib, True))
 144         return ret
 145 
 146     def draw(self,scr,pos):
 147         '''Draw input box on surface scr at position pos
 148         backuping an underlying part of surface'''
 149         self.BackingStore=(self.Paper.copy(),scr,pos)
 150         self.BackingStore[0].blit(self.BackingStore[1],(0,0),(self.BackingStore[2],self.Size))
 151         self.BackingStore[1].blit(self.render(),self.BackingStore[2])
 152         self.Status|=SHOWN
 153 
 154     def undraw(self):
 155         '''Remove the box from the surface it was drawn
 156         restoring underlying part of surface'''
 157         if self.BackingStore:
 158             self.BackingStore[1].blit(self.BackingStore[0],self.BackingStore[2])
 159         self.Status&=~SHOWN
 160 
 161     def activate(self, *pargs, **nargs):
 162         '''Enable input from the box.
 163         If either pargs or nargs is given, call __update__().
 164         Return False if no activation was needed, True otherwise.
 165 
 166         Calling __update__() means resetting every field,
 167         so use `inputbox.activate("<any prompt>")' to replace
 168         last entered value with the default one.
 169         '''
 170         if self.Status&ACTIVE and not pargs and not nargs:
 171             return False
 172         if pargs or nargs:
 173             self.__update__(*pargs, **nargs)
 174         self.Cursor=len(self.Text)
 175         self.Margin=0
 176         self.Status=ACTIVE|FIRST
 177         self.RepeatStore=pygame.key.get_repeat()
 178         pygame.key.set_repeat(*self.RepeatDefault)
 179         return True
 180 
 181     def deactivate(self):
 182         '''Disable input from the box'''
 183         if self.Status:
 184             self.Status=0
 185             pygame.key.set_repeat(*self.RepeatStore)
 186 
 187     def is_active(self): return self.Status&ACTIVE
 188     def is_done(self): return self.Status&DONE
 189     def is_failed(self): return self.Status&FAILED
 190     def is_cancelled(self): return self.Status&CANCEL
 191     def is_shown(self): return self.Status&SHOWN
 192 
 193     def is_success(self):
 194         return self.is_done() and not (self.is_failed() or self.is_cancelled())
 195 
 196     def edit(self,ev):
 197         '''Proceed event for editing input box.
 198         Supported keys:
 199             <any unicode symbol>:   input character
 200             <Return>/<Enter>:       finish input
 201             <Backspace>/<Del>:      delete character under/after the cursor
 202             <Esc>:                  restore default value
 203             <Home>/<End>/→/←:       move cursor
 204             <Esc><Esc>:             cancel input'''
 205         if ev.type is pygame.KEYDOWN:
 206             if ev.key == pygame.K_BACKSPACE:
 207                 if self.Cursor>0:
 208                     self.Text=self.Text[:self.Cursor-1]+self.Text[self.Cursor:]
 209                     self.Cursor-=1
 210             elif ev.key == pygame.K_DELETE:
 211                 if self.Cursor<len(self.Text):
 212                     self.Text=self.Text[:self.Cursor]+self.Text[self.Cursor+1:]
 213             elif ev.unicode >= u' ':
 214                 if self.Status&FIRST:
 215                     self.Text=ev.unicode
 216                     self.Cursor=1
 217                 else:
 218                     self.Text=self.Text[:self.Cursor]+ev.unicode+self.Text[self.Cursor:]
 219                     self.Cursor+=1
 220             elif ev.key == pygame.K_ESCAPE:
 221                 if self.Text==self.DefaultText:
 222                      self.Status|=CANCEL|DONE
 223                 self.Text=self.DefaultText
 224                 self.Margin=0
 225             elif ev.key in (pygame.K_RETURN, pygame.K_KP_ENTER):
 226                 if self.value() is None:
 227                     if self.RetryIncorrect:
 228                         # TODO signal an error
 229                         self.Text=self.DefaultText
 230                         self.Margin=0
 231                     else:
 232                         self.Status|=DONE|FAILED
 233                 else:
 234                     self.Status|=DONE
 235             elif ev.key == pygame.K_HOME:
 236                 self.Cursor=0
 237             elif ev.key == pygame.K_END:
 238                 self.Cursor=len(self.Text)
 239             elif ev.key == pygame.K_RIGHT:
 240                 self.Cursor=min(self.Cursor+1,len(self.Text))
 241             elif ev.key == pygame.K_LEFT:
 242                 self.Cursor=max(self.Cursor-1,0)
 243             self.Status&=~FIRST
 244 
 245     def input(self, scr, pos, *pargs, **nargs):
 246         '''Perform synchronous input on surface scr at position pos.
 247         All unused events are ignored.'''
 248         self.activate(*pargs, **nargs)
 249         while not self.is_done():
 250             self.edit(pygame.event.wait())
 251             self.draw(scr,pos)
 252             pygame.display.flip()
 253             self.undraw()
 254         self.deactivate()
 255         return self.value()
 256 
 257 def Print(scr, text, pos=None, font=None, size=24, antialias=True, color=None, background=None):
 258     '''Print text on surface screen using as many defaults as possible'''
 259     # TODO font is registered every time you print, fix this
 260     f=pygame.font.Font(font, size)
 261     if pos == None:
 262         pos=scr.get_size()
 263         sz=f.size(text)
 264         pos=(pos[0]-sz[0])/2,(pos[1]-sz[1])/2
 265     if color == None:
 266         color=scr.get_at(pos)
 267         color.r, color.g, color.b = 255^color.r, 255^color.g, 255^color.b
 268     if background:
 269         pr=f.render(text, antialias, color, background=background)
 270     else:
 271         pr=f.render(text, antialias, color)
 272     scr.blit(pr, pos)
 273 
 274 def __main():
 275     import random
 276     _=random.randint
 277     pygame.init()
 278     Size=(760,180)
 279     Scr=pygame.display.set_mode(Size)
 280     Scr.fill(pygame.Color("Black"))
 281     r=20
 282     x,y=Size[0]/12,Size[1]/5
 283     inp=Input("Input",Size=(Size[0]-2*x,Size[1]-2*y))
 284     cont,verbose=True,False
 285     defaults,defcnt=(("String",u""),("Int",0),("Float",0.)),0
 286     while cont:
 287         pos=_(r,Size[0]-r),_(r,Size[1]-r)
 288         col=_(10,255),_(10,255),_(10,255)
 289         pygame.draw.circle(Scr,col,pos,r)
 290         for ev in pygame.event.get():
 291             if verbose:
 292                 print ev
 293             if ev.type is pygame.QUIT:
 294                 cont=False
 295             if ev.type is pygame.KEYDOWN and inp.is_active():
 296                 inp.edit(ev)
 297             elif ev.type is pygame.KEYDOWN:
 298                 if ev.key in (pygame.K_KP_ENTER, pygame.K_RETURN, 13):
 299                     if not inp.is_active():
 300                         inp.activate(*defaults[defcnt])
 301                         defcnt=(defcnt+1)%len(defaults)
 302                 elif ev.key == pygame.K_F1:
 303                     verbose=True
 304                 elif ev.key is pygame.K_ESCAPE:
 305                     if not inp.is_active():
 306                         cont=False
 307 
 308         if inp.is_done():
 309             if inp.is_failed():
 310                 print "Data incorrect"
 311             elif inp.is_cancelled():
 312                 print "Input is cancelled"
 313             else:
 314                 print u"Result: '{0}'".format(inp.value())
 315             inp.deactivate()
 316 
 317         if inp.is_active(): inp.draw(Scr,(x,y))
 318         pygame.display.flip()
 319         if inp.is_active(): inp.undraw()
 320     quit()
 321 
 322 if __name__ == "__main__":
 323     __main()

Прикреплённые файлы

Для ссылки на прикреплённый файл в тексте страницы напишите attachment:имяфайла, как показано ниже в списке файлов. Не используйте URL из ссылки «[получить]», так как он чисто внутренний и может измениться.

Вам нельзя прикреплять файлы к этой странице.