Прикреплённый файл «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.05
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=""
49 DefaultText=""
50 CheckType=str
51 RetryIncorrect=True
52 TextLength=0
53 FontID=None
54 Font=None
55 FontSize=24
56 PromptGap=None
57 Size=None
58 BackingStore=None
59 RepeatStore=None
60 RepeatDefault=(500,100)
61
62 def __update__(self, *pargs, **nargs):
63 '''Update box properties.
64 Positional parameters: [Prompt, [DefaultText]].
65 Named parameters: all class data fields.
66
67 - Size supercedes FontSize, default FontSize is 24
68 - setting DefaultText also sets CheckType (so use 0., not "0" for float input)
69 '''
70 if len(pargs)>0:
71 self.Prompt=pargs[0]
72 if len(pargs)>1:
73 self.DefaultText=unicode(pargs[1])
74 self.CheckType=type(pargs[1])
75 for prop in nargs:
76 if hasattr(self,prop):
77 setattr(self,prop,nargs[prop])
78 if not self.FontID:
79 self.FontID=pygame.font.match_font("sans")
80 if self.PromptGap is None and self.Prompt:
81 self.PromptGap=" "
82 if "CheckType" not in nargs and "DefaultText" in nargs:
83 self.CheckType=type(nargs["DefaultText"])
84 if "Size" in nargs:
85 self.FontSize=self.Size[1]-2*self.BorderWidth
86 self.Font=pygame.font.Font(self.FontID, self.FontSize)
87 elif not self.Size:
88 self.Font=pygame.font.Font(self.FontID, self.FontSize)
89 self.Size=self.Font.size(self.Prompt+self.PromptGap+"W"*max(self.TextLength,len(self.DefaultText)+1,2))
90 self.Size=self.Size[0]+2*self.BorderWidth,self.Size[1]+2*self.BorderWidth
91 self.Paper=pygame.Surface(self.Size)
92 # TODO background image/transparency for Paper
93 self.Paper.fill(self.PaperColor)
94 pr=self.Font.render(self.Prompt, True, self.PromptColor)
95 self.Paper.blit(pr, (self.BorderWidth,self.BorderWidth+self.FontSize-self.Font.get_height()))
96 self.Text=unicode(self.DefaultText)
97 self.Cursor=len(self.Text)
98
99 def __init__(self, *pargs, **nargs):
100 '''Create a text input entity. Call __update__() next.'''
101 self.__update__(*pargs, **nargs)
102
103 def __sawtoothed__(self, block, side, mult=3):
104 '''Create a sawtoothed mark for left (False) or right (True) side'''
105 w,h=block.get_size()
106 nw=mult*self.BorderWidth
107 n=(h/nw)|1
108 x,d=side and (w-1,-nw) or (0,nw)
109 return [(x+d*(i%2),h*i/n) for i in xrange(n)]
110
111 def value(self):
112 '''Check if input is correct and return it, return None if it is not'''
113 try:
114 return self.CheckType(self.Text)
115 except:
116 return None
117
118 def render(self):
119 '''Return paper surface with current prompt and text printed on'''
120 ret=self.Paper.copy()
121 wl=self.Font.size(self.Prompt+self.PromptGap)[0]
122 ib=ret.subsurface((wl,0,ret.get_width()-wl,ret.get_height()))
123 ia=ret.subsurface((wl+self.BorderWidth,self.BorderWidth,ret.get_width()-wl-2*self.BorderWidth,ret.get_height()-2*self.BorderWidth))
124 pr=self.Font.render(self.Text, True, self.TextColor)
125 w=self.Font.size(self.Text[:self.Cursor])[0]
126 while self.Margin and w-self.Font.size(self.Text[:self.Margin])[0]<self.CursorWidth:
127 self.Margin-=1
128 while w-self.Font.size(self.Text[:self.Margin])[0]>ia.get_width()-self.CursorWidth:
129 self.Margin+=1
130 Margin=-self.Font.size(self.Text[:self.Margin])[0]
131 ia.blit(pr,(Margin,self.FontSize-self.Font.get_height()))
132 pygame.draw.line(ia, self.CursorColor, (w+Margin,2), (w+Margin,ia.get_height()-2),self.CursorWidth)
133 if Margin<0:
134 pygame.draw.polygon(ib, self.OverColor, self.__sawtoothed__(ib, False))
135 if Margin+pr.get_width()>ia.get_width()-self.CursorWidth:
136 pygame.draw.polygon(ib, self.OverColor, self.__sawtoothed__(ib, True))
137 return ret
138
139 def draw(self,scr,pos):
140 '''Draw input box on surface scr at position pos
141 backuping an underlying part of surface'''
142 self.BackingStore=(self.Paper.copy(),scr,pos)
143 self.BackingStore[0].blit(self.BackingStore[1],(0,0),(self.BackingStore[2],self.Size))
144 self.BackingStore[1].blit(self.render(),self.BackingStore[2])
145 self.Status|=SHOWN
146
147 def undraw(self):
148 '''Remove the box from the surface it was drawn
149 restoring underlying part of surface'''
150 if self.BackingStore:
151 self.BackingStore[1].blit(self.BackingStore[0],self.BackingStore[2])
152 self.Status&=~SHOWN
153
154 def activate(self, *pargs, **nargs):
155 '''Enable input from the box.
156 If either pargs or nargs is given, call __update__().
157 Return False if no activation was needed, True otherwise.
158
159 Calling __update__() means resetting every field,
160 so use `inputbox.activate("<any prompt>")' to replace
161 last entered value with the default one.
162 '''
163 if self.Status&ACTIVE and not pargs and not nargs:
164 return False
165 if pargs or nargs:
166 self.__update__(*pargs, **nargs)
167 self.Cursor=len(self.Text)
168 self.Margin=0
169 self.Status=ACTIVE|FIRST
170 self.RepeatStore=pygame.key.get_repeat()
171 pygame.key.set_repeat(*self.RepeatDefault)
172 return True
173
174 def deactivate(self):
175 '''Disable input from the box'''
176 if self.Status:
177 self.Status=0
178 pygame.key.set_repeat(*self.RepeatStore)
179
180 def is_active(self): return self.Status&ACTIVE
181 def is_done(self): return self.Status&DONE
182 def is_failed(self): return self.Status&FAILED
183 def is_cancelled(self): return self.Status&CANCEL
184 def is_shown(self): return self.Status&SHOWN
185
186 def is_success(self):
187 return self.is_done() and not (self.is_failed() or self.is_cancelled())
188
189 def edit(self,ev):
190 '''Proceed event for editing input box.
191 Supported keys:
192 <any unicode symbol>: input character
193 <backspace>: delete character under the cursor
194 <esc>: restore default value
195 <esc><esc>: force unsuccsessful input'''
196 if ev.type is pygame.KEYDOWN:
197 if ev.key == pygame.K_BACKSPACE:
198 if self.Cursor>0:
199 self.Text=self.Text[:self.Cursor-1]+self.Text[self.Cursor:]
200 self.Cursor-=1
201 elif ev.key == pygame.K_DELETE:
202 if self.Cursor<len(self.Text):
203 self.Text=self.Text[:self.Cursor]+self.Text[self.Cursor+1:]
204 elif ev.unicode >= u' ':
205 if self.Status&FIRST:
206 self.Text=ev.unicode
207 self.Cursor=1
208 else:
209 self.Text=self.Text[:self.Cursor]+ev.unicode+self.Text[self.Cursor:]
210 self.Cursor+=1
211 elif ev.key == pygame.K_ESCAPE:
212 if self.Text==self.DefaultText:
213 self.Status|=CANCEL|DONE
214 self.Text=self.DefaultText
215 self.Margin=0
216 elif ev.key == pygame.K_RETURN:
217 if self.value() is None:
218 if self.RetryIncorrect:
219 # TODO signal an error
220 self.Text=self.DefaultText
221 self.Margin=0
222 else:
223 self.Status|=DONE|FAILED
224 else:
225 self.Status|=DONE
226 elif ev.key == pygame.K_HOME:
227 self.Cursor=0
228 elif ev.key == pygame.K_END:
229 self.Cursor=len(self.Text)
230 elif ev.key == pygame.K_RIGHT:
231 self.Cursor=min(self.Cursor+1,len(self.Text))
232 elif ev.key == pygame.K_LEFT:
233 self.Cursor=max(self.Cursor-1,0)
234 self.Status&=~FIRST
235
236 def __main():
237 import random
238 _=random.randint
239 pygame.init()
240 Size=(760,180)
241 Scr=pygame.display.set_mode(Size)
242 Scr.fill(pygame.Color("Black"))
243 #inp=Input("", "Default text", FontSize=36)
244 #inp=Input("Float input:", "123", FontSize=36, CheckType=float)
245 r=20
246 for i in xrange(100):
247 pos=_(r,Size[0]-r),_(r,Size[1]-r)
248 col=_(10,255),_(10,255),_(10,255)
249 pygame.draw.circle(Scr,col,pos,r)
250 x,y=Size[0]/12,Size[1]/5
251 inp=Input("Input",Size=(Size[0]-2*x,Size[1]-2*y))
252 cont,verbose=True,False
253 defaults,defcnt=(("String",""),("Int",0),("Float",0.)),0
254 while cont:
255 for ev in pygame.event.get():
256 if verbose:
257 print ev
258 if ev.type is pygame.QUIT:
259 cont=False
260 if ev.type is pygame.KEYDOWN and inp.is_active():
261 inp.edit(ev)
262 elif ev.type is pygame.KEYDOWN:
263 if ev.key in (pygame.K_KP_ENTER, pygame.K_RETURN, 13):
264 if not inp.is_active():
265 inp.activate(*defaults[defcnt])
266 defcnt=(defcnt+1)%len(defaults)
267 elif ev.key == pygame.K_F1:
268 verbose=True
269 elif ev.key is pygame.K_ESCAPE:
270 if not inp.is_active():
271 cont=False
272
273 if inp.is_done():
274 if inp.is_failed():
275 print "Data incorrect"
276 elif inp.is_cancelled():
277 print "Input is cancelled"
278 else:
279 print "Result: '{0}'".format(inp.value())
280 inp.deactivate()
281
282 if inp.is_active(): inp.draw(Scr,(x,y))
283 pygame.display.flip()
284 if inp.is_active(): inp.undraw()
285 quit()
286
287 if __name__ == "__main__":
288 __main()
Прикреплённые файлы
Для ссылки на прикреплённый файл в тексте страницы напишите attachment:имяфайла, как показано ниже в списке файлов. Не используйте URL из ссылки «[получить]», так как он чисто внутренний и может измениться.- [получить | показать] (2013-12-28 10:06:44, 11.2 KB) [[attachment:pyginput.py]]
- [получить | показать] (2013-12-28 10:06:50, 1.7 KB) [[attachment:pyginput_example.py]]
Вам нельзя прикреплять файлы к этой странице.