1

I am making an interface to display available com ports as buttons and, when pressed to pass the com name to a variable. Currently I can make dynamic buttons but I am having trouble passing the values to an outside function.

The code creates two buttons for com7 and com8 (current ports on my system) with a dictionary in which i[1]=com7 and i[2]=com8. When I press the button for i[1] I get a Keyerror because the current value of K is now 3, which is not a key in the dictionary. Obviously I want the button to reference the value of K when it was created, not the current value of K.

Essentially I want python to evaluate i[k] at the moment of button creation, not at the moment the button is pressed.

import serial, sys, os, Tkinter
from Tkinter import *

def com():
    global ser
    global p

    def pget(inport):
        print inport
        test.destroy()


    test=Tk()

    try:
        from serial.tools.list_ports import comports
    except ImportError:
        comports = None
        V = ""
        while V is "" :
            for port, desc, hwid in sorted(comports()):
                print "\nAvailable Ports:\n"
                V =sys.stderr.write(' %-10s %s\n' % (port, desc))
            if V is "":
                print "No ports available, plug in COM port and hit ENTER"
                raw_input()
    def Port():
        i={}
        k=1
        if comports:
            l=Label(test, text="Available ports:").pack(side=TOP)
            sys.stderr.write('\n Available ports:\n')
        for port, desc, hwid in sorted(comports()):
            print port
            i[k]=port
            bport=Button(test,text=port+": "+desc,command=lambda: pget(i[k])).pack()
            print k, i[k]
            k+=1
        test.mainloop()
    Port()


com()

1 Answer 1

4

Change

bport=Button(test,text=port+": "+desc,command=lambda: pget(i[k])).pack()

To:

bport=Button(test,text=port+": "+desc,command=lambda port = i[k]: pget(port))
bport.pack()

This forces the value to be sent in to pget() to be set at creation of the Button rather than when mainloop() is initiated.

I also moved pack() to another line (this is a rare case of saving lines not being a good practice). If you were to try to refer back to bport it would be referencing the what the method pack() returns, which is always None.

Separating the method call and the object creation will prevent this.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, you just gave me an epiphany about using lambda with tkinter!
You're welcome, and as a side note... It is generally not a good idea to create the Tkinter object and use pack() on the same expression. This means that bport refers to the pack() method rather than the button object itself.
Small correction: when using pack() (or grid or place for that matter) on the same line as the creation of the button, the variable will not refer to the pack() method, but it will contain whatever pack() returns, which is always None.
I forget the power and syntax of lambda related work. Great answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.