I learned the book "programming python' these days. When I run the examples, I met the problem. The shell showed me the error:
AttributeError: 'NoneType' object has no attribute 'pack'
However, I copy the exactly code from the book. I'm a freshman of Python. I try to fix it by myself, but I failed. So I hope anyone could kindly help me.
Thanks !!!!!!
CODEļ¼
#File test.py
from tkinter import *
from tkinter.messagebox import showinfo
def MyGui(Frame):
def __init__(self, parent = None):
Frame.__init__(self, parent)
button = Button(self, text='press', command=reply)
button.pack()
def reply(self):
showinfo(title = 'popup',message ='Button pressed!')
if __name__ == '__main__':
window = MyGui()
window.pack()
window.mainloop()
#File test2.py
from tkinter import *
from test import MyGui
mainwin = Tk()
Label(mainwin,text = __name__).pack()
popup = Toplevel()
Label(popup,text = 'Attach').pack(side = LEFT)
MyGui(popup).pack(side=RIGHT)
mainwin.mainloop()
You can fix this with the following code:
#File test.py
from tkinter import *
from tkinter.messagebox import showinfo
class MyGui(Frame):
def __init__(self, parent = None):
Frame.__init__(self, parent)
button = Button(self, text='press', command=self.reply)
button.pack()
def reply(self):
showinfo(title = 'popup',message ='Button pressed!')
if __name__ == '__main__':
window = MyGui()
window.pack()
window.mainloop()
Basically two small syntax errors. First you were trying to make a class of MyGui, but you used keyword def which made a function instead (that returned None, hence the error you received.) It is syntaxically correct in python to define functions inside of functions so it was a little harder to catch. You have to use the keyword class to define a class.
Secondly when referencing the function reply you must use self.reply within the class itself.
Related
I am writing an application in tkinter consisting of several modules in which there are classes. Each module to a separate page of the app. As I move the buttons between the pages "next", "previous" it opens a new window for me every time. How do I make it so that each time calling pages opens in the same window?
I give draft code.
thank you for your help :D
task1.py
import tkinter as tk
from Test.modul.task1 import FirstPage1
class FirstPage0:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page(self):
FirstPage1(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
tk.Button(self.root, text="Start page", command=self.get_second_page).pack()
tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage0(tk.Tk())
first.get_run_first_page()
task2.py
import tkinter as tk
class FirstPage1:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page1(self):
from Test.task import FirstPage0
FirstPage0(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
tk.Button(self.root, text="Back", command=self.get_second_page1).pack()
tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage1(tk.Tk())
first.get_run_first_page()
Solution
#AdrianSz, you wanted to make the buttons not stack under each other. There are three ways to do so. One, is to keep only one button and change its command and text parameters each time when the frames change. Another would be to unpack the button not needed and pack the button needed. The third would be to pack the buttons in the root window instead of frame and change the text and command parameters. I would recommend the second method as it is easier and less prone to errors.
Code
task1.py
import tkinter as tk
from Test.modul.task1 import FirstPage1
class FirstPage0:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page(self):
self.root.pg_0_btn_start.pack_forget()
self.root_pg_0_btn_exit.pack_forget()
FirstPage1(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
self.root.pg_0_btn_start = tk.Button(self.root, text="Start page",
command=self.get_second_page).pack()
self.root_pg_0_btn_exit = tk.Button(self.root, text="Exit",
command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage0(tk.Tk())
first.get_run_first_page()
task2.py
import tkinter as tk
class FirstPage1:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.geometry("100x200")
def get_second_page1(self):
from Test.task import FirstPage0
self.root.pg_1_btn_back.pack_forget()
self.root_pg_1_btn_exit.pack_forget()
FirstPage0(tk.Toplevel()).get_run_first_page()
def get_button(self):
# Add buttons
self.root.pg_1_btn_back = tk.Button(self.root, text="Back", command=self.get_second_page1).pack()
self.root.pg_1_btn_exit = tk.Button(self.root, text="Exit", command=self.root.destroy).pack()
def get_run_first_page(self):
# Launching the application
self.get_settings()
self.get_button()
self.root.mainloop()
if __name__ == '__main__':
first = FirstPage1(tk.Tk())
first.get_run_first_page()
Note: I couldn't test this code from my side so if there are any errors, please comment on this answer
Suggestions
Since you are using modules for these, just make them inherit a class specified in a different file and operate them both from that file. There you can use self to access the methods of a subclass because the subclasses instantiate the base class and thus the self is a object of the subclass and is passed to the base class. The type of code you used is quite confusing too. I have added code to give you your wanted output using the principles I mentioned here. Hope this helped!
I am working on a project that uses buttons, but I would like to make it modular. The only problem I can't seem to get pass by is getting if someone is pressing the button from a different file
-file 1
from tkinter import*
class app2:
def __init__(self):
s = Tk()
but = Button(text="Test",command=subcommand)
but.pack()
s.mainloop()
def subcommand():
x = command()
x.command
class command:
def __init__(self,int):
self.command = ()
y = app2()
file 2-
from tkinter import*
from idea2 import *
def clicked():
print("HI")
x = command()
x.command(clicked())
My code basically just takes a function from another file and loads it into the command class. So basically the button can get different commands from different files. I don't know what to put for "x = command(???)" because I am trying to get that from a different file.
Basic question:
How to make my Tkinter button modular.
just pass the callback into the app2 constructor
file1
from tkinter import*
class app2:
def __init__(self,btn_callback=subcommand):
s = Tk()
but = Button(text="Test",command=btn_callback)
but.pack()
s.mainloop()
file2
from idea import app2
def my_callback(*args,**kwargs):
print("Buttton pressed...")
app = app2(my_callback)
or better yet make use of the event system in tkinter ... something like this
import tkinter as Tkinter
import tkinter.messagebox as tkMessageBox
def helloCallBack(*a,**kw):
tkMessageBox.showinfo( "Hello Python", "Hello World")
class MyApp(Tkinter.Tk):
def __init__(self):
super().__init__()
self.b = Tkinter.Button(self, text ="Hello", command = lambda:self.event_generate("<<button_click>>"))
self.b.pack()
app = MyApp()
app.bind("<<button_click>>",helloCallBack)
app.mainloop()
I have this simple code with self.btn1
from tkinter import Tk, ttk, messagebox
import tkinter as tk
class Main(tk.Frame):
def __init__(self, root):
super().__init__(root)
self.btn1 = ttk.Button(self, text="test")
self.btn1.pack()
if __name__ == "__main__":
root = tk.Tk()
app = Main(root)
app.pack()
root.mainloop()
and this code without self button
from tkinter import Tk, ttk, messagebox
import tkinter as tk
class Main(tk.Frame):
def __init__(self, root):
super().__init__(root)
btn1 = ttk.Button(self, text="test")
btn1.pack()
if __name__ == "__main__":
root = tk.Tk()
app = Main(root)
app.pack()
root.mainloop()
Both of them work similarly, but what's the difference, which one should I use?
The only real difference lies in how easy it is to retrieve the reference to the Button instance should you need one. With the former, it's just app.btn1. With the latter, it's app.winfo_children()[0].
>>> app.winfo_children()[0] is app.btn1
True
The difference is that btn1 is a local variable that is only available in the function in which it is used. self.btn1 is an instance variable that is available in every function within the class.
I am writing a Tkinter application that requires parts of the user display to exist in two different class, both imported from another file. I need to take a piece of user input and pass it from one class to another. In the toy example below, the user is supposed to type something into my_entry_input which later the class PullVariable is supposed to access.
Code is below. I had a few thoughts, such as somehow using globals or creating a function within the original class to get the variables and then pass them back. In all cases, I get:
AttributeError: type object 'Application' has no attribute 'my_entry'
The best solution I can think of is to create a function that responds to the binding, then pass the .get() to the other class from that function. My feeling is that tkinter doesn't like .get() in between classes.
Thanks to the community for your help.
MAIN
from import_test1 import *
root=Tk()
ui = Application(root)
ui.hello_world()
ui.entry()
pv = PullVariable()
if __name__ == '__main__':
root.mainloop()
IMPORTED CODE
from tkinter import *
from tkinter import ttk
class Application(Frame):
def __init__(self, parent, *args, **kwargs):
print('Application init')
Frame.__init__(self, parent, *args, **kwargs)
self.parent=parent
self.parent.grid()
def entry(self):
self.my_entry = StringVar(self.parent)
my_entry_input = Entry(self.parent, textvariable=self.my_entry,
width=16)
my_entry_input.bind('<FocusOut>', self.show_entry)
my_entry_input.grid(column=0, row=1)
self.show_label = Label(self.parent, text = '')
self.show_label.grid(column=0, row=2)
def hello_world(self):
print('hello world')
self.hw = Label(self.parent, text='Hello World!')
self.hw.grid(column=0, row=0)
def show_entry(self, event):
PullVariable(Application).find_entry()
class PullVariable:
def __init__(self, app):
self.app = app
print('Pull initiated')
def find_entry(self, event=None):
self.pulled_entry = self.app.my_entry.get()
self.app.show_label['text'] = self.pulled_entry
my_entry is not a attribute of Application class, so you can't do Application.my_entry, by it is an attribute of instance of Application class, so you can do Application().my_entry. You can probably add either instance of Application or my_entry to the __init__ method of PullVariable. I'll use the former.
# ...
class PullVariable:
def __init__(self, app): # add app here
self.pulled_entry = app.my_entry.get() # use app here
print(self.pulled_entry)
# and then
root=Tk()
ui = Application(root)
ui.hello_world()
ui.entry()
pv = PullVariable(ui) # supply PullVariable with instance of Application
I've got this Tkinter thing going with Python 3.5. I'm trying to pass the contents of an entry-box over to a different module so I can do something else with it. I'm making the Tkinter window like this:
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Label(self,text="Testers Name").grid()
self.testers_name = tk.Entry(self,text="Testers Name").grid()
tk.Label(self,text="Report Name").grid()
self.report_name = tk.Entry(self,text="Report Name").grid()
submitButton = tk.Button(self, text="Test Selected",command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
some_stuff_blah_blah_blah
if __name__ == "__main__":
gui = GUI()
print(gui.report_name)
gui.mainloop()
So, that works OK. At least the print call in __main__ reports None and not an error.
I have another module called pdf.py and that has a call in it that fails when I try to pull in report_name like this:
def myFirstPage(canvas, doc):
import gui
print(gui.report_name)
canvas.saveState()
canvas.setFont('Times-Bold',16)
canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
canvas.setFont('Times-Roman',9)
canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)
canvas.restoreState()
I've tried everything I can think of. Let's see, how about:
print(gui.gui.report_name)
print(gui.report_name)
print(gui.report_name.get())
print(gui.GUI.report_name)
print(gui.GUI().report_name)
print(gui.GUI().report_name.get())
I've made gui global in and out of the __main__ call.
I've done this in various places in pdf.py:
import gui
from gui import *
from gui import gui
from gui import GUI
in conjunction with permutations of the last slew of calls...Nothing works.
I can print it find in its own module, but I can't get it to show up outside of there. How do I do this?
Not entirely sure but I guess that your issue is related to the following line
if __name__ == "__main__":
meaning that your class GUI is not instantiated as can be seen in [0]. Moving the creation of the GUI class, that is using the following code snippet
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Label(self,text="Testers Name").grid()
self.testers_name = tk.Entry(self,text="Testers Name").grid()
tk.Label(self,text="Report Name").grid()
self.report_name = tk.Entry(self,text="Report Name").grid()
submitButton = tk.Button(self, text="Test Selected",command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
some_stuff_blah_blah_blah
gui = GUI()
print(gui.report_name)
gui.mainloop()
will at least allow you to import the variable gui from that module.
However, I strongly recommand to not instantiate the class at the end of the module because it is reinstantiated every time your import that module!
EDIT1:
I would do it as follows:
thiscode.py
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Label(self,text="Testers Name").grid()
self.testers_name = tk.Entry(self,text="Testers Name").grid()
tk.Label(self,text="Report Name").grid()
self.report_name = tk.Entry(self,text="Report Name").grid()
submitButton = tk.Button(self, text="Test Selected",command=self.query_checkbuttons)
submitButton.grid()
def query_checkbuttons(self):
some_stuff_blah_blah_blah
def start_gui():
gui = GUI()
# print(gui.report_name)
gui.mainloop()
if __name__ == "__main__":
start_gui()
and then in pdf.py
def myFirstPage(canvas, doc):
import gui
gui.start_gui() # --> manually open up the gui after import <--
# print(gui.report_name)
canvas.saveState()
canvas.setFont('Times-Bold',16)
canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
canvas.setFont('Times-Roman',9)
canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)
canvas.restoreState()
Now let me clarify that when you call thiscode.py via python thiscode.py you will end up with exactly one GUI being opened up. This is however not the case if you import the module as can be seen in pdf.py. Hence, we directly instantiate one GUI object via function invocation which must be done manually (see line 3 with the unmistakable inline comment in pdf.py).
[0] https://docs.python.org/3/tutorial/modules.html#executing-modules-as-scripts
The whole point of the if __name__ block is to only execute its contents when that file is run directly, and not when it is imported. So if you do want to import the gui instance, you should not create it within that block.