Print the difference result between two counters in Python - python-3.x

I have two counters whose results are displayed in a small part of the GUI for reference, but the important data I need is the result of IN minus OUT. For example, if I have 113 IN and 86 OUT, I am going to display those two data but I need to display also the result, 27 (113-86=27).
Screenshot of the app
Given these two counters:
def counter_label_IN(label):
def count():
s.sendall(b'gcounters\x00')
L_IN = next(iter(s.recv(1024)))
print(L_IN)
label_IN.config(text=str(L_IN))
label_IN.after(500, count)
count()
def counter_label_OUT(label):
def count():
s.sendall(b'gcounters\x00')
L_OUT = next(iter(s.recv(1024)))
print(L_OUT)
label_OUT.config(text=str(L_OUT))
label_OUT.after(500, count)
count()
How can I have another timer keeping refreshing the difference between the two?
Also, if the difference is a negative number, I need to display only 0.
EDIT after #j_4321 kind reply.
Thank you so much for the reply. I am a little lost and I guess it is one of those time the reply is right in front of my eyes but it is so bright that I can't see it... The code written gives no error, but the counts displayed in all the labels are zeroes. This is a simplified code, but still you can see the 0:
EDIT 2 after #j_4321 kind reply.
As suggested, I added:
counter_label_IN()
counter_label_OUT()
To the code, which I am pasting here for your consideration. It now gives me this error:
PS C:\Users\lorenzo.amati> & C:/Users/lorenzo.amati/AppData/Local/Programs/Python/Python38-32/python.exe "c:/Users/lorenzo.amati/Desktop/Software PCN/PCN-local-getdata-prova.py"
Traceback (most recent call last):
File "c:/Users/lorenzo.amati/Desktop/Software PCN/PCN-local-getdata-prova.py", line 55, in <module>
counter_label_IN()
File "c:/Users/lorenzo.amati/Desktop/Software PCN/PCN-local-getdata-prova.py", line 38, in counter_label_IN
count()
File "c:/Users/lorenzo.amati/Desktop/Software PCN/PCN-local-getdata-prova.py", line 27, in count
s.sendall(b'gcounters\x00')
OSError: [WinError 10038] An operation was attempted on something that is not a socket
Here is the code:
import socket
import tkinter as tk
import tkinter.ttk as ttk
# Main settings
IP_address = ('172.16.0.103')
root = tk.Tk()
root.title("People Counter")
root.configure(bg="#006bb3")
font1 = "-family {Source sans pro} -size 36 -weight bold"
font2 = "-family {Source sans pro} -size 150 -weight bold"
# use IntVar as counters
counter_IN = tk.IntVar(root)
counter_OUT = tk.IntVar(root)
counter_DIFF = tk.IntVar(root)
# get the sensor data
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP_address,5400))
def counter_label_IN():
def count():
s.sendall(b'gcounters\x00')
L_IN = next(iter(s.recv(1024)))
print(L_IN)
counter_IN.set(L_IN) # update counter IntVar instead of label
# update DIFF counter:
diff = L_IN - counter_OUT.get()
if diff < 0:
diff = 0
counter_DIFF.set(diff)
label_IN.after(500, count)
count()
def counter_label_OUT():
def count():
s.sendall(b'gcounters\x00')
L_OUT = next(iter(s.recv(1024)))
print(L_OUT)
counter_OUT.set(L_OUT) # update counter IntVar instead of label
diff = counter_IN.get() - L_OUT
if diff < 0:
diff = 0
counter_DIFF.set(diff)
label_OUT.after(500, count)
count()
s.close()
counter_label_IN()
counter_label_OUT()
# In/Out Informations display on top
FrameTOP = tk.Frame(root, bg="#006bb3", borderwidth="0")
FrameTOP.grid(row=0, column=0, columnspan=4, sticky='nsew')
Label_RTC_IN = tk.Label(FrameTOP, anchor='w',bg="#006bb3", fg='white',justify='left', text='Realtime count of people IN:')
Label_RTC_OUT = tk.Label(FrameTOP, anchor='w',bg="#006bb3", fg='white',justify='left', text='Realtime count of people OUT:')
label_IN = tk.Label(FrameTOP, textvariable=counter_IN, anchor='w',bg="white",fg="black", justify='left', relief='sunken', borderwidth="2")
label_OUT = tk.Label(FrameTOP, textvariable=counter_OUT, anchor='w',bg='white', fg='black', justify='left', relief='sunken', borderwidth="2")
Label_RTC_IN.grid(row=0, column=0, padx=10, pady=10)
label_IN.grid(row=0, column=1, padx=10, pady=10)
Label_RTC_OUT.grid(row=0, column=2, padx=10, pady=10)
label_OUT.grid(row=0, column=3, padx=10, pady=10)
# Frame in which to put the differential data of people inside
FrameCENTRAL = tk.Frame(root, bg="#006bb3",borderwidth="0")
FrameCENTRAL.grid(row=1, column=0, pady=50, columnspan=4,) #sticky='nsew')
TitleCentral = tk.Label(FrameCENTRAL,anchor='w', bg="#006bb3", fg='white', font=font1, text='''People Inside''')
TitleCentral.grid(row=0, column=0, padx=10, pady=10)
label_DIFF = tk.Label(FrameCENTRAL, textvariable=counter_DIFF, font=font2, bg="#006bb3", fg='white', anchor='center')
label_DIFF.grid(row=1, column=0, padx=10, pady=10)
# IP Informations
FrameBOTTOM = tk.Frame(root, bg="#006bb3", borderwidth="0")
FrameBOTTOM.grid(row=2, column=0, columnspan=4, sticky='nsew')
Label_IP_ADDRESS = tk.Label(FrameBOTTOM,anchor='w',bg="#006bb3",fg='white',text='''People counter sensor locally connected to local IP address:''')
Label_IP = tk.Label(FrameBOTTOM,anchor='w',bg="#006bb3",fg='white',text=(IP_address))
Label_IP_ADDRESS.grid(row=0, column=0, padx=10, pady=10)
Label_IP.grid(row=0, column=1, padx=10, pady=10)
# Closing button
close = tk.Button(FrameBOTTOM, bg="#006bb3",fg='white',text='Stop and close', width=16, command=root.destroy)
close.grid(row=0, column=3, padx=10, sticky='e')
root.mainloop()
Screenshot of the app after the modification, showing all 0

You can use IntVar as the IN, OUT and DIFF = IN - OUT counters and use the textvariable option of the Label to automatically update the text when updating the counters. In the counter_label_...() functions, the IntVar value will be updated instead of updating the label's text.
import tkinter as tk
root = tk.Tk()
# use IntVar as counters
counter_IN = tk.IntVar(root)
counter_OUT = tk.IntVar(root)
counter_DIFF = tk.IntVar(root)
# use textvariable to keep the labels updated
label_IN = tk.Label(root, textvariable=counter_IN)
label_OUT = tk.Label(root, textvariable=counter_OUT)
label_DIFF = tk.Label(root, textvariable=counter_DIFF)
# database polling
def counter_label_IN():
def count():
s.sendall(b'gcounters\x00')
L_IN = next(iter(s.recv(1024)))
print(L_IN)
counter_IN.set(L_IN) # update counter IntVar instead of label
# update DIFF counter:
diff = L_IN - counter_OUT.get()
if diff < 0:
diff = 0
counter_DIFF.set(diff)
label_IN.after(500, count)
count()
def counter_label_OUT():
def count():
s.sendall(b'gcounters\x00')
L_OUT = next(iter(s.recv(1024)))
print(L_OUT)
counter_OUT.set(L_OUT) # update counter IntVar instead of label
diff = counter_IN.get() - L_OUT
if diff < 0:
diff = 0
counter_DIFF.set(diff)
label_OUT.after(500, count)
count()
# start monitoring the database
counter_label_IN()
counter_label_OUT()

Related

All other widgets are affected when one of the widgets move in a GUI (using the tkinter library in python 3)

Whenever I try to move individual widgets or click a button that produces words effectively moving other widgets that had nothing to do those both specified actions above.
Here is my code:
import tkinter as tk
# Create the main window
window = tk.Tk()
window.title("Price Calculator")
window.geometry("800x600")
window.config(background = "#777474")
def calculate_price():
global input_field
input_field_value = input_field.get()
try:
input = int(input_field_value)
price = input* 0.1
answer.config(text = (f"Your price is ","%.3f"%price,"KWD"))
except ValueError as ve:
answer.config(text = 'What you have just entered are not numbers whatsoever, try again!', fg = "#CD5F66")
price_input = tk.Label(window, text = "Total Pages:", font = "Arial", bg = "#777474", fg = "#FEFCF2")
price_input.grid(column = 0, row = 0)
input_field = tk.Entry(window, font = "Arial", bg = "#FEFCF2")
input_field.grid(column = 1, row = 0, padx = 0,pady = 10)
answer = tk.Label(window, bg = "#777474")
answer.grid(pady = 20)
button_return = tk.Button(window, text = "Calculate Price", command = calculate_price).grid()
# Run the main loop
window.mainloop()
This is my GUI before I click on the button which is called "Calculate Price":
This is my GUI after I have clicked on the button:
The problem here is that I don't want the Total Pages and the entry field to move away from each other whenever I click on the button.
I would suggest to put price_input and input_field into a frame for easier layout management. I also use sticky="w" on the frame, answer and button_return so that they are aligned at the left.
Below is the modified code:
import tkinter as tk
FGCOLOR = "#FEFCF2"
BGCOLOR = "#777474"
ERRCOLOR = "#FF5F66"
# Create the main window
window = tk.Tk()
window.title("Price Calculator")
window.geometry("800x600")
window.config(background="#777474")
def calculate_price():
input_field_value = input_field.get()
try:
input = int(input_field_value)
price = input * 0.1
answer.config(text=f"Your price is {price:.3f} KWD", fg=FGCOLOR)
except ValueError as ve:
answer.config(text='What you have just entered are not numbers whatsoever, try again!', fg=ERRCOLOR)
# create a frame for price_input and input_field
frame = tk.Frame(window, bg=BGCOLOR)
frame.grid(column=0, row=0, padx=10, pady=10, sticky="w")
price_input = tk.Label(frame, text="Total Pages:", font="Arial", bg=BGCOLOR, fg=FGCOLOR)
price_input.grid(column=0, row=0)
input_field = tk.Entry(frame, font="Arial", bg=FGCOLOR)
input_field.grid(column=1, row=0)
answer = tk.Label(window, bg=BGCOLOR)
answer.grid(column=0, row=1, padx=10, pady=10, sticky="w")
button_return = tk.Button(window, text="Calculate Price", command=calculate_price)
button_return.grid(column=0, row=2, sticky="w", padx=10, pady=10)
# Run the main loop
window.mainloop()

How to get the values of all the OptionMenu widgets within a frame inside a canvas in Tkinter

I'm writing a minimalist image tagging app that will list out all the image files in a specific location alongside a dropdown menu to select the options for tagging the image. Once the images are tagged, I need to save the changes to a JSON file and I've got a button for that. How can we read all the options selected so that it can be written into a file?
Following is the code so far:
from tkinter import N, RIGHT, Button, OptionMenu, Scrollbar, StringVar, Tk, Canvas, Frame, Label
class App:
def __init__(self):
self.root = Tk()
self.tags = ['Apples', 'Oranges', 'Berries']
self.GetRows()
self.SaveButton()
self.root.mainloop()
def GetRows(self):
self.canvas = Canvas(self.root)
self.scroll_y = Scrollbar(self.root, orient="vertical", command=self.canvas.yview)
self.frame = Frame(self.canvas)
lst = [f"A01{str(i)}.JPG" for i in range(100)]
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# put the frame in the canvas
self.canvas.create_window(0, 0, anchor='nw', window=self.frame)
# make sure everything is displayed before configuring the scrollregion
self.canvas.update_idletasks()
self.canvas.configure(scrollregion=self.canvas.bbox('all'),
yscrollcommand=self.scroll_y.set)
self.canvas.pack(fill='both', expand=True, side='left')
self.scroll_y.pack(fill='y', side='right')
def SaveState(self):
pass
def SaveButton(self):
self.save_button = Button(self.root, text="Save Changes", padx=50, pady=10, command=self.SaveState)
self.save_button.pack(side=RIGHT)
if __name__ == '__main__':
App()
The SaveState method is what will be used to write the selections so far into a file.
Thanks in advance!
In order to make OptionMenu results available try modifying your code so that
all StringVars are accessible outside of GetRows.
def GetRows(self):
...
# Define label as a list
self.label = []
for idx, r in enumerate(lst):
filename = Label(self.frame, text=r)
filename.grid(row=idx+2, column=0, sticky=N)
label = StringVar()
drop = OptionMenu(self.frame, label, *self.tags)
drop.grid(row=idx+2, column=1)
# Save StringVar reference
self.label.append(label)
...
def SaveState(self):
self.data = dict()
# Retrieve results into dictionary
for i, a in enumerate(self.label):
self.data[f"A_{i}"] = a.get()
print(self.data)
Then use json.dump(self.data, a_file) to save it

How to add multiple entries in tkinter?

I am extremely new to Tkinter. I have been trying to create something that basically calculates the average of the marks inputted. I am trying to give the user the option to choose the number of subjects, and accordingly create that many entries.
from tkinter import *
root = Tk()
x, y, d = 0, 0, {}
for i in range(1, int(input('Enter no of subjects')) + 1):
sub1 = Entry(root, width=15, borderwidth=5)
sub1.grid(row=x, column=y)
max1 = Entry(root, width=15, borderwidth=5)
max1.grid(row=x, column=y+2)
sub1label = Label(root, text='Marks attained', bg='grey', fg='white')
sub1label.grid(row=x, column=y+1)
max_sub1label = Label(root, text='Max Marks', bg='grey', fg='white')
max_sub1label.grid(row=x, column=y+3)
x += 1
root.mainloop()
Is there a way to store the data inputted each time so as to compute the percentage acquired? Or is there another method I can use?
You can store the values in the list and then index the list later on with the required value and get the items you wants. Here is your corrected code:
from tkinter import *
root = Tk()
def show():
for i in range(tot_sub): # Loop through the number of subjects
attained = attained_marks[i].get() # Get the indexed item from the list and use get()
max_mark = max_marks[i].get()
print(f'Attained marks: {attained}, Max marks: {max_mark}') # Print the values out
attained_marks = [] # Empty list to populate later
max_marks = [] # Empty list to populate later
tot_sub = int(input('Enter no. of subjects: ')) # Number of subjects
for i in range(tot_sub):
sub1 = Entry(root, width=15, borderwidth=5)
sub1.grid(row=i, column=0)
attained_marks.append(sub1) # Append each entry to the list
max1 = Entry(root, width=15, borderwidth=5)
max1.grid(row=i, column=2)
max_marks.append(sub1) # Append each entry to the list
sub1label = Label(root, text='Marks attained', bg='grey', fg='white')
sub1label.grid(row=i, column=1, padx=5)
max_sub1label = Label(root, text='Max Marks', bg='grey', fg='white')
max_sub1label.grid(row=i, column=3, padx=5)
root.bind('<Return>',lambda e: show()) # For demonstration of getting all the data
root.mainloop()
I have also changed the loop a bit as you don't need to initialize x,y,d and so on, as it can be easily achieved from inside the loop itself. I have also expanded the code so you can understand easily. Also I dont recommend using input() as it is for the terminal, use an Entry instead.
Alternatively: You can also use a dict and avoid the use of 2 lists, the dictionary would be something like {'Alternative Marks':[att1,att2,...],'Maximum Mark':[max1,max2,...]}, but this would make looping through and indexing a bit more lengthy.

Using Tkinter to disable entry with specified input

I would like to use Tkinter to be able to disable one entry if 'no' is selected from a drop down menu.
from tkinter import *
def disableEntry(entry):
entry.config(state='disable')
def allowEntry(entry):
entry.config(state='normal')
def main():
print("test")
root = Tk() #create a TK root window
root.title("Lunch and Learn") #Title of the window
L1 = Label(root, text = "Label 1").grid(row=0, column=0, padx=30, pady=(20,5))
L2 = Label(root, text = "Label 2").grid(row=1, column=0, pady=5)
var = StringVar()
E1 = Entry(root,bd =3)
E1.grid(row=0, column=1)
D1 = OptionMenu(root,var,"yes","no")
D1.grid(row=1,column=1)
if var.get() == 'no':
disableEntry(E1)
elif var.get() == 'yes':
allowEntry(E1)
B2 = Button(text = "Submit", command=main).grid(row=4, column=2)
root.mainloop()
the above code is a simple example of what i have tried. I have created two functions called 'disableEntry' and 'allowEntry' which should change the state of the entry box but they don't appear to do anything when i change the input of the drop down menu.
i dont know if i am approaching this the wrong way or if there is a standardized way to do this.
any help would be appreciated.
You need a way to check the state of the selection after it is changed. That can be achieved with adding a callback command to the OptionMenu widget.
You were checking the correct variable, but the point you were checking it at was before the screen window had even displayed.
from tkinter import Label, StringVar, OptionMenu, Entry, Tk, Button
# change the state of the Entry widget
def change_state(state='normal'):
E1.config(state=state)
def main():
print("test")
# callback function triggered by selecting from OptionMenu widget
def callback(*args):
if var.get() == 'no':
change_state(state='disable')
elif var.get() == 'yes':
change_state(state='normal')
root = Tk() #create a TK root window
root.title("Lunch and Learn") #Title of the window
L1 = Label(root, text="Label 1").grid(row=0, column=0, padx=30, pady=(20, 5))
L2 = Label(root, text="Label 2").grid(row=1, column=0, pady=5)
var = StringVar()
E1 = Entry(root, bd=3)
E1.grid(row=0, column=1)
D1 = OptionMenu(root, var, "yes", "no", command=callback)
D1.grid(row=1, column=1)
B2 = Button(text = "Submit", command=main).grid(row=4, column=2)
root.mainloop()

Tkinter background color issue at scrollbar

I am working on a tkinter script which has a vertical and horizontal scrollbar.
A portion of the window below the vertical scrollbar is not picking up the background color I'm applying.
I have tried the following color options still the small portion is not picking up.
Image:
Sample window snapshot
Full Code:
from tkinter.tix import *
from tkinter import *
import collections
root = Tk()
root.configure(background='steel blue')
# Global variables
fname = ''
# Variables for setting the height and width of widget
# Variables to set Height
actualRootHeight = 300
rootHScale = 25
rootHOffset = 100
canvasHeight = 300
root2CanvasHMargin =65
# Variables to set Width
rootWScale = 10
rootWOffset = 200
canvasWidth = 300
root2CanvasWMargin = 20
inpWidth = 0
# Lists to save configs
inpParamList = collections.OrderedDict()
paramListRef = collections.OrderedDict()
updatedParamList = collections.OrderedDict()
entryList = []
labels = []
# All widget coding is done here
class guiList(Frame):
global root
# Constructor - Use as a control structure
def __init__(self,parent):
Frame.__init__(self,parent)
self.parent = parent
self.readParams()
self.setGeometry()
self.initUI()
self.controlUI()
def onFrameConfigure(self, event, Canvas1):
# Reset the scroll region to encompass the inner frame
Canvas1.configure(scrollregion=Canvas1.bbox("all"), background='steel blue')
# All widget edition is done here
def initUI(self):
global paramListRef
titleStr = sys.argv[1]
self.parent.title(titleStr)
self.grid(row=0, column=0)
inpConfigs = inpParamList.items()
# Add a canvas and call Frame as it's child widget
# Scrollbar can be added to Canvas
Canvas1 = Canvas(self, width=canvasWidth, height=canvasHeight, borderwidth=0, bg="light steel blue")
vsb = Scrollbar(self, orient="vertical", command=Canvas1.yview, bg="light steel blue", troughcolor="steel blue", highlightcolor="light steel blue", activebackground="light steel blue", highlightbackground="light steel blue")
Canvas1.configure(yscrollcommand=vsb.set)
vsb.grid(column=2, sticky='NS')
hsb = Scrollbar(self, orient="horizontal", command=Canvas1.xview, bg="light steel blue", troughcolor="steel blue")
Canvas1.configure(xscrollcommand=hsb.set)
hsb.grid(column=0, sticky='EW')
Canvas1.grid(row=0, column=0, sticky='NWES')
# Create new Frame for input configs
Frame1 = Frame(Canvas1, width=canvasWidth, height=canvasHeight, bg="light steel blue")
Canvas1.create_window((1,1),window=Frame1, anchor="nw", tags="Frame1")
Frame1.bind("<Configure>", lambda event, arg=Canvas1: self.onFrameConfigure(event, arg))
# Loop through the input configs
i = 0
# Add label and combobox in loop
for k,v in inpConfigs:
# Label widgets
lbl1 = Label(Frame1, text=k, bg="light steel blue", font=("Helvetica", 12, "bold"), fg="steel blue")
lbl1.grid(row = i, column = 0, padx=10, pady=5, sticky='W')
labels.append(lbl1)
# Combo-box widget for configurations
tkvar = StringVar(Frame1)
tkvar.set(v[0])
entry1 = OptionMenu(Frame1, tkvar, *v)
entry1.configure(width=20, anchor=W, bg="steel blue", fg="white", font=("Helvetica", 11, "bold"))
entry1.grid(row = i, column=1, padx=10, pady=5, sticky='E')
#entry1.grid_columnconfigure(2, weight=2)
paramListRef[k] = tkvar
i += 1
# Read the updated configs after the button click
def readUpdatedParams(self):
global updatedParamList
for k,v in paramListRef.items():
updatedParamList[k] = v.get()
root.destroy()
self.writeBack()
# Seperate Frame for buttons
# Upon clicking read updted params
def controlUI(self):
Frame2 = Frame(self, bg="steel blue")
Frame2.grid(row=2, column = 0, sticky="EW")
b = Button(Frame2, text="OK", command=self.readUpdatedParams, bg="light steel blue", fg="steel blue", font=("Helvetica", 11, "bold"))
#b.grid(row=1, column=1, pady=10)
b.pack(fill="none", expand=True, pady = 10)
# Read the file and create a key, value pair for configs
# Lines in file is split with space as delimiter
# First column is the config name and rest are all possible value
def readParams(self):
global inpParamList
global inpWidth
f = open(fname)
for line in f:
val = {}
val = line.split()
key = val.pop(0)
# Get the max width of key to adjust widget width
inpWidth = len(key) if (len(key) > inpWidth) else inpWidth
inpParamList[key] = val
# Geometry ( X-width x Y-width + X-position + Y-position)
# Based on the number of elements in the config list
# the height of the widget is set (Max is 75% of screen size)
def setGeometry(self):
global actualRootHeight
global canvasWidth
global canvasHeight
listLen = len(inpParamList)
rootWinwidth = int(inpWidth *rootWScale) + rootWOffset
rootWinheight = (listLen * rootHScale ) + rootHOffset
screenWidth = self.winfo_screenwidth()
screenHeight = int(self.winfo_screenheight() * 0.75)
if rootWinheight < screenHeight :
actualRootHeight = rootWinheight
else :
actualRootHeight = screenHeight
canvasWidth = rootWinwidth - root2CanvasWMargin
canvasHeight = actualRootHeight - root2CanvasHMargin
rootWinresolution = str(rootWinwidth)+'x'+str(actualRootHeight)+'+'+'0'+'+'+'0'
root.geometry(rootWinresolution)
# Sub routine to write back the config file.
def writeBack(self):
fr = open("bt_top.param.config",'w')
for k,v in updatedParamList.items():
print(k,v)
fr.write(k)
fr.write(" ")
fr.write(v)
fr.write("\n")
fr.close()
# Main Function
# Define Window geometry and other top level stuff here
# Do not go into widget coding here
def main():
global fname
# Get File name from command line argument
fname = sys.argv[2]
app = guiList(root)
root.mainloop()
# The __name__ variable decides what to run
# Below lines make this file run stand-alone
if __name__ == '__main__':
Any suggestions are welcome. Thanks
-Vinay
The piece of black is the background of self, your guiList class. Add a line self.config(bg="steel blue") to its __init__() function (or to initUI() I suppose) to fix it.

Resources