[Tutor] My best GUI app so far.
Jacob S.
keridee at jayco.net
Tue Jan 11 21:48:54 CET 2005
> > Great! I took the improvements you gave me an added support for keys (So
you
> > can type in 1.25+2= instead of having to type the buttons.) As always, I
> > encourage improvements to my code. Maybe that will be my disclaimer... I
> > have always liked and wanted to adopt Liam's.
>
> Here's a few thoughts.... :)
>
>
>
> > def equal(self,*args):
> > if self.action:
> > self.newnum = self.distext.get()
> > self.newnum =
str(eval(self.oldnum+self.action+self.newnum))
> > self.distext.set(self.newnum)
> > self.oldnum = '0'
> > self.action = ''
> > self.shouldblank = True
>
>
> Instead of using text representations of the operators, and eval()ing
> a string, why not use the operator module?
>
> Your operator keys can set self.action to be operator.add,
> operator.subtract, etc; then your equal() function becomes
>
> def equal(self, *args):
> if self.action:
> self.newnum = self.distext.get()
> self.newnum= str(self.action(float(self.oldnum), \
> float(self.newnum)))
> self.distext.set(self.newnum)
> self.oldnum = '0'
> self.action = '' # I'd actually prefer None here...
> self.shouldblank = True
>
> >
> > def add(self,*args):
> > self.handleOperator('+')
>
> becomes
>
> def add(self, *args):
> self.handleOperator(operator.add)
>
> The handleOperator() function can stay the same.
>
>
> > def clear(self):
> > self.action = ''
> > self.oldnum = '0'
> > self.distext.set('0')
> > self.shouldblank = True
>
> As I mentioned in a comment above, I'd prefer to use None for
> self.action when it's unset. There's no real practical difference,
> but conceptually it's more accurate to use a null-object than to use
> an empty string. Minor style point, I know, but you *did* ask for
> advice. ;)
>
> > def memminus(self):
> > self.memory = str(eval(self.memory+"-"+self.distext.get()))
> > self.shouldblank = True
> >
> > def memplus(self):
> > self.memory = str(eval(self.memory+"+"+self.distext.get()))
> > self.shouldblank = True
>
> Why use eval() here? You could just as easily do these as
>
> def memminus(self):
> self.memory = str(float(self.memory) - \
> float(self.distext.get()))
> self.shouldblank = True
>
> I try to avoid using eval() wherever possible, which is almost
> everywhere. ;) There's a huge security hole in using eval() on
> arbitrary strings, and it's not the greatest performance either.
> (Each invocation of eval() will generate bytecode and create a code
> object that does essentially the same thing as my explicit conversion
> code does, so by doing it manually you save a parsing/compiling step.)
> You're not eval()ing arbitrary strings here, at least, but it's
> still good practice to only use eval() when absolutely necessary.
>
>
> > def __init__(self, master=None):
> > Frame.__init__(self,master)
> > self.master.title("Calculator by Jacob, Inc.")
> > self.pack(expand=True)
> > m = lambda x: self.adddigit(x)
> > self.bl = [lambda *x: self.adddigit('0',x),
> > lambda *x: self.adddigit('1',x),
> > lambda *x: self.adddigit('2',x),
> > lambda *x: self.adddigit('3',x),
> > lambda *x: self.adddigit('4',x),
> > lambda *x: self.adddigit('5',x),
> > lambda *x: self.adddigit('6',x),
> > lambda *x: self.adddigit('7',x),
> > lambda *x: self.adddigit('8',x),
> > lambda *x: self.adddigit('9',x)]
> > for y in range(10):
> > self.bind_all(str(y),self.bl[y])
> > self.bind_all("+",lambda x: self.add(x))
> > self.bind_all("-",lambda x: self.subtract(x))
> > self.bind_all("*",lambda x: self.multiply(x))
> > self.bind_all("/",lambda x: self.divide(x))
> > self.bind_all("=",lambda x: self.equal(x))
> > self.bind_all(".",lambda x: self.adddigitdot(x))
>
> There's absolutely no point to doing lambda x: somefunc(x) -- all
> you're doing is adding an extra layer of function call. You can
> replace all of those with something like
>
> self.bind_all('+', self.add)
>
> And actually, because I'm not fond of lambda to begin with, I'd
> redefine your adddigit() method:
>
> def make_adddigit_callback(self, digit):
> def adddigit(*args):
> self.ctb()
> self.distext.set(self.distext.get()+digit)
> return adddigit
>
> (You may not need the *args above, if the button callback is expected
> to be zero parameters -- I don't use Tkinter, so I'm not sure
> offhand.) Then your button bindings can simply be
>
> self.bl = [ self.make_adddigit_callback('0'),
> self.make_adddigit_callback('1'),
> self.make_adddigit_callback('2'),
> ... ]
>
> Or even --
>
> self.bl = [self.make_adddigit_callback(digit) \
> for digit in '0123456789']
>
> Remember, there's nothing particularly special about lambdas -- you
> can create and pass around regular named functions, too. Each time
> that make_adddigit_callback() is called, it creates and returns a new
> function object which captures the current value of 'digit', in
> exactly the same way that lambda does. A function object (whether
> named with def, or lambda) like this, which captures a variable's
> current state, is called a closure. Closures are indispensible for
> GUI callbacks like this, and many people automatically turn to lambda
> when they want a closure. For me, though, having a proper def
> statement somewhere feels clearer. (The merits of lambda vs. def'd
> functions are a frequent subject of heated debate on comp.lang.python,
> so if you prefer to stick with the lambdas in this case, I'm sure
> you'd be able to find plenty of people to support you... ;) )
>
> Jeff Shannon
> Technician/Programmer
> Credit International
I know why people make money programming... ;-)
I'm not adverse to any conventional way of getting the project to work.
There are only a few exceptions like the proposal in python3 to remove
raw_input(). You would have to import sys to get sys.stdin.readlines(). Just
odd, quirky, whys.
I digress.
I think the make a new function idea will work. The only reason I used
lambdas is because Kent suggested them when I pointed out that you can't
call the functions when defining them in the buttons, so you can't pass
parameters. Or so I thought. Yes, I think your idea will work. What am I
talking about? Of course your idea will work. ;-)
Thanks,
Jacob Schmidt
More information about the Tutor
mailing list