0

By clicking the Start button, the value on label must be increased by 3 every 500 milliseconds until I destroy the window, but the value is stuck on 0.

from tkinter import *

def start(value):
    value+=3
    label['text']=str(value)
    if True:
        root.after(500, start, value)
def stop():
    root.destroy()

root = Tk()
root.geometry("420x250")
value = 0

label = Label(root, background="pink", width=20, height=2, text = str(value))
label.pack(anchor=W, pady=5)
btn1 = Button(root, text="Start", width=6, height=2, command=start).pack(
            side=LEFT, anchor=NW, padx=2, pady=2)
btn2 = Button(root, text="Stop", width=6, height=2, command=stop).pack(
            side=LEFT, anchor=NW, pady=2)

root.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
Jose
  • 21
  • 4
  • 1
    A Button's `command=` function will be called with *no parameters* when the Button is clicked - but yours requires a `value` parameter, so the call fails. (There should be an error message telling you about this!). You need to access `value` in some other way - as a global variable, perhaps. – jasonharper Jun 07 '22 at 23:14
  • 1
    You can set the default value of `value` argument to zero, like `def start(value=0)`. Moreover you need to make sure that `start()` is executed only once, otherwise there may be more than one *`after`* loop created. – acw1668 Jun 08 '22 at 00:40
  • @Jose. Your code is working. I don't see no issue. – toyota Supra Feb 11 '23 at 21:11

3 Answers3

0

The reason your code doesn't work is because the command=start for the Start button isn't passing it the parameter the start() function expects. That could be fixed by removing the parameter from the function's definition and modifying it to directly access the global variable.

However I think it would be better in this case to use a tkinter's "Variable Classes" to do things given what else it looks like what you're trying to do. (See The Variable Classes (BooleanVar, DoubleVar, IntVar, StringVar) for a little documentation.) One of the most useful features they have is that they can be used in many widgets (such as Labels) instead of supplying a static text string. When that's done, the widget's display of the variable's value will automatically get update itself whenever the value of tkinter.Variable changes.

Below is example code showing how to use them which also makes use of an additional tkinter.Variable to hold the value of a boolean flag the controls whether the updating is enabled. I also added a third Button to quit since I changed what the Stop button does in your code.

from tkinter import *

def start():
    incflag_var.set(True)
    inc_value()  # Start incrementing.

def inc_value():
    value_var.set(value_var.get() + 3)  # Will auto update label.

    if incflag_var.get():
        root.after(500, inc_value)

def stop():
    incflag_var.set(False)


root = Tk()
root.geometry("420x250")

value_var = IntVar(value=0)
incflag_var = BooleanVar(value=False)

label = Label(root, background="pink", width=20, height=2, textvariable=value_var)
label.pack(anchor=W, pady=5)
Button(root, text="Start", width=6, height=2, command=start).pack(
            side=LEFT, anchor=NW, padx=2, pady=2)
Button(root, text="Stop", width=6, height=2, command=stop).pack(
            side=LEFT, anchor=NW, pady=2)
Button(root, text="Quit", width=6, height=2, command=root.quit).pack(
            side=LEFT, anchor=NW, pady=2)

root.mainloop()

martineau
  • 119,623
  • 25
  • 170
  • 301
  • Why don't you just use `textvariable=value_var` for `label`? Then you don't need to call `label['text'] = str(value)` inside `inc_value()`. Also it is better to execute `inc_value()` only when `incflag_var` is set to `False`, otherwise there may be more than one *after loop* executed when `Start` button is clicked more than once. – acw1668 Jun 08 '22 at 01:04
  • @acw1668: Yeah, realized the first thing myself and have modified my answer accordingly. Not worried about multiple after loops running so haven't bothered since I don't think it's that likely an issue. – martineau Jun 08 '22 at 01:09
  • I discovered recently that `text` can "natively" support integers btw so that conversion wouldn't be necessary, it could be `label["text"] = value` @acw1668 – Matiiss Jun 08 '22 at 11:04
  • in fact, it would be possible to do `label["text"] += 3` – Matiiss Jun 08 '22 at 11:07
  • @Matiiss: You don't need to update the label's `"text"` value when using an `IntVar`. – martineau Jun 08 '22 at 12:12
  • I'm saying that you don't need an `IntVar` in the first place – Matiiss Jun 08 '22 at 12:30
0

As it turns out tkinter.Label can support integer values too, so the way you can do incrementing is by simply using label["text"] += 3 if the starting text of the label is an integer (or float probably works too).

You also need to not let it create multiple after "loops" so setting some flags and checking those also should be added, the way I implemented it is by creating an attribute for that same label that tells whether it has started counting and if it hasn't then start.

import tkinter as tk


def start():
    if not getattr(label, "counting", False):
        update()
    label.counting = True


def stop():
    root.destroy()


def update():
    label["text"] += 3
    root.after(500, update)


root = tk.Tk()
root.geometry("420x250")

label = tk.Label(root, background="pink", width=20, height=2, text=0)
label.pack(anchor="w", pady=5)
tk.Button(root, text="Start", width=6, height=2, command=start).pack(
    side="left", anchor="nw", padx=2, pady=2
)
tk.Button(root, text="Stop", width=6, height=2, command=stop).pack(
    side="left", anchor="nw", pady=2
)

root.mainloop()

Also note that there is no point in assigning the returned values of pack (or grid or place) to a variable, you can just simply create buttons without those variables, label on the other hand of course needs to be referenced so it's properly made for that.

Also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.

Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • If there's a separate "incrementing" flag with only one way to turn it on and off, you don't need to worry about multiple `after` "loops". – martineau Jun 08 '22 at 12:14
0

the value on label must be increased by 3 every 500 milliseconds until I destroy the window, but the value is stuck on 0.

The problem can be fix.

You need to declare variable. then add global in start function. Remove value in parameter root.after.

Change this:

def start(value):
    value+=3
    label['text']=str(value)
    if True:
        root.after(500, start, value)

to:

value = 0
def start():
    global value
    value+=3
    label['text']= value 
    if True:
        root.after(500, start) 
toyota Supra
  • 3,181
  • 4
  • 15
  • 19