So, I'm following a tutorial on making a simple calculator, and I'm Getting an attribute error AttributeError:'_tkinter.tkapp' object has no attribute 'displayframe' , apparently, the attribute doesn't exist in the source code even if it has been defined? would be grateful if someone helped
from tkinter import *
SMALL_FONT_STYLE = "Arial 16"
LIGHT_GRAY = "#F5F5F5"
LABEL_COLOR = "#25265E"
LARGE_FONT_STYLE = "Arial 40 bold"
WHITE = "#FFFFFF"
class Calculator(Tk):
def __init__(self):
super().__init__()
self.geometry("375x677")
self.resizable(0,0)
self.title("Calculator")
self.total_expression = "0"
self.current_expression = "0"
self.total_label, self.label = self.create_display_labels()
self.digits = {7:(1,1), 8:(1,2), 9:(1,3), 4:(2,1), 5:(2,2), 6:(2,3), 1:(3,1),2:(3,2), 3:(3,3), 0:(4,2), '.':(4,1)}
self.create_digits_buttons()
self.display_frame = self.create_display_frame()
self.buttons_frame = self.create_buttons_frame()
def create_display_labels(self):
total_label = Label(self.display_frame, text=self.total_expression, anchor=E, bg=LIGHT_GRAY, fg=LABEL_COLOR, padx=24, font=SMALL_FONT_STYLE)
total_label.pack(expand=True, fill="both")
label = Label(self.display_frame, text=self.total_expression, anchor=E, bg=LIGHT_GRAY, fg=LABEL_COLOR, padx=24, font=LARGE_FONT_STYLE)
label.pack(expand=True, fill="both")
return total_label, label
def create_display_frame(self):
frame = Frame(self, height=221, bg=LIGHT_GRAY)
frame.pack(expand=True, fill="both")
return frame
def create_buttons_frame(self):
frame = Frame(self)
frame.pack(expand=TRUE, fill="both")
return frame
def create_digits_buttons(self):
for digit, grid_value in self.digits.items():
button = Button(self.buttons_frame, text= str(digit), bg=WHITE, fg=LABEL_COLOR)
button.grid(row = grid_value[0], column = grid_value[1], sticky=NSEW)
if __name__ == '__main__':
Calc = Calculator()
Calc.mainloop()```
This error is because you call self.create_display_labels() before calling self.create_display_frame(). You also call self.create_digits_buttons() before calling self.create_buttons_frame().
Move self.create_display_labels() and self.create_digits_buttons() below self.create_display_frame() and self.create_buttons_frame(). Here is what __init__() should look like:
def __init__(self):
super().__init__()
self.geometry("375x677")
self.resizable(0,0)
self.title("Calculator")
self.total_expression = "0"
self.current_expression = "0"
self.digits = {7:(1,1), 8:(1,2), 9:(1,3), 4:(2,1), 5:(2,2), 6:(2,3), 1:(3,1),2:(3,2), 3:(3,3), 0:(4,2), '.':(4,1)}
self.display_frame = self.create_display_frame()
self.buttons_frame = self.create_buttons_frame()
self.create_digits_buttons() ### MOVED THIS LINE
self.total_label, self.label = self.create_display_labels() ### MOVED THIS LINE
Related
I'm working with tkinter at the moment, ive needed to call a method onto an object that has a Tk() class. im trying to call the method not in the Tk() class, but from the class ive already created, which the tkinter object is in already.
Ive tried to call .Format() onto l_label, but i think it is trying to find an attribute or method within the Label class from tkinter, because it returns: AttributeError: 'Label' object has no attribute 'Format'
Any thoughts?
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
thing = Tk()
class App():
def __init__(self, master, _title, back='white'):
self.master = master
self._title = _title
self.back = back
self.master.title(self._title)
self.master.configure(background=back)
def label(self, _text, back='white', w=1, h=1):
self.back = back
self.w = w
self.h = h
self._text = _text
l_label = Label(self.master, text=self._text, bg=self.back, width=self.w, height=self.h)
return l_label
def Format(self, l_label, Row=1, Column=1):
self.Row = Row
self.Column = Column
l_label.grid(row=Row, column=Column)
app = App(thing, 'hello world')
label = app.label('this is a text boc', 'white').Format()
thing.mainloop()
What you are looking for is method chaining. In order for that to work your function needs to return self
class App(object):
def __init__(self, master, _title, back='white'):
self.master = master
self._title = _title
self.back = back
self.master.title(self._title)
self.master.configure(background=back)
self.w = None
self.h = None
self._text = None
self.Row = None
self.Column = None
self.l_label = None
def label(self, _text, back='white', w=1, h=1):
self.back = back
self.w = w
self.h = h
self._text = _text
self.l_label = Label(self.master, text=_text, bg=back, width=w, height=h)
return self
def Format(self, Row=1, Column=1):
self.Row = Row
self.Column = Column
self.l_label.grid(row=Row, column=Column)
return self
Notice how I removed the l_label from Format and assigned Label(self.master, text=_text, bg=back, width=w, height=h) to self.l_label
Then you would be able to do:
thing = Tk()
app = App(thing, 'hello world')
label = app.label('this is a text box', 'white', 15, 15).Format()
thing.mainloop()
My code:
import tkinter
class latinwords:
def __init__(self):
self.main = tkinter.Tk()
self.top = tkinter.Frame(self.main)
self.mid = tkinter.Frame(self.main)
self.latinword1 = tkinter.Button(self.mid, text = 'sinister', command = self.cbfunction)
self.latinword2 = tkinter.Button(self.mid, text = 'dexter', command = self.cbfunction2)
self.latinword3 = tkinter.Button(self.mid, text = 'medium', command = self.cbfunction3)
self.toplabel = tkinter.Label(self.top, text= 'Latin')
self.toplabel2 = tkinter.Label(self.top, text= '\tEnglish')
self.value = tkinter.StringVar()
self.value1 = tkinter.StringVar()
self.value2 = tkinter.StringVar()
self.labels = tkinter.Label(self.bot, textvariable = self.value)
self.labels1 = tkinter.Label(self.bot, textvariable = self.value1)
self.labels2 = tkinter.Label(self.bot, textvariable = self.value2)
self.labels.pack()
self.labels1.pack()
self.labels2.pack()
self.top.pack()
self.mid.pack()
self.latinword1.pack()
self.latinword2.pack()
self.latinword3.pack()
self.toplabel.pack(side='left')
self.toplabel2.pack(side='left')
tkinter.mainloop()
def cbfunction(self):
value = 'left'
self.value1.set(value)
def cbfunction2(self):
value = 'right'
self.value.set(value)
def cbfunction3(self):
value = 'centre'
self.value2.set(value)
s = latinwords()
Unexpected output:
Expected output:
As you can see, I am trying to get my expected output with 3 buttons that can show the English word after its being pressed. But I got my output vertically with my own code. I am expecting my button and the matched word are on same horizontal level. Can anyone help me with this issue? Thanks.
It is better to put all the labels and buttons in same frame and use grid() instead of pack():
import tkinter
class latinwords:
def __init__(self):
self.main = tkinter.Tk()
self.mid = tkinter.Frame(self.main)
self.mid.pack()
self.latinword1 = tkinter.Button(self.mid, text='sinister', command=self.cbfunction)
self.latinword2 = tkinter.Button(self.mid, text='dexter', command=self.cbfunction2)
self.latinword3 = tkinter.Button(self.mid, text='medium', command=self.cbfunction3)
self.toplabel = tkinter.Label(self.mid, text='Latin')
self.toplabel2 = tkinter.Label(self.mid, text='English')
self.value = tkinter.StringVar()
self.value1 = tkinter.StringVar()
self.value2 = tkinter.StringVar()
self.labels = tkinter.Label(self.mid, textvariable=self.value)
self.labels1 = tkinter.Label(self.mid, textvariable=self.value1)
self.labels2 = tkinter.Label(self.mid, textvariable=self.value2)
self.labels.grid(row=1, column=1)
self.labels1.grid(row=2, column=1)
self.labels2.grid(row=3, column=1)
self.latinword1.grid(row=1, column=0)
self.latinword2.grid(row=2, column=0)
self.latinword3.grid(row=3, column=0)
self.toplabel.grid(row=0, column=0)
self.toplabel2.grid(row=0, column=1)
tkinter.mainloop()
def cbfunction(self):
value = 'left'
self.value.set(value)
def cbfunction2(self):
value = 'right'
self.value1.set(value)
def cbfunction3(self):
value = 'centre'
self.value2.set(value)
s = latinwords()
Solution:
The self.bot attribute is missing.
self.bot = tkinter.Frame(self.main)
You should set the position of packing of frames.
self.top.pack(side=tkinter.TOP)
self.mid.pack(side=tkinter.LEFT)
self.bot.pack(side=tkinter.RIGHT)
Test/Output:
>>> python3 test.py
NOTE:
The order of English words are not correct but it is not related to question.
I recommend to use the grid instead of pack. You can make more nice GUI with grid in your case. See more: https://tkdocs.com/tutorial/grid.html
I would like to personalized a menu bar. For example I want to delete the border that appears around the tk.Menu widget (with the add_command() method)
That's my code (I'm using Windows 10)
import tkinter as tk
from tkinter import ttk
dark_grey = "#212121"
dark_blue="#102A43"
blue_1="#243B53"
root = tk.Tk()
root.state('zoomed')
container = tk.Frame(root, bg = dark_grey)
container.grid_rowconfigure(0, weight = 0)
container.grid_columnconfigure(0, weight = 1)
menu_frame = tk.Frame(container, bg = dark_blue)
menu1 = tk.Menubutton(menu_frame, text = "Menu1", bg = dark_blue, fg =
"white", activebackground = blue_1, activeforeground =
"white")
menu1.grid(row = 0, column = 0)
submenu1 = tk.Menu(menu1, tearoff = 0, bg = dark_blue,
activebackground= blue_1, fg = "white",borderwidth = 0, activeborderwidth= 0)
submenu1.add_command(label = "Option 1.1")
submenu1.add_command(label = "Option 1.2")
menu1.configure(menu = submenu1)
menu_frame.grid(row = 0, column = 0, sticky = "ew")
container.pack(fill = tk.BOTH, expand = "True")
root.mainloop()
My idea is to create a menu without using tk.Menu and tk.MenuButton. I would like "bind" an <Enter> event to a label, in order to create a sort of drop down under the label. Is it possible?
Question: Customized menu bar without using the widget tk.Menu?
This example uses a tk.Toplevel as a popup window and displays the added tk.Menubutton.
The Submenu follows ths defined Style of the Top tk.Menubutton.
TODO:
Close the Popup Window if clicked outside or another Top Menubutton.
Extend with other than only tk.Menubutton
Keyboard support
import tkinter as tk
class Menu:
def __init__(self, parent, **kwargs):
self._popup = None
self._menubutton = []
self.parent = parent
self.parent.bind('<Button-1>', self.on_popup)
def on_popup(self, event):
w = event.widget
x, y, height = self.parent.winfo_rootx(), self.parent.winfo_rooty(), self.parent.winfo_height()
self._popup = tk.Toplevel(self.parent.master, bg=self.parent.cget('bg'))
self._popup.overrideredirect(True)
self._popup.geometry('+{}+{}'.format(x, y + height))
for kwargs in self._menubutton:
self._add_command(**kwargs)
def add_command(self, **kwargs):
self._menubutton.append(kwargs)
def _add_command(self, **kwargs):
command = kwargs.pop('command', None)
menu = self.parent
mb = tk.Menubutton(self._popup, text=kwargs['label'],
bg=menu.cget('bg'),
fg=menu.cget('fg'),
activebackground=menu.cget('activebackground'),
activeforeground=menu.cget('activeforeground'),
borderwidth=0,
)
mb._command = command
mb.bind('<Button-1>', self._on_command)
mb.grid()
def _on_command(self, event):
w = event.widget
print('_on_command("{}")'.format(w.cget('text')))
self._popup.destroy()
if w._command is not None:
w._command()
Usage:
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
style = {'bg': "#102A43", 'fg': "white",
'activebackground': "#243B53", 'activeforeground': "white",
'borderwidth': 0}
menu1 = tk.Menubutton(self, text="Menu1", **style)
submenu1 = Menu(menu1)
submenu1.add_command(label="Option 1.1")
submenu1.add_command(label="Option 1.2")
menu1.grid(row=0, column=0)
menu2 = tk.Menubutton(self, text="Menu2", **style)
submenu2 = Menu(menu2)
submenu2.add_command(label="Option 2.1")
submenu2.add_command(label="Option 2.2")
menu2.grid(row=0, column=2)
if __name__ == "__main__":
App().mainloop()
Tested with Python: 3.5 - 'TclVersion': 8.6 'TkVersion': 8.6
Forgive me in advance for my code, but I'm simply making this for friend of mine to automatically populate a GUI interface with song information, channel information for each song, and things such as images attached to the songs. Right now I'm only scraping from a playlist on Youtube and a playlist on Soundcloud. I have all of that properly working, but me being new to frontend development left me in a horrible spot to make a decent application for him. I had a lot in mind that I could have done, but now I'm simply creating buttons with each song title as the text. Here is an image of my progress. I still have to find a way to attach each image to each button for the on_enter event, but that is for later on. As you can see, I have a on_leave function commented out. I was using that to delete the self.image_window each time I left a button. Problem is even a minuscule amount of mouse movement would cause the window to be delete and recreated dozens of times. How do I make it static so when I am hovering over a button it doesn't spam create/delete the window?
Thanks!
from Tkinter import *
import json
import os
import webbrowser
class GUIPopulator(Frame):
def __init__(self, parent):
Frame.__init__(self)
self.parent = parent
self.configure(bg='PeachPuff2')
self.columnconfigure(20, weight=1)
self.rowconfigure(30, weight=1)
self.curtab = None
self.tabs = {}
self.pack(fill=BOTH, expand=1, padx=5, pady=5)
self.column = 0
self.row = 0
def on_enter(self, event):
self.image_window = Toplevel(self)
self.img_path = os.getcwd() + '/Rotating_earth_(large).gif'
self.img = PhotoImage(file=self.img_path)
#self.image_window.minsize(width=200, height=250)
self.image_window.title("Preview")
canvas = Canvas(self.image_window, width=200, height=200)
canvas.pack()
canvas.create_image(100, 100, image=self.img)
#def on_leave(self, enter):
def addTab(self, id):
tabslen = len(self.tabs)
tab = {}
if self.row < 30:
btn = Button(self, text=id,highlightbackground='PeachPuff2' ,command=lambda: self.raiseTab(id))
btn.grid(row=self.row, column=self.column, sticky=W+E)
btn.bind("<Enter>", self.on_enter)
#btn.bind("<Leave>", self.on_leave)
tab['id']=id
tab['btn']=btn
self.tabs[tabslen] = tab
self.raiseTab(id)
self.row +=1
else:
self.row = 0
self.column +=1
btn = Button(self, text=id,highlightbackground='PeachPuff2' ,command=lambda: self.raiseTab(id))
btn.grid(row=self.row, column=self.column, sticky=W+E)
tab['id']=id
tab['btn']=btn
self.tabs[tabslen] = tab
self.raiseTab(id)
def raiseTab(self, tabid):
with open(os.getcwd() + '/../PlaylistListener/CurrentSongs.json') as current_songs:
c_songs = json.load(current_songs)
print(tabid)
if self.curtab!= None and self.curtab != tabid and len(self.tabs)>1:
try:
#webbrowser.open(c_songs[tabid]['link'])
webbrowser.open_new('http://youtube.com')
except:
pass
def main():
root = Tk()
root.title('Playlist Scraper')
root.geometry("1920x1080+300+300")
t = GUIPopulator(root)
with open(os.getcwd() + '/../PlaylistListener/CurrentSongs.json') as current_songs:
c_songs = json.load(current_songs)
for song in c_songs:
t.addTab(song)
root.mainloop()
if __name__ == '__main__':
main()
Example of JSON file provided:
{
"F\u00d8RD - Shadows (feat. Samsaruh)": {
"page_title": "youtube",
"link": "youtube.com/watch?v=CNiV6Pne50U&index=32&list=PLkx04k4VGz1tH_pnRl_5xBU1BLE3PYuzd",
"id": "CNiV6Pne50U",
"channel": "youtube.com/watch?v=CNiV6Pne50U&index=32&list=PLkx04k4VGz1tH_pnRl_5xBU1BLE3PYuzd",
"image_path": [
"http://i4.ytimg.com/vi/CNiV6Pne50U/hqdefault.jpg",
"CNiV6Pne50U.jpg"
]
},
"Katelyn Tarver - You Don't Know (tof\u00fb remix)": {
"page_title": "youtube",
"link": "youtube.com/watch?v=7pPNv38JzD4&index=43&list=PLkx04k4VGz1tH_pnRl_5xBU1BLE3PYuzd",
"id": "7pPNv38JzD4",
"channel": "youtube.com/watch?v=7pPNv38JzD4&index=43&list=PLkx04k4VGz1tH_pnRl_5xBU1BLE3PYuzd",
"image_path": [
"http://i4.ytimg.com/vi/7pPNv38JzD4/hqdefault.jpg",
"7pPNv38JzD4.jpg"
]
},
"Illenium - Crawl Outta Love (feat. Annika Wells)": {
"page_title": "youtube",
"link": "youtube.com/watch?v=GprXUDZrdT4&index=7&list=PLkx04k4VGz1tH_pnRl_5xBU1BLE3PYuzd",
"id": "GprXUDZrdT4",
"channel": "youtube.com/watch?v=GprXUDZrdT4&index=7&list=PLkx04k4VGz1tH_pnRl_5xBU1BLE3PYuzd",
"image_path": [
"http://i4.ytimg.com/vi/GprXUDZrdT4/hqdefault.jpg",
"GprXUDZrdT4.jpg"
]
}
}
After some testing I have come up with some code I think you can use or is what you are looking for.
I have changes a few things and add some others.
1st we needed to create a place holder for the top window that we can use later in the code. So in the __init__ section of GUIPopulatior add self.image_window = None. We will talk about this part soon.
next I created the image path as a class attribute on init this can be changed later with update if need be.
next I added a bind() to the init that will help us keep the self.image_window placed in the correct location even if we move the root window. so we add self.parent.bind("<Configure>", self.move_me) to the __init__ section as well.
next we create the method move_me that we just created a bind for.
the way it is written it will only take effect if self.image_window is not equal to None this should prevent any errors while self.image_window is being used however I have not created the error handling to deal with what happens after the toplevel window is closed by the user. Its not difficult but I wanted to answer for the main issue at hand.
Here is the move_me method:
def move_me(self, event):
if self.image_window != None:
h = self.parent.winfo_height() # gets the height of the window in pixels
w = self.parent.winfo_width() # gets the width of the window in pixels
# gets the placement of the root window then uses height and width to
# calculate where to place the window to keep it at the bottom right.
self.image_window.geometry('+{}+{}'.format(self.parent.winfo_x() + w - 250,
self.parent.winfo_y() + h - 250))
next we need to modify the on_enter method to create the toplevel window if out class attribute self.image_window is equal to None if it is not equal to None then we can use the else portion of the if statement to just update the image.
Here is the modified on_enter method:
def on_enter(self, event):
if self.image_window == None:
self.image_window = Toplevel(self)
#this keeps the toplevel window on top of the program
self.image_window.attributes("-topmost", True)
h = self.parent.winfo_height()
w = self.parent.winfo_width()
self.image_window.geometry('+{}+{}'.format(self.parent.winfo_x() + w - 250,
self.parent.winfo_y() + h - 250))
self.img = PhotoImage(file=self.img_path)
self.image_window.title("Preview")
self.canvas = Canvas(self.image_window, width=200, height=200)
self.canvas.pack()
self.canv_image = self.canvas.create_image(100, 100, image=self.img)
else:
self.img = PhotoImage(file= self.img_path)
self.canvas.itemconfig(self.canv_image, image = self.img)
all that being said there are some other issues with your code that need to be addressed however this answer should point you in the right direction.
Below is a section of your code you need to replace:
class GUIPopulator(Frame):
def __init__(self, parent):
Frame.__init__(self)
self.parent = parent
self.configure(bg='PeachPuff2')
self.columnconfigure(20, weight=1)
self.rowconfigure(30, weight=1)
self.curtab = None
self.tabs = {}
self.pack(fill=BOTH, expand=1, padx=5, pady=5)
self.column = 0
self.row = 0
def on_enter(self, event):
self.image_window = Toplevel(self)
self.img_path = os.getcwd() + '/Rotating_earth_(large).gif'
self.img = PhotoImage(file=self.img_path)
#self.image_window.minsize(width=200, height=250)
self.image_window.title("Preview")
canvas = Canvas(self.image_window, width=200, height=200)
canvas.pack()
canvas.create_image(100, 100, image=self.img)
#def on_leave(self, enter):
With this:
class GUIPopulator(Frame):
def __init__(self, parent):
Frame.__init__(self)
self.parent = parent
self.configure(bg='PeachPuff2')
self.columnconfigure(20, weight=1)
self.rowconfigure(30, weight=1)
self.curtab = None
self.image_window = None
self.img_path = os.getcwd() + '/Rotating_earth_(large).gif'
self.tabs = {}
self.pack(fill=BOTH, expand=1, padx=5, pady=5)
self.parent.bind("<Configure>", self.move_me)
self.column = 0
self.row = 0
def move_me(self, event):
if self.image_window != None:
h = self.parent.winfo_height()
w = self.parent.winfo_width()
self.image_window.geometry('+{}+{}'.format(self.parent.winfo_x() + w - 250,
self.parent.winfo_y() + h - 250))
def on_enter(self, event):
if self.image_window == None:
self.image_window = Toplevel(self)
self.image_window.attributes("-topmost", True)
h = self.parent.winfo_height()
w = self.parent.winfo_width()
self.image_window.geometry('+{}+{}'.format(self.parent.winfo_x() + w - 250,
self.parent.winfo_y() + h - 250))
self.img = PhotoImage(file=self.img_path)
self.image_window.title("Preview")
self.canvas = Canvas(self.image_window, width=200, height=200)
self.canvas.pack()
self.canv_image = self.canvas.create_image(100, 100, image=self.img)
else:
self.img = PhotoImage(file= self.img_path)
self.canvas.itemconfig(self.canv_image, image = self.img)
The problem I am having is that the function below works fine on the first pass with regards to displaying the chosen value at the top of the OptionMenu before the 'Enter' button is clicked:
First pass
However on a second pass and any future passes to the function the chosen value is not displayed at the top of the OptionMenu (although if selected it will be used) before the 'Enter' Button is pressed:
Second etc. passes
The code is:
def load_create_list(self):
self.app = Toplevel(self.parent)
self.parent.withdraw()
self.app.title("Rgs2")
self.student = []
self.student1=[]
self.gs1=0
self.ng1=0
options = ["2", "3", "4", "5", "6", "7"]
self.results=[]
self.ngg=StringVar() #for the option menu widget the variable must be a stringVar not a normal string, int etc.
self.ng = Label(self.app, text="Number of groups")
self.ng.grid(row=2, column=0)
self.ngg.set("3")
self.ng1 = OptionMenu(self.app, self.ngg, *options)
self.ng1.grid(row=2, column=1, ipadx=10)
for i in range(0,len(self.x3)):
self.L1 = Label(self.app,text="Student")
self.L1.grid(row=i+4, column=0)
self.en = Entry(self.app)
self.en.insert(END, self.x3[i])
self.en.grid(row=i+4, column=1)
self.student.append(self.en)
self.el = int(len(self.student)+1)
for h in range(self.el,self.el+5):
self.L2 = Label(self.app,text="Student")
self.L2.grid(row=h+4, column=0)
self.em = Entry(self.app)
self.em.grid(row=h+4, column=1)
self.student.append(self.em)
button=Button(self.app,text="enter",command=lambda : self.hallo2()).grid(row=h+7,column=0)
button=Button(self.app,text="Save List",command=lambda : self.save_list2()).grid(row=h+7,column=1)
I have tried everything but can't understand what might be causing this issues. Any help would be gladly appreciated. Seems like very odd behaviour could it be something outside of the function that is causing the issue?
Full code is:
from Tkinter import *
from tkFileDialog import askopenfile
class main_menu:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
parent.title("Random Group Sorter")
self.myContainer1.pack()
self.button1 = Button(self.myContainer1, command = lambda : self.hope())
self.button1.configure(text="Create List", height = 1, width = 20, font=("Arial", 16))
self.button1.pack(side=TOP)
self.button1.focus_force()
self.button3 = Button(self.myContainer1, command = lambda : self.hope1())
self.button3.configure(text="Load List", height = 1, width = 20, font=("Arial", 16))
self.button3.pack(side=TOP)
self.button2 = Button(self.myContainer1, command = lambda : exit(), )
self.button2.configure(text="Exit",height = 1, width = 20, font=("Arial", 16))
self.button2.pack(side=TOP)
def hope(self):
self.myParent.destroy()
create_list()
def hope1(self):
self.myParent.destroy()
load_list()
class create_list():
def __init__(self):
parent = Tk()
self.parent=parent
self.student = []
self.student1 =[]
self.student2 =[]
self.fake_student = []
self.results=[]
self.ngg = StringVar()# for the option menu widget the variable must be a stringVar not a normal string, int etc.
self.ng = Label(text="Number of groups")
self.ng.grid(row=2, column=0)
self.ng = OptionMenu(parent, self.ngg, "2", "3", "4", "5","6")
self.ng.grid(row=2, column=1)
for i in range(3,21):
self.L1 = Label(text="Student")
self.L1.grid(sticky=E)
self.en = Entry(parent)
self.en.grid(row=i, column=1)
self.student.append(self.en)
button=Button(parent,text="Enter",command=lambda : self.hallo()).grid(row=23,column=0)
button=Button(parent,text="Save List",command=lambda : self.save_list()).grid(row=23,column=1)
parent.mainloop()
def hallo(self):
self.gs1 = int(len(self.student))
self.ng1 = int(self.ngg.get())# still need to use .get even though a stringvar
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
for i in self.student1:# this is added as there are duplicate entries in the student list if saved
if i not in self.student2:
self.student2.append(i)
self.parent.destroy()
lis_an(self.student2,self.ng1)
def save_list(self):
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
import tkFileDialog
root = Tk()
root.withdraw()
file_name = tkFileDialog.asksaveasfile(parent=root)
root.destroy()
print >>file_name, "\n"
print >>file_name, "\n".join(self.student1)
file_name.close()
class load_list:
def __init__(self):
self.x = []
self.x3 = []
self.load_clean()
self.root = Tk()
def load_clean(self):#option to load an already created file, cleans unwanted info from the list
import tkFileDialog
root = Tk()
root.withdraw()
file_name = tkFileDialog.askopenfile(parent=root)
root.destroy()
self.x = file_name.readlines()
file_name.close()
x2 =self.x[:]
for z in self.x:
if z [0:6]== "Random" or z == '\n' or z[0:5] == "Group":
x2.remove(z)
for c in range (0,len(x2)):
v = x2[c].rstrip()# this strip spaces and \n from each list item and returns the cleaned up string
self.x3.append(v)
self.load_create_list()
def load_create_list(self):
parent = Tk()
self.parent=parent
self.student = []
self.student1=[]
self.gs1=0
self.ng1=0
self.results=[]
self.ngg = StringVar()# for the option menu widget the variable must be a stringVar not a normal string, int etc.
self.ng = Label(text="Number of groups")
self.ng.grid(row=2, column=0)
self.ng = OptionMenu(parent, self.ngg, "2", "3", "4", "5", "6")
self.ng.grid(row=2, column=1)
for i in range(0,len(self.x3)):
self.L1 = Label(text="Student")
self.L1.grid(row=i+3, column=0)
self.en = Entry(parent)
self.en.insert(END, self.x3[i])
self.en.grid(row=i+3, column=1)
self.student.append(self.en)
self.el = int(len(self.student)+1)
for h in range(self.el,self.el+5):
self.L2 = Label(text="Student")
self.L2.grid(row=h+3, column=0)
self.em = Entry(parent)
self.em.grid(row=h+3, column=1)
self.student.append(self.em)
button=Button(parent,text="enter",command=lambda : self.hallo2()).grid(row=h+6,column=0)
button=Button(parent,text="Save List",command=lambda : self.save_list2()).grid(row=h+6,column=1)
parent.mainloop()
def hallo2(self):
self.student2= []
self.gs1 = int(len(self.student))
self.ng1 = int(self.ngg.get())# still need to use .get even though a stringvar
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
for i in self.student1:# this is added as there are duplicate entries in the student list if saved
if i not in self.student2:
self.student2.append(i)
self.parent.destroy()
lis_an(self.student2,self.ng1)
def save_list2(self):
for entry in self.student:
self.student1.append(entry.get())
self.student1 = filter(None, self.student1)
import tkFileDialog
root = Tk()
root.withdraw()
file_name = tkFileDialog.asksaveasfile(parent=root)
root.destroy()
print >>file_name, "\n"
print >>file_name, "\n".join(self.student1)
file_name.close()
class lis_an:
def __init__(self,student1,ng1):
self.student1 = student1
self.ng1=ng1
self.results = []
self.randomList()
def randomList(self): # this creates a random list of students on the course
import random
studentb = self.student1[:]# this is added as otherwise the student list is overwritten
studentc = []
for i in range(len(studentb)):
element = random.choice(studentb)
studentb.remove(element)
studentc.append(element)
self.student1 = studentc
self.partition()
def partition(self): # this creates sub list of the student list containing the groups of students
increment = len(self.student1) / float(self.ng1)
last = 0
i = 1
while last < len(self.student1):
idx = int(round(increment * i))
self.results.append(self.student1[last:idx])
last = idx
i += 1
output(self.results, self.ng1)
class output:
def __init__(self, student, ng1):
self.ng1 = ng1
self.student = student
self.parent = Tk()
for item1 in range (0,len(self.student)):
test1 = "Group " + str(item1+1)+ ":"
v = Label(self.parent, text=test1, font=("Arial", 13))
test = "\n".join(self.student[item1])
w = Label(self.parent, text=test, justify = LEFT, font=("Arial", 12))
v.pack(side="top", anchor="w")
w.pack(side="top", anchor="w")
button=Button(self.parent,text="Repeat",command=lambda : self.join_list()).pack(side="top", anchor="w")
button=Button(self.parent,text="Main Menu",command=lambda : self.menu_link()).pack(side="top", anchor="w")
mainloop()
def join_list(self):#this function creates a new undivided version of student to feed back to lis_an
self.parent.destroy()
self.student = [j for i in self.student for j in i]
lis_an(self.student,self.ng1)
def menu_link(self):#destroys the parent frame and returns back to main menu
self.parent.destroy()
main()
def main():
parent = Tk()
myapp = main_menu(parent)
parent.mainloop()
main()
Very Simple solution to this problem. I was not defining self.ngg as a StringVar in the parent widget so:
self.ngg = StringVar()
Would cause the error, and
self.ngg = StringVar(self.app)
Solves the problem. Not quite sure why this would occur on the second and subsequent uses of the function but not the first.