I am trying to do a message box to warns the user in my UI that they will be moved to the homepage after 10 seconds, codes are as following:
from tkinter import *
import warnings
import random
import time
import sqlite3
from tkinter import simpledialog
from tkinter import messagebox
from tkcalendar import *
from tkinter import ttk
import math
from PIL import Image, ImageTk
import winsound
##-------------Frames setup--------------------------
class VendingApp(Tk):
def __init__(self):
Tk.__init__(self)
self._frame = None
self.switch_frame(Home)
def switch_frame(self, frame_class):
#Destroys current frame and replaces it with a new one.
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.pack()
####-----------------------Home page---------------------------
class Home(Frame):
def __init__(self, master):
Frame.__init__(self, master)
##-----------------------fuctions-----------------------------------
def clicked(a):
if (a.x <=1920) and (a.y<=1080):
master.switch_frame(Store)
print ("1")
else:
None
None
##----------------setup------------------------------------------
self._images = list()
img_banner = Image.open("pic/banner.jpg")
img_banner = img_banner.resize((400,100), Image.ANTIALIAS)
banner = ImageTk.PhotoImage(img_banner)
self._images.append(banner)
##---------------------Top frame Home------------------------------------
topFrame = Frame(self,width = 1920, height = 1080)
topFrame.pack()
canvasM = Canvas(topFrame,height=1080, width=1920)
canvasM['highlightthickness'] = 0
canvasM.pack()
body = canvasM.create_rectangle(0, 0,1920,1080, fill = 'gray95')
mylabel = canvasM.create_text((960, 390),font=("Purisa", 40), text="Touch anywhere to continue")
canvasM.tag_bind(body,"<Button>",clicked)
canvasM.tag_bind(mylabel,"<Button>",clicked)
#-------------------------Store page--------------------------------------------------------------
class Store(Frame):
def __init__(self, master):
Frame.__init__(self, master)
def timeset():
global time
time = 15
def active(event):
global time
time = 15
print (time)
def timeout():
global time
if time >= 10:
time -= 1
master.after(1000, timeout)
elif time <= 10 and time > 0:
askuser()
time -= 1
master.after(1000, timeout)
elif time <= 0:
master.switch_frame(Home)
print(time)
def askuser():
ask = messagebox.showinfo('Are you there?','Returning to home page in ' + (str(time)) + ' seconds. \n please touch the screen to continue using the app')
self._images = list()
img_banner = Image.open("pic/banner.jpg")
img_banner = img_banner.resize((400, 100), Image.ANTIALIAS)
banner = ImageTk.PhotoImage(img_banner)
self._images.append(banner)
##---------------------pictures and filters Store------------------------------------
PicFrame = Frame(self, width=400, height=100)
PicFrame.grid(row = 0, column = 0)
PicFrame.grid_propagate(False)
canvas_for_banner = Canvas(PicFrame, height=100, width=400 ) # banner image
canvas_for_banner.pack(anchor = N)
canvas_for_banner['highlightthickness'] = 0
canvas_for_banner.grid_propagate(False)
canvas_for_banner.create_image(0, 0, anchor=NW, image=banner)
##---------------------------------Midd store-------------------
FilterFrame =Frame(self, width=400, height=930, relief = RAISED, bd =1)
FilterFrame.grid(row = 1, column = 0)
FilterFrame.pack_propagate(0)
Ava_title = Label(FilterFrame, text = "Availability", font = ('Helvetica', 20, 'bold'))
Ava_title.pack(pady = (100,0))
self.stock_yes = IntVar()
self.stock_no = IntVar()
check_ava = Checkbutton(FilterFrame, text = 'Avaliable', variable = self.stock_yes, font = 20, command = None)
check_ava.pack( anchor = 'w', padx = 30, pady =10)
check_unava = Checkbutton(FilterFrame, text = 'Unavaliable', font = 20, variable = self.stock_no, command = None)
check_unava.pack(anchor = 'w', padx = 30, pady = 10)
gend_title = Label(FilterFrame, text = "Gender", font = ('Helvetica', 20, 'bold'))
gend_title.pack(pady = (30,0))
self.boi = IntVar()
self.girl = IntVar()
check_boi = Checkbutton(FilterFrame, text = 'Male', font = 20, variable = self.boi)
check_boi.pack( anchor = 'w', padx = 30, pady = 10)
cehck_girl = Checkbutton(FilterFrame, text = 'Female', font = 20, variable = self.girl)
cehck_girl.pack(anchor = 'w', padx = 30, pady =10)
Class_title = Label(FilterFrame, text = "Uniform class", font = ('Helvetica', 20, 'bold'))
Class_title.pack(pady = (30,0))
self.formal = IntVar()
self.sport = IntVar()
check_formal = Checkbutton(FilterFrame, text = 'Formal', font = 20, variable = self.formal)
check_formal.pack( anchor = 'w', padx = 30, pady =10)
check_sport = Checkbutton(FilterFrame, text = 'Sport', font = 20, variable = self.sport)
check_sport.pack(anchor = 'w', padx = 30, pady =10)
type_title = Label(FilterFrame, text = "Type", font = ('Helvetica', 20, 'bold'))
type_title.pack(pady = (30,0))
self.shirts = IntVar()
self.pants = IntVar()
self.misc = IntVar()
check_shirts = Checkbutton(FilterFrame, text = 'Shirts', font = 20, variable = self.shirts)
check_shirts.pack( anchor = 'w', padx = 30, pady =10)
check_pants = Checkbutton(FilterFrame, text = 'Pants', font = 20, variable = self.pants)
check_pants.pack(anchor = 'w', padx = 30, pady =10)
check_misc = Checkbutton(FilterFrame, text = 'Misc', font = 20, variable = self.misc)
check_misc.pack(anchor = 'w', padx = 30, pady =10)
##------------------------\\\\\\\\\\\\\\\\\\\\\\---------------------------------------------
MidFrame = Frame(self,width = 1520, height = 1030, relief = SUNKEN, bd = 2)
MidFrame.grid(row = 0, column = 1, rowspan = 2)
MidFrame.grid_propagate(False)
store_canvas = Canvas(MidFrame, width = 1520, height = 1030)
store_canvas.pack()
store_canvas.pack_propagate(0)
frames = []
frame_order = []
num = 1
for x in range(4):
frames.append([])
for y in range(4):
frames[x].append(0)
for x in range(4):
for y in range(4):
frames[x][y] = Frame(store_canvas, width=1520 / 4, height=1030 / 4, bd = 2, relief = SOLID)
frames[x][y].grid(row=y, column=x)
frames[x][y].pack_propagate(False)
frame_order.append(frames[x][y])
for frame in frame_order:
Label(frame, text=num, anchor='nw').pack( side = 'left')
num += 1
##------------------------\\\\\\\\\\\\\\\\\\\\\\---------------------------------------------
BottomFrame = Frame(self,width = 1920, height = 50, bd = 2, relief = RAISED)
BottomFrame.grid(row = 2, column =0, columnspan = 2)
BottomFrame.pack_propagate(False)
help_btn = Button(BottomFrame, width = 5, height = 3, text = '?', image = None)
help_btn.pack(side = 'right')
master.bind("<Button>",active)
timeset()
timeout()
#----------------------------------------------------------------------------------------
if __name__ == "__main__":
root = VendingApp()
#Renames the TITLE of the window
root.title("Vending machine")
root.geometry("1920x1080")
root.attributes('-fullscreen', True)
root.resizable(False, False)
root.mainloop()
The problem is that the whole program 'freezes' every time the msg box pop up and unless the user confirms 'ok' then the function will pick up where it left off. Is there any way to keep the function going, hence the number in the msg box will update according to the time remaining? Am I approaching this problem the wrong way? Is there another module for this all along and I am just using the wrong module for the task? Please go easy on me, I am still learning. All responses all much appreciated.
messagebox will pause the further execution until it receives an input. To prevent this you can try the following
Use threading
from tkinter import *
from tkinter import messagebox
from threading import Thread
def msgbox():
def _display():
messagebox.showinfo('Info','Self distruction after 2 seconds')
Thread(target=_display).start()
root.after(2000,root.destroy)
root=Tk()
button=Button(root,text='Run',command=msgbox)
button.pack()
root.mainloop()
Create your own info box using Toplevel (the below example will create a replica (sort of) of standard windows info box)
from tkinter import *
import tkinter.ttk as ttk
class InfoBox(Toplevel):
def __init__(self,title,message,parent=None):
Toplevel.__init__(self,parent)
self.bell()
self.transient(self.master)
self.title(title)
self.config(bg='white')
top_frame=Frame(self,bd=0,bg=self['bg'])
top_frame.pack(side='top',fill='x',pady=20)
bottom_frame=Frame(self,bd=0)
bottom_frame.pack(side='bottom',fill='x')
self.info_icon=Canvas(top_frame,width=36,height=36
,bg=self['bg'],bd=0,highlightthickness=0)
self.info_icon.create_oval(0,0,30,30,fill='#0077be',outline='#0077be')
self.info_icon.create_text(15,16,text='i',font=('',18),fill='white')
self.info_icon.pack(side='left',padx=(20,0),anchor='center')
self.label=Label(top_frame,text=message,bg=self['bg'])
self.label.pack(padx=(1,20),pady=5,anchor='center')
self.ok_button=ttk.Button(bottom_frame,text='OK',
state='active',command=self.destroy)
self.ok_button.pack(anchor='e',padx=15,pady=10)
self.update()
center_x=self.winfo_screenwidth()//2-self.winfo_width()//2
center_y=self.winfo_screenheight()//2-self.winfo_height()//2
self.geometry(f'+{center_x}+{center_y}')
def msgbox():
InfoBox('Info','Self distruction after 2 seconds')
root.after(2000,root.destroy)
root=Tk()
button=Button(root,text='Run',command=msgbox)
button.pack()
root.mainloop()
UPDATE
Stacking is automatically prevented
from tkinter import *
import tkinter.ttk as ttk
class InfoBox(Toplevel):
def __init__(self):
self.exists=False
def call(self,title,message,parent=None):
if self.exists:
self._destroy()
Toplevel.__init__(self,parent)
self.bell()
self.transient(self.master)
self.title(title)
self.config(bg='white')
top_frame=Frame(self,bd=0,bg=self['bg'])
top_frame.pack(side='top',fill='x',pady=20)
bottom_frame=Frame(self,bd=0)
bottom_frame.pack(side='bottom',fill='x')
self.info_icon=Canvas(top_frame,width=36,height=36
,bg=self['bg'],bd=0,highlightthickness=0)
self.info_icon.create_oval(0,0,30,30,fill='#0077be',outline='#0077be')
self.info_icon.create_text(15,16,text='i',font=('',18),fill='white')
self.info_icon.pack(side='left',padx=(20,0),anchor='center')
self.label=Label(top_frame,text=message,bg=self['bg'])
self.label.pack(padx=(1,20),pady=5,anchor='center')
self.ok_button=ttk.Button(bottom_frame,text='OK',
state='active',command=self._destroy)
self.ok_button.pack(anchor='e',padx=15,pady=10)
self.update()
center_x=self.winfo_screenwidth()//2-self.winfo_width()//2
center_y=self.winfo_screenheight()//2-self.winfo_height()//2
self.geometry(f'+{center_x}+{center_y}')
self.protocol('WM_DELETE_WINDOW',self._destroy)
self.exists=True
def _destroy(self):
self.destroy()
self.exists=False
root=Tk()
infobox=InfoBox()
button=Button(root,text='Hello',command=lambda:infobox.call('Info','Hello'))
button.pack()
button1=Button(root,text='World',command=lambda:infobox.call('Info','World'))
button1.pack()
root.mainloop()
I am trying to get input from an entry widget using Python Tkinter using Notebook. I have managed to get it to work on my main canvas but as soon as I introduced tabs, it would not work. I am clearly missing something linking the .get function to the correct location but I can't work out what. Any ideas?
import tkinter
import win32api
from tkinter import ttk
checklist = tkinter.Tk()
checklist.title("My Checklist")
ScreenWidth=checklist.winfo_screenwidth()
ScreenHeight=checklist.winfo_screenheight()
checklist.geometry("%dx%d+0+0" % (ScreenWidth, ScreenHeight))
count = 0
tasks = []
tabcontrol = ttk.Notebook(checklist, width = 500, height = 500)
tab = ttk.Frame(tabcontrol)
tabcontrol.add(tab, text = "Checklist Home Page")
tabcontrol.grid(row = 0, column = 0)
main_canvas = tkinter.Canvas(checklist, width = 1000, height = 1000, highlightbackground="green")
main_canvas.grid(row=1, column = 0)
main_canvas.columnconfigure(3)
main_canvas.rowconfigure(6)
def create_checklist():
global count
count += 1
if count > 10:
win32api.MessageBox(0, 'Maximum number of checklists made!')
else:
tab = ttk.Frame(checklist, width = 500, height = 500)
tabcontrol.add(tab, text = count)
tabcontrol.grid(row = 0, column = 0)
button_add_task = tkinter.Button(tab, text="Add task", width=20, height=1, bg="purple", command=add_task).grid(row = 2, column= 2, pady = (100, 1))
item_entry = tkinter.Entry(tab).grid(row=6, column =2)
list_items = tkinter.Listbox(tab)
list_items.grid(row =7, column =2, pady=10)
def add_task():
task = item_entry.get()
if task !="":
tasks.append('- ' + task)
update_listbox()
button_create_checklist = tkinter.Button(main_canvas, text="New Checklist", width=20, height=1, bg = "purple", command=create_checklist).grid(row = 3, column = 2, pady = 1 )
checklist.mainloop()
My error is currently:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\lucas\anaconda3\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:\Users\lucas\OneDrive\Documents\Employability\untitled0999py.py", line 40, in add_task
task = item_entry.get()
NameError: name 'item_entry' is not defined
Since item_entry is local variable of function create checklist(), it can't be used in add_task() function. So, we'll pass value of Entry field whenever Add Task button is pressed. To do so, we can use lambda expression in command option of Button. There are some minor changes in add_task() function.
Remember: grid is used to organize widgets and it returns None, so item_entry will be a NoneType object in your code and using get() with it will raise AttributeError.
So, we need to replace
item_entry = tkinter.Entry(tab).grid(row=6, column =2)
with
item_entry = tkinter.Entry(master=tab)
item_entry.grid(row=6, column =2)
This code works Fine:
def create_checklist():
global count
count += 1
if count > 10:
win32api.MessageBox(0, 'Maximum number of checklists made!')
else:
tab = ttk.Frame(checklist, width = 500, height = 500)
tabcontrol.add(tab, text = count)
tabcontrol.grid(row = 0, column = 0)
item_entry = tkinter.Entry(master=tab) # Changes in
item_entry.grid(row=6, column =2) # these lines
button_add_task = tkinter.Button(tab, text="Add task", width=20, height=1, bg="purple", command=lambda :add_task(item_entry.get())).grid(row = 2, column= 2, pady = (100, 1))
list_items = tkinter.Listbox(tab)
list_items.grid(row =7, column =2, pady=10)
def add_task(task):
if task !="":
tasks.append('- ' + task)
update_listbox()
Hope this helps.
get_recs is triggered by the start button. A loop in get_recs trys to first delete any existing labels (but always fails), then creates the label, writes to it and then adds it to the grid. However each time start is pressed the existing labels are not destroyed and new labels are created instead of being replaced. I can only assume this means that each time the loop is executed it creates separate labels, which would explain why they are never destroyed but i dont understand why. Here is the code:
import pandas as pd
import numpy as np
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showwarning, showinfo
movies = pd.read_csv('C:/Users/Admin/Python Programs/ml-latest-small/movies.csv')
ratings = pd.read_csv('C:/Users/Admin/Python Programs/ml-latest-small/ratings.csv')
ratings.drop(['timestamp'], axis=1, inplace= True)
class App(Frame):
def replace_name(x):
return movies[movies['movieId']==x].title.values[0]
ratings.movieId = ratings.movieId.map(replace_name)
M = ratings.pivot_table(index=['userId'], columns=['movieId'], values='rating')
def pearsons(s1, s2):
s1_c = s1 - s1.mean()
s2_c = s2 - s2.mean()
return np.sum(s1_c * s2_c) / np.sqrt(np.sum(s1_c ** 2) * np.sum(s2_c ** 2))
def get_recs(self):
movie_name = self.mn.get()
num_recs_str = self.nr.get()
num_recs = int(num_recs_str)
reviews = []
for title in App.M.columns:
if title==movie_name:
continue
cor = App.pearsons(App.M[movie_name], App.M[title])
if np.isnan(cor):
continue
else:
reviews.append((title, cor))
reviews.sort(key=lambda tup: tup[1], reverse=True)
Frame3= Frame(root, bg= 'red')
Frame3.grid()
#for i in range(num_recs):
#exec("Label%d=Label(Frame3,text='', fg = 'blue')\nLabel%d.grid()" % (i,i))
#var_exists = 'Label0' in locals() or 'Label0' in globals()
for i in range(num_recs):
try:
exec("Label%d.destroy()" % (i))
except (NameError):
pass
exec("Label%d=Label(Frame3,text='', fg = 'blue')\n" % (i))
exec("Label%d.config(text=%s)" % (i,reviews[i]))
exec("Label%d.grid()" % (i))
print ("success")
#exec("print (%d)" % (i))
#for x in reviews:
#self.label3.config(text= "\n" + str(reviews[:num_recs]))
#self.label3.config(text=reviews[:num_recs])
return reviews[:num_recs]
def __init__(self, master):
Frame.__init__(self, master)
Frame1 = Frame(master)
Frame1.grid()
Frame2 = Frame(master)
Frame2.grid()
self.filename = None
label1=Label(Frame1, text="Movie: ").grid(row=0, sticky=W)
label2=Label(Frame1, text="Recommendations: ").grid(row=1, sticky=W)
#self.label3=Label(master, text = "", font = 'Purisa', fg='blue')
#self.label3.grid(row = 3)
self.mn = Entry(Frame1)
self.mn.grid(row = 0, column = 1, sticky=W)
#self.mn.delete(0, END)
self.mn.insert(0, "Enter Movie Name")
self.nr = Entry(Frame1)
self.nr.grid(row = 1, column = 1, sticky=W)
#self.nr.delete(0, END)
self.nr.insert(0, "Enter Number of Recommendations")
button1 = Button(Frame2, text="Start", command=self.get_recs)
button2 = Button(Frame2, text="Exit", command=master.destroy)
button1.grid(row = 0, column = 0, padx= 5, pady = 5, sticky =W)
button2.grid(row = 0, column = 1, padx = 5, pady =5, sticky =W)
self.grid()
root = Tk()
root.title("Recommender")
root.geometry("500x500")
app = App(root)
root.mainloop()
Your code throws a NameError because your labels are created locally. On your second click you can't reach them and since you are just passing in except block, you don't see anything.
One approach is, you can create labels as class variables
for i in range(num_recs):
try:
exec("self.Label%d.destroy()" % (i))
except NameError:
print("A NameError has occured")
except AttributeError:
exec("self.Label%d=Label(Frame3,text='', fg = 'blue')\n" % (i))
exec("self.Label%d.config(text=%s)" % (i,reviews[i]))
exec("self.Label%d.grid()" % (i))
Instead of this approach, you can put all your labels into a list then check if said label is in list or not. Which is, IMHO, much better approach.
#a list created in global scope to store all labels
labels = []
def get_recs(self):
....
for i in range(num_recs):
try:
for label in labels:
if label["text"] == reviews[i]:
label.destroy() #destroy the matching label
labels.remove(label) #remove it from labels list
except (NameError):
#Please don't just pass Exceptions. They are there for a reason
print("A NameError has occured")
labels.append(Label(Frame3,text='', fg = 'blue'))
labels[-1].config(text=reviews[i])
labels[-1].grid()
I have a list of radiobuttons and linked to the same variable and I have a submit button that when clicked I want it to pass the current variable value to another function which will then use that number. I used lambda function but when the function should be called I get global name 'num' is not defined. num is my function. Below is my code. Thank you very much.
from tkinter import *
import random
class App:
def __init__(self, master):
def say_one(self):
v = IntVar()
window = Toplevel(root)
for i in range(1,11):
self.label = Radiobutton(
window, text = i , fg = "red",
value = i, variable = v
).grid(row =i, column =0)
# error is occurring in the next line
self.report = Button(
window, text="submit", command= lambda : num (v)
).grid(row = 12, column = 0)
def num( number):
print(number)
root = Tk()
app = App(root)
root.mainloop()
Indent your code correctly.
from tkinter import *
import random
class App:
def __init__(self, master):
self.say_one()
def say_one(self):
v = IntVar()
window = Toplevel(root)
for i in range(1,11):
self.label = Radiobutton(
window, text = i , fg = "red",
value = i, variable = v
).grid(row =i, column =0)
# error is occurring in the next line
self.report = Button(
window, text="submit", command=lambda: num(v)
).grid(row = 12, column = 0)
def num(var):
print(var.get())
root = Tk()
app = App(root)
root.mainloop()
I fixed the indentation, changed the num function to print variable's value instead of printing variable itself.