'Tkinter: Cannot edit entry widget after 2nd selection

I've made a simple GUI for placing objects in RoboDK via a Python script using Tkinter.

Essentailly, the user selects an object using the btnSelect button, which then updates the entry widgets with its coordinates (x, y, z, then Euler rotation). The user can then edit the entry widgets then select the "Move object" button (or btnMove in the code) to move the object to the new position. However, when selecting an object for the second time, the entry fields cannot be edited without selecting a new object.

from tkinter.constants import DISABLED, NORMAL, CENTER, END
from typing import *
import tkinter as tk
import threading

from robolink import *    # RoboDK API
from robodk import *      # Robot toolbox

X_MAX = 500
X_MIN = 0
Y_MAX = 300
Y_MIN = -360
ROTZ_MAX = 180
ROTZ_MIN = -180

# Keep track of selected item
obj = None

def main():
    rdk = Robolink()
    window = buildGUI(rdk)
    window.mainloop()

def buildGUI(rdk: Robolink) -> tk.Tk:
    window = tk.Tk()
    canvas = tk.Canvas(window, width=200)
    canvas.grid(columnspan=3, rowspan=14)

    # Set the window title (must be unique for the docking to work, try to be creative)
    window_title = 'Move object window'
    window.title(window_title)

    title = tk.Label(window, text="Move Object", font="none 14 bold")
    title.grid(columnspan=3, column=0, row=0)

    # Delete the window when we close it
    window.protocol("WM_DELETE_WINDOW", lambda: onClose(window))

    deadspace1 = tk.Label(text="")
    deadspace1.grid(columnspan=3, column=0, row=1)

    selectText = tk.StringVar()
    btnSelect = tk.Button(window, textvariable=selectText, height=2, width=0,
                        bg="#bbbbbb", fg='white', justify=CENTER)
    selectText.set("Select object")
    btnSelect.grid(column=1, row=2)

    deadspace2 = tk.Label("")
    deadspace2.grid(columnspan=3, column=0, row=3)

    objName = tk.StringVar()
    objLabel = tk.Label(window, textvariable=objName, font="none 12 bold")
    objName.set("None Selected")
    objLabel.grid(columnspan=3, column=0, row=4)

    deadspace2 = tk.Label("")
    deadspace2.grid(columnspan=3, column=0, row=5)

    # Position options

    xLabel = tk.Label(window, text="x: ", font='none 12')
    xLabel.grid(column=0, row=6)
    xEntry = tk.Entry(window, width=10)
    xEntry.grid(columnspan=2, column=1, row=6)

    yLabel = tk.Label(window, text="y: ", font='none 12')
    yLabel.grid(column=0, row=7)
    yEntry = tk.Entry(window, width=10)
    yEntry.grid(columnspan=2, column=1, row=7)

    zLabel = tk.Label(window, text="z: ", font='none 12')
    zLabel.grid(column=0, row=8)
    zEntry = tk.Entry(window, width=10)
    zEntry.grid(columnspan=2, column=1, row=8)

    # Rotation options

    rxLabel = tk.Label(window, text="rx: ", font='none 12')
    rxLabel.grid(column=0, row=9)
    rxEntry = tk.Entry(window, width=10)
    rxEntry.grid(columnspan=2, column=1, row=9)

    ryLabel = tk.Label(window, text="ry: ", font='none 12')
    ryLabel.grid(column=0, row=10)
    ryEntry = tk.Entry(window, width=10)
    ryEntry.grid(columnspan=2, column=1, row=10)

    rzLabel = tk.Label(window, text="rz: ", font='none 12')
    rzLabel.grid(column=0, row=11)
    rzEntry = tk.Entry(window, width=10)
    rzEntry.grid(columnspan=2, column=1, row=11)

    entries = [xEntry, yEntry, zEntry, rzEntry, ryEntry, rxEntry]

    deadspace3 = tk.Label(text="")
    deadspace3.grid(columnspan=3, column=0, row=12)

    btnMove = tk.Button(window, text="Move object", height=2, width=0,
                        bg="#bbbbbb", fg='white', justify=CENTER,
                        command=lambda: moveObject(entries))
    btnMove.grid(column=1, row=13)

    selectCallback = lambda: select_item(rdk, selectText, objName, entries)
    btnSelect['command'] = selectCallback

    EmbedWindow(window_title)
    return window

# Close the window
def onClose(window: tk.Tk):
    window.destroy()
    quit(0)

def select_item(rdk: Robolink, selectText: tk.StringVar, objName: tk.Label,
                entries: List[tk.Entry]):
    def thread_btnSelect():
        selectText.set("Waiting...")
        item = rdk.ItemUserPick('Select an item', ITEM_TYPE_OBJECT)

        if item.Valid():
            global obj
            obj = item
            objName.set(item.Name())
            updateObjectPosition(item, entries)
        
        selectText.set("Select object")
        
    # Prevent RoboDK from freezing
    threading.Thread(target=thread_btnSelect).start()

def updateObjectPosition(item: Item, entries: List[tk.Entry]):
    pose = item.Pose()
    coords = Pose_2_KUKA(pose)

    for entry, coord in zip(entries, coords):
        entry.delete(0, END)
        entry.insert(0, str(coord))

def moveObject(entries: tk.Entry):
    global obj

    if obj is None:
        ShowMessage("No object selected")
        return

    try:
        coords = getCoords(entries)
        obj.setPose(KUKA_2_Pose(coords))
    except Exception as err:
        ShowMessage(str(err))

def getCoords(entries: List[tk.Entry]) -> list:
    coords = [0] * 6

    for i, entry in enumerate(entries):
        coords[i] = float(entry.get())
    
    return coords

if __name__ == "__main__":
    main()

enter image description here



Solution 1:[1]

Using a tkinter.Variable such as tkinter.DoubleVar with your entries should fix this issue. You can also use tkinter.DoubleSpin for convenience.

x, y, z, rx, ry, rz = Pose_2_TxyzRxyz(item.Pose())
xVar = tk.DoubleVar(value=x)
xEntry = tk.Spinbox(window, textvariable=xVar, format="%.2f", from_=-9999999, to=9999999)
xVal = xVal.get()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 sambert20