Tkinter class module reference exception - python

I'm having some trouble calling a module(updateUI) within a class (Eventsim).
The line Sim = EventSim() throws an exception because it's missing an argument (parent). I can't figure out how to fix this / reference the parent object.
This is my first attempt wit Tkinter and my python knowledge is also rather limited (for now).
from Tkinter import *
class EventSim(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
def updateUI(self,IP_Address,Port_Number,Events_Directory):
self.parent.title('ECP Event Simulator')
self.parent.resizable(0, 0)
self.pack(fill=BOTH, expand=True)
frame1 = Frame(self)
frame1.pack(fill=X)
frame2 = Frame(self)
frame2.pack(fill=X)
frame3 = Frame(self)
frame3.pack(fill=X)
frame4 = Frame(self)
frame4.pack(fill=X)
frame5 = Frame(self)
frame5.pack(fill=X)
frame6 = Frame(self)
frame6.pack(fill=X,pady=(10,30))
frame7 = Frame(self)
frame7.pack(fill=X)
frame8 = Frame(self)
frame8.pack(fill=X,pady=(10,0))
Main_Label = Label(frame1,text='ECP EventSim')
Main_Label.pack(side=LEFT,padx=100)
IP_Label = Label(frame2,text='IP Address:')
IP_Label.pack(side=LEFT,padx=10)
Port_Label = Label(frame2,text='Port:')
Port_Label.pack(side=RIGHT,padx=70)
IP_Text = Entry(frame3)
IP_Text.pack(fill=X,side=LEFT,padx=10)
IP_Text = Entry(frame3)
IP_Text.pack(fill=X,side=RIGHT,padx=10)
Dir_Label = Label(frame4,text='Events Directory:')
Dir_Label.pack(side=LEFT,padx=10)
Dir_Text = Entry(frame5)
Dir_Text.pack(fill=X,side=LEFT,padx=10,expand=True)
Save_Button = Button(frame6,text='Save Config')
Save_Button.pack(fill=X,side=LEFT,padx=10,expand=True)
Con_Button = Button(frame7,text='Connect')
Con_Button.pack(fill=X,side=LEFT,padx=10,expand=True)
Send_Button = Button(frame8,text='Start Sending Events')
Send_Button.pack(fill=X,side=LEFT,padx=10,expand=True)
def main():
root = Tk()
root.geometry("300x300+750+300")
app = EventSim(root)
root.mainloop()
Sim = EventSim()
Sim.updateUI('1','1','1')
main()

The parent should be root. So, replacing:
def main():
root = Tk()
root.geometry("300x300+750+300")
app = EventSim(root)
root.mainloop()
Sim = EventSim()
Sim.updateUI('1','1','1')
main()
with:
root = Tk()
root.geometry("300x300+750+300")
Sim = EventSim(root)
Sim.updateUI('1','1','1')
root.mainloop()
brings up the desired window. The updateUI method requires work to populate the entry fields but you can remove its parent parameter since you already have the parent instance variable.

Remove Sim = EventSim() and move Sim.updateUI('1','1','1') to main:
def main():
root = Tk()
root.geometry("300x300+750+300")
app = EventSim(root)
app.updateUI('1','1','1')
root.mainloop()
main()

Related

Python Tkinter - Unable to obtain entry value after widgets in window are destroyed

I'm writing a program using Tkinter that prompts the user to enter their name as a value, then displays it after the contents of the window are cleared. However, I'm unable to retrieve this value. I'd like to know how I could fix this.
Below is my code:
import tkinter
class Window:
def __init__(self):
self.main_window = tkinter.Tk()
self.main_window.title("Window Title")
self.main_window.geometry("300x100")
self.frame = tkinter.Frame(self.main_window)
self.label1 = tkinter.Label(self.frame,
text="Enter name:")
self.label1.pack(pady=5)
self.name_entry = tkinter.Entry(self.frame,
width=20)
self.name_entry.pack(pady=5)
self.name = tkinter.StringVar()
self.name_label = tkinter.Label(self.frame,
textvariable=self.name)
def frame2():
for widget in self.frame.winfo_children():
widget.destroy()
self.frame.pack_forget()
self.frame2.pack(pady=20)
self.name_enter = tkinter.Button(self.frame,
text="Confirm",
command=lambda:
[self.getname(),frame2()])
self.name_enter.pack(pady=5)
self.frame.pack()
self.frame2 = tkinter.Frame(self.main_window)
self.label2 = tkinter.Label(self.frame2,
text="Your name is " +
str(self.name.get()) +
".")
self.label2.pack(pady=5)
tkinter.mainloop()
def getname(self):
nameget = str(self.name_entry.get())
self.name.set(nameget)
if __name__ == '__main__':
window = Window()
Using the suggestion from jasonharper I edit the code for a possible fix. I added another label above your label that takes in the StringVar() and then changed the label parameters to textvariable=self.name which gets the input.
import tkinter
class Window:
def __init__(self):
self.main_window = tkinter.Tk()
self.main_window.title("Window Title")
self.main_window.geometry("300x100")
self.frame = tkinter.Frame(self.main_window)
self.label1 = tkinter.Label(self.frame,
text="Enter name:")
self.label1.pack(pady=5)
self.name_entry = tkinter.Entry(self.frame,
width=20)
self.name_entry.pack(pady=5)
self.name = tkinter.StringVar()
self.name_label = tkinter.Label(self.frame,
textvariable=self.name)
def frame2():
for widget in self.frame.winfo_children():
widget.destroy()
self.frame.pack_forget()
self.frame2.pack(pady=20)
self.name_enter = tkinter.Button(self.frame,
text="Confirm",
command=lambda:
[self.getname(),frame2()])
self.name_enter.pack(pady=5)
self.frame.pack()
self.frame2 = tkinter.Frame(self.main_window)
self.labelx = tkinter.Label(self.frame2, text="Your name is")
self.labelx.pack()
self.label2 = tkinter.Label(self.frame2, textvariable=self.name)
self.label2.pack(pady=5)
tkinter.mainloop()
def getname(self):
nameget = str(self.name_entry.get())
self.name.set(nameget)
if __name__ == '__main__':
window = Window()

How to pass value of Entry from one class to another class?

Target / Goal:
My goal is to pass an Entry widget from one class Window1 to another class Window2?
Code:
Here's my current code:
from tkinter import *
import tkinter.ttk as ttk
def main():
root = Tk()
app = Window1(root)
root.mainloop()
class Window1:
def __init__(self,master):
self.master = master
self.master.geometry('400x150')
self.frame = Frame(self.master)
self.frame.pack(fill="both", expand=True)
self.label_username = Label(self.frame, text="Username: ",font=("bold",16))
self.entry_username = Entry(self.frame, font = ("bold", 14))#pass this into Window 2 as label
self.label_username.pack()
self.entry_username.pack()
self.logbtn = Button(self.frame, text="Login", font = ("bold", 10), command=self._login_btn_clicked)
self.logbtn.pack()
def _login_btn_clicked(self):
# print("Clicked")
username = self.entry_username.get()
if username == 'test':
self.master.withdraw()
self.newWindow = Toplevel(self.master)
self.app = Window2(self.newWindow)
else:
self.entry_username.delete(0,"end")
class Window2:
def __init__(self,master):
notebook = ttk.Notebook(master)
notebook.pack(expand = 1, fill = "both")
#Frames
main = ttk.Frame(notebook)
notebook.add(main, text='Main-Screen')
self.output = Label(main, text = )#OUTPUT HERE
self.output.pack()
if __name__ == '__main__':
main()
What I have tried:
Here, at Window2 code:
self.output = Label(main, text = )#OUTPUT HERE
self.output.pack()
Text is currently empty, but I am trying to return the value from Window1 at widget name: self.entry_username but not sure how to do it.
This is what I have tried:
self.output = Label(main, text = Window1().entry_username.get())#OUTPUTHERE
self.output.pack()
Error:
But this is the error:
self.output = Label(main, text = Window1().entry_username.get())#OUTPUT HERE
TypeError: __init__() missing 1 required positional argument: 'master'
This really has nothing to do with tkinter, you use the same technique whether it's plain python or tkinter or anything else. To access an attribute of an object, you need to have a reference to the object.
For example, if you want Window2 to have access to data in Window1, you can pass the instance of Window1 to Window2:
def _login_btn_clicked(self):
...
self.newWindow = Toplevel(self.master)
self.app = Window2(self.newWindow, window1=self)
...
class Window2:
def __init__(self,master, window1):
self.window1=window1
...
username = self.window1.entry_username.get()
self.output = Label(main, text=username)

How to add a scrollbar to window made with tkinter

A simple quiz game
I got this code and I need scrollbars, I tried to search how to add it on stackoverflow (ScrolledWindow with tix...) but I still can't get something that works properly. Could someone help me?
from tkinter import *
from random import randint
root = Tk()
root.title("Quiz")
root.geometry("400x300")
class Window:
def __init__(self, question, answer):
self.text = [question, answer]
self.createLabel()
# self.createText()
self.createEntry()
self.createButton()
def retrieve_input(self):
# inputValue = self.textBox.get("1.0", "end-1c")
# print(inputValue)
if self.mystring.get() == self.text[1]:
print("Esatto. è " + self.text[1])
self.left['text'] = "Esatto"
def createLabel(self):
self.labelframe = LabelFrame(root, text="Domanda:")
self.labelframe.pack(fill="both", expand="yes")
self.left = Label(self.labelframe, text=self.text[0])
self.left.pack()
def createText(self):
self.textBox = Text(height=1)
self.textBox.pack()
def createEntry(self):
self.mystring = StringVar()
self.myentry = Entry(root, textvariable=self.mystring).pack()
def createButton(self):
self.but = Button(text="Click", command=self.retrieve_input)
self.but.pack()
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Window("Quanto fa " + str(one) + "+" + str(two) + "?", str(one + two))
root.mainloop()
output
With ScrolledFrame it can look like this
I renamed Window into Question because it makes more sense
I use self.question and self.answer instead of self.text = [question, answer] to make it more readable.
I put classes and functions before root = tk.Tk() to make it more readable.
I use import tkinter as tk instead of from tkinter import * to make it more readable.
Question gets inner frame from ScrolledFrame and use as parent for LabelFrame. Other widgets use labelframe as parent.
BTW: you had entry = Entry(..).pack() which assign None to entry because pack()/grid()/place() returns None. I put pack() in next line and now I can get text directly from Entry (without StringVar)
Code
import tkinter as tk
from random import randint
# --- classes ---
class ScrolledFrame(tk.Frame):
def __init__(self, parent, vertical=True, horizontal=False):
super().__init__(parent)
# canvas for inner frame
self._canvas = tk.Canvas(self)
self._canvas.grid(row=0, column=0, sticky='news') # changed
# create right scrollbar and connect to canvas Y
self._vertical_bar = tk.Scrollbar(self, orient='vertical', command=self._canvas.yview)
if vertical:
self._vertical_bar.grid(row=0, column=1, sticky='ns')
self._canvas.configure(yscrollcommand=self._vertical_bar.set)
# create bottom scrollbar and connect to canvas X
self._horizontal_bar = tk.Scrollbar(self, orient='horizontal', command=self._canvas.xview)
if horizontal:
self._horizontal_bar.grid(row=1, column=0, sticky='we')
self._canvas.configure(xscrollcommand=self._horizontal_bar.set)
# inner frame for widgets
self.inner = tk.Frame(self._canvas, bg='red')
self._window = self._canvas.create_window((0, 0), window=self.inner, anchor='nw')
# autoresize inner frame
self.columnconfigure(0, weight=1) # changed
self.rowconfigure(0, weight=1) # changed
# resize when configure changed
self.inner.bind('<Configure>', self.resize)
self._canvas.bind('<Configure>', self.frame_width)
def frame_width(self, event):
# resize inner frame to canvas size
canvas_width = event.width
self._canvas.itemconfig(self._window, width = canvas_width)
def resize(self, event=None):
self._canvas.configure(scrollregion=self._canvas.bbox('all'))
class Question:
def __init__(self, parent, question, answer):
self.parent = parent
self.question = question
self.answer = answer
self.create_widgets()
def get_input(self):
value = self.entry.get()
print('value:', value)
if value == self.answer:
print("Esatto. è " + self.answer)
self.label['text'] = "Esatto"
def create_widgets(self):
self.labelframe = tk.LabelFrame(self.parent, text="Domanda:")
self.labelframe.pack(fill="both", expand=True)
self.label = tk.Label(self.labelframe, text=self.question)
self.label.pack(expand=True, fill='both')
self.entry = tk.Entry(self.labelframe)
self.entry.pack()
self.button = tk.Button(self.labelframe, text="Click", command=self.get_input)
self.button.pack()
# --- main ---
root = tk.Tk()
root.title("Quiz")
root.geometry("400x300")
window = ScrolledFrame(root)
window.pack(expand=True, fill='both')
for i in range(10):
one = randint(1, 10)
two = randint(1, 10)
Question(window.inner, "Quanto fa {} + {} ?".format(one, two), str(one + two))
root.mainloop()

Display selected options from checkbox to a new list in next tkinter window

I want to display the selected options from my checkbox list in a new list in a new Tkinter window and when browsing it comes back to the main screen
(using Python 3.5 with Ubuntu 16.04).
import tkinter as tk
import tkMessageBox
lista=['jpeg','jfit','tiff','gif','png','bmp']
class PopUp(tk.Toplevel):
def __init__(self, number=10):
tk.Toplevel.__init__(self)
self.global_state = tk.BooleanVar()
cb = tk.Checkbutton(self, text="select/deselect all", variable=self.global_state, command=self.select_clear_states)
cb.grid(row=0, column=0, padx=5, pady=1)
self.states = []
for n in range(len(lista)):
var = tk.BooleanVar()
cb = tk.Checkbutton(self, text=str(lista[n]), variable=var)
cb.grid(row=n+1, column=0, padx=5, pady=1)
self.states.append(var)
def select_clear_states(self):
state = self.global_state.get()
for x in self.states:
x.set(state)
def popup(num):
win = PopUp(num)
root = tk.Tk()
b = tk.Button(root, text="5 checkboxes", command=lambda:popup(5))
b.pack()
root.mainloop()
Assuming I understand you correctly, if you wanted to do this with Checkbuttons you could do something like the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.top = Toplevel(root)
self.frame = Frame(self.top)
self.frame.pack()
self.check = []
self.var = []
for i in range(10):
self.var.append(IntVar())
self.var[i].trace("w", self.callback)
self.check.append(Checkbutton(self.root, text="Option "+str(i), variable=self.var[i]))
self.check[i].pack()
def callback(self, *args):
self.frame.destroy()
self.frame = Frame(self.top)
self.frame.pack()
for i, c in zip(self.check, self.var):
if c.get() == 1:
Label(self.frame, text=i.cget("text")).pack()
root = Tk()
App(root)
root.mainloop()
Alternatively you might find that a Listbox accomplishes what you want and (IMO) looks a lot cleaner and more user friendly:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.listbox = Listbox(self.root, selectmode=MULTIPLE)
self.listbox.pack()
for i in range(10):
self.listbox.insert(END, "Option "+str(i))
root = Tk()
App(root)
root.mainloop()
Using the Listbox you probably wouldn't even need two windows as the selection quite clearly shows which options are selected by highlighting them.

Update User interface when a button is pressed Tkinter

So basically i want the user interface to be updated when a user presses the start button, however when i call the maininit function it returns an error saying that root is not defined, is there any way i can get around this?
from PIL import Image, ImageTk
from Tkinter import Tk, Label, BOTH,W, N, E, S, Entry, Text, INSERT, Toplevel
from ttk import Frame, Style, Button, Label
import Tkinter
import Callingwordlist
class MainGameUI(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Type!")
self.pack(fill=BOTH, expand=1)
style = Style()
style.configure("TFrame", background="black")
Type = Image.open("Type!.png")
Typet = ImageTk.PhotoImage(Type)
label3 = Label(self, image=Typet)
label3.image = Typet
label3.place(x=0, y=0)
self.pack(fill=BOTH, expand=1)
MenuButton = Button(self, text="Main Menu")
MenuButton.pack()
MenuButton.place(x=560,y=20,height = 80,width = 100)
QuitButton = Button(self,text="Quit",command=self.parent.destroy)
QuitButton.pack()
QuitButton.place(x=680,y=20,height = 80,width = 100)
StartButton = Button(self, text="Start",command=maininit)
StartButton.pack()
StartButton.place(x=440,y=20,height=80,width=100)
def maininit():
entry1 = Entry(root,font =("Courier",38), width = 22)
entry1.pack(ipady=10)
entry1.config(bg="#CEF6F5")
entry1.place(x=90,y=200)
entry2 = Entry(root,font =("Courier",38), width = 22)
entry2.pack(ipady=10)
entry2.config(bg="#CEF6F5")
entry2.place(x=90,y=350)
text1 = Text(root,width=23,height=1,font=("Courier",38))
text1.pack()
text1.config(bg="black",fg="white",bd=0)
text1.place(x=90,y=150)
text1.insert(INSERT,"Hello")
text2 = Text(root,width=23,height=1,font=("Courier",38))
text2.pack()
text2.config(bg="black",fg="white",bd=0)
text2.place(x=90,y=300)
text2.insert(INSERT,"Test")
dtext = Text(root,font=("Courier",28),width=10,height=1)
dtext.pack()
dtext.config(bg="black",fg="white",bd=0)
dtext.insert(INSERT,"Difficulty")
dtext.place(x=90,y=500)
atext = Text(root,font=("Courier",28),width=8,height=1)
atext.pack()
atext.config(bg="black",fg="white",bd=0)
atext.insert(INSERT,"Accuracy")
atext.place(x=595,y=500)
dentry = Text(root,font=("Courier",28),width=1,height=1)
dentry.pack()
dentry.config(bg="white",bd=0)
dentry.place(x=180,y=550)
dentry.insert(INSERT,"Test")
def main():
root = Tk()
root.geometry("860x640+300+300")
app = MainGameUI(root)
root.mainloop()
if __name__ == '__main__':
main()
In your application, root is a locally defined variable confined to main(). You need to somehow pass root as an argument to maininit. Here is one way to do this:
First, change maininit() so that it accepts a parameter root:
def maininit(root):
...
Now, change the callback on StartButton so it passes maininit() the root object:
class MainGameUI(Frame):
...
def initUI(self):
...
StartButton = Button(self, text="Start",command=lambda: maininit(self.parent))
...
You are defining root inside a function, whose calling namespace is not available to the maininit() function. If you omit the definition of main() and instead write
if __name__ == "__main__":
root = Tk()
root.geometry("860x640+300+300")
app = MainGameUI(root)
root.mainloop()
root will then be defined in the global module namespace, where it IS available to the code in the function.

Categories

Resources