Same function but different frames - python

I'm trying to make a mini timer programme, but I have a question: each frame uses the same function called setTimer, so what frame should I reference in the setTimer function? Here is my code:
def createFrames(self):
self.timer1_frame = Frame(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = Frame(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = Frame(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = Frame(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
def createWidgets(self):
self.setTimer1_button = Button(self.timer1_frame, text="SET TIMER", command=self.setTimer)
self.setTimer1_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer2_button = Button(self.timer2_frame, text="SET TIMER", command=self.setTimer)
self.setTimer2_button.grid(row=0, column=1, padx=30, pady=20)
self.setTimer3_button = Button(self.timer3_frame, text="SET TIMER", command=self.setTimer)
self.setTimer3_button.grid(row=1, column=0, padx=30, pady=20)
self.setTimer4_button = Button(self.timer4_frame, text="SET TIMER", command=self.setTimer)
self.setTimer4_button.grid(row=1, column=1, padx=30, pady=20)
def setTimer(self):
self.hoursLabel = Label(root, text="Hours: ")
self.minutesLabel = Label(root, text="Minutes: ")
self.secondsLabel = Label(root, text="Seconds: ")
As you can see, I'm referencing root in my setTimer function but I don't think it's correct, what would I put there instead so it knows which frame I'm referring to, rather than having to write 4 lots of the same code (is this possible?)

You could create method with argument frame which is used in Label
def setTimer(self, frame):
self.hoursLabel = Label(frame, text="Hours: ")
# ... rest ...
and then you can use lambda in command= to assign function with argument
self.setTimer1_button = tk.Button..., command=lambda:self.setTimer(self.timer1_frame))
And it works.
But it has one problem: in all frames it assigns labels to the same variables self.hoursLabel, etc. so you can't access labels to change text (ie. to update time). You would have to use separated variables but it would need to keep them on list or dictionary and use frame as key.
import tkinter as tk
class Window():
def __init__(self):
# dictionary for labels
self.labels = {}
self.createFrames()
self.createWidgets()
def createFrames(self):
self.timer1_frame = tk.Frame(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = tk.Frame(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = tk.Frame(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = tk.Frame(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
def createWidgets(self):
self.setTimer1_button = tk.Button(self.timer1_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer1_frame))
self.setTimer1_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer2_button = tk.Button(self.timer2_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer2_frame))
self.setTimer2_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer3_button = tk.Button(self.timer3_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer3_frame))
self.setTimer3_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer4_button = tk.Button(self.timer4_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer4_frame))
self.setTimer4_button.grid(row=0, column=0, padx=30, pady=20)
def setTimer(self, frame):
self.hoursLabel = tk.Label(frame, text="Hours: ")
self.minutesLabel = tk.Label(frame, text="Minutes: ")
self.secondsLabel = tk.Label(frame, text="Seconds: ")
self.hoursLabel.grid(row=1)
self.minutesLabel.grid(row=2)
self.secondsLabel.grid(row=3)
# remember labels in dictionary
self.labels[frame] = [self.hoursLabel, self.minutesLabel, self.secondsLabel]
# start update time
self.updateTimer(frame, 0)
def updateTimer(self, frame, seconds):
secondsLabel = self.labels[frame][2]
secondsLabel['text'] = "Seconds: {}".format(seconds)
seconds += 1
root.after(1000, self.updateTimer, frame, seconds)
root = tk.Tk()
Window()
root.mainloop()
EDIT:
It can be simpler to use Frame to create own widget with one Frame, one Button, all Labels and variables needed in timer. And later use this widget 4 times in main window.
import tkinter as tk
class MyTimer(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.hours = 0
self.minutes = 0
self.seconds = 0
self.button = tk.Button(self, text="SET TIMER", command=self.set_timer)
self.button.grid(row=0, column=0, padx=30, pady=20)
self.hours_label = None
self.minutes_label = None
self.seconds_label = None
def set_timer(self):
if self.hours_label is None:
self.hours_label = tk.Label(self, text="Hours: ")
self.minutes_label = tk.Label(self, text="Minutes: ")
self.seconds_label = tk.Label(self, text="Seconds: ")
self.hours_label.grid(row=1)
self.minutes_label.grid(row=2)
self.seconds_label.grid(row=3)
# reset time
self.hours = 0
self.minutes = 0
self.seconds = 0
# start updating time
self.update_timer()
def update_timer(self):
self.hours_label['text'] = "Hours: {}".format(self.hours)
self.minutes_label['text'] = "Minutes: {}".format(self.minutes)
self.seconds_label['text'] = "Seconds: {}".format(self.seconds)
self.seconds += 1
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
# update again after 1000ms (1s)
root.after(1000, self.update_timer)
class Window():
def __init__(self):
self.createFrames()
def createFrames(self):
self.timer1_frame = MyTimer(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = MyTimer(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = MyTimer(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = MyTimer(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
root = tk.Tk()
Window()
root.mainloop()

Related

tkinter scrollbar (frame/canvas) in application that create SubFrames

I have researched and tested a number of solutions on stackoverflow and other sites but I cannot get a scrollbar to work with my application.
I'm looking for a scrollbar capable of scrolling the SubFrame group that my application creates. I also need to delimit a minimum window height, and that this window can be extended, but without breaking the scrollbar.
Thank you in advance to all who will help me.
I admit not having understood which should go above the other, the Canvas or the Frame.
Here is a piece of code, with all the graphical widgets to visualize the scrollbar effect :
import tkinter as tk
from tkinter import ttk
class FrameStack(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.subframes = []
self.topFrame = tk.Frame(root)
self.topFrame.pack(side="top", fill="x")
self.groupOfFrames = tk.Frame(root)
self.groupOfFrames.pack(side="top", fill="both", expand=True, pady=(32,0))
self.scrollbar = tk.Scrollbar(self.groupOfFrames, orient="vertical")
self.scrollbar.pack(side="right",fill="y")
#self.canvas = tk.Canvas(self.groupOfFrames, yscrollcommand=self.scrollbar.set)
#self.canvas.pack()
#self.canvas_window = self.canvas.create_window(0, 0, anchor="nw", window=self.groupOfFrames)
self.create_widget()
def create_widget(self):
tk.Label(self.topFrame, text="BUS").grid(row=0, column=0)
tk.Label(self.topFrame, text="IP").grid(row=1, column=0)
tk.Label(self.topFrame, text="REG").grid(row=2, column=0)
tk.Label(self.topFrame, text="SIZE").grid(row=3, column=0)
self.combobox_bus = ttk.Combobox(self.topFrame, values=list(dic.keys()), width=40, justify='center', state="readonly")
self.combobox_bus.bind('<<ComboboxSelected>>', self.getUpdateDataIP)
self.combobox_bus.grid(row=0, column=1, sticky="nsew")
self.combobox_ip = ttk.Combobox(self.topFrame, justify='center', width=40, state="readonly")
self.combobox_ip.bind('<<ComboboxSelected>>', self.getUpdateDataReg)
self.combobox_ip.grid(row=1, column=1, sticky="nsew")
self.button_read = tk.Button(self.topFrame, text="Read", command=self.read)
self.button_write = tk.Button(self.topFrame, text="Write", command=self.write)
self.button_read.grid(row=0, column=2, columnspan=2, sticky="nsew")
self.button_write.grid(row=1, column=2, columnspan=2, sticky="nsew")
self.button_hsid = tk.Button(self.topFrame, text="HSID")
self.button_hsid.grid(row=0, column=4, columnspan=2, sticky="nsew")
self.button_add = tk.Button(self.topFrame, text="+", command=self.add_frame)
self.button_add.grid(row=1, column=4, columnspan=2, sticky="nsew")
self.combobox_reg_dl = ttk.Combobox(self.topFrame, width=40, justify='center', state="readonly")
self.combobox_reg_dl.bind('<<ComboboxSelected>>', self.getUpdateDisplayReg)
self.combobox_reg_dl.grid(row=2, column=1, sticky="nsew")
self.button_dump = tk.Button(self.topFrame, text="Dump", command=self.dump)
self.button_load = tk.Button(self.topFrame, text="Load", command=self.load)
self.button_dump.grid(row=2, column=2, columnspan=2, sticky="nsew")
self.button_load.grid(row=2, column=4, columnspan=2, sticky="nsew")
self.select_size = tk.StringVar()
self.select_size.set("0")
self.entry_size = tk.Entry(self.topFrame, textvariable=self.select_size, justify='center')
self.entry_size.grid(row=3, column=1, sticky="nsew")
tk.Scale(self.topFrame, from_=0, to=1024, variable=self.select_size)
self.select_read_size = tk.IntVar()
self.select_read_size.set(8)
self.radio_8 = tk.Radiobutton(self.topFrame, text=" 8", variable=self.select_read_size, value=8)
self.radio_16 = tk.Radiobutton(self.topFrame, text="16", variable=self.select_read_size, value=16)
self.radio_32 = tk.Radiobutton(self.topFrame, text="32", variable=self.select_read_size, value=32)
self.radio_64 = tk.Radiobutton(self.topFrame, text="64", variable=self.select_read_size, value=64)
self.radio_8.grid(row=3, column=2)
self.radio_16.grid(row=3, column=3)
self.radio_32.grid(row=3, column=4)
self.radio_64.grid(row=3, column=5)
def getUpdateDataIP(self, event):
self.combobox_ip['values'] = list(dic[self.combobox_bus.get()].keys())
self.combobox_ip.set('')
self.combobox_reg_dl['values'] = ['']
self.combobox_reg_dl.set('')
for f in self.subframes:
f.combobox_reg_rw['values'] = ['']
f.combobox_reg_rw.set('')
def getUpdateDataReg(self, event):
self.combobox_reg_dl['values'] = list(dic[self.combobox_bus.get()][self.combobox_ip.get()].keys())
self.combobox_reg_dl.set('')
for f in self.subframes:
f.combobox_reg_rw['values'] = list(dic[self.combobox_bus.get()][self.combobox_ip.get()].keys())
f.combobox_reg_rw.set('')
def getUpdateDisplayReg(self, event):
self.combobox_reg_dl.set(self.combobox_reg_dl.get()+" ("+dic[self.combobox_bus.get()][self.combobox_ip.get()][self.combobox_reg_dl.get()]+")")
def delete_frame(self, frame):
self.subframes.remove(frame)
frame.destroy()
def add_frame(self):
f = SubFrame(parent=self.groupOfFrames, controller=self)
if self.combobox_bus.get()!="" and self.combobox_ip.get()!="":
f.combobox_reg_rw['values'] = list(dic[self.combobox_bus.get()][self.combobox_ip.get()].keys())
self.subframes.append(f)
f.pack(side="top", fill="x", pady=(0,5))
def read(self):
pass
def write(self):
pass
def dump(self):
pass
def load(self):
pass
class SubFrame(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.parent = parent
self.controller = controller
self.create_widget()
def create_widget(self):
self.combobox_reg_rw = ttk.Combobox(self, width=47, justify='center', state="readonly")
self.combobox_reg_rw.bind('<<ComboboxSelected>>', self.getUpdateDisplayReg)
self.select_val = tk.StringVar()
self.entry_value = tk.Entry(self, bd=1.5, width=20, textvariable=self.select_val, justify='center')
self.button_write = tk.Button(self, text="Write", command=self.write)
self.button_read = tk.Button(self, text="Read", command=self.read)
self.button_help = tk.Button(self, text="?", command=self.help)
self.button_remove = tk.Button(self, text="-", command=self.remove)
self.combobox_reg_rw.grid(row=0, column=0, columnspan=2, sticky="ew")
self.entry_value.grid(row=0, column=2, columnspan=2, sticky="ew")
self.button_write.grid(row=1, column=0, sticky="ew")
self.button_read.grid(row=1, column=1, sticky="ew")
self.button_help.grid(row=1, column=2, sticky="nsew")
self.button_remove.grid(row=1, column=3, sticky="nsew")
def remove(self):
self.controller.delete_frame(self)
def getUpdateDisplayReg(self, event):
self.combobox_reg_rw.set(self.combobox_reg_rw.get()+" ("+dic[self.controller.combobox_bus.get()][self.controller.combobox_ip.get()][self.combobox_reg_rw.get()]+")")
def write(self):
print(self.entry_value.get())
def read(self):
print(self.combobox_reg_rw.get())
def help(self):
pass
if __name__ == "__main__":
#dic = extractor.main()
dic = {'1': {'1.1': {'1.1.1': 'A', '1.1.2': 'B'}, '1.2': {'1.2.1': 'C', '1.2.2': 'D'}}, '2': {'2.1': {'2.1.1': 'E', '2.2.2': 'F'}, '2.2': {'2.2.1': 'G', '2.2.2': 'H'}}}
root = tk.Tk()
root.title("XXXXXX")
root.resizable(False, True)
fs = FrameStack(root)
fs.pack(fill="both", expand=True)
root.mainloop()
It is the canvas that has the ability to scroll, so your self.groupOfFrames needs to go inside the canvas. And since you want the scrollbar and canvas to appear as a single complex object, they should go in a frame.
You need to make sure that when the window is resized, the frame inside the canvas is resized as well. And you also need to make sure that when you add something to self.groupOfFrames you also update the scrollregion. These can be done by binding to the <Configure> event of each widget.
Thus, I would create the FrameStack like the following. Please note that self.topFrame and self.bottomFrame are children of self rather than root. That is a mistake I didn't catch in the code I gave you in your previous question.
class FrameStack(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.subframes = []
self.topFrame = tk.Frame(self)
self.bottomFrame = tk.Frame(self)
self.topFrame.pack(side="top", fill="x")
self.bottomFrame.pack(side="bottom", fill="both", expand=True)
self.canvas = tk.Canvas(self.bottomFrame, bd=0)
vsb = tk.Scrollbar(self.bottomFrame, command=self.canvas.yview)
self.canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.groupOfFrames = tk.Frame(self.canvas)
self.canvas.create_window(0, 0, anchor="nw", window=self.groupOfFrames, tags=("inner",))
self.canvas.bind("<Configure>", self._resize_inner_frame)
self.groupOfFrames.bind("<Configure>", self._reset_scrollregion)
def _reset_scrollregion(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def _resize_inner_frame(self, event):
self.canvas.itemconfig("inner", width=event.width)

How to add timer to each divide screen in tkinter

I created one window and split it into 3 different windows. I want to add a clock to each screen, that does not depend on the other clocks.
my code opens 2 window- one is the timer and the second one is the window that i split to 3 windows.
from tkinter import *
import Tkinter as tk
class split_screen(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label = tk.Label(self, text="", width=10,fg="black", bg ="red", font="david 18 bold underline")
self.label.pack()
self.remaining = 0
self.countdown(1000)
self.configure(background="black")
def screen(self):
root = Tk()
root.geometry("650x700")
root.configure(bg='black')
root.title("test")
left = Frame(root, borderwidth=200, relief="solid")
right = Frame(root, borderwidth=20, relief="solid")
box3 = Frame(right, borderwidth=2, relief="solid")
box1 = Frame(left, borderwidth=2, relief="solid")
box2 = Frame(left, borderwidth=2, relief="solid")
label1 = Label(box3, text="winner's" + "\n\n\n" + "Player 1",fg= "black", bg = "red", font = "david 18 bold underline")
label2 = Label(box1, text="Computer 1",fg = "black", bg = "red", font= "david 18 bold underline")
label3 = Label(box2, text="Computer 2",fg = "black", bg = "red", font= "david 18 bold underline")
left.pack(side="left", expand=True, fill="both")
right.pack(side="right", expand=True, fill="both")
box3.pack(expand=True, fill="both", padx=10, pady=10)
box1.pack(expand=True, fill="both", padx=10, pady=10)
box2.pack(expand=True, fill="both", padx=10, pady=10)
label1.pack()
label2.pack()
label3.pack()
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="time's up!")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
if __name__ == "__main__":
app = split_screen()
app.screen()
app.mainloop()
I did multiple changes :
As Bryan Oakley said, don't create multiple instances of Tk(). You also don't need to import tkinter twice.
import tkinter as tk
class split_screen(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.geometry("650x700")
self.configure(bg='black')
self.title("test")
left = tk.Frame(self, borderwidth=200, relief="solid")
right = tk.Frame(self, borderwidth=20, relief="solid")
box1 = tk.Frame(left, borderwidth=2, relief="solid")
box2 = tk.Frame(left, borderwidth=2, relief="solid")
box3 = tk.Frame(right, borderwidth=2, relief="solid")
label1 = tk.Label(box3, text="winner's" + "\n\n\n" + "Player 1",fg= "black", bg = "red", font = "david 18 bold underline")
label2 = tk.Label(box1, text="Computer 1",fg = "black", bg = "red", font= "david 18 bold underline")
label3 = tk.Label(box2, text="Computer 2",fg = "black", bg = "red", font= "david 18 bold underline")
clock1 = Clock(box1, 1000)
clock2 = Clock(box2, 2000)
clock3 = Clock(box3, 1300)
left.pack(side="left", expand=True, fill="both")
right.pack(side="right", expand=True, fill="both")
box3.pack(expand=True, fill="both", padx=10, pady=10)
box1.pack(expand=True, fill="both", padx=10, pady=10)
box2.pack(expand=True, fill="both", padx=10, pady=10)
label1.pack()
label2.pack()
label3.pack()
class Clock():
def __init__(self, frame, count):
self.frame = frame
self.label = tk.Label(frame, text="", width=10,fg="black", bg ="red", font="david 18 bold underline")
self.label.pack()
self.remaining = 0
self.countdown(count)
frame.configure(background="black")
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="time's up!")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.frame.after(1000, self.countdown)
if __name__ == "__main__":
app = split_screen()
app.mainloop()
The simplest solution -- and arguably the best -- is to create a class that represents a single timer. You can then make as many instances as you want. This is precisely the sort of thing that classes are for: to encapsulate some behavior inside an object.
Since you're using a label to display the time, you can either have the timer class inherit from Label, or you can pass the label in when creating the timer.
Here's an example which inherits from tk.Label:
import tkinter as tk
class TimerLabel(tk.Label):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.after_id = None
self.remaining = None
def countdown(self, seconds):
if seconds <= 0:
self.configure(text="Time's up!")
else:
self.configure(text=f"{seconds}")
self.after_id = self.after(1000, self.countdown, seconds-1)
def stop(self):
if self.after_id:
self.configure(text="cancelled")
self.after_cancel(self.after_id)
Using the above class definition, you can create as many timer widgets as you want. Here's an example that creates three:
root = tk.Tk()
timer1 = TimerLabel(root, width=10)
timer2 = TimerLabel(root, width=10)
timer3 = TimerLabel(root, width=10)
timer1.pack(side="top", padx=20)
timer2.pack(side="top", padx=20)
timer3.pack(side="top", padx=20)
timer1.countdown(30)
timer2.countdown(20)
timer3.countdown(10)
root.mainloop()

python tkinter entry input divided by label input from optionMenu

I need a bit off help..
In the example below I have two optionsMenus, two entries, and some labels.
What I'm trying to do, is to divide my input from the entry by the labels value, choosen from the optionsMenu, and then show the new value in the next column. But I'm a bit stuck now and can't get it to work.
from tkinter import *
class App(Frame):
def __init__(self, root=None):
Frame.__init__(self, root)
self.materialPrice = {'Brick': 70, 'Rockwool': 50, 'Concrete': 20}
materialvariable1 = StringVar(self, root)
materialvariable1.set("Choose material")
materialvariable2 = StringVar(self, root)
materialvariable2.set("Choose materiale")
self.w1 = OptionMenu(root, materialvariable1, *self.materialPrice, command=self.displayPrice).grid(row=2,
column=0,
columnspan=1,
sticky='WE')
self.w2 = OptionMenu(root, materialvariable2, *self.materialPrice, command=self.displayPrice2).grid(row=3,
column=0,
columnspan=1,
sticky='WE')
self.var = IntVar()
self.var.set(float(0.00))
self.var2 = IntVar()
self.var2.set(float(0.00))
self.entry1 = Entry(root, textvariable=self.var).grid(row=2, column=1)
self.entry2 = Entry(root, textvariable=self.var2).grid(row=3, column=1)
self.priceVarLabel1 = IntVar()
self.priceVarLabel1.set(float(0.00))
self.priceVarLabel2 = IntVar()
self.priceVarLabel2.set(float(0.00))
self.priceVarValue1 = Label(root, textvariable=self.priceVarLabel1, relief='sunken').grid(row=2,
column=2,
columnspan=1,
sticky='WE')
self.priceVarValue2 = Label(root, textvariable=self.priceVarLabel2, relief='sunken').grid(row=3,
column=2,
columnspan=1,
sticky='WE')
self.label1 = Label(root, textvariable=self.displayResult).grid(row=2, column=3)
self.label2 = Label(root, textvariable=self.displayResult2).grid(row=3, column=3)
def displayPrice(self, value):
self.priceVarLabel1.set(self.materialPrice[value])
def displayPrice2(self, value):
self.priceVarLabel2.set(self.materialPrice[value])
def displayResult(self):
self.label1.set(self.entry1 / self.priceVarValue1)
def displayResult2(self):
self.label1.set(self.entry1 / self.priceVarValue1)
root = Tk()
app = App(root)
root.title("help")
root.mainloop()
Just add the division to your function:
def displayPrice(self, value):
self.priceVarLabel1.set(self.materialPrice[value] / self.var.get())
You may want to change the starting value to 1 so that you don't get a ZeroDivisionError right off the bat.
BTW, initializing a widget and laying it out on the same line is a well known bug source. Always use 2 lines.
# very bad:
self.entry1 = Entry(root, textvariable=self.var).grid(row=2, column=1)
# good:
self.entry1 = Entry(root, textvariable=self.var)
self.entry1.grid(row=2, column=1)

Scrollable frame will not render all items in it Python Tkinter

I am working on a program where there is a scrollable frame that will be containing a large quantity of items. But with my app it does not render all of them. Can someone possibly tell me why? And how I can fix it?
Code:
#700x650
from Tkinter import *
import ttk
class itemLoad:
def __init__(self):
pass
def item(self):
items = "Video File,Image File,None,King King"
return items
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack(fill=BOTH)
self.loadItem = itemLoad()
self.one = None
self.create_widgets()
self.loadItems()
def create_widgets(self):
self.mainFrame = Frame(self, width=700, height=650)
self.mainFrame.pack_propagate(False)
self.mainFrame.pack()
self.menu = Frame(self.mainFrame, width=150, height=650, bg="Gray92")
self.menu.pack_propagate(False)
self.menu.pack(side=LEFT)
self.itemMenu = Frame(self.mainFrame, width=550, height=650)
self.itemMenu.pack_propagate(False)
self.itemMenu.pack(side=LEFT)
self.vScroller = ttk.Scrollbar(self.itemMenu, orient=VERTICAL)
self.vScroller.pack(side=RIGHT, fill=Y)
self.canvas = Canvas(self.itemMenu, bd=0, width=534, highlightthickness=0, yscrollcommand=self.vScroller.set)
self.canvas.pack_propagate(False)
self.canvas.pack(side=LEFT, fill=BOTH)
self.vScroller.config(command=self.canvas.yview)
self.innerFrame = Frame(self.canvas, width=550, height=650, bg="Pink")
self.canvas.create_window(0, 0, window=self.innerFrame, anchor=NW)
def update(event):
self.canvas.config(scrollregion=self.canvas.bbox("all"))
self.innerFrame.bind("<Configure>", update)
self.spacer = Frame(self.mainFrame, bg="Gray")
self.spacer.pack(side=LEFT, fill=Y)
frame = Frame(self.menu, bg="Gray92")
frame.pack(side=TOP, fill=X)
high = Frame(frame, bg="Gray92", width=10)
high.pack(side=LEFT, fill=Y)
self.bu1 = Label(frame, font=("Calibri", 14), text=" Main Folder", width=12, anchor=W, bg="Gray92")
self.bu1.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
frame2 = Frame(self.menu, bg="Gray92")
frame2.pack(side=TOP, fill=X)
high2 = Frame(frame2, bg="Gray92", width=10)
high2.pack(side=LEFT, fill=Y)
self.bu2 = Label(frame2, font=("Calibri", 14), text=" Favorited", width=12, anchor=W, bg="Gray92")
self.bu2.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
frame3 = Frame(self.menu, bg="Gray92")
frame3.pack(side=TOP, fill=X)
high3 = Frame(frame3, bg="Gray92", width=10)
high3.pack(side=LEFT, fill=Y)
self.bu3 = Label(frame3, font=("Calibri", 14), text=" Trash Can", width=12, anchor=W, bg="Gray92")
self.bu3.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
frame4 = Frame(self.menu, bg="Gray92")
frame4.pack(side=BOTTOM, fill=X)
high4 = Frame(frame4, bg="Gray92", width=10)
high4.pack(side=LEFT, fill=Y)
self.bu4 = Label(frame4, font=("Calibri", 14), text=" Log Out", width=12, anchor=W, bg="Gray92")
self.bu4.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
def hover(event):
widg = event.widget
items = widg.winfo_children()
if items[1].cget("text") == self.one:
pass
else:
items[0].config(bg="Gray85")
items[1].config(bg="Gray85")
def unHover(event):
widg = event.widget
text = None
items = widg.winfo_children()
if items[1].cget("text") == self.one:
pass
else:
items[0].config(bg="Gray92")
items[1].config(bg="Gray92")
def clicked(event):
widg = event.widget
par = widg.winfo_parent()
par = self.menu._nametowidget(par)
for item in self.menu.winfo_children():
items = item.winfo_children()
items[0].config(bg="Gray92")
for item in par.winfo_children():
try:
self.one = item.cget("text")
except:
item.config(bg="lightBlue")
frame.bind("<Enter>", hover)
frame2.bind("<Enter>", hover)
frame3.bind("<Enter>", hover)
frame4.bind("<Enter>", hover)
frame.bind("<Leave>", unHover)
frame2.bind("<Leave>", unHover)
frame3.bind("<Leave>", unHover)
frame4.bind("<Leave>", unHover)
high.bind("<Button-1>", clicked)
self.bu1.bind("<Button-1>", clicked)
high2.bind("<Button-1>", clicked)
self.bu2.bind("<Button-1>", clicked)
high3.bind("<Button-1>", clicked)
self.bu3.bind("<Button-1>", clicked)
high4.bind("<Button-1>", clicked)
self.bu4.bind("<Button-1>", clicked)
def loadItems(self):
theItems = self.loadItem.item()
for i in range(0, 500):
none = Frame(self.innerFrame, width=200, height=500, bg="red")
none.pack_propagate(False)
none.pack(side=TOP, padx=10, pady=10)
let = Label(none, text=i)
let.pack(side=TOP)
root = Tk()
root.geometry("700x650")
root.resizable(0,0)
app = App(root)
root.mainloop()
I think you're exceeding the limits of the tkinter canvas. The frame you're trying to scroll is 250,000 pixels tall. I doubt the canvas can handle that.
When I make all of your inner widgets considerably smaller your code works fine.

Check what tkinter OptionMenu item was selected

How do you check if the user has selected "Other" from the hopOptions selection, and then enable otherEntry if they did? And then disable it again if they select one of the other options.
class Interface():
def __init__(self, window):
frame = Frame(window)
frame.pack()
self.hopLabel = Label(frame, text="Hop:", anchor=E)
self.hopLabel.grid(row=0, column=0, sticky=EW)
hops = range(0,6)
hops.append("Other")
self.selectedHop = StringVar(frame)
self.selectedHop.set(hops[0])
self.hopOptions = OptionMenu(frame, self.selectedHop, *hops)
self.hopOptions.grid(row=0, column=2, sticky=EW)
self.otherEntry = Entry(frame, state=DISABLED)
self.otherEntry.grid(row=0, column=1, sticky=EW)
root = Tk()
app = Interface(root)
root.mainloop()
Bind the option menu to a command and add another method to your class. The command will run the class method with the value as an argument anytime an option is changed in the menu. There you can do validation to update the otherEntry widget. Also I would advise not doing from Tkinter import * as it appears that's what you've done. Generally importing an entire package could have conflicts with your namespace. This should suit your needs:
from Tkinter import *
class Interface():
def __init__(self, window):
frame = Frame(window)
frame.pack()
self.hopLabel = Label(frame, text="Hop:", anchor=E)
self.hopLabel.grid(row=0, column=0, sticky=EW)
hops = range(0,6)
hops.append("Other")
self.selectedHop = StringVar(frame)
self.selectedHop.set(hops[0])
self.hopOptions = OptionMenu(frame, self.selectedHop, *hops, command=self.optupdate)
self.hopOptions.grid(row=0, column=2, sticky=EW)
self.otherEntry = Entry(frame, state=DISABLED)
self.otherEntry.grid(row=0, column=1, sticky=EW)
def optupdate(self, value):
if value == "Other":
self.otherEntry.config(state=NORMAL)
else:
self.otherEntry.config(state=DISABLED)
if __name__ == "__main__":
root = Tk()
app = Interface(root)
root.mainloop()
As an alternative to iChar's command approach, Use selectedHop.trace to register a function that will be called whenever the selected item changes.
from Tkinter import *
class Interface():
def __init__(self, window):
frame = Frame(window)
frame.pack()
self.hopLabel = Label(frame, text="Hop:", anchor=E)
self.hopLabel.grid(row=0, column=0, sticky=EW)
hops = range(0,6)
hops.append("Other")
self.selectedHop = StringVar(frame)
self.selectedHop.set(hops[0])
self.selectedHop.trace("w", self.selected_hop_changed)
self.hopOptions = OptionMenu(frame, self.selectedHop, *hops)
self.hopOptions.grid(row=0, column=2, sticky=EW)
self.otherEntry = Entry(frame, state=DISABLED)
self.otherEntry.grid(row=0, column=1, sticky=EW)
def selected_hop_changed(self, *args):
value = self.selectedHop.get()
if value == "Other":
self.otherEntry.config(state=NORMAL)
else:
self.otherEntry.config(state=DISABLED)
root = Tk()
app = Interface(root)
root.mainloop()

Categories