I have been trying to make a Python script that counts how many people are coming and going from your store.
How it is supposed to work:
you have a +1, and a -1 buttons;
when someone comes in, you click +1, and when someone goes away, you do -1.
What is wrong: when I click +1 it changes the counter from 0 to 1, but if I click it again, it doesn't change to 2.
The same thing happens when I do -1.
Here is the code:
import tkinter as tk
import customtkinter as ctk
app = ctk.CTk()
app.title('People Counter')
app.geometry('300x180')
ctk.set_appearance_mode("System")
ctk.set_default_color_theme("blue")
Label = ctk.CTkLabel
StringVar = ctk.StringVar
counter = 0
l1 = tk.Label(app, text=counter)
def pl1():
l1.config(text=counter + 1)
def mi1():
l1.config(text=counter - 1)
p1 = ctk.CTkButton(master=app,text = '+1', command=pl1, height=5 , width=30)
p1.place(relx=0.3, rely=0.5, anchor=tk.CENTER)
m1 = ctk.CTkButton(master=app, text="-1", command=mi1, height=5 , width=30)
m1.place(relx=0.7, rely=0.5, anchor=tk.CENTER)
l1.pack()
app.mainloop()
You only update the text on the label. You don't actually change the value of the counter variable. Try this:
def pl1():
global counter
counter += 1
l1.config(text=counter)
def mi1():
global counter
counter -= 1
l1.config(text=counter)
Another option is to create a simple Counter class with methods add() and sub(). It will help you avoid the use of global variables. I also added a test to prevent the counter get negative numbers.
import tkinter as tk
import customtkinter as ctk
app = ctk.CTk()
app.title('People Counter')
app.geometry('300x180')
ctk.set_appearance_mode("System")
ctk.set_default_color_theme("blue")
Label = ctk.CTkLabel
StringVar = ctk.StringVar
class Counter():
def __init__(self):
self.count = 0
def add(self):
self.count += 1
counter_display.config(text=self.count)
return self.count
def sub(self):
self.count -= 1
if self.count < 0:
self.count = 0
counter_display.config(text=self.count)
return self.count
def __repr__(self):
return str(self.count)
counter = Counter()
counter_display = tk.Label(app, text=counter)
add_one = ctk.CTkButton(
master=app,
text='+1',
command=counter.add,
height=5,
width=30
)
add_one.place(relx=0.3, rely=0.5, anchor=tk.CENTER)
sub_one = ctk.CTkButton(
master=app,
text='-1',
command=counter.sub,
height=5,
width=30
)
sub_one.place(relx=0.7, rely=0.5, anchor=tk.CENTER)
counter_display.pack()
app.mainloop()
Related
import tkinter as tk
counter = 0
def counter_label(label):
counter = 0
def count():
global counter
counter += 1
label.config(text=str(counter))
label.after(10, count)
count()
root = tk.Tk()
root.title("counter")
label= tk.Label(root, fg = "dark green")
label.pack()
counter_label(label)
button = tk.Button(root, text="stop", width=40, command = root.destroy)
button.pack()
root.mainloop()
You can do it by not scheduling the count() function to be called again (via the after() method) after the counter has reached or exceeded a specified limit value.
You also don't need to use a global variable. An advantage of that being that it would allow the same function to be applied to than one Label at a time — each using its own counter. Note I also added an optional rate argument which controls how much the counter is incremented each iteration.
import tkinter as tk
def counter_label(label, limit, rate=1):
"""Update counter display on label at given rate until it reaches limit."""
counter = 0
def count():
nonlocal counter
counter += rate
label.config(text=str(counter))
if counter < limit:
label.after(10, count)
count() # Start counting.
root = tk.Tk()
root.title("counter")
label= tk.Label(root, fg="dark green")
label.pack()
counter_label(label, 500)
button = tk.Button(root, text="stop", width=40, command=root.destroy)
button.pack()
root.mainloop()
Generalizing
Here's a more generic version of the counter_label() function that avoids the use of hardcoded quantities and calculates what it needs from the arguments passed to it.
import tkinter as tk
def counter_label(label, limit, duration, updates_per_sec):
""" Update counter display on label over time period at given updates per
second until it reaches limit.
"""
incr_per_sec = limit / duration
incr_per_call = incr_per_sec / updates_per_sec
ms_delay = int(1/updates_per_sec * 1000)
counter = 0
def count():
nonlocal counter
counter += incr_per_call
label.config(text=format(counter, '.3f'))
if counter < limit:
label.after(ms_delay, count)
count() # Start counting.
root = tk.Tk()
root.title("counter")
label= tk.Label(root, fg="dark green")
label.pack()
counter_label(label, 1000, 5, 20)
button = tk.Button(root, text="stop", width=40, command=root.destroy)
button.pack()
root.mainloop()
So Here i have a function:
def test():
global value
if value ==0:
value=1
return True
else:
value=0
return False
Here is what I come up with:
import tkinter as tk
value=0
while True:
root = tk.Tk()
root.title("my title")
root.geometry('200x150')
root.configure(background='black')
clock_frame = tk.Label(root, font = ('caviar dreams', 130), bg='black')
clock_frame.pack()
def test():
global value
if value ==0:
value=1
return True
else:
value=0
return False
if test() is True:
clock_frame.config(text="HelloWorld",fg="red")
else:
clock_frame.config(text="HelloWorld",fg="white")
root.mainloop()
I want to display the result of this in a Tkinter GUI. I want to change a label while the function is True or False. I want this change to happen every second.
But i don't know how to do it.
Thank you
You can use after() to periodically call test() and update the label:
import tkinter as tk
root = tk.Tk()
root.geometry('+100+100')
root.config(bg='black')
clock = tk.Label(root, text='HelloWorld', font=('caviar dreams', 130), bg='black')
clock.pack()
value = 0
def test():
global value
value = 1 - value
return value == 1
def update():
color = 'red' if test() else 'white'
clock.config(fg=color)
root.after(1000, update) # call update() after 1 second
update() # start the periodic update
root.mainloop()
Use the after method then. The syntax is like this: widget.after(milisecond, action). First you add the time to wait and next you add the action to do.
help this is result recurs like this
Total Account: 13
Total Account: 13
def update():
file = open("list.txt", "r")
line_count = 0
for line in file:
if line != "\n":
line_count += 1
file.close()
Label(kariata,bg="white", text=('Total Account:',(line_count)) , fg="green", font="bold").pack()
kariata.after(1000, update) # call update() after 1 second
update() # start the periodic update
This question already has answers here:
Update Tkinter Label from variable
(3 answers)
Closed 2 years ago.
I am creating a program for scorekeeping basketball. I have a few different files with classes and what not. My main problem is trying to update the points of each players.
For example:
I have a button set up on the screen;
pointsButton = Button(root, text='1PT', command=addPoint)
pointsButton.grid(row=0, column=1)
And a label next to that, that calls the points of a specific player (supposedly).
plabel = Label(root, text=(str(p.points)), relief='groove', bg='#41B6E6', fg = '#DB3EB1', padx=numX, pady=numY)
plabel.grid(row=rowNumber, column=4)
Here's the code from my player class that is probably needed to understand my problem.
class BasketballPlayer:
#Constructor
def __init__(self , preName, lastName, jerseyNumber):
self.preName = preName
self.lastName = lastName
self.jerseyNumber = jerseyNumber
self.points = 0
self.assists = 0
self.rebounds = 0
self.steals = 0
self.blocks = 0
self.fouls = 0
self.threePointers = 0
self.careerHighPoints = 0
self.careerHighAssists = 0
self.careerHighRebounds = 0
self.careerHighSteals = 0
self.careerHighBlocks = 0
self.careerHighThreePointers = 0
And a couple functions from the class:
def addPoints(self, p):
self.points += p
def incrementOnePoint(self):
self.points += 1
def getPoints(self):
return self.points
Here's a couple functions I've tried.
def addPoint():
p.incrementOnePoint()
plabel.config(text=p.points)
Or:
def addPoint():
p.addPoints(1)
plabel.config(text=p.points)
I really thought it would just automatically update because I'm adding a integer to a variable, but it's not updating at all.
Here is a minimal reproducible example as requested in the comments.
from tkinter import *
root = Tk()
class bballPlayer:
def __init__(self):
self.points = 0
def incrementOnePoint(self):
self.points += 1
def getPoints(self):
return self.points
def addOnePoint():
p.incrementOnePoint
global pointslabel
pointslabel.config(text=str(p.points))
p = bballPlayer()
pointslabel = Label(root, text=str(p.points))
pointslabel.grid(row=0, column=1)
btn = Button(root, text='Add Point', command=addOnePoint)
btn.grid(row=0, column=0)
root.mainloop()
Ahhh, now I see the problem, your code isn't calling the incrementOnePoint() method (only referencing its name).
def addOnePoint():
p.incrementOnePoint() # ADD MISSING PARENTHESES TO END.
global pointslabel # NOT STRICTLY NECESSARY BTW.
pointslabel.config(text=str(p.points))
I've been wanting to make a sorting algorithm visualizer using python and settled to use the Tkinter library as my way of visualizing the data (if anyone has better libraries to use I'm open to suggestions, I looked into matplotlib but became discouraged). My problem is that as I'm sorting the array, I want to make a swap, show the updated array after the swap, then continue the sorting; but what ends up happening is that the array sorts and then the entire sorted array is updated.
import tkinter as tk
from tkinter import ttk
import random
import time
class SortingVisualizer(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Sorting Visualizer")
tk.Tk.wm_minsize(self, width=600, height=500)
tk.Tk.wm_resizable(self, width=False, height=False)
self.topFrame = tk.Frame(self)
self.topFrame.grid(row=0, sticky='w')
self.sortOptions = ['Select Algorithm','Bubble sort','Quicksort', 'Merge sort']
self.optionVar = tk.StringVar()
self.optionDrop = ttk.OptionMenu(self.topFrame, self.optionVar, *self.sortOptions)
self.optionDrop.config(width=15)
self.optionDrop.grid(row=0, column=1, sticky='ew')
self.sortButton = ttk.Button(self.topFrame, text = "Sort", command = lambda: bubbleSort(self))
self.sortButton.grid(row=0, column=2, sticky='w')
self.genButton = ttk.Button(self.topFrame, text = "Generate New Array", command = self.newArray)
self.genButton.grid(row=0, column=0)
self.generateArray()
def newArray(self):
self.sortCanvas.destroy()
self.generateArray()
def generateArray(self):
self.array = []
self.numOperations = 0
i = 0
while i < 15:
height = random.randint(15, 200)
self.array.append(height)
i = i + 1
self.drawCanvas()
def drawCanvas(self):
self.sortCanvas = tk.Canvas(self, width=600, height=450)
self.sortCanvas.grid(row=1)
self.sortCanvas.create_line(15, 15, 585, 15)
label = "Number of Operations: " + str(self.numOperations)
self.numLabel = tk.Label(self.topFrame, text = label)
self.numLabel.grid(row=1)
bar_width = 20
bar_gap = bar_width + 10
start_x = 30
start_y = 15
for bar_height in self.array:
x1 = start_x + bar_width
y1 = start_y + bar_height
self.sortCanvas.create_rectangle(start_x, start_y, x1, y1*2, fill='green')
start_x = start_x + bar_gap
def redrawCanvas(self):
self.sortCanvas.destroy()
self.drawCanvas()
def bubbleSort(self):
n = len(self.array)
for i in range(n):
for j in range(0, n-i-1):
if self.array[j]>self.array[j+1]:
temp = self.array[j]
self.array[j] = self.array[j+1]
self.array[j+1] = temp
self.numOperations += 1
self.after(300, self.redrawCanvas)
app = SortingVisualizer()
app.mainloop()
I have also tried app.after(300, self.redrawCanvas) and get the same result
You made a very good first attempt and you were almost there. I have made some changes in your code, most importantly introduced a root object (tk.Tk()) so I can do root.update() in order to redraw to sort_canvas in a new method blip_canvas. To avoid some 'flickering' rather to destroy the canvas each time it is better to delete the 'bar' elements only.
Further I took the liberty to change some of the variable names to make it a bit more Pythonic (should make use of underscores rather than capitals) and added the if _name__ == '__main__' statement.
Have a look at the code below.
import tkinter as tk
from tkinter import ttk
import random
class SortingVisualizer:
def __init__(self):
self.root = tk.Tk()
self.root.wm_title("Sorting Visualizer")
self.root.wm_minsize(width=600, height=500)
self.root.wm_resizable(width=False, height=False)
self.top_frame = tk.Frame(self.root)
self.top_frame.grid(row=0, sticky='w')
self.sort_options = ['Select Algorithm', 'Bubble sort', 'Quicksort', 'Merge sort']
self.option_var = tk.StringVar()
self.option_drop = ttk.OptionMenu(
self.top_frame, self.option_var, *self.sort_options)
self.option_drop.config(width=15)
self.option_drop.grid(row=0, column=1, sticky='ew')
self.sort_button = ttk.Button(
self.top_frame, text="Sort", command=self.bubble_sort)
self.sort_button.grid(row=0, column=2, sticky='w')
self.gen_button = ttk.Button(
self.top_frame, text="Generate New Array", command=self.new_array)
self.gen_button.grid(row=0, column=0)
self.sort_canvas = tk.Canvas(self.root)
self.bars = []
def new_array(self):
self.generate_array()
self.blip_canvas()
def generate_array(self):
self.array = []
self.num_operations = 0
i = 0
while i < 15:
height = random.randint(15, 200)
self.array.append(height)
i = i + 1
def draw_canvas(self):
label = "Number of Operations: " + str(self.num_operations)
self.num_label = tk.Label(self.top_frame, text=label)
self.num_label.grid(row=1)
self.sort_canvas = tk.Canvas(self.root, width=600, height=450)
self.sort_canvas.grid(row=1)
self.sort_canvas.create_line(15, 15, 585, 15)
bar_width = 20
bar_gap = bar_width + 10
start_x = 30
start_y = 15
self.bars = []
for bar_height in self.array:
x1 = start_x + bar_width
y1 = start_y + bar_height
self.bars.append(self.sort_canvas.create_rectangle(
start_x, start_y, x1, y1*2, fill='green'))
start_x = start_x + bar_gap
def blip_canvas(self):
self.sort_canvas.delete(self.bars)
self.draw_canvas()
self.root.update()
self.root.after(200)
def bubble_sort(self):
n = len(self.array)
for i in range(n):
for j in range(n-i-1):
if self.array[j] > self.array[j+1]:
self.array[j], self.array[j+1] = self.array[j+1], self.array[j]
self.num_operations += 1
self.blip_canvas()
def start(self):
tk.mainloop()
if __name__ == '__main__':
app = SortingVisualizer()
app.start()
Note in bubble_sort you do not need the temp variable to swap the values of array[j] and array[j+1]
Rather than to use time.sleep(0.2) to set a delay I have used:
self.root.update()
self.root.after(200)
as suggested in Update button after delay
You can also stick to your original code and just make a few changes.
1) Change the sortButton
self.sortButton = ttk.Button(self.topFrame, text = "Sort", command=self.bubbleSort)
2) Indent the bubbleSort method to be aligned with SortingVisualizer
3) Change method redrawCanvas to:
def redrawCanvas(self):
self.sortCanvas.destroy()
self.drawCanvas()
self.update()
self.after(300)
and
4) in bubbleSort make the call to redrawCanvas:
for j in range(0, n-i-1):
if self.array[j]>self.array[j+1]:
temp = self.array[j]
self.array[j] = self.array[j+1]
self.array[j+1] = temp
self.numOperations += 1
self.redrawCanvas()
et voila, it will work!
You can Thread your function bubbleSort. Also it seems to make more sense for bubbleSort to be a method of the class:
import threading, time
class SortingVisualizer(tk.Tk):
def __init__(self, *args, **kwargs):
...
self.sortButton = ttk.Button(self.topFrame, text = "Sort", command = lambda: threading.Thread(target=self.bubbleSort).start())
...
...
def bubbleSort(self):
n = len(self.array)
for i in range(n):
for j in range(0, n-i-1):
if self.array[j]>self.array[j+1]:
temp = self.array[j]
self.array[j] = self.array[j+1]
self.array[j+1] = temp
self.numOperations += 1
self.redrawCanvas()
time.sleep(0.1)
Just an alternative solution besides threading. I used to encounter a problem that by threading the functions, they may access some external devices (I created a GUI to monitor some hardware) at the same time and cause clashes. By using .after(), tkinter will handle the order of the tasks to avoid clashes.
You can redefine your bubbleSort function so that each iterations of the for loop is changed to a recursion by calling the function again.
def bubbleSort(self, i = 1, j = 0):
n = len(self.array)
if self.array[j]>self.array[j+1]:
temp = self.array[j]
self.array[j] = self.array[j+1]
self.array[j+1] = temp
self.numOperations += 1
j += 1
if j == n-i-1:
j = 0
i += 1
if i < n:
self.after(1, lambda: self.bubbleSort(i,j))
My variable is not changing and I know it's not changing because "1" is printed to the console.
I'm trying to make the label increment when i press the button. However when I press the button, the variable stays at 1.
What am I doing wrong?
I've looked online for an answer but I cannot really find one that I can understand.
num = 0
import tkinter
box = tkinter.Tk()
v = tkinter.StringVar()
labels = tkinter.Label(box, textvariable = v)
labels.pack()
def numberz(num,v):
num += 1
v.set(num)
print(num)
class MainWindow():
box.title("My Stupid Program")
buddon = tkinter.Button(box, text='PRESS ME', command = lambda:numberz(num,v))
buddon.pack()
box.mainloop()
num = 0
import tkinter
box = tkinter.Tk()
v = tkinter.StringVar()
labels = tkinter.Label(box, textvariable = v)
labels.pack()
def numberz(num,v):
num += 1
v.set(num)
print(num)
class MainWindow():
box.title("My Stupid Program")
buddon = tkinter.Button(box, text='PRESS ME', command = lambda:numberz(num,v))
buddon.pack()
box.mainloop()
You are changing the parameter num and not the global variable num
To change the global you need to specifically reference it. Notice how num is not passed in the lambda and now there is a global num in you function.
num = 0
import tkinter
box = tkinter.Tk()
v = tkinter.StringVar()
labels = tkinter.Label(box, textvariable = v)
labels.pack()
def numberz(v):
global num
num += 1
v.set(num)
print(num)
class MainWindow():
box.title("My Stupid Program")
buddon = tkinter.Button(box, text='PRESS ME', command = lambda:numberz(v))
buddon.pack()
box.mainloop()
In any case, using globals should be restricted to very specific cases and not be of general use.