[Tutor] My best GUI app so far.

Kent Johnson kent37 at tds.net
Tue Jan 11 11:55:58 CET 2005


Jacob S. wrote:
> 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.
> 
> Oh, by the way, I'm not trying to make you go blind, it's just I don't tend
> to worry about *my* eyesight and in my subconcious I'm just not considerate
> enough to implement healthy eye habits. sorry...

I exagerated. I don't really think it is an eye health issue. I do think that careful use of white 
space can greatly enhance the readability of your code, which is good for others who might be 
reviewing it, and also for you when you come back to it later.

> Okay, Jacob, reality check -- it's after midnight...
> Here's the code.

You can simplify the way you add in key bindings.

First, you don't need to add the *args argument to all the handlers. You know what the argument will 
be (an event object) and you don't want to use it, so you can just throw it away in the lambdas. 
Kind of the opposite of what I did with adddigit - instead of adding an argument, you throw one away. So
   lambda *x: self.adddigit('0',x)
becomes
   lambda event: self.adddigit('0')

There is no need for an extra loop to set up the key bindings. You have all the information you need 
in makeButton. It could read like this:

      def makeButton(self, text, command, row, column):
          button = Button(self,text=text,command=command,width=4,height=3)
          button.grid(row=row,column=column)
          if len(text) == 1
              self.bind_all(text,lambda x: command())

Or you could pass in an optional argument for the key text so you can bind to 'ON/C' etc.

Nice work, by the way :-)

Kent

> 
> ###Start of Calculator.py###
> from __future__ import division
> from Tkinter import *
> 
> class Application(Frame):
>      def ctb(self):
>          if self.shouldblank:
>              self.distext.set('')
>              self.shouldblank = False
> 
>      def adddigit(self, digit, *args):
>          self.ctb()
>          self.distext.set(self.distext.get()+digit)
> 
>      def adddigitdot(self,*args):
>          if not self.distext.get().count('.'):
>              self.ctb()
>              self.distext.set(self.distext.get()+'.')
> 
>      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
> 
>      def add(self,*args):
>          self.handleOperator('+')
> 
>      def subtract(self,*args):
>          self.handleOperator('-')
> 
>      def multiply(self,*args):
>          self.handleOperator('*')
> 
>      def divide(self,*args):
>          self.handleOperator('/')
> 
> 
>      def handleOperator(self, oper):
>          if self.action:
>              self.equal()
>              self.oldnum = self.distext.get()
>              self.action = oper
>          else:
>              self.oldnum = self.distext.get()
>              self.action = oper
>              self.shouldblank = True
> 
> 
>      def clear(self):
>          self.action = ''
>          self.oldnum = '0'
>          self.distext.set('0')
>          self.shouldblank = True
> 
>      def memrecall(self):
>          self.distext.set(self.memory)
>          self.shouldblank = True
> 
>      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
> 
> 
>      def makeButton(self, text, command, row, column):
>          button = Button(self,text=text,command=command,width=4,height=3)
>          button.grid(row=row,column=column)
> 
> 
>      def createWidgets(self):
>          self.distext = StringVar()
>          self.display
> =Entry(self,textvariable=self.distext,width=22,justify='right')
>          self.display.grid(row=0,column=1,columnspan=4)
> 
>          self.makeButton(text='0',command=self.bl[0],row=5,column=1)
>          self.makeButton(text='1',command=self.bl[1],row=4,column=1)
>          self.makeButton(text='2',command=self.bl[2],row=4,column=2)
>          self.makeButton(text='3',command=self.bl[3],row=4,column=3)
>          self.makeButton(text='4',command=self.bl[4],row=3,column=1)
>          self.makeButton(text='5',command=self.bl[5],row=3,column=2)
>          self.makeButton(text='6',command=self.bl[6],row=3,column=3)
>          self.makeButton(text='7',command=self.bl[7],row=2,column=1)
>          self.makeButton(text='8',command=self.bl[8],row=2,column=2)
>          self.makeButton(text='9',command=self.bl[9],row=2,column=3)
>          self.makeButton(text='.',command=self.adddigitdot,row=5,column=2)
>          self.makeButton(text="=",command=self.equal,row=5,column=3)
>          self.makeButton(text='+',command=self.add,row=5,column=4)
>          self.makeButton(text="-",command=self.subtract,row=4,column=4)
>          self.makeButton(text='x',command=self.multiply,row=3,column=4)
>          self.makeButton(text='/',command=self.divide,row=2,column=4)
>          self.makeButton(text='ON/C',command=self.clear,row=1,column=4)
>          self.makeButton(text='MRC',command=self.memrecall,row=1,column=1)
>          self.makeButton(text="M-",command=self.memminus,row=1,column=2)
>          self.makeButton(text="M+",command=self.memplus,row=1,column=3)
> 
> 
>      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))
>          self.oldnum = '0'
>          self.memory = '0'
>          self.action = ''
>          self.shouldblank = True
>          self.createWidgets()
> 
> app = Application()
> app.mainloop()
> ###End of Calculator.py###
> 
> 
> 
> 
> 
>>My suggestions:
>>
>>- How about some white space! Yikes, you trying to make me go blind?
>>- You don't actually need to save all the buttons to attributes.
>>- Much of the button creation is common - I made a button creating
> 
> function and call that.
> 
>>   Copy / paste is not your friend, it is a danger! Refactor instead of
> 
> copying!
> 
>>- Now you can get rid of all the adddigit functions by making a single
> 
> generic adddigit
> 
>>   and calling it with a lambda.
>>- The four operation functions are identical except the operator; factor
> 
> out handleOperation()
> 
>>   (Copy / paste is not your friend!)
>>
>>- I didn't put the button generation in a loop, once you strip out the
> 
> repeated arguments what is
> 
>>left is pretty readable as is.
>>
>>Kent
>>
>>
>>from __future__ import division
>>from Tkinter import *
>>
>>class Application(Frame):
>>     def ctb(self):
>>         if self.shouldblank:
>>             self.distext.set('')
>>             self.shouldblank = False
>>
>>     def adddigit(self, digit):
>>         self.ctb()
>>         self.distext.set(self.distext.get()+digit)
>>
>>     def adddigitdot(self):
>>         if not self.distext.get().count('.'):
>>             self.ctb()
>>             self.distext.set(self.distext.get()+'.')
>>
>>     def equal(self):
>>         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
>>
>>     def add(self):
>>         self.handleOperator('+')
>>
>>     def subtract(self):
>>         self.handleOperator('-')
>>
>>     def multiply(self):
>>         self.handleOperator('*')
>>
>>     def divide(self):
>>         self.handleOperator('/')
>>
>>
>>     def handleOperator(self, oper):
>>         if self.action:
>>             self.equal()
>>             self.oldnum = self.distext.get()
>>             self.action = oper
>>         else:
>>             self.oldnum = self.distext.get()
>>             self.action = oper
>>             self.shouldblank = True
>>
>>
>>     def clear(self):
>>         self.action = ''
>>         self.oldnum = '0'
>>         self.distext.set('0')
>>         self.shouldblank = True
>>
>>     def memrecall(self):
>>         self.distext.set(self.memory)
>>         self.shouldblank = True
>>
>>     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
>>
>>
>>     def makeButton(self, text, command, row, column):
>>         button = Button(self,text=text,command=command,width=4,height=3)
>>         button.grid(row=row,column=column)
>>
>>
>>     def createWidgets(self):
>>         self.distext = StringVar()
>>         self.display
> 
> =Entry(self,textvariable=self.distext,width=22,justify='right')
> 
>>         self.display.grid(row=0,column=1,columnspan=4)
>>
>>         self.makeButton(text='0',command=lambda:
> 
> self.adddigit('0'),row=5,column=1)
> 
>>         self.makeButton(text='1',command=lambda:
> 
> self.adddigit('1'),row=4,column=1)
> 
>>         self.makeButton(text='2',command=lambda:
> 
> self.adddigit('2'),row=4,column=2)
> 
>>         self.makeButton(text='3',command=lambda:
> 
> self.adddigit('3'),row=4,column=3)
> 
>>         self.makeButton(text='4',command=lambda:
> 
> self.adddigit('4'),row=3,column=1)
> 
>>         self.makeButton(text='5',command=lambda:
> 
> self.adddigit('5'),row=3,column=2)
> 
>>         self.makeButton(text='6',command=lambda:
> 
> self.adddigit('6'),row=3,column=3)
> 
>>         self.makeButton(text='7',command=lambda:
> 
> self.adddigit('7'),row=2,column=1)
> 
>>         self.makeButton(text='8',command=lambda:
> 
> self.adddigit('8'),row=2,column=2)
> 
>>         self.makeButton(text='9',command=lambda:
> 
> self.adddigit('9'),row=2,column=3)
> 
>>         self.makeButton(text='.',command=self.adddigitdot,row=5,column=2)
>>         self.makeButton(text="=",command=self.equal,row=5,column=3)
>>         self.makeButton(text='+',command=self.add,row=5,column=4)
>>         self.makeButton(text="-",command=self.subtract,row=4,column=4)
>>         self.makeButton(text='x',command=self.multiply,row=3,column=4)
>>         self.makeButton(text='/',command=self.divide,row=2,column=4)
>>         self.makeButton(text='ON/C',command=self.clear,row=1,column=4)
>>         self.makeButton(text='MRC',command=self.memrecall,row=1,column=1)
>>         self.makeButton(text="M-",command=self.memminus,row=1,column=2)
>>         self.makeButton(text="M+",command=self.memplus,row=1,column=3)
>>
>>
>>     def __init__(self, master=None):
>>         Frame.__init__(self,master)
>>         self.master.title("Calculator by Jacob, Inc.")
>>         self.pack(fill='both')
>>         self.oldnum = '0'
>>         self.memory = '0'
>>         self.action = ''
>>         self.shouldblank = True
>>         self.createWidgets()
>>
>>app = Application()
>>app.mainloop()
>>### End of Calculator.py ###
>>
>>
>>
>>Jacob S. wrote:
>>
>>>Here's the code. If you have time, look it over and give me suggestions
> 
> for
> 
>>>improvement!
>>>(big toothy grin)
>>>
>>>### Start of Calculator.py ###
>>>from __future__ import division
>>>from Tkinter import *
>>>
>>>class Application(Frame):
>>>    def ctb(self):
>>>        if self.shouldblank:
>>>            self.distext.set('')
>>>            self.shouldblank = False
>>>    def adddigit0(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'0')
>>>    def adddigit1(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'1')
>>>    def adddigit2(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'2')
>>>    def adddigit3(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'3')
>>>    def adddigit4(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'4')
>>>    def adddigit5(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'5')
>>>    def adddigit6(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'6')
>>>    def adddigit7(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'7')
>>>    def adddigit8(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'8')
>>>    def adddigit9(self):
>>>        self.ctb()
>>>        self.distext.set(self.distext.get()+'9')
>>>    def adddigitdot(self):
>>>        if not self.distext.get().count('.'):
>>>            self.ctb()
>>>            self.distext.set(self.distext.get()+'.')
>>>    def equal(self):
>>>        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
>>>    def add(self):
>>>        if self.action:
>>>            self.equal()
>>>            self.oldnum = self.distext.get()
>>>            self.action = '+'
>>>        else:
>>>            self.oldnum = self.distext.get()
>>>            self.action = '+'
>>>            self.shouldblank = True
>>>    def subtract(self):
>>>        if self.action:
>>>            self.equal()
>>>            self.oldnum = self.distext.get()
>>>            self.action = '-'
>>>        else:
>>>            self.oldnum = self.distext.get()
>>>            self.action = '-'
>>>            self.shouldblank = True
>>>    def multiply(self):
>>>        if self.action:
>>>            self.equal()
>>>            self.oldnum = self.distext.get()
>>>            self.action = '*'
>>>        else:
>>>            self.oldnum = self.distext.get()
>>>            self.action = '*'
>>>            self.shouldblank = True
>>>    def divide(self):
>>>        if self.action:
>>>            self.equal()
>>>            self.oldnum = self.distext.get()
>>>            self.action = '/'
>>>        else:
>>>            self.oldnum = self.distext.get()
>>>            self.action = '/'
>>>            self.shouldblank = True
>>>    def clear(self):
>>>        self.action = ''
>>>        self.oldnum = '0'
>>>        self.distext.set('0')
>>>        self.shouldblank = True
>>>    def memrecall(self):
>>>        self.distext.set(self.memory)
>>>        self.shouldblank = True
>>>    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
>>>
>>>
>>>
>>>    def createWidgets(self):
>>>        self.distext = StringVar()
>>>        self.display =
>>>Entry(self,textvariable=self.distext,width=22,justify='right')
>>>        self.display.grid(row=0,column=1,columnspan=4)
>>>
>>>        self.b0 =
>>>Button(self,text='0',command=self.adddigit0,width=4,height=3)
>>>        self.b0.grid(row=5,column=1)
>>>        self.b1 =
>>>Button(self,text='1',command=self.adddigit1,width=4,height=3)
>>>        self.b1.grid(row=4,column=1)
>>>        self.b2 =
>>>Button(self,text='2',command=self.adddigit2,width=4,height=3)
>>>        self.b2.grid(row=4,column=2)
>>>        self.b3 =
>>>Button(self,text='3',command=self.adddigit3,width=4,height=3)
>>>        self.b3.grid(row=4,column=3)
>>>        self.b4 =
>>>Button(self,text='4',command=self.adddigit4,width=4,height=3)
>>>        self.b4.grid(row=3,column=1)
>>>        self.b5 =
>>>Button(self,text='5',command=self.adddigit5,width=4,height=3)
>>>        self.b5.grid(row=3,column=2)
>>>        self.b6 =
>>>Button(self,text='6',command=self.adddigit6,width=4,height=3)
>>>        self.b6.grid(row=3,column=3)
>>>        self.b7 =
>>>Button(self,text='7',command=self.adddigit7,width=4,height=3)
>>>        self.b7.grid(row=2,column=1)
>>>        self.b8 =
>>>Button(self,text='8',command=self.adddigit8,width=4,height=3)
>>>        self.b8.grid(row=2,column=2)
>>>        self.b9 =
>>>Button(self,text='9',command=self.adddigit9,width=4,height=3)
>>>        self.b9.grid(row=2,column=3)
>>>        self.bdot =
>>>Button(self,text='.',command=self.adddigitdot,width=4,height=3)
>>>        self.bdot.grid(row=5,column=2)
>>>        self.equalsign =
>>>Button(self,text="=",command=self.equal,width=4,height=3)
>>>        self.equalsign.grid(row=5,column=3)
>>>        self.plussign =
>>>Button(self,text='+',command=self.add,width=4,height=3)
>>>        self.plussign.grid(row=5,column=4)
>>>        self.minussign =
>>>Button(self,text="-",command=self.subtract,width=4,height=3)
>>>        self.minussign.grid(row=4,column=4)
>>>        self.timessign =
>>>Button(self,text='x',command=self.multiply,width=4,height=3)
>>>        self.timessign.grid(row=3,column=4)
>>>        self.divsign =
>>>Button(self,text='/',command=self.divide,width=4,height=3)
>>>        self.divsign.grid(row=2,column=4)
>>>        self.clearb =
>>>Button(self,text='ON/C',command=self.clear,width=4,height=3)
>>>        self.clearb.grid(row=1,column=4)
>>>        self.mrc =
>>>Button(self,text='MRC',command=self.memrecall,width=4,height=3)
>>>        self.mrc.grid(row=1,column=1)
>>>        self.mm =
>>>Button(self,text="M-",command=self.memminus,width=4,height=3)
>>>        self.mm.grid(row=1,column=2)
>>>        self.mp =
>>>Button(self,text="M+",command=self.memplus,width=4,height=3)
>>>        self.mp.grid(row=1,column=3)
>>>
>>>
>>>    def __init__(self, master=None):
>>>        Frame.__init__(self,master)
>>>        self.master.title("Calculator by Jacob, Inc.")
>>>        self.pack(fill='both')
>>>        self.oldnum = '0'
>>>        self.memory = '0'
>>>        self.action = ''
>>>        self.shouldblank = True
>>>        self.createWidgets()
>>>
>>>app = Application()
>>>app.mainloop()
>>>### End of Calculator.py ###
>>>
>>>Is there some way that I could loop over those button definitions?
>>>
>>>proposal - code
>>>
>>>a = """
>>>def adddigit%s(self):
>>>    self.ctb()
>>>    self.distext.set(self.distext.get()+'%s')
>>>"""
>>>for i in range(10):
>>>    exec a % (i,i)
>>>
>>>Pretty cool, huh? I've got my thinking cap on today!
>>>
>>>Thanks in advance for suggestions,
>>>Jacob
>>>
>>>
>>>_______________________________________________
>>>Tutor maillist  -  Tutor at python.org
>>>http://mail.python.org/mailman/listinfo/tutor
>>>
>>
>>_______________________________________________
>>Tutor maillist  -  Tutor at python.org
>>http://mail.python.org/mailman/listinfo/tutor
>>
>>
> 
> 
> 


More information about the Tutor mailing list