最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Tkinter widget not being tracked properly - Stack Overflow

programmeradmin3浏览0评论

In this code, hitting the weightpos/weightneg buttons should change the value by 5. When using the original buttons that arent created from the addrow() function, it works fine, but using the created buttons it only increments by 1.

Code:

import tkinter as tk
from tkinter import ttk
from tkinter import *
from PIL import ImageTk, Image
import os
weight_entry='0'
root=tk.Tk()

movement_chest = ["Flat Bench", 'Incline Bench', 'Decline Bench', 'Flat DB Press', "Incline DB Press", 'High-Low Cable Raise', "Low-High Cable Raise",]
files_movement_chest = ["flat_bench.gif", 'incline_bench.gif', 'decline_bench.gif', 'db_flat.gif', "db_incline.gif", 'high_low_cable.gif', "low_high_cable.gif",]
row_counter=1

class Toolbar(Frame):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.master.title("Workout Tracker")
        menubar = Menu(self.master)
        self.master.config(menu=menubar)

        fileMenu = Menu(menubar)
        fileMenu.add_command(label="Exit", command=self.onExit)
        menubar.add_cascade(label="File", menu=fileMenu)

    def onExit(self):
        self.quit()

# Create a variable to hold the image reference
image_label = None
img = None

# Set button function
def plusminus(increment, entry, row_counter):
    if entry.get() == '': 
        new_value = 0
    else:
        current_value = int(entry.get())  # Get the current value from the entry widget
        if entry == weight_entry:
            # If the entry widget is the weight field, change the value by 5
            if increment:
                new_value = current_value + 5
            else:
                new_value = current_value - 5
        else:
            # For other fields like sets, change by 1
            if increment:
                new_value = current_value + 1
                print(entry)
            else:
                new_value = current_value - 1
        
    if new_value < 0:
        new_value = 0

    entry.delete(0, tk.END)  # Clear the entry
    entry.insert(0, str(new_value))  # Insert the new value


def on_combobox_select(event=None, row_counter=0):  # Allow for manual function call (no event)
    global image_label, img  # Use global variables
    motion = Dropdown.get()
    
    # Remove the old image if it exists
    if image_label:
        image_label.destroy()
    
    # Find the index of the selected motion in movement_chest
    if motion in movement_chest:
        index = movement_chest.index(motion)  # Get the index of the selected motion
        
        # Get the corresponding filename for the selected motion
        img_path = files_movement_chest[index]
        
        # Check if the image file exists and load it
        if os.path.exists(img_path):
            img = ImageTk.PhotoImage(Image.open(img_path))
        else:
            print(f"Image file '{img_path}' not found.")
            img = None
        
        # Only add the label if the image is loaded correctly
        if img:
            image_label = Label(root, image=img)
            image_label.grid(row=row_counter, column=0, columnspan=3, sticky="nsew")  # Use grid here with spanning
    else:
        img_path = "workout.gif"
        if os.path.exists(img_path):
            img = ImageTk.PhotoImage(Image.open(img_path))
        else:
            print("Image file 'workout.gif' not found.")
            img = None
        if img:  # Only add the label if the image is loaded correctly
            panel = Label(root, image=img)
            panel.grid(row=row_counter, column=0, columnspan=3, sticky="nsew")  # Use grid here with spanning

# Create a function that adds a new row of widgets
def add_row():
    global row_counter
    
    # Add row-specific widgets like motion dropdown, weight entry, set entry, etc.
    label_dropdown = tk.Label(root, text='Motion')
    label_dropdown.grid(row=row_counter, column=4, pady=(15, 0), sticky='n')

    # Create a new row of widgets for motion
    motion_dropdown = ttk.Combobox(
        root,
        values=movement_chest,
        state="readonly",
    )
    motion_dropdown.grid(row=row_counter, column=4, padx=5, pady=5)
    motion_dropdown.bind("<<ComboboxSelected>>", lambda event: on_combobox_select(event, row_counter))
    
    # Create weight entry for this row
    label_weight = tk.Label(root, text='Weight')
    label_weight.grid(row=row_counter, column=5, pady=(15, 0), sticky='n')

    weight_entry = ttk.Entry(root)
    weight_entry.grid(row=row_counter, column=5, padx=5, pady=5)
    
    # Buttons for incrementing/decrementing weight
    btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, row_counter)) 
    btn_weightpos.grid(row=row_counter, column=5, padx=(0, 19), pady=(0, 8), sticky='s')
    
    btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, row_counter))
    btn_weightneg.grid(row=row_counter, column=5, padx=(19, 0), pady=(0, 8), sticky='s')
    
    # Create set entry for this row
    label_set = tk.Label(root, text='Sets')
    label_set.grid(row=row_counter, column=6, pady=(15, 58), sticky='n')

    set_entry = ttk.Entry(root)
    set_entry.grid(row=row_counter, column=6, padx=5, pady=5)
    
    # Buttons for incrementing/decrementing sets
    btn_setpos = tk.Button(root, text='+', command=lambda: plusminus(True, set_entry, row_counter)) 
    btn_setpos.grid(row=row_counter, column=6, padx=(0, 19), pady=(0, 8), sticky='s')
    
    btn_setneg = tk.Button(root, text='-', command=lambda: plusminus(False, set_entry, row_counter))
    btn_setneg.grid(row=row_counter, column=6, padx=(19, 0), pady=(0, 8), sticky='s')

    # Create an image label placeholder for each row (so that we can update it later)
    image_label = None
    img = None

    # Increment row_counter for the next row
    row_counter += 1

# Dropdown (Combobox)
Dropdown = ttk.Combobox(
    root,
    values=movement_chest,
    state="readonly",
)
label_dropdown = tk.Label(root, text='Motion')
label_dropdown.grid(row=0, column=4, pady=(70, 0), sticky='n')
Dropdown.grid(row=0, column=4, padx=5, pady=5)
Dropdown.bind("<<ComboboxSelected>>", on_combobox_select)

# Weight field
label_weight = tk.Label(root, text='Weight')
weight_entry = ttk.Entry(root, text='Weight')
label_weight.grid(row=0, column=5, pady=(70, 0), sticky='n')
weight_entry.grid(row=0, column=5, padx=5, pady=5)
# button to increment the weight value
btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, 0))  # Using lambda to pass argument
btn_weightpos.grid(row=0, column=5, padx=(0, 19), pady=(0, 65), sticky='s')
# button to decrement the weight value
btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, 0))
btn_weightneg.grid(row=0, column=5, padx=(19, 0), pady=(0, 65), sticky='s')

# Set field
label_set = tk.Label(root, text='Sets')
set_entry = ttk.Entry(root, text='Sets')
label_set.grid(row=0, column=6, pady=(70, 0), sticky='n')
set_entry.grid(row=0, column=6, padx=5, pady=5)
# button to increment the set value
btn_setpos = tk.Button(root, text='+', command=lambda: plusminus(True, set_entry, 0))  # Using lambda to pass argument
btn_setpos.grid(row=0, column=6, padx=(0, 19), pady=(0, 65), sticky='s')
# button to decrement the set value
btn_setneg = tk.Button(root, text='-', command=lambda: plusminus(False, set_entry, 0))
btn_setneg.grid(row=0, column=6, padx=(19, 0), pady=(0, 65), sticky='s')

# Manually trigger the combobox selection to load the default image (workout.gif)
on_combobox_select()
# Add "Add Row" button
add_row_button = tk.Button(root, text="Add Row", command=add_row)
add_row_button.grid(row=0, column=7, padx=5, pady=5)

# Initial row setup (first row)

root.mainloop()

I tried using variables instead of weight_entry when calling the function and it did not work

In this code, hitting the weightpos/weightneg buttons should change the value by 5. When using the original buttons that arent created from the addrow() function, it works fine, but using the created buttons it only increments by 1.

Code:

import tkinter as tk
from tkinter import ttk
from tkinter import *
from PIL import ImageTk, Image
import os
weight_entry='0'
root=tk.Tk()

movement_chest = ["Flat Bench", 'Incline Bench', 'Decline Bench', 'Flat DB Press', "Incline DB Press", 'High-Low Cable Raise', "Low-High Cable Raise",]
files_movement_chest = ["flat_bench.gif", 'incline_bench.gif', 'decline_bench.gif', 'db_flat.gif', "db_incline.gif", 'high_low_cable.gif', "low_high_cable.gif",]
row_counter=1

class Toolbar(Frame):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.master.title("Workout Tracker")
        menubar = Menu(self.master)
        self.master.config(menu=menubar)

        fileMenu = Menu(menubar)
        fileMenu.add_command(label="Exit", command=self.onExit)
        menubar.add_cascade(label="File", menu=fileMenu)

    def onExit(self):
        self.quit()

# Create a variable to hold the image reference
image_label = None
img = None

# Set button function
def plusminus(increment, entry, row_counter):
    if entry.get() == '': 
        new_value = 0
    else:
        current_value = int(entry.get())  # Get the current value from the entry widget
        if entry == weight_entry:
            # If the entry widget is the weight field, change the value by 5
            if increment:
                new_value = current_value + 5
            else:
                new_value = current_value - 5
        else:
            # For other fields like sets, change by 1
            if increment:
                new_value = current_value + 1
                print(entry)
            else:
                new_value = current_value - 1
        
    if new_value < 0:
        new_value = 0

    entry.delete(0, tk.END)  # Clear the entry
    entry.insert(0, str(new_value))  # Insert the new value


def on_combobox_select(event=None, row_counter=0):  # Allow for manual function call (no event)
    global image_label, img  # Use global variables
    motion = Dropdown.get()
    
    # Remove the old image if it exists
    if image_label:
        image_label.destroy()
    
    # Find the index of the selected motion in movement_chest
    if motion in movement_chest:
        index = movement_chest.index(motion)  # Get the index of the selected motion
        
        # Get the corresponding filename for the selected motion
        img_path = files_movement_chest[index]
        
        # Check if the image file exists and load it
        if os.path.exists(img_path):
            img = ImageTk.PhotoImage(Image.open(img_path))
        else:
            print(f"Image file '{img_path}' not found.")
            img = None
        
        # Only add the label if the image is loaded correctly
        if img:
            image_label = Label(root, image=img)
            image_label.grid(row=row_counter, column=0, columnspan=3, sticky="nsew")  # Use grid here with spanning
    else:
        img_path = "workout.gif"
        if os.path.exists(img_path):
            img = ImageTk.PhotoImage(Image.open(img_path))
        else:
            print("Image file 'workout.gif' not found.")
            img = None
        if img:  # Only add the label if the image is loaded correctly
            panel = Label(root, image=img)
            panel.grid(row=row_counter, column=0, columnspan=3, sticky="nsew")  # Use grid here with spanning

# Create a function that adds a new row of widgets
def add_row():
    global row_counter
    
    # Add row-specific widgets like motion dropdown, weight entry, set entry, etc.
    label_dropdown = tk.Label(root, text='Motion')
    label_dropdown.grid(row=row_counter, column=4, pady=(15, 0), sticky='n')

    # Create a new row of widgets for motion
    motion_dropdown = ttk.Combobox(
        root,
        values=movement_chest,
        state="readonly",
    )
    motion_dropdown.grid(row=row_counter, column=4, padx=5, pady=5)
    motion_dropdown.bind("<<ComboboxSelected>>", lambda event: on_combobox_select(event, row_counter))
    
    # Create weight entry for this row
    label_weight = tk.Label(root, text='Weight')
    label_weight.grid(row=row_counter, column=5, pady=(15, 0), sticky='n')

    weight_entry = ttk.Entry(root)
    weight_entry.grid(row=row_counter, column=5, padx=5, pady=5)
    
    # Buttons for incrementing/decrementing weight
    btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, row_counter)) 
    btn_weightpos.grid(row=row_counter, column=5, padx=(0, 19), pady=(0, 8), sticky='s')
    
    btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, row_counter))
    btn_weightneg.grid(row=row_counter, column=5, padx=(19, 0), pady=(0, 8), sticky='s')
    
    # Create set entry for this row
    label_set = tk.Label(root, text='Sets')
    label_set.grid(row=row_counter, column=6, pady=(15, 58), sticky='n')

    set_entry = ttk.Entry(root)
    set_entry.grid(row=row_counter, column=6, padx=5, pady=5)
    
    # Buttons for incrementing/decrementing sets
    btn_setpos = tk.Button(root, text='+', command=lambda: plusminus(True, set_entry, row_counter)) 
    btn_setpos.grid(row=row_counter, column=6, padx=(0, 19), pady=(0, 8), sticky='s')
    
    btn_setneg = tk.Button(root, text='-', command=lambda: plusminus(False, set_entry, row_counter))
    btn_setneg.grid(row=row_counter, column=6, padx=(19, 0), pady=(0, 8), sticky='s')

    # Create an image label placeholder for each row (so that we can update it later)
    image_label = None
    img = None

    # Increment row_counter for the next row
    row_counter += 1

# Dropdown (Combobox)
Dropdown = ttk.Combobox(
    root,
    values=movement_chest,
    state="readonly",
)
label_dropdown = tk.Label(root, text='Motion')
label_dropdown.grid(row=0, column=4, pady=(70, 0), sticky='n')
Dropdown.grid(row=0, column=4, padx=5, pady=5)
Dropdown.bind("<<ComboboxSelected>>", on_combobox_select)

# Weight field
label_weight = tk.Label(root, text='Weight')
weight_entry = ttk.Entry(root, text='Weight')
label_weight.grid(row=0, column=5, pady=(70, 0), sticky='n')
weight_entry.grid(row=0, column=5, padx=5, pady=5)
# button to increment the weight value
btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, 0))  # Using lambda to pass argument
btn_weightpos.grid(row=0, column=5, padx=(0, 19), pady=(0, 65), sticky='s')
# button to decrement the weight value
btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, 0))
btn_weightneg.grid(row=0, column=5, padx=(19, 0), pady=(0, 65), sticky='s')

# Set field
label_set = tk.Label(root, text='Sets')
set_entry = ttk.Entry(root, text='Sets')
label_set.grid(row=0, column=6, pady=(70, 0), sticky='n')
set_entry.grid(row=0, column=6, padx=5, pady=5)
# button to increment the set value
btn_setpos = tk.Button(root, text='+', command=lambda: plusminus(True, set_entry, 0))  # Using lambda to pass argument
btn_setpos.grid(row=0, column=6, padx=(0, 19), pady=(0, 65), sticky='s')
# button to decrement the set value
btn_setneg = tk.Button(root, text='-', command=lambda: plusminus(False, set_entry, 0))
btn_setneg.grid(row=0, column=6, padx=(19, 0), pady=(0, 65), sticky='s')

# Manually trigger the combobox selection to load the default image (workout.gif)
on_combobox_select()
# Add "Add Row" button
add_row_button = tk.Button(root, text="Add Row", command=add_row)
add_row_button.grid(row=0, column=7, padx=5, pady=5)

# Initial row setup (first row)

root.mainloop()

I tried using variables instead of weight_entry when calling the function and it did not work

Share Improve this question asked 2 days ago Benjamin WalkerBenjamin Walker 1 New contributor Benjamin Walker is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 1
  • Please edit your question down to a minimal reproducible example, that is, only the code necessary to reproduce the issue. – JRiggles Commented 2 days ago
Add a comment  | 

2 Answers 2

Reset to default 0

You'll need to update your lambdas in add_row to correctly pass in the local weight_entry widget:

btn_weightpos = tk.Button(root, text='+', command=lambda entry=weight_entry: plusminus(True, entry, row_counter)) 
btn_weightpos.grid(row=row_counter, column=5, padx=(0, 19), pady=(0, 8), sticky='s')

btn_weightneg = tk.Button(root, text='-', command=lambda entry=weight_entry: plusminus(False, entry, row_counter))
btn_weightneg.grid(row=row_counter, column=5, padx=(19, 0), pady=(0, 8), sticky='s')

The weight_entry created inside add_row() is not the same as the global created weight_entry, so the checking entry == weight_entry inside plusminus() will be evaluated as False because weight_entry inside plusminus() refers to the global one.

You can create a custom class for the weight entry:

class WeightEntry(ttk.Entry): pass

Then create those weight entry using this custom class:

def add_row():
    ...
    # local one
    weight_entry = WeightEntry(...)
    ...

...

# global one
weight_entry = WeightEntry(...)
...

Then you can use isinstance(entry, WeightEntry) inside plusminus() to check whether entry is an instance of WeightEntry:

def plusminus(increment, entry, row_counter):
    if entry.get() == '':
        new_value = 0
    else:
        ...
        if isinstance(entry, WeightEntry):
            ...
    ...

Another suggestion is to pass the actual value (+5/-5, +1/-1) to the argument increment of plusminus() and add this actual value to the passed entry, i.e. new_value = max(current_value+increment, 0).

发布评论

评论列表(0)

  1. 暂无评论