Search code examples
pythonpython-3.xtkinterclone

Clone Label, Button and Textbox by pressing a button


I'm trying to write a simple stock counting program. Created a line where you can put the article name and add, take away or reset the value. I would like to add a button that will clone the whole row and paste a new one below it. I could increase the number of counters and just copy and paste part of the code with the new counter number. Not very convenient to adjust the code when a new entry will arrive. Here is my program so far no clue how to approach it, any help much appreciated:

import tkinter
import sys

root = tkinter.Tk()
root.geometry("800x800")
root.title("Counter")
counter = tkinter.IntVar()


def onClick(event=None):
    counter.set(counter.get() + 1)

tkinter.Label(root, textvariable=counter).place(x=480, y=20)


tkinter.Button(root, text="+1", command=onClick, fg="dark green", bg = "white").place(x=520, y=20)
def onClick(event=None):
    counter.set(counter.get() + 10)
tkinter.Button(root, text="+10", command=onClick, fg="dark green", bg = "white").place(x=560, y=20)

def onClick(event=None):
    counter.set(counter.get() - 1)


tkinter.Button(root, text="-1", command=onClick, fg="dark red", bg = "white").place(x=440, y=20)
def onClick(event=None):
    counter.set(counter.get() - 10)
tkinter.Button(root, text="-10", command=onClick, fg="dark red", bg = "white").place(x=400, y=20)


def onClick(event=None):
    counter.set(counter.get()-counter.get()) 

tkinter.Button(root, text="Reset", command=onClick, fg="Black", bg = "white").place(x=620, y=20)



T = tkinter.Text(root, height=1, width=40)

    T.place(x=10, y=20)

root.mainloop()

Solution

  • To clone you would have to first keep widgets in (global) variables.

    But instead of cloning you should create function which creates new Label, Text, Button when you press button.

    But instead of creating Label, Text, Button you can also use tkinter.Frame to define own widget - ie. class Row - and then you have to only create Row when you press button. Widget can also has own counter to keep value only for this row.


    I define class Row which use Frame to keep Label, Text, Button and IntVar. And I can create new Row everytime when I press button "Add row"

    I also use pack() instead of place() so I don't have to calculate x, y

    I keep rows on list so I can get counter from every row and sum them.

    import tkinter
    import sys
    
    # --- classes ----
    
    class Row(tkinter.Frame):
    
        def __init__(self, parent, row_number):
            super().__init__(parent)
    
            self.counter = tkinter.IntVar()
    
            tkinter.Label(self, text=str(row_number)).pack(side='left')
    
            t = tkinter.Text(self, height=1, width=40)
            t.pack(side='left')
    
            tkinter.Button(self, text="+1",  command=lambda:self.add(1)).pack(side='left')
            tkinter.Button(self, text="+10", command=lambda:self.add(10)).pack(side='left')
    
            tkinter.Label(self, textvariable=self.counter).pack(side='left')
    
            tkinter.Button(self, text="-1",  command=lambda:self.add(-1)).pack(side='left')
            tkinter.Button(self, text="-10", command=lambda:self.add(-10)).pack(side='left')
            tkinter.Button(self, text="Reset", command=self.set).pack(side='left')
    
        def add(self, value):
            self.counter.set(self.counter.get() + value)
    
        def set(self, value=0):
            self.counter.set(value) 
    
    # --- functions ---
    
    def add_row():
        number = len(all_rows) + 1
        row = Row(root, number)
        row.pack()
        all_rows.append(row)
    
    def sum_rows():
        # get counters from all rows and sum them
        label_sum['text'] = sum(row.counter.get() for row in all_rows)
    
    # --- main ---
    
    all_rows = []
    
    root = tkinter.Tk()
    root.geometry("800x800")
    root.title("Counter")
    
    tkinter.Button(root, text='Add row', command=add_row).pack()
    tkinter.Button(root, text='Sum rows', command=sum_rows).pack()
    
    label_sum = tkinter.Label(root, text='')
    label_sum.pack()
    
    add_row() # one row at start
    
    root.mainloop()