2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
7
"""Implementation module for the graphical version of the `mktap` command.
11
import Tkinter, tkMessageBox, tkFileDialog, StringIO, os
15
from twisted.application import service
16
from twisted.internet import tksupport, reactor
17
from twisted.scripts import mktap
18
from twisted.python import usage, reflect
19
from twisted.copyright import version
22
class TkMkAppFrame(Tkinter.Frame):
24
A frame with all the necessary widgets to configure a Twisted Application.
27
# Plugin currently selected
30
# Options instance currently displayed
33
# Frame options are displayed in
36
def __init__(self, master, coil):
37
Tkinter.Frame.__init__(self, master)
44
# Create all of the "mktap" option widgets
45
appFrame = Tkinter.Frame(self)
47
f = Tkinter.Frame(appFrame)
48
listLabel = Tkinter.Label(f, text='TAp Format')
49
self.typeList = Tkinter.Listbox(f, background='white')
50
self.typeList['height'] = 3
51
for t in ('pickle', 'xml', 'source'):
52
self.typeList.insert(Tkinter.END, t)
53
self.typeList.selection_set(0)
55
listLabel.pack(side=Tkinter.TOP)
56
self.typeList.pack(side=Tkinter.TOP)
57
f.pack(side=Tkinter.LEFT, anchor=Tkinter.N)
59
f = Tkinter.Frame(appFrame)
60
tapLabel = Tkinter.Label(f, text='TAp Filename')
61
tapButton = Tkinter.Button(f, text="Choose", command=self.pickTapFile)
62
self.tapfile = Tkinter.Entry(f, background='white')
64
tapLabel.pack(side=Tkinter.LEFT)
65
self.tapfile.pack(side=Tkinter.LEFT)
66
tapButton.pack(side=Tkinter.LEFT)
67
f.pack(side=Tkinter.TOP, anchor=Tkinter.E)
69
f = Tkinter.Frame(appFrame)
70
nameLabel = Tkinter.Label(f, text='Application Process Name')
71
self.appname = Tkinter.Entry(f, background='white')
73
nameLabel.pack(side=Tkinter.LEFT)
74
self.appname.pack(side=Tkinter.LEFT)
75
f.pack(side=Tkinter.TOP, anchor=Tkinter.E)
77
f = Tkinter.Frame(appFrame)
78
encLabel = Tkinter.Label(f, text='Passphrase')
79
self.passphrase = Tkinter.Entry(f, background='white')
81
encLabel.pack(side=Tkinter.LEFT)
82
self.passphrase.pack(side=Tkinter.LEFT)
83
f.pack(side=Tkinter.TOP, anchor=Tkinter.E)
85
f = Tkinter.Frame(appFrame)
86
self.append = Tkinter.BooleanVar()
87
appLabel = Tkinter.Label(f, text='Append')
88
appButton = Tkinter.Checkbutton(f, variable=self.append)
90
appLabel.pack(side=Tkinter.LEFT)
91
appButton.pack(side=Tkinter.LEFT)
92
f.pack(side=Tkinter.LEFT, anchor=Tkinter.E)
94
f = Tkinter.Frame(appFrame)
95
s = Tkinter.StringVar()
96
s.set(not hasattr(os, 'getuid') and '0' or str(os.getuid()))
97
uidLabel = Tkinter.Label(f, text='UID')
98
self.uid = Tkinter.Entry(f, text=s, background='white')
100
uidLabel.pack(side=Tkinter.LEFT)
101
self.uid.pack(side=Tkinter.LEFT)
102
f.pack(side=Tkinter.BOTTOM)
104
f = Tkinter.Frame(appFrame)
105
s = Tkinter.StringVar()
106
s.set(not hasattr(os, 'getgid') and '0' or str(os.getgid()))
107
gidLabel = Tkinter.Label(f, text='GID')
108
self.gid = Tkinter.Entry(f, text=s, background='white')
110
gidLabel.pack(side=Tkinter.LEFT)
111
self.gid.pack(side=Tkinter.LEFT)
112
f.pack(side=Tkinter.BOTTOM)
114
appFrame.grid(row=0, column=0, columnspan=3, sticky=Tkinter.N + Tkinter.S)
117
def pickTapFile(self):
118
r = tkFileDialog.askopenfilename()
120
self.tapfile.delete(0, Tkinter.END)
121
self.tapfile.insert(Tkinter.END, r)
124
def reset(self, coil):
126
Remove the existing coil-specific widgets and then create and add
127
new ones based on the given plugin object.
129
if coil is self.coil:
133
opt = coil.load().Options()
135
f = StringIO.StringIO()
136
traceback.print_exc(file=f)
137
# XXX - Why is this so narrow?
138
tkMessageBox.showerror(title="Options Error", message=f.getvalue(), parent=self)
142
self.optFrame.forget()
143
self.optFrame.destroy()
148
self.optFrame = TkConfigFrame(self, self.options)
149
self.optFrame.grid(row=1, column=0)
151
# self.tapfile.delete(0, Tkinter.END)
153
# self.tapfile.insert(Tkinter.END, self.coil.tapname)
154
# except AttributeError:
155
# self.tapfile.insert(Tkinter.END, self.coil.name)
158
def copyOptions(self):
159
# Snarf the data out of the widgets and place them into the Options
161
extra = self.optFrame.updateConfig(self.options)
163
self.options['filename'] = self.tapfile.get()
164
self.options['appname'] = self.appname.get()
165
self.options['passphrase'] = self.passphrase.get()
167
self.options['append'] = self.append.get()
168
self.options['encrypted'] = len(self.options['passphrase'])
170
self.options['uid'] = int(self.uid.get())
171
self.options['gid'] = int(self.gid.get())
174
self.options['type'] = self.typeList.curselection()[0]
176
raise usage.UsageError("Select a TAp Format")
177
self.options['help'] = 0
181
# XXX - this is wrong. It needs to respect quotes, etc.
182
self.options.parseArgs(extra.split())
184
raise usage.UsageError("Wrong number of extra arguments")
185
self.options.postOptions()
188
def createApplication(self):
190
tkMessageBox.showerror(message="Select an Application first")
195
except usage.UsageError, e:
196
tkMessageBox.showerror(message=str(e))
199
exists = os.path.exists(self.options['filename'])
200
if self.options['append'] and exists:
201
a = service.loadApplication(
202
self.options['filename'],
203
self.options['type'],
204
self.options['passphrase']
208
overwrite = tkMessageBox.askyesno(title='File Exists', message='Overwrite?')
211
a = service.Application(self.coil.name, self.options['uid'], self.options['gid'])
214
s = mktap.makeService(
216
self.options['appname'],
219
except usage.UsageError:
220
f = StringIO.StringIO()
221
traceback.print_stack(file=f)
222
tkMessageBox.showerror(title="Usage Error", message=f.getvalue(), parent=self)
225
mktap.addToApplication(
226
s, self.coil.name, self.options['append'],
227
self.options['appname'], self.options['type'],
228
self.options['encrypted'], self.options['uid'],
232
f = StringIO.StringIO()
233
traceback.print_exc(file=f)
235
tkMessageBox.showerror(title="Usage Error", message=f.getvalue(), parent=self)
237
filename = self.options['filename']
239
filename = self.coil.name
240
tkMessageBox.showinfo(message="Wrote " + filename)
245
Tkinter.Frame.destroy(self)
249
# This class was written based on code from Drew "drewp" Pertulla
250
# (<drewp (at) bigasterisk (dot) com>) - without his help, tkmktap
251
# would be an ugly POS.
253
class ParameterLine(Tkinter.Frame):
254
def __init__(self, master, lines, label, desc, default, cmd, **kw):
255
Tkinter.Frame.__init__(self, master, relief='raised', bd=1, **kw)
260
self, text=label, wraplen=200,
261
width=30, anchor='w', justify='left'
264
s = Tkinter.StringVar()
267
self.entry = Tkinter.Entry(self, text=s, background='white')
270
more = Tkinter.Button(
272
command=lambda f = cmd, a = label, b = default, c = desc: f(a, b, c)
275
l.pack(side=Tkinter.LEFT, fill='y')
276
self.entry.pack(side=Tkinter.LEFT)
277
more.pack(side=Tkinter.LEFT)
279
l.bind("<Enter>", self.highlight)
280
l.bind("<Leave>", self.unhighlight)
282
l.bind("<ButtonPress-1>", self.press)
283
l.bind("<B1-ButtonRelease>", self.release)
284
l.bind("<B1-Motion>", self.motion)
287
def highlight(self, ev, hicolor = 'gray90'):
288
# make the label light up when you mouseover
289
ev.widget._oldcolor = self.cget('bg')
290
ev.widget.config(bg=hicolor)
293
def unhighlight(self, ev):
294
# make the label return to its old setting
296
ev.widget.config(bg=ev.widget._oldcolor)
297
del ev.widget._oldcolor
302
# make the frame change order when you drag it (by its label)
305
self._oldrelief = self.cget('relief'), self.cget('bd')
307
self.config(relief='raised', bd=3)
310
def motion(self, ev):
311
this = self.lines.index(self)
312
framey = ev.y + self.winfo_y() # get mouse y coord in parent frame
313
replace = this # replace will be the index of the row to swap with
314
for i, l in zip(range(len(self.lines)), self.lines):
316
y2 = y1 + l.winfo_height()
320
# we moved over another row-- swap them
321
self.lines[replace], self.lines[this] = self.lines[this], self.lines[replace]
323
# and re-assign all rows in the new order
324
for i, l in zip(range(len(self.lines)), self.lines):
325
l.grid(row=i, column=0)
328
def release(self, ev):
329
# restore the old border width
331
rel, bd = self._oldrelief
332
self.config(relief=rel, bd=bd)
338
class TkConfigFrame(Tkinter.Frame):
344
previousCommand = None
349
def __init__(self, master, options):
350
Tkinter.Frame.__init__(self, master)
351
self.options = options
354
self.setupOptParameters()
355
self.setupSubCommands()
359
def getOptFlags(self):
363
def getOptParameters(self):
365
for p in self.paramLines:
366
r.append((p.flag, p.entry.get()))
370
def updateConfig(self, options):
371
for (opt, var) in self.getOptFlags():
375
continue # XXX - this is poor - add a '-' button to remove options
377
f = getattr(options, 'opt_' + opt, None)
383
for (opt, var) in self.getOptParameters():
386
continue # XXX - this is poor - add a '-' button to remove options
388
f = getattr(options, 'opt_' + opt, None)
393
return self.extra.get()
396
def setupOptFlags(self):
399
if hasattr(self.options, 'optFlags'):
400
flags.extend(self.options.optFlags)
404
for meth in reflect.prefixedMethodNames(self.options.__class__, 'opt_'):
406
func = getattr(self.options, full)
408
if not usage.flagFunction(func) or meth in ('help', 'version'):
411
if soFar.has_key(func):
415
existing = d.setdefault(func, meth)
417
if len(existing) < len(meth):
420
for (func, name) in d.items():
421
flags.append((name, None, func.__doc__))
424
self.optFrame = f = Tkinter.Frame(self)
425
for (flag, _, desc) in flags:
426
b = Tkinter.BooleanVar()
427
c = Tkinter.Checkbutton(f, text=desc, variable=b, wraplen=200)
428
c.pack(anchor=Tkinter.W)
429
self.optFlags.append((flag, b))
430
f.grid(row=1, column=1)
433
def setupOptParameters(self):
435
if hasattr(self.options, 'optParameters'):
436
params.extend(self.options.optParameters)
440
for meth in reflect.prefixedMethodNames(self.options.__class__, 'opt_'):
442
func = getattr(self.options, full)
444
if usage.flagFunction(func) or soFar.has_key(func):
449
existing = d.setdefault(func, meth)
451
if len(existing) < len(meth):
453
for (func, name) in d.items():
454
params.append((name, None, None, func.__doc__))
457
self.paramFrame = Tkinter.Frame(self)
459
for (flag, _, default, desc) in params:
461
default = self.options[flag]
464
self.makeField(flag, default, desc)
465
self.paramFrame.grid(row=1, column=2)
469
def makeField(self, flag, default, desc):
470
line = ParameterLine(
471
self.paramFrame, self.paramLines, flag, desc, default,
474
self.paramLines.append(line)
475
line.grid(row=len(self.paramLines), column=0)
478
def setupSubCommands(self):
480
if hasattr(self.options, 'subCommands'):
481
self.commandFrame = f = Tkinter.Frame(self)
482
self.cmdList = Tkinter.Listbox(f)
483
for (cmd, _, opt, desc) in self.options.subCommands:
484
self.cmdList.insert(Tkinter.END, cmd)
485
self.optMap[cmd] = opt()
487
self.subCmdPoll = reactor.callLater(0.1, self.pollSubCommands)
488
f.grid(row=1, column=3)
491
def setupExtra(self):
492
f = Tkinter.Frame(self)
493
l = Tkinter.Label(f, text='Extra Options')
494
self.extra = Tkinter.Entry(f, background='white')
496
self.extra.pack(fill='y')
497
f.grid(row=2, column=1, columnspan=2)
500
def pollSubCommands(self):
501
s = self.cmdList.curselection()
504
if s != self.previousCommand:
506
self.subOptFrame.forget()
507
self.subOptFrame.destroy()
508
self.subOptFrame = TkConfigFrame(self.commandFrame, self.optMap[s])
509
self.subOptFrame.pack()
510
self.subCmdPoll = reactor.callLater(0.1, self.pollSubCommands)
513
class TkAppMenu(Tkinter.Menu):
514
def __init__(self, master, create, callback, items):
515
Tkinter.Menu.__init__(self, master)
517
cmdMenu = Tkinter.Menu(self)
518
self.add_cascade(label="Actions", menu=cmdMenu)
520
cmdMenu.add_command(label='Create', command=create)
521
cmdMenu.add_separator()
522
cmdMenu.add_command(label='Quit', command=reactor.crash)
524
tapMenu = Tkinter.Menu(self)
525
self.add_cascade(label="Applications", menu=tapMenu)
529
label=item, command=lambda i=item, c = callback: c(i)
534
taps = mktap.loadPlugins()
538
keyList = taps.keys()
541
config = TkMkAppFrame(r, None)
544
config.createApplication,
545
lambda i, d = taps, c = config: c.reset(d[i]),
552
r.title('Twisted Application Maker ' + version)
557
if __name__ == '__main__':