I have two modules file1.py and file2.py. In file1.py i have created a class and a function with label and entry widgets. In file2.py, i inherit the class of file1.py and create a submit button in a function. So, when i click the submit button, the value entered in entry widget in file1.py should be displayed. But what i observe is, submit button is not dislayed and when i close the window, the entered value is displayed. I'm unable to understand this behavior, can anyone correct my mistake.
file1.py
from Tkinter import *
top = Tk()
class TestClass(object):
def __init__(self, master = None):
self.frame = Frame(master)
self.frame.pack()
self.func()
def func(self):
self.label = Label(self.frame, text = "LabelName")
self.label.pack()
self.x = StringVar()
self.entry = Entry(self.frame, textvariable=self.x)
self.entry.pack()
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
file2.py
from file1 import *
class ImportClass(TestClass):
def __init__(self):
super(ImportClass,self).__init__(master=None)
self.imp_func()
def imp_func(self):
def get_func():
print app.x.get()
self.s = Button(self.frame, text="Submit", command=get_func())
self.s.pack()
Im = ImportClass()
I see your problem, to get this to work you have to fix a few things:
First, you need to use your imported class as app, which has the submit button, to be still able to run just file1 you can check in file1.py the __name__ whether it's '__main__' like:
if __name__ == '__main__':
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
Secondly, your function is not called because you call the function and give the result to Button, here you should just pass the function without calling it:
self.s = Button(self.frame, text="Submit", command=get_func())
in the function itself you should not use a global variable like app, because for example if you have multiple instances of the same class they would all depend on one instance and in the TestClass you have set self.x which is also accessible in ImportClass so you should replace the print statement with print self.x.get() instead of print app.x.get() to set the master from ImportClass to top. I also added *args and **kwargs to be passed on in the __init__ method so all in all you get:
file1.py
from Tkinter import *
class TestClass(object):
def __init__(self, master = None):
self.frame = Frame(master)
self.frame.pack()
self.func()
def func(self):
self.label = Label(self.frame, text = "LabelName")
self.label.pack()
self.x = StringVar()
self.entry = Entry(self.frame, textvariable=self.x)
self.entry.pack()
if __name__ == '__main__':
#just run if the file is called as main
top = Tk()
app = TestClass(master = top)
top.minsize(400, 400)
top.mainloop()
and file2.py
from file1 import *
from Tkinter import *
class ImportClass(TestClass):
def __init__(self, *args, **kwargs):
#passing all args and kwargs to super
super(ImportClass,self).__init__(*args, **kwargs)
self.imp_func()
def imp_func(self):
def get_func():
print self.x.get()
#using class property instead of global
self.s = Button(self.frame, text="Submit", command=get_func)
#pass function not it's return value which has been None
self.s.pack()
if __name__ == '__main__':
top = Tk()
app = ImportClass(master = top)
#using the ImportClass to display the window
top.minsize(400, 400)
top.mainloop()
so this should work. Hopefully, this helps you to prevent further problems like this.
The main reason you're having trouble is that no lines will run after mainloop until the Tk instance is closed or an event happened. When you import file1, mainloop is eventually run and then the GUI is waited to be closed in order to first define the ImportClass and then later to initialize an object for it.
Simply remove:
top.mainloop()
from file1 and add:
top.mainloop()
to file2 as the last line.
After which there's another issue, command option of a button expects a reference to a callable object, as opposed to an actual call. Replace:
self.s = Button(..., command=get_func())
with:
self.s = Button(..., command=get_func)
Also, note that I think your imports are in reverse order, obtain GUI objects by the module that has the Tk instance as opposed to vice-versa.
Related
from tkinter import *
root = Tk()
def main():
Window1 = Window(root, "hello", "500x500",)
class Window:
def __init__(self, root, title, geometry,):
self.root = root
root.title(title)
root.geometry(geometry)
root.mainloop()
class Button(Window):
def __init__(self, message):
self.message = message
super().__init__(root,)
Button(root, text=message,).pack()
root.mainloop()
Button("HI")
main()
One of the two major issues I see is that your Button class is hiding the one that tkinter defines with the same name that would have been available via the from tkinter import *. The second one is that your Button shouldn't be derived from your Window class because subclassing implies there's is an "is a" relationship between the two classes — which is clearly not the case with them.
Below is an object-oriented way to do things that does work:
import tkinter as tk # PEP 8 recommends avoiding `import *`
def main():
root = tk.Tk()
window1 = Window(root, "hello", "500x500")
Button(root, "HI")
root.mainloop()
class Window:
def __init__(self, root, title, geometry):
self.root = root
root.title(title)
root.geometry(geometry)
class Button:
def __init__(self, parent, message):
self.message = message
tk.Button(parent, text=message).pack()
if __name__ == '__main__':
main()
There are a couple of issues.
Firstly, you're creating a subclass of Window called "Button". When you subclass something, it means that it will be of a similar type as it's parent (Window != Button). But more than that, when you define Button, you're actually hiding tkinter's button!
Second, you need to think about the event loop. When working with GUIs, you want to set everything up (where is the button, where are form elements, etc.) before running the mainloop (where possible). You're calling the root.mainloop function in each element, when you should only really call it once (and probably in your main() method).
So how to actually do it? You're code may look something like this
from tkinter import *
class Window:
def __init__(self, root, title, geometry, ):
self.root = root
root.title(title)
root.geometry(geometry)
def add_button(self, label):
btn = Button(self.root, text=label)
btn.pack(side='top')
def main():
root = Tk()
Window1 = Window(root, "hello", "500x500", )
Window1.add_button("Hi!")
root.mainloop()
if __name__ == "__main__":
main()
Here, the window has a method called "add_button", where you can add whatever button you want. Note that it is just creating a new Button object (the parent is "root") and then is "packing" it (feel free to read more about tk's layouts), which puts it in its place
I've also cleaned up the main function and called it under the classic 'if name == "main":' line.
import tkinter as tk
def main():
root = tk.Tk()
window1 = Window(root, "hello", "500x500")
Button(root, "Click me", 2, 2, 10, 5)
root.mainloop()
class Window:
def __init__(self, root, title, geometry):
self.root = root
root.title(title)
root.geometry(geometry)
class Button:
def __init__(self, parent, message, row, column, width, height,):
self.message = message
self.row = row
self.column = column
self.width = width
self.height = height
tk.Button(parent, text=message, height=height, width=width).grid(column=column, row=row)
if __name__ == '__main__':
main()
the grid function isnt working now lol
I have a problem with opening existing class from button which is in another class.
I don't have any idea how to make it working because I know I should in function add (Toplevel) but it's also not working.
class na_dane(object):
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.frame.pack()
# CODE ........
def close_windows(self):
self.master.destroy()
# this code opens another window (class) which I have defined but not included here...
def new_window(self):
self.newWindow = tk.Toplevel.iconify(self.master)
self.app = sinus(self.newWindow)
# here is the main class where when I click button it should open na_dane Class
class menu(na_dane):
def __init__(self, master):
self.master = master
przycisk_Jednostkowo_Liniowa = tk.Button(
text="Funkcja jednostkowo-liniowa",
width=250,
height=2,
bg="blue",
fg="yellow"
).pack()
# HERE IS THE PROBLEM WHERE I TRY TO BIND BUTTON TO CLASS
przycisk_Jednostkowo_Liniowa.bind("<Button>", lambda e: na_dane(master))
przycisk_Jednostkowo_Liniowa.pack()
def main():
root = tk.Tk()
root.geometry("250x200")
root.title("GRUPA 3")
root['bg'] = '#FFFFFF'
app = menu(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem seems to be that you attempt to pack przycisk_Jednostkowo_Liniowa in the same line as it is created. What this line of code is doing is creating a button with the characteristics described in your arguments, applying .pack() to it, then assigning the output of that .pack() execution to przycisk_Jednostkowo_Liniowa. Because .pack() doesn't have a return value, przycisk_Jednostkowo_Liniowa becomes None, so when you later attempt to bind a lambda to it, it doesn't recognize .bind() as a valid method. I tested the code with that .pack() removed and it worked for me.
I am new to python. working on python 3.7, windows os. suppose that i have created a file named
Class1.py in which
import tkinter as tk
import Class2
class main_window:
def openanotherwin():
Class2.this.now()
def create():
root = tk.Tk()
button1 = tk.Button(root, text="Open another window", command = openanotherwin )
button1.pack()
root.mainloop()
Now my Class2.py contains:
import tkinter as tk
class this():
def now():
new = tk.Toplevel(root) #Error displayed: root is not defined
lb = tk.Label(new, text = "Hello")
lb.pack()
new.mainloop()
and my Main.py contains:
import Class1
Class1.main_window.create()
Error displayed is: root is not defined in Class2.py. I have tried root = Class1.main_window.root to bring the value of root but it showed error that function has no attribute root.
Please help me solving my problem.
I think function need to get root
def now(root):
new = tk.Toplevel(root) #Error displayed: root is not defined
Then in class1:
def openanotherwin(root):
Class2.this.now(root)
And third:
button1 = tk.Button(root, text="Open another window", command=lambda: main_window.openanotherwin(root) )
===
Class1.py
import tkinter as tk
import Class2
class main_window:
def openanotherwin(root):
Class2.this.now(root)
def create():
root = tk.Tk()
button1 = tk.Button(root, text="Open another window", command=lambda: main_window.openanotherwin(root) )
button1.pack()
root.mainloop()
Class2.py
import tkinter as tk
class this():
def now(root):
new = tk.Toplevel(root) #Error displayed: root is not defined
lb = tk.Label(new, text = "Hello")
lb.pack()
new.mainloop()
First, an error might be in the name "this" of your class in Class2.
I guess that "this" is a reserved name for the current object instance.
You should change that to something else, e.g. "class2"
Then you should instantiate class2 in your class1 and pass root to the constructor as an argument. Only then, you can use root in class2.
Here's an example for passing arguments to a class constructor:
class DemoClass:
num = 101
# parameterized constructor
def __init__(self, data):
self.num = data
# a method
def read_number(self):
print(self.num)
# creating object of the class
# this will invoke parameterized constructor
obj = DemoClass(55)
# calling the instance method using the object obj
obj.read_number()
# creating another object of the class
obj2 = DemoClass(66)
# calling the instance method using the object obj
obj2.read_number()
I'm currently trying to make a UI for a future project using tkinter. However, I'm having a lot of trouble setting up some basic OOP concepts.
I have two files, main.py and pressed.py, I'm trying to write a function for a button press in pressed.py but I'm not sure how I'm supposed to get access to variables in main.py
Here's the error I'm currently running into:
Class 'UI' has no 'py_entry' member
I've tried using a lot of other Stack Overflow posts for reference, but none of them has worked.
main.py
import tkinter as tk
import os
import geocoder
from tkinter import messagebox
from PIL import Image
from PIL import ImageTk
import pressed
# Paths
assets_path = "./assets/"
class UI(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# Set window title
parent.title("geo-location-data")
# Set window geometry
parent.geometry("800x600")
# Address entry label
py_label = tk.Label(parent, text="Emter an addres:", font=("Helvetica", 12))
py_label.place(x=0, y=0)
# Address entry field
py_entry = tk.Entry(parent, font=("Helvetica", 12))
py_entry.place(x=130, y=2)
# Open button_normal.png
button_normal_img = Image.open(assets_path + "button_normal.png")
# Resize button_normal.png
button_normal_img_resize = button_normal_img.resize((16, 16), Image.BILINEAR)
button_normal_img_resize_format = "resized_button_normal" + ".png"
# Set path
path_for_button_normal = os.path.join(assets_path, button_normal_img_resize_format)
# Save to path
button_normal_img_resize.save(path_for_button_normal)
# Open saved image
button_normal_img_r_open = Image.open(assets_path + "resized_button_normal.png")
button_normal_img_r_open = ImageTk.PhotoImage(button_normal_img_r_open)
#def button_pressed(self):
# If address entry field is blank
#if(py_entry.index("end") == 0):
#messagebox.showerror("Error", "Entry field was left blank.")
#return self
#else:
#print("test")
#return self
# Pressed
# ADD COMMAND #
py_button = tk.Button(parent, font=("Helvetica", 12), width=16, height=16, image=button_normal_img_r_open)
py_button.place(x=320, y=2)
if __name__ == "__main__":
root = tk.Tk()
UI(root).pack(side="top", fill="both", expand=True)
root.mainloop()
pressed.py
from sample import main
from tkinter import messagebox
class Pressed(main.UI):
def __init__(self):
def button_press(self):
# This is where I'm getting the error
if (main.UI.):
messagebox.showerror("Error", "Address entry field was left blank.")
return self
else:
print("Button pressed!")
return self
Your problem seems to be that you are declaring local variables inside the class which are only accessible from within the __init__() function. Python has a way to create class variables by either declaring them inside the class (outside of a method) or by calling self.variable_name. You then can access that variable by calling self.variable_name every place inside the class.
self is a reference to the current instance of your class.
For your example the way you probably want to could declare the variable is with the self keyword:
class UI(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.py_entry = tk.Entry(parent, font=("Helvetica", 12))
self.py_entry.place(x=130, y=2)
You should then be able to access the py_entry field after creating an instance of the UI class (calling UI()).
I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it