Aru
Aru

Reputation: 352

Tkinter Button to creat entries

I am trying to learn tkinter and got stuck with a little problem. I have created a very simple window that has 2 messages on top "Activity:", "Time Spend:" and a Button "+" on the Bottom of the Window.

from tkinter import *

root = Tk()
root.geometry("274x520+868+240")
root.minsize(120, 1)
root.maxsize(3284, 1068)
root.resizable(0, 0)
root.title("Learning")
root.configure(background="black")


def new_entry():
    n = [n for n in range(30, 490, 30)]

    for i in n:
        e1 = Entry(root)
        e2 = Entry(root)
        e1.place(y=i, width=133)
        e2.place(x=141, y=i, width=137)

        print(e1, e2)
        return e1, e2


b1 = Button(root)
b1.place(y=499, width=276)
b1.configure(text="+")
b1.configure(command=new_entry)
b1.configure(background="darkgrey")

msg1 = Message(root)
msg1.place(width=133, height=29)
msg1.configure(text="Activity:")
msg1.configure(background="darkgrey")

msg2 = Message(root)
msg2.place(x=141, width=137, height=29)
msg2.configure(text="Time Spend:")
msg2.configure(background="darkgrey")

root.mainloop()

The Button is supposed to create 2 entrys next to each other, every time it is clicked, but if i click the button, it will create the entrys once, clicking it again wont create the entrys on the window, but just creates this in my terminal:

.!entry .!entry2
.!entry3 .!entry4
.!entry5 .!entry6
.!entry7 .!entry8 

If I remove the "return e1,e2" statement, all entrys are created in terminal as well as in the window, with 1 click of the button, instead of being created 2 by 2 for every click of the button.

If I remove the "print(e1,e2)" statement, clicking the button will create the 2 entrys only once, and doesnt show anything in the terminal.

New entrys should be created with a little space in between, until the upper border of the Button is reached (in this case 32 Entrys, 16 rows).

Could someone also explain why msg1 is displayed wrong (it is not centered and it is splitted in two lines), but msg2 is fine, they have the same specs??

Upvotes: 3

Views: 90

Answers (3)

TheEagle
TheEagle

Reputation: 5980

def new_entry():
    n = [n for n in range(30, 490, 30)]

    for i in n:
        e1 = Entry(root)
        e2 = Entry(root)
        e1.place(y=i, width=133)
        e2.place(x=141, y=i, width=137)

        print(e1, e2)
        return e1, e2 ```

The return is the problem. Look at following sample code:

def iterate():
    for i in range(10):
        print(i)
        return i
val = iterate()
print(val)

When you run this it will print:

0
0

Because, return makes the function stop immediately and return something. If you really had to return every value, you could create a list, append the things to it, and return that list after the loop:

def iterate():
    arr = []
    for i in range(10):
        print(i)
        arr.append(i)
    return arr
val = iterate()
print(val)

which will print:

0
1
2
...
10
[0, 1, 2, ... 10]

or use the yield keyword (it will return the value without making the loop stop):

```python
def iterate():
    for i in range(10):
        print(i)
        yield i
val = iterate()
print(val)
print([i for i in val])

will print:

0
1
2
...
10
<generator object iterate at 0x7fb64e584f68> 
[0, 1, 2, 3 ... 10]

With yield, val will be a generator object, over which you must iterate again to get the values out of it. But, as the return value isn't used anywhere in your code, you can just remove the return e1, e2.

Upvotes: 1

oskros
oskros

Reputation: 3305

Your code stops as soon as the return statement is reached, and thus your loop never goes further than the first iteration.

On the other hand, if you remove the return statement, there is nothing in place for stopping your loop so it keeps running until it has created all your widgets.

I took an approach using a generator to solve your issue, such that instead of having a function new_entry(), instead you have a function generating the entries, and another function placing the entries when the generator is called.

I also simplified your code a little at the widget generation, and changed a spelling error (spent instead of spend). I also replaced from tkinter import * with import tkinter as tk, to not pollute your namespace.

import tkinter as tk

root = tk.Tk()
root.geometry("274x520+868+240")
root.minsize(120, 1)
root.maxsize(3284, 1068)
root.resizable(0, 0)
root.title("Learning")
root.configure(background="black")


def entry_generator():
    for i in range(30, 490, 30):
        yield place_entry(i)


def place_entry(i):
    e1 = tk.Entry(root)
    e2 = tk.Entry(root)
    e1.place(y=i, width=133)
    e2.place(x=141, y=i, width=137)
    print(e1, e2)


ne = entry_generator()
b1 = tk.Button(root, command=lambda: next(ne), text='+', background='darkgrey')
b1.place(y=499, width=276)

msg1 = tk.Label(root, text='Activity:', background='darkgrey')
msg1.place(width=133, height=29)

msg2 = tk.Label(root, text='Time Spent:', background='darkgrey')
msg2.place(x=141, width=137, height=29)

root.mainloop()

Upvotes: 2

JacksonPro
JacksonPro

Reputation: 3275

Another method achieve might be to keep a count of the entry and multiply the count with the height of the Entry widget

Here is an example:

count = 0

def new_entry():
    global count
    e1 = Entry(root)
    e2 = Entry(root)

    count += 1
    y_pos = count*e1.winfo_reqheight()
    e1.place(x=0, y=y_pos*1.5, width=133) # change the multiplication factor to change the padding b/w entry
    e2.place(x=141, y=y_pos*1.5, width=137) # change the multiplication factor to change the padding b/w entry

    print(e1, e2)

Also note that it might be better to use grid layout for such programs.

Upvotes: 0

Related Questions