Create n number of windows in tkinter - python

I am writing a programm in which I am trying to open a n number of windows. My code is:
from tkinter import *
from tkinter import ttk
class Main_window(ttk.Frame):
"""A program"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Creates all the objects in the window"""
self.min_lbl = ttk.Label(self, text = "1").grid(row = 0, column = 0,
sticky = W)
self.max_lbl = ttk.Label(self, text = "100").grid(row = 0, column = 2,
sticky = W)
spinval = IntVar()
self.scale = ttk.Scale(self, orient = HORIZONTAL,
length = 200,
from_ = 1, to = 100,
variable = spinval,
command=self.accept_whole_number_only)
self.scale.grid(row = 0,column = 1,sticky = W)
self.spinbox = Spinbox(self, from_ = 1, to = 100,
textvariable = spinval,
command = self.update,
width = 10)
self.spinbox.grid(row = 0,column =3,sticky = W)
self.go_bttn = ttk.Button(self, text = "Go",
command = self.create_windows
).grid(row = 1, column = 1, sticky = W)
def accept_whole_number_only(self, e=None):
"""Makes the numbers from the scale whole"""
value = self.scale.get()
if int(value) != value:
self.scale.set(round(value))
def update(self):
"""Updates the scale and spinbox"""
self.scale.set(self.spinbox.get())
def create_windows(self):
"""This function will create all the new windows"""
value = self.scale.get()
window_num = value
negative_window_num = 1
while window_num != 0:
root = Tk()
root.title("This is Window "+str(window_num)[:-2]+" of "+str(value)[:-2])
root.geometry("350x200")
app = Window_creator(root)
root.mainloop()
window_num -= 1
class Window_creator(ttk.Frame):
"""makes child windows"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""creates all the widgets in the window"""
def main():
"""Loops the window"""
root = Tk()
root.title("Programm")
root.geometry("350x200")
app = Main_window(root)
root.mainloop()
main()
What I want this code to do is I want to be able to set the spinbox or scale to number n and then when I click the Button i want n numbers of child windows to appear. I tried this with a while loop but it doesn't quite work like I want it to by creating a new window just after the I closed the prevoius window. You also have to close the main window first for it to work (I am going to make the button close the window automatically later). Any Ideas on how I could make this work?

Call child = Toplevel(), instead of root = Tk().
Also, you can not call mainloop more than once (since there should be only one event loop).
from tkinter import *
from tkinter import ttk
class Main_window(ttk.Frame):
"""A program"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""Creates all the objects in the window"""
self.min_lbl = ttk.Label(self, text = "1").grid(row = 0, column = 0,
sticky = W)
self.max_lbl = ttk.Label(self, text = "100").grid(row = 0, column = 2,
sticky = W)
spinval = IntVar()
self.scale = ttk.Scale(self, orient = HORIZONTAL,
length = 200,
from_ = 1, to = 100,
variable = spinval,
command=self.accept_whole_number_only)
self.scale.grid(row = 0,column = 1,sticky = W)
self.spinbox = Spinbox(self, from_ = 1, to = 100,
textvariable = spinval,
command = self.update,
width = 10)
self.spinbox.grid(row = 0,column =3,sticky = W)
self.go_bttn = ttk.Button(self, text = "Go",
command = self.create_windows
).grid(row = 1, column = 1, sticky = W)
def accept_whole_number_only(self, e=None):
"""Makes the numbers from the scale whole"""
value = self.scale.get()
if int(value) != value:
self.scale.set(round(value))
def update(self):
"""Updates the scale and spinbox"""
self.scale.set(self.spinbox.get())
def create_windows(self):
"""This function will create all the new windows"""
value = self.scale.get()
window_num = value
negative_window_num = 1
for n in range(int(window_num)):
child = Toplevel()
child.title("This is Window "+str(window_num)[:-2]+" of "+str(value)[:-2])
child.geometry("350x200")
app = Window_creator(child)
class Window_creator(ttk.Frame):
"""makes child windows"""
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""creates all the widgets in the window"""
def main():
"""Loops the window"""
root = Tk()
root.title("Programm")
root.geometry("350x200")
app = Main_window(root)
root.mainloop()
main()

Related

How can I detect which on which frame was a Button Clicked in Tkinter?

I have a question. I have this code:
import tkinter as tk
class new_f:
def __init__(self,root,num):
self.new_frame=tk.Frame(root,width=100,height=100,bg='white',bd=3,relief=tk.GROOVE)
self.new_frame.pack(side=tk.LEFT,fill=tk.X,expand=True)
self.num=num
def add_label(self,t):
self.l1=tk.Label(self.new_frame,bg='white',text=t)
self.l1.pack()
def return_instance(self):
return self.num
class Main_win:
def __init__(self,root):
self.root=root
self.bind_number=0
self.current_index=0
self.instance_list=[]
self.b1=tk.Button(self.root,text='Add Frame',command=self.add_frame_win)
self.b1.pack(side=tk.BOTTOM)
self.b2=tk.Button(self.root,text='Add text',command=self.add_text_frame)
self.b2.pack(side=tk.BOTTOM)
def return_instance_num(self,num,*args):
self.current_index=num
def add_frame_win(self):
new_in=new_f(self.root,self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>',lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index=new_in.return_instance()
self.bind_number+=1
def add_text_frame(self):
instance=self.instance_list[self.current_index]
instance.add_label('Hello World')
root=tk.Tk()
ob=Main_win(root)
root.mainloop()
What I a trying to achieve is that I want to detect on which frame was the left mouse-button clicked so as to make that Frame active and add the labels to that particular Frame. However, I am stuck on how would I go about writing the code. I need a new class Because I don't know how many frames will the user need.
This is a short example of the code I will be implementing later. So my question is:
How will I go to detect which frame was picked so as to make it active to add the labels?
In this approach I have label l1 bound to Button-1
This was achieved by passing self to new_f instead of root
and binding self.l1 to Button-1
import tkinter as tk
class new_f:
def __init__(self, prog, num):
self.prog = prog
self.new_frame = tk.Frame(prog.root, width = 100, height = 100, bg = 'white', bd = 3, relief = tk.GROOVE)
self.new_frame.pack(side = tk.LEFT, fill = tk.X, expand = True)
self.num = num
def add_label(self, t):
self.l1 = tk.Label(self.new_frame, bg = 'white', text = t)
self.l1.pack()
# binding button-1 press to label
self.l1.bind("<Button-1>", lambda evnt: self.prog.return_instance_num(self.return_instance()))
def return_instance(self):
return self.num
class Main_win:
def __init__(self, root):
self.root = root
self.bind_number = 0
self.current_index = 0
self.instance_list = []
self.b1 = tk.Button(self.root, text = 'Add Frame', command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(self.root, text = 'Add text', command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def return_instance_num(self, num, *args):
self.current_index = num
def add_frame_win(self):
# note passing self not root
new_in = new_f(self, self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>', lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index = new_in.return_instance()
self.bind_number = self.bind_number + 1
def add_text_frame(self):
instance = self.instance_list[self.current_index]
instance.add_label('Hello World')
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
Here is an alternative method that uses dictionaries to store l1 and new_frame objects as keys and new_f instances as values.
This method can be used for other tkinter objects (Entry, Listbox, Text, Canvas)
import tkinter as tk
class new_f:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(
parent.root, width = 100, height = 100,
bg = "white", bd = 3, relief = tk.GROOVE)
self.frame.pack(
side = tk.LEFT, fill = tk.X, expand = True)
self.frame.bind("<Button-1>", parent.get_current_frame)
def add_label(self, t):
self.label = tk.Label(self.frame, bg = "white", text = t)
self.label.pack(fill = tk.BOTH, expand = True)
# bind button-1 to label, set instance_label and current to self
self.label.bind("<Button-1>", self.parent.get_current_label)
self.parent.instance_label[self.label] = self.parent.current = self
class Main_win:
instance_label = dict() # This method can be expanded for other objects
instance_frame = dict() # that you may want to create in frames
def __init__(self, root):
self.root = root
self.b1 = tk.Button(
self.root, text = "Add Frame", command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(
self.root, text = "Add text", command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def get_current_label(self, ev):
self.current = self.instance_label[ev.widget]
def get_current_frame(self, ev):
self.current = self.instance_frame[ev.widget]
def add_frame_win(self):
# note passing self not root
self.new_in = new_f(self)
self.instance_frame[self.new_in.frame] = self.current = self.new_in
def add_text_frame(self):
# Change message with entry tool?
self.current.add_label("Hello World")
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()

Calling a class method in another class - Tkinter

I am using Tkinter in Python 3 to create a converter between hex, binary and denary - while creating my own methods for conversions instead of using the built in methods.
I've completed the denary to binary conversion through the use of my 'highestPowerOf2' method in the DenToBin class. I want to use this method in my HexToBin class, but whenever I try to, an additional blank window pops up and I receive - AttributeError: 'NoneType' object has no attribute 'title' - error.
How would I run 'highestPowerOf2' in the HexToBin class?
(also sorry for any poor programming practise)
Code:
from tkinter import *
from functools import partial
class Menu(Frame):
def __init__(self, master= None): # initialise Menu
Frame.__init__(self, master) # initialise Frame
self.master = master # what does this do
self.createWindow()
def createWindow(self):
self.pack(fill = BOTH, expand = 1)
denToBin = Button(self, text = 'Denary to Binary', command = lambda: self.changeWindow(DenToBin))
denToBin.pack(fill = X, padx = 10, pady = 10)
hexToBin = Button(self, text = 'Hex to Binary', command = lambda: self.changeWindow(HexToBin))
hexToBin.pack(fill = X, padx = 10, pady = 10)
def changeWindow(self, object):
root.withdraw()
currentFrame = object(root)
class DenToBin(Toplevel):
def __init__(self, master = None):
Toplevel.__init__(self, master)
self.master = master
self.Window()
self.denary = 0
def Window(self):
# print(self.denary) -- Why is this not recognised?????????
self.master.title('Unit Converter: Denary to Binary')
instructionDen = Label(self, text = 'Denary Value: ')
instructionDen.grid(row = 1, column = 0, padx = 10)
instructionBin = Label(self, text = 'Binary Value: ')
instructionBin.grid(row = 2, column = 0, padx = 10)
self.denaryEntry = Entry(self)
self.denaryEntry.grid(row = 1, column = 2, padx = 10, pady = 5)
convertButton = Button(self, text = 'Convert!', command = lambda: self.highestPowerOf2(10)) # as an example
convertButton.grid(row = 3, column = 1)
# finds highest power of 2 that is less than denary number - helps self.convert()
def highestPowerOf2(self,number):
print('I find the highest power of 2 that fits into the number given.')
class HexToBin(Toplevel):
def __init__(self, master = None):
Toplevel.__init__(self, master)
self.master = master
self.Window()
self.denary = 0
def Window(self):
self.master.title('Unit Converter: Hexadecimal to Binary')
instructionHex = Label(self, text = 'Hexadecimal Value: ')
instructionHex.grid(row = 1, column = 0, padx = 10)
instructionBin = Label(self, text = 'Binary Value: ')
instructionBin.grid(row = 2, column = 0, padx = 10)
self.hexadecimalEntry = Entry(self)
self.hexadecimalEntry.grid(row = 1, column = 2, padx = 10, pady = 5)
convertButton = Button(self, text = 'Convert!', command = self.convert)
convertButton.grid(row = 3, column = 1)
def convert(self):
# I need to call the 'highestPowerOf2' method here.
pass
root = Tk()
unitConverter = Menu(root) # root is master
root.mainloop()

Binding 'Return' press to Button in Python

I am trying to get the Return/Enter key to do the same thing as the 'Enter' button in my program. I have looked at other answers on StackOverflow but none of them solved my problem.
from tkinter import *
from tkinter import messagebox
import random
class Application(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.create_widgets()
self.number = random.randrange(10)
def create_widgets(self):
self.guess_lbl = Label(self, text = "Enter a Guess:")
self.guess_lbl.grid(row = 2, column = 0, sticky = W)
self.guess_ent = Entry(self, width = 10)
self.guess_ent.grid(row = 2, column = 1, sticky = W)
self.submit_bttn = Button(self, text = "Enter", command = self.reveal)
self.submit_bttn.grid(row = 6, column = 0, sticky = W)
self.submit_bttn.bind('<Return>', self.reveal)
def reveal(self):
guess = self.guess_ent.get()
if int(guess) == int(self.number):
messagebox.showinfo("Guessing game ", "Good Guess, that is correct!")
# Main
root = Tk()
root.title("Guessing Game")
root.geometry("300x100")
app = Application(root)
root.mainloop()
You need to add this to your def __init__(self, master) function:
master.bind('<Return>', self.reveal)
And you need to update your def reveal(self): method to:
def reveal(self, event=None):
Finally you need to remove the last line under your def create_widgets(self): method.
self.submit_bttn.bind('<Return>', self.reveal) # delete this
Afterwards, you will be able to press your Enter key on your keyboard and it should work for you.

Issue with crash in TKinter Application

I have a program running that is having issues when the timer runs. Every time "start" is hit, the application crashes. Any thoughts?
##-- Imports --##
import time
import openpyxl as xl
from Tkinter import *
##-- Classes --##
class App(Frame):
def startTimer(self):
self.check = True
self.now = time.strftime("%H:%M:%S")
while self.check == True:
self.timer.configure(text = self.now)
time.sleep(1)
def initUI(self):
self.parent.title("Emma's Time Manager")
self.pack(fill = BOTH, expand = 1)
def initWidget(self):
##Create button definitions##
self.buttonwidth = 12
self.quit = Button(self, text = "Quit", comman = self.quit, width = self.buttonwidth)
self.newClient = Button(self, text = "Add Client", command = lambda:self.newClientFunc(), width = self.buttonwidth)
self.timeStart = Button(self, text = "Start", command = lambda:self.startTimer(), width = self.buttonwidth)
self.timeEnd = Button(self, text = "End", command = lambda:self.endTimer(), width = self.buttonwidth)
self.saveBut = Button(self, text = "Save", command = lambda:self.saveFunc(), width = self.buttonwidth)
self.viewClient = Button(self, text = "View Client", command = lambda:self.viewCliFunc(), width = self.buttonwidth)
##Create lable definitions##
self.timer = Label(self, text = "00:00:00") ##self.timer used as display for timer##
##Create the listbox for Client Selection##
self.wb = xl.load_workbook("clients.xlsx")
self.clientNames = self.wb.get_sheet_names()
self.clivar = StringVar(self)
self.clivar.set(self.clientNames[0])
self.clilist = apply(OptionMenu, (self, self.clivar) + tuple(self.clientNames))
##Create Entry Box to describe work information##
self.info = Entry(self, width = 50)
##Create GUI for widgets##
self.clilist.grid(row = 0, column = 0)
self.timer.grid(row = 0, column = 1)
self.timeStart.grid(row = 0, column = 2)
self.timeEnd.grid(row = 0, column = 3)
self.info.grid(row = 1, column = 0, columnspan = 4)
self.newClient.grid(row = 2, column = 0)
self.viewClient.grid(row = 2, column = 1)
self.saveBut.grid(row = 2, column = 2)
self.quit.grid(row = 2, column = 3)
def __init__(self, parent):
Frame.__init__(self, parent, background = "light blue")
self.parent = parent
self.initUI()
self.initWidget()
def main():
try:
xl.load_workbook("clients.xlsx")
except:
temp = xl.Workbook()
temp.save("clients.xlsx")
temp.remove_sheet(temp.get_sheet_by_name("Sheet"))
root = Tk()
bob = App(root)
root.mainloop()
main()
Please note that most of the program is not yet finished. I just cannot seem to get this timer to run properly.
Looks like you have no way out of your while loop. You'll either need to set self.check to False, or break out of the loop.
while self.check == True:
self.timer.configure(text = self.now)
time.sleep(1)

Python Tkinter: Display description in status bar

For my Tkinter GUI, I have already created a Menu and a Status Bar. However, how to display description when mouse is over menu items?
Please run the following code (eg. when mouse is over menu item "About", status bar should write "Information":
from Tkinter import Tk, Frame, BOTH, Menu, Label, SUNKEN, X, BOTTOM
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background = "white")
parent.configure(bg = "black")
self.pack(fill = BOTH, expand = True, padx = 20, pady = 20)
self.parent = parent
# Maximize window
self.screenWidth = self.parent.winfo_screenwidth() - 5
self.screenHeight = self.parent.winfo_screenheight() - 110
self.parent.geometry('%dx%d+%d+%d' % (self.screenWidth, self.screenHeight, 0, 0))
self.parent.resizable(0, 0)
# Status bar
self.statusBar = StatusBar(self.parent)
self.statusBar.pack(side = BOTTOM, fill = X)
# Menu bar
menubar = Menu(self.parent)
self.parent.config(menu = menubar)
self.commandMenu = Menu(menubar, tearoff = 0)
self.commandMenu.add_command(label = "Rename", command = self.onRename)
menubar.add_cascade(label = "Command", menu = self.commandMenu)
self.helpMenu = Menu(menubar, tearoff = 0)
self.helpMenu.add_command(label = "About", command = self.onAbout)
menubar.add_cascade(label = "Help", menu = self.helpMenu)
def onRename(self):
pass
def onAbout(self):
pass
class StatusBar(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.label = Label(self, bd = 1, relief = SUNKEN, anchor = "w")
self.label.pack(fill=X)
def set(self, format0, *args):
self.label.config(text = format0 % args)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
def main():
root = Tk()
Application(root)
root.mainloop()
if __name__ == '__main__':
main()
So, the thing you're looking for is the <<MenuSelect>>.
If you bind the self.helpMenu to <<MenuSelect>>, like in my update of your code:
__author__ = 'rcope'
from Tkinter import Tk, Frame, BOTH, Menu, Label, SUNKEN, X, BOTTOM
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background = "white")
parent.configure(bg = "black")
self.pack(fill = BOTH, expand = True, padx = 20, pady = 20)
self.parent = parent
# Maximize window
self.screenWidth = self.parent.winfo_screenwidth() - 5
self.screenHeight = self.parent.winfo_screenheight() - 110
self.parent.geometry('%dx%d+%d+%d' % (self.screenWidth, self.screenHeight, 0, 0))
self.parent.resizable(0, 0)
# Status bar
self.statusBar = StatusBar(self.parent)
self.statusBar.pack(side = BOTTOM, fill = X)
# Menu bar
menubar = Menu(self.parent)
self.parent.config(menu = menubar)
self.commandMenu = Menu(menubar, tearoff = 0)
self.commandMenu.add_command(label = "Rename", command = self.onRename)
menubar.add_cascade(label = "Command", menu = self.commandMenu)
self.helpMenu = Menu(menubar, tearoff = 0)
self.helpMenu.add_command(label = "About", command = self.onAbout)
menubar.add_cascade(label = "Help", menu = self.helpMenu)
self.helpMenu.bind("<<MenuSelect>>", self.statusBarUpdate)
def onRename(self):
pass
def onAbout(self):
pass
def statusBarUpdate(self, event=None):
print "Status Bar Update Called"
if self.parent.call(event.widget, "index", "active") == 0:
self.statusBar.set("About This Application")
else:
self.statusBar.set("")
class StatusBar(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.label = Label(self, bd = 1, relief = SUNKEN, anchor = "w")
self.label.pack(fill=X)
def set(self, format0, *args):
self.label.config(text = format0 % args)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
def main():
root = Tk()
Application(root)
root.mainloop()
if __name__ == '__main__':
main()
The only thing you need to keep track of now, is what index corresponds to what menu item. I would recommend keeping an int you increment each time you add a menu entry, and using each index via a dict to get whatever relevant information about the menu item you need. Tkinter doesn't tell you a lot through the entry in the statusBarUpdate callback (like that this was called from the menu command labeled "About"), so you need to roll this yourself, I think.

Categories