2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
5 """Utilities for building L{PB<twisted.spread.pb>} clients with L{Tkinter}.
8 from tkSimpleDialog import _QueryString
9 from tkFileDialog import _Dialog
10 from twisted.spread import pb
11 from twisted.internet import reactor
12 from twisted import copyright
16 #normalFont = Font("-adobe-courier-medium-r-normal-*-*-120-*-*-m-*-iso8859-1")
17 #boldFont = Font("-adobe-courier-bold-r-normal-*-*-120-*-*-m-*-iso8859-1")
18 #errorFont = Font("-adobe-courier-medium-o-normal-*-*-120-*-*-m-*-iso8859-1")
20 class _QueryPassword(_QueryString):
21 def body(self, master):
23 w = Label(master, text=self.prompt, justify=LEFT)
24 w.grid(row=0, padx=5, sticky=W)
26 self.entry = Entry(master, name="entry",show="*")
27 self.entry.grid(row=1, padx=5, sticky=W+E)
30 self.entry.insert(0, self.initialvalue)
31 self.entry.select_range(0, END)
35 def askpassword(title, prompt, **kw):
36 '''get a password from the user
38 @param title: the dialog title
39 @param prompt: the label text
40 @param **kw: see L{SimpleDialog} class
44 d = apply(_QueryPassword, (title, prompt), kw)
47 def grid_setexpand(widget):
48 cols,rows=widget.grid_size()
50 widget.columnconfigure(i,weight=1)
52 widget.rowconfigure(i,weight=1)
55 def __init__(self,parent,labels,disablesorting=0,**kw):
56 Frame.__init__(self,parent)
59 self.disablesorting=disablesorting
60 kw["exportselection"]=0
61 for i in range(len(labels)):
62 b=Button(self,text=labels[i],anchor=W,height=1,pady=0)
63 b.config(command=lambda s=self,i=i:s.setSort(i))
64 b.grid(column=i,row=0,sticky=N+E+W)
65 box=apply(Listbox,(self,),kw)
66 box.grid(column=i,row=1,sticky=N+E+S+W)
67 self.lists.append(box)
69 self.rowconfigure(0,weight=0)
70 self._callall("bind",'<Button-1>',self.Button1)
71 self._callall("bind",'<B1-Motion>',self.Button1)
72 self.bind('<Up>',self.UpKey)
73 self.bind('<Down>',self.DownKey)
76 def _callall(self,funcname,*args,**kw):
79 func=getattr(l,funcname)
80 ret=apply(func,args,kw)
81 if ret!=None: rets.append(ret)
85 index=self.nearest(e.y)
86 self.select_clear(0,END)
87 self.select_set(index)
92 index=self.index(ACTIVE)
94 self.select_clear(0,END)
95 self.select_set(index-1)
99 index=self.index(ACTIVE)
100 if index!=self.size()-1:
101 self.select_clear(0,END)
102 self.select_set(index+1)
105 def setSort(self,index):
108 elif self.sort[0]==index:
109 self.sort[1]=-self.sort[1]
115 if self.disablesorting:
120 li=list(self.get(0,END))
121 li.sort(lambda x,y,i=ind,d=direc:d*cmp(x[i],y[i]))
125 def activate(self,index):
126 self._callall("activate",index)
128 # def bbox(self,index):
129 # return self._callall("bbox",index)
131 def curselection(self):
132 return self.lists[0].curselection()
134 def delete(self,*args):
135 apply(self._callall,("delete",)+args)
138 bad=apply(self._callall,("get",)+args)
142 for i in range(len(bad[0])):
144 for j in range(len(bad)):
149 def index(self,index):
150 return self.lists[0].index(index)
152 def insert(self,index,items):
153 self._insert(index,items)
156 def _insert(self,index,items):
157 for i in range(len(items)):
158 self.lists[i].insert(index,items[i])
161 return self.lists[0].nearest(y)
164 self._callall("see",index)
167 return self.lists[0].size()
169 def selection_anchor(self,index):
170 self._callall("selection_anchor",index)
172 select_anchor=selection_anchor
174 def selection_clear(self,*args):
175 apply(self._callall,("selection_clear",)+args)
177 select_clear=selection_clear
179 def selection_includes(self,index):
180 return self.lists[0].select_includes(index)
182 select_includes=selection_includes
184 def selection_set(self,*args):
185 apply(self._callall,("selection_set",)+args)
187 select_set=selection_set
189 def xview(self,*args):
190 if not args: return self.lists[0].xview()
191 apply(self._callall,("xview",)+args)
193 def yview(self,*args):
194 if not args: return self.lists[0].yview()
195 apply(self._callall,("yview",)+args)
198 def __init__(self, master=None, orientation="horizontal",
199 min=0, max=100, width=100, height=18,
200 doLabel=1, appearance="sunken",
201 fillColor="blue", background="gray",
202 labelColor="yellow", labelFont="Verdana",
203 labelText="", labelFormat="%d%%",
205 # preserve various values
207 self.orientation=orientation
213 self.fillColor=fillColor
214 self.labelFont= labelFont
215 self.labelColor=labelColor
216 self.background=background
217 self.labelText=labelText
218 self.labelFormat=labelFormat
220 self.frame=Frame(master, relief=appearance, bd=bd)
221 self.canvas=Canvas(self.frame, height=height, width=width, bd=0,
222 highlightthickness=0, background=background)
223 self.scale=self.canvas.create_rectangle(0, 0, width, height,
225 self.label=self.canvas.create_text(self.canvas.winfo_reqwidth() / 2,
226 height / 2, text=labelText,
227 anchor="c", fill=labelColor,
230 self.canvas.pack(side='top', fill='x', expand='no')
232 def updateProgress(self, newValue, newMax=None):
235 self.value = newValue
239 # Trim the values to be between min and max
245 # Adjust the rectangle
246 if self.orientation == "horizontal":
247 self.canvas.coords(self.scale, 0, 0,
248 float(value) / self.max * self.width, self.height)
250 self.canvas.coords(self.scale, 0,
251 self.height - (float(value) /
252 self.max*self.height),
253 self.width, self.height)
254 # Now update the colors
255 self.canvas.itemconfig(self.scale, fill=self.fillColor)
256 self.canvas.itemconfig(self.label, fill=self.labelColor)
257 # And update the label
261 pvalue = int((float(value) / float(self.max)) *
265 self.canvas.itemconfig(self.label, text=self.labelFormat
268 self.canvas.itemconfig(self.label, text='')
270 self.canvas.itemconfig(self.label, text=self.labelFormat %
272 self.canvas.update_idletasks()
274 class DirectoryBrowser(_Dialog):
275 command = "tk_chooseDirectory"
277 def askdirectory(**options):
278 "Ask for a directory to save to."
280 return apply(DirectoryBrowser, (), options).show()
282 class GenericLogin(Toplevel):
283 def __init__(self,callback,buttons):
284 Toplevel.__init__(self)
285 self.callback=callback
286 Label(self,text="Twisted v%s"%copyright.version).grid(column=0,row=0,columnspan=2)
289 for stuff in buttons:
290 label,value=stuff[:2]
294 Label(self,text=label+": ").grid(column=0,row=row)
295 e=apply(Entry,(self,),dict)
296 e.grid(column=1,row=row)
298 self.entries[label]=e
300 Button(self,text="Login",command=self.doLogin).grid(column=0,row=row)
301 Button(self,text="Cancel",command=self.close).grid(column=1,row=row)
302 self.protocol('WM_DELETE_WINDOW',self.close)
310 for k in self.entries.keys():
311 values[string.lower(k)]=self.entries[k].get()
312 self.callback(values)
315 class Login(Toplevel):
319 initialUser = "guest",
320 initialPassword = "guest",
321 initialHostname = "localhost",
323 initialPortno = pb.portno):
324 Toplevel.__init__(self)
325 version_label = Label(self,text="Twisted v%s" % copyright.version)
326 self.pbReferenceable = referenced
327 self.pbCallback = callback
328 # version_label.show()
329 self.username = Entry(self)
330 self.password = Entry(self,show='*')
331 self.hostname = Entry(self)
332 self.service = Entry(self)
333 self.port = Entry(self)
335 self.username.insert(0,initialUser)
336 self.password.insert(0,initialPassword)
337 self.service.insert(0,initialService)
338 self.hostname.insert(0,initialHostname)
339 self.port.insert(0,str(initialPortno))
341 userlbl=Label(self,text="Username:")
342 passlbl=Label(self,text="Password:")
343 servicelbl=Label(self,text="Service:")
344 hostlbl=Label(self,text="Hostname:")
345 portlbl=Label(self,text="Port #:")
346 self.logvar=StringVar()
347 self.logvar.set("Protocol PB-%s"%pb.Broker.version)
348 self.logstat = Label(self,textvariable=self.logvar)
349 self.okbutton = Button(self,text="Log In", command=self.login)
351 version_label.grid(column=0,row=0,columnspan=2)
353 for i in [[userlbl,self.username],
354 [passlbl,self.password],
355 [hostlbl,self.hostname],
356 [servicelbl,self.service],
357 [portlbl,self.port]]:
358 i[0].grid(column=0,row=z+1)
359 i[1].grid(column=1,row=z+1)
362 self.logstat.grid(column=0,row=6,columnspan=2)
363 self.okbutton.grid(column=0,row=7,columnspan=2)
365 self.protocol('WM_DELETE_WINDOW',self.tk.quit)
367 def loginReset(self):
368 self.logvar.set("Idle.")
370 def loginReport(self, txt):
372 self.after(30000, self.loginReset)
375 host = self.hostname.get()
376 port = self.port.get()
377 service = self.service.get()
382 user = self.username.get()
383 pswd = self.password.get()
384 pb.connect(host, port, user, pswd, service,
385 client=self.pbReferenceable).addCallback(self.pbCallback).addErrback(
386 self.couldNotConnect)
388 def couldNotConnect(self,f):
389 self.loginReport("could not connect:"+f.getErrorMessage())
391 if __name__=="__main__":
393 o=CList(root,["Username","Online","Auto-Logon","Gateway"])
395 for i in range(0,16,4):
396 o.insert(END,[i,i+1,i+2,i+3])