You are not logged in.

#1 2011-03-11 02:35:19

jtkiv
Member
Registered: 2011-02-07
Posts: 37

python -- improvement of simple program

i've been learning python.  past couple of days i've been working with a GUI for the first time.  things went pretty well, but im sure (very sure) this program has room for improvement.  was wondering if anyone had suggestions/comments.

this program converts to and from multiple capacitor values, decodes color bands on resistors, and also encodes the colors from a value.  in python3 and tkinter/ttk.

KzLS4.png

# Boring, Imbecilic Converter for Electrical Parts <-.
# Thomas Kirkpatrick (jtkiv) ... i thought of this---'

from tkinter import *
from tkinter import ttk

# capacitor converter
def capacitorCalc(*args):
    capInValueEntry.selection_clear()
    capInUnitsValue.selection_clear()
    capOutUnitsValue.selection_clear()
    if capInValue.get() == '' or capInValue.get().isdigit() == False:
        capOutValue.set('')
    else:
        # this is the numerical value!!!
        capInV = float(capInValue.get())
        # these two are the unit (farads, microfarads, etc.) values!!!
        capInU = capInUnitsValue.current()
        capOutU = capOutUnitsValue.current()

        # "mltplr" is multiplied times capInValue (capInV)
        if capInU == capOutU:
            mltplr = 1
        else:
            mltplr = 10**((capOutU - capInU)*3)
        if int(capInV*mltplr) == (capInV*mltplr):
            capOutValue.set(int(capInV*mltplr))
        else:
            capOutValue.set(capInV*mltplr)

# resistor decoder
def resistorDecoder(*args):
    resOne.selection_clear()
    resTwo.selection_clear()
    resMlt.selection_clear()
    resOneIn = resOne.current()
    resTwoIn = resTwo.current()
    resMltIn = resMlt.current()

    if resOneIn == -1:
        resOneIn = ''

    if resTwoIn == -1:
        resTwoIn = ''

    if resMltIn == 1:
        resMltStr = '0'
    elif resMltIn == 2:
        resMltStr = '00'
    elif resMltIn == 3:
        resMltStr = 'k'
    elif resMltIn == 4:
        resMltStr = '0k'
    elif resMltIn == 5:
        resMltStr = '00k'
    elif resMltIn == 6:
        resMltStr = 'M'
    elif resMltIn == 7:
        resMltStr = '0M'
    elif resMltIn == 8:
        resMltStr = '00M'
    elif resMltIn == 9:
        resMltStr = '000M'
    else:
        resMltStr = ''

    resOut.set(str(resOneIn) + str(resTwoIn) + resMltStr + '\u2126')

# resistor encoder
def resistorEncoder(*args):
    resCodEntry.selection_clear()
    rcvList = list(str(resCodValue.get()))
    rcvOut = ''
    for i in rcvList:
        if i.isnumeric() == True:
            rcvOut += i
        elif i.isalpha() == True:
            if i == 'k' or i == 'K':
                rcvOut += '000'
            elif i == 'm' or i == 'M':
                rcvOut += '000000'
            else:
                rcvOut += '?' + i + '?'
        else:
            rcvOut += '?' + i + '?'

    if rcvOut.isnumeric() == False:
        resCodTwo.set('ERROR')
    else:
        rcvListOut = list(rcvOut)

        resistorColors = ['black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'white']
        
        resCodOne.set(resistorColors[(int(rcvListOut[0]))])
        resCodTwo.set(resistorColors[(int(rcvListOut[1]))])
        resCodThr.set(resistorColors[(rcvListOut[2:].count('0'))])

# start of GUI code
root = Tk()
root.title("BICEP")
root.minsize(230, 260)
root.maxsize(230, 0)

# frames
bicepFrame = ttk.Frame(root, padding="4 4 8 8")
bicepFrame.grid(column=0, row=0, sticky="nesw")

# capacitor converter
capacitorLabel = ttk.Label(bicepFrame, text="Capacitor Converter", anchor="center")
capacitorLabel.grid(column=0, row=0, columnspan="3", sticky="we")

capInValue = StringVar()
capInValueEntry = ttk.Entry(bicepFrame, width="8", justify="right", textvariable=capInValue)
capInValueEntry.grid(column=0, row=1, sticky="e")
capInValueEntry.bind('<KeyRelease>', capacitorCalc)

capInUnitsValue = ttk.Combobox(bicepFrame, width="14")
capInUnitsValue['values'] = ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (\u03BCF)', 'nanofarads (nF)', 'picofarads (pF)')
capInUnitsValue.grid(column=1, row=1, columnspan="2", sticky="e")
capInUnitsValue.state(['readonly'])
capInUnitsValue.bind('<<ComboboxSelected>>', capacitorCalc)

capOutValue = StringVar()
resultLabel = ttk.Label(bicepFrame, textvariable=capOutValue, anchor="e", width=8, wraplength=66)
resultLabel.grid(column=0, row=2, sticky="e")

capOutUnitsValue = ttk.Combobox(bicepFrame, width="14")
capOutUnitsValue['values'] = ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (\u03BCF)', 'nanofarads (nF)', 'picofarads (pF)')
capOutUnitsValue.grid(column=1, row=2, columnspan="2", sticky="e")
capOutUnitsValue.state(['readonly'])
capOutUnitsValue.bind('<<ComboboxSelected>>', capacitorCalc)

capSep = ttk.Separator(bicepFrame, orient=HORIZONTAL)
capSep.grid(column=0, row=3, columnspan="3", sticky="we")

# resistor decoder
decoderLabel = ttk.Label(bicepFrame, text="Resistor Decoder", anchor="center")
decoderLabel.grid(column=0, row=4, columnspan="3", sticky="we")

resOne = ttk.Combobox(bicepFrame, width="6")
resOne['values'] = ('black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'white')
resOne.grid(column=0, row=5, sticky="w")
resOne.state(['readonly'])
resOne.bind('<<ComboboxSelected>>', resistorDecoder)

resTwo = ttk.Combobox(bicepFrame, width="6")
resTwo['values'] = ('black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'white')
resTwo.grid(column=1, row=5, sticky="w")
resTwo.state(['readonly'])
resTwo.bind('<<ComboboxSelected>>', resistorDecoder)

resMlt = ttk.Combobox(bicepFrame, width="6")
resMlt['values'] = ('black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'white')
resMlt.grid(column=2, row=5, sticky="e")
resMlt.state(['readonly'])
resMlt.bind('<<ComboboxSelected>>', resistorDecoder)

resOut = StringVar()
resOutLabel = ttk.Label(bicepFrame, textvariable=resOut)
resOutLabel.grid(column=1, row=6)

decoderSep = ttk.Separator(bicepFrame, orient=HORIZONTAL)
decoderSep.grid(column=0, row=7, columnspan="3", sticky="we")

# resistor encoder
encoderLabel = ttk.Label(bicepFrame, text="Resistor Encoder", anchor="center")
encoderLabel.grid(column=0, row=8, columnspan=3, sticky="we")

resCodValue = StringVar()
resCodEntry = ttk.Entry(bicepFrame, width="6", justify="center", textvariable=resCodValue)
resCodEntry.grid(column=1, row=9, sticky="we")
resCodEntry.bind('<KeyRelease>', resistorEncoder)

resCodOne = StringVar()
resCodOneLabel = ttk.Label(bicepFrame, textvariable=resCodOne, anchor="center", width="6")
resCodOneLabel.grid(column=0, row=10, sticky="e")

resCodTwo = StringVar()
resCodTwoLabel = ttk.Label(bicepFrame, textvariable=resCodTwo, anchor="center", width="6")
resCodTwoLabel.grid(column=1, row=10, sticky="we")

resCodThr = StringVar()
resCodThrLabel = ttk.Label(bicepFrame, textvariable=resCodThr, anchor="center", width="6")
resCodThrLabel.grid(column=2, row=10, sticky="w")

# padding for widgets
for child in bicepFrame.winfo_children():
    child.grid_configure(padx=4, pady=4)

# focus
capInValueEntry.focus()

root.mainloop()

Offline

#2 2011-03-11 07:54:18

tomd123
Developer
Registered: 2008-08-12
Posts: 565

Re: python -- improvement of simple program

A circuit simulator might be nice smile

Offline

#3 2011-03-11 15:33:21

Cyrusm
Member
From: Bozeman, MT
Registered: 2007-11-15
Posts: 1,053

Re: python -- improvement of simple program

Just for fun, you could try implementing a display that actually shows the color bands graphically instead of as text (maybe a graphic of a resistor with variable color bands) 
Not really necessary, but it might be a fun exercise that will help you learn some more fun GUI tricks. You may also want to include a display for the color value of the tolerance band.

another nice feature might be a radio button to select either 4 or 5 band resistor markings for small tolerance resistors, for the 4 band, have the graphic of the resistor displayed as tan
with 4 colored bars displaying the value, for the 5 band, have the resistor displayed in light-blue with 5 color bands displaying the value.

here's a crappy drawing....
resistor_color_chart.gif

Also for the capacitor calculator, you should have it output the capacitor code that one might read off of the side of the cap. i.e 2.2uF = 225.
and implement support for pF as well.  You could reuse this same code in implementing a decoder for SMD resistors and inductors, which have a similar coding scheme.

Last edited by Cyrusm (2011-03-11 15:34:21)


Hofstadter's Law:
           It always takes longer than you expect, even when you take into account Hofstadter's Law.

Offline

#4 2011-03-11 16:20:08

jtkiv
Member
Registered: 2011-02-07
Posts: 37

Re: python -- improvement of simple program

i'd probably want to keep the text (for color-troubled people like myself lol), but colors would look pretty.  4th band (and 5th) would throw my grid off a little, but it'd be good to have.  cap value code would be nice.  ill definitely add that.  picofarads are supported.  ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (\u03BCF)', 'nanofarads (nF)', 'picofarads (pF)') are all good.

anyway to have tkinter/ttk use a GTK2+ theme?  (any links to good tutorials/documentation/etc.)

Offline

#5 2011-03-11 21:04:09

jwhendy
Member
Registered: 2010-04-01
Posts: 621

Re: python -- improvement of simple program

Very cool! I really want to learn python -- this inspires me to get on that. I like the idea of a graphical representation, even if it's just a static image showing the various band combinations (e.g. 4 band = 2 for digits, 1 multiplier, 1 tolerance / 5 band = 3 for digits, 1 multiplier, and 1 for tolerance).

As a side note, and don't take this wrongly... when you say that you don't want to do color for "color-troubled people" -- how do such people figure out which colors to input into the converter in the first place? Just curious!

Offline

#6 2011-03-12 13:34:06

jtkiv
Member
Registered: 2011-02-07
Posts: 37

Re: python -- improvement of simple program

lol, its a good question.  comparison to other colors.  some are easy to see (yellow, black), others you just stare at for a while and imagine the rainbow.  smile

Offline

#7 2011-03-12 22:39:25

Dusty
Schwag Merchant
From: Medicine Hat, Alberta, Canada
Registered: 2004-01-18
Posts: 5,986
Website

Re: python -- improvement of simple program

Here's some style tips:



# Boring, Imbecilic Converter for Electrical Parts <-.
# Thomas Kirkpatrick (jtkiv) ... i thought of this---'

from tkinter import *
from tkinter import ttk

# capacitor converter
def capacitorCalc(*args):
    capInValueEntry.selection_clear()
    capInUnitsValue.selection_clear()
    capOutUnitsValue.selection_clear()
    if capInValue.get() == '' or capInValue.get().isdigit() == False:

Since ''.isdigit() is False, and it's better not to check for booleans directly, this would be more elegant:

if not capInValue.get().isdigit():

capOutValue.set('')
    else:
        # this is the numerical value!!!
        capInV = float(capInValue.get())
        # these two are the unit (farads, microfarads, etc.) values!!!
        capInU = capInUnitsValue.current()
        capOutU = capOutUnitsValue.current()

        # "mltplr" is multiplied times capInValue (capInV)
        if capInU == capOutU:
            mltplr = 1
        else:
            mltplr = 10**((capOutU - capInU)*3)
        if int(capInV*mltplr) == (capInV*mltplr):

In that last line, I'm not 100% certain what you're trying to achieve, but I think this is equivalent and more readable:

if (capInV*multplr).is_integer():

capOutValue.set(int(capInV*mltplr))
        else:
            capOutValue.set(capInV*mltplr)


# resistor decoder
def resistorDecoder(*args):
    resOne.selection_clear()
    resTwo.selection_clear()
    resMlt.selection_clear()
    resOneIn = resOne.current()
    resTwoIn = resTwo.current()
    resMltIn = resMlt.current()

    if resOneIn == -1:
        resOneIn = ''

    if resTwoIn == -1:
        resTwoIn = ''

    if resMltIn == 1:
        resMltStr = '0'
    elif resMltIn == 2:
        resMltStr = '00'
    elif resMltIn == 3:
        resMltStr = 'k'
    elif resMltIn == 4:
        resMltStr = '0k'
    elif resMltIn == 5:
        resMltStr = '00k'
    elif resMltIn == 6:
        resMltStr = 'M'
    elif resMltIn == 7:
        resMltStr = '0M'
    elif resMltIn == 8:
        resMltStr = '00M'
    elif resMltIn == 9:
        resMltStr = '000M'
    else:
        resMltStr = ''

I'd replace that great big if with a dictionary:

res_multipliers = {
1: '0',
2: '00',
3: 'k',
<etc>
}
resMltStr = res_multipliers.get(resMltIn, '')

resOut.set(str(resOneIn) + str(resTwoIn) + resMltStr + '\u2126')

I'd use a format string here (see: http://www.python.org/dev/peps/pep-3101/, or my book for a more readable chapter):

resOut.set("{0}{1}{2}\u2126".format(resOneIn, resTwoIn, resMltStr)

# resistor encoder
def resistorEncoder(*args):
    resCodEntry.selection_clear()
    rcvList = list(str(resCodValue.get()))
    rcvOut = ''
    for i in rcvList:

If the only reason you converted rcvList to a string and then a list was to iterate over it, you can remove the list() call three lines up.

if i.isnumeric() == True:
            rcvOut += i
        elif i.isalpha() == True:

Generally, in Python, it's better to use implicit comparisons. So drop the ==True.

if i.isnumeric():

if i == 'k' or i == 'K':
                rcvOut += '000'
            elif i == 'm' or i == 'M':
                rcvOut += '000000'
            else:
                rcvOut += '?' + i + '?'
        else:
            rcvOut += '?' + i + '?'

I'd use string formtting here, too. I suspect your logic can be improved, but I didn't think about it too much. Also, for the letter comparisons, this looks a bit neater:

if i.lower() == k:

if rcvOut.isnumeric() == False:
        resCodTwo.set('ERROR')
    else:
        rcvListOut = list(rcvOut)

I've lost track, but I think rcvOut is a str, correct? You shouldn't need to convert it to a list in order to make the index lookups below. "hello"[1] == e is true.

resistorColors = ['black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'white']
       
        resCodOne.set(resistorColors[(int(rcvListOut[0]))])
        resCodTwo.set(resistorColors[(int(rcvListOut[1]))])
        resCodThr.set(resistorColors[(rcvListOut[2:].count('0'))])

I won't comment on specific parts of the GUI code, but notice you have some duplicate values in there. Things like the resistorColors list should be defined at the start of the file, and you can create functions to create similar looking components if they share attributes. Overall, except for simplifying booleans, it's pretty clean python code, nice first attempt!

Dusty

Offline

#8 2011-03-13 00:33:35

ewaller
Administrator
From: Pasadena, CA
Registered: 2009-07-13
Posts: 20,296

Re: python -- improvement of simple program

And I would consider distilling ResCodeOne, ResCodeTwo, ... into a list of dictionaries.  Roll the ResCodxxxLabel variables into the dictionary.  You can then use iterators.
Also, map the GUI elements into lists where the element numbers correspond to the ResCode elements


Nothing is too wonderful to be true, if it be consistent with the laws of nature -- Michael Faraday
Sometimes it is the people no one can imagine anything of who do the things no one can imagine. -- Alan Turing
---
How to Ask Questions the Smart Way

Offline

#9 2011-03-13 21:07:09

jtkiv
Member
Registered: 2011-02-07
Posts: 37

Re: python -- improvement of simple program

# Boring, Imbecilic Converter for Electrical Parts <-.
# Thomas Kirkpatrick (jtkiv) ... i thought of this---'

from tkinter import *
from tkinter import ttk

# capacitor converter
def capacitorCalc(*args):
    capInValueEntry.selection_clear()
    capInUnitsValue.selection_clear()
    capOutUnitsValue.selection_clear()
    if not capInValue.get().isdigit():
        capOutValue.set('')
    else:
        # this is the numerical value!!!
        capInV = float(capInValue.get())
        # these two are the unit (farads, microfarads, etc.) values!!!
        capInU = capInUnitsValue.current()
        capOutU = capOutUnitsValue.current()

        # "mltplr" is multiplied times capInValue (capInV)
        if capInU == capOutU:
            mltplr = 1
        else:
            mltplr = 10**((capOutU - capInU)*3)

        # value is set on label -- removes decimal place if unneeded.
        if (capInV*mltplr).is_integer():
            capOutValue.set(int(capInV*mltplr))
        else:
            capOutValue.set(capInV*mltplr)

# resistor decoder
def resistorDecoder(*args):
    resOne.selection_clear()
    resTwo.selection_clear()
    resMlt.selection_clear()
    resOneIn = resOne.current()
    resTwoIn = resTwo.current()
    resMltIn = resMlt.current()

    if resOneIn == -1:
        resOneIn = ''

    if resTwoIn == -1:
        resTwoIn = ''

    if resMltIn in range(1, 10):
        resMltStr = resSuffixes[resMltIn]
    else:
        resMltStr = ''

    resOut.set("{0}{1}{2}\u2126".format(resOneIn, resTwoIn, resMltStr))

# resistor encoder
def resistorEncoder(*args):
    resCodEntry.selection_clear()
    rcvList = str(resCodValue.get())
    rcvOut = ''
    for i in rcvList:
        if i.isnumeric():
            rcvOut += i
        elif i.isalpha():
            if i.lower() == 'k':
                rcvOut += '000'
            elif i.lower() == 'm':
                rcvOut += '000000'
            else:
                rcvOut += i
        else:
            rcvOut += i

    if not rcvOut.isnumeric():
        resCodOne.set('')
        resCodTwo.set('ERROR')
        resCodThr.set('')
    else:
        resCodOne.set(avaResValues[(int(rcvOut[0]))])
        resCodTwo.set(avaResValues[(int(rcvOut[1]))])
        resCodThr.set(avaResValues[(rcvOut[2:].count('0'))])

# lists of values
avaCapValues = ['kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (\u03BCF)', 'nanofarads (nF)', 'picofarads (pF)']
avaResValues = ['black', 'brown', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'white']
resSuffixes = ['', '0', '00', 'k', '0k', '00k', 'M', '0M', '00M', '000M']

# start of GUI code
root = Tk()
root.title("BICEP")
root.minsize(230, 260)
root.maxsize(230, 0)

# frames
bicepFrame = ttk.Frame(root, padding="4 4 8 8")
bicepFrame.grid(column=0, row=0, sticky="nesw")

# capacitor converter
capacitorLabel = ttk.Label(bicepFrame, text="Capacitor Converter", anchor="center")
capacitorLabel.grid(column=0, row=0, columnspan="3", sticky="we")

capInValue = StringVar()
capInValueEntry = ttk.Entry(bicepFrame, width="8", justify="right", textvariable=capInValue)
capInValueEntry.grid(column=0, row=1, sticky="e")
capInValueEntry.bind('<KeyRelease>', capacitorCalc)

capInUnitsValue = ttk.Combobox(bicepFrame, width="14")
capInUnitsValue['values'] = avaCapValues
capInUnitsValue.grid(column=1, row=1, columnspan="2", sticky="e")
capInUnitsValue.state(['readonly'])
capInUnitsValue.bind('<<ComboboxSelected>>', capacitorCalc)

capOutValue = StringVar()
resultLabel = ttk.Label(bicepFrame, textvariable=capOutValue, anchor="e", width=8, wraplength=66)
resultLabel.grid(column=0, row=2, sticky="e")

capOutUnitsValue = ttk.Combobox(bicepFrame, width="14")
capOutUnitsValue['values'] = avaCapValues
capOutUnitsValue.grid(column=1, row=2, columnspan="2", sticky="e")
capOutUnitsValue.state(['readonly'])
capOutUnitsValue.bind('<<ComboboxSelected>>', capacitorCalc)

capSep = ttk.Separator(bicepFrame, orient=HORIZONTAL)
capSep.grid(column=0, row=3, columnspan="3", sticky="we")

# resistor decoder
decoderLabel = ttk.Label(bicepFrame, text="Resistor Decoder", anchor="center")
decoderLabel.grid(column=0, row=4, columnspan="3", sticky="we")

resOne = ttk.Combobox(bicepFrame, width="6")
resOne['values'] = avaResValues
resOne.grid(column=0, row=5, sticky="w")
resOne.state(['readonly'])
resOne.bind('<<ComboboxSelected>>', resistorDecoder)

resTwo = ttk.Combobox(bicepFrame, width="6")
resTwo['values'] = avaResValues
resTwo.grid(column=1, row=5, sticky="w")
resTwo.state(['readonly'])
resTwo.bind('<<ComboboxSelected>>', resistorDecoder)

resMlt = ttk.Combobox(bicepFrame, width="6")
resMlt['values'] = avaResValues
resMlt.grid(column=2, row=5, sticky="e")
resMlt.state(['readonly'])
resMlt.bind('<<ComboboxSelected>>', resistorDecoder)

resOut = StringVar()
resOutLabel = ttk.Label(bicepFrame, textvariable=resOut)
resOutLabel.grid(column=1, row=6)

decoderSep = ttk.Separator(bicepFrame, orient=HORIZONTAL)
decoderSep.grid(column=0, row=7, columnspan="3", sticky="we")

# resistor encoder
encoderLabel = ttk.Label(bicepFrame, text="Resistor Encoder", anchor="center")
encoderLabel.grid(column=0, row=8, columnspan=3, sticky="we")

resCodValue = StringVar()
resCodEntry = ttk.Entry(bicepFrame, width="6", justify="center", textvariable=resCodValue)
resCodEntry.grid(column=1, row=9, sticky="we")
resCodEntry.bind('<KeyRelease>', resistorEncoder)

resCodOne = StringVar()
resCodOneLabel = ttk.Label(bicepFrame, textvariable=resCodOne, anchor="center", width="6")
resCodOneLabel.grid(column=0, row=10, sticky="e")

resCodTwo = StringVar()
resCodTwoLabel = ttk.Label(bicepFrame, textvariable=resCodTwo, anchor="center", width="6")
resCodTwoLabel.grid(column=1, row=10, sticky="we")

resCodThr = StringVar()
resCodThrLabel = ttk.Label(bicepFrame, textvariable=resCodThr, anchor="center", width="6")
resCodThrLabel.grid(column=2, row=10, sticky="w")

# padding for widgets
for child in bicepFrame.winfo_children():
    child.grid_configure(padx=4, pady=4)

# focus
capInValueEntry.focus()

root.mainloop()

cut out 11 lines smile

that big if just seemed to be the simplest way to do it.  didnt take up that much space so i went for it.  this is much better.

@Dusty - Thanks for all those suggestions, i implemented most.  used a list instead of dictionary, seeing as each multiplier only had a unique string suffix.

@ewaller - seems that each resCodxxx has positioning, textvariable, w/e different from the others.  wouldn't that minimize the effectiveness of a dictionary?  could you show a little example of what you mean?

Offline

Board footer

Powered by FluxBB