I'm having an issue trying to call a class's method from a command on tkinters button. I have tried:
command = Alarm_clock.save_alarm()
command = self.save_alarm()
command = Alarm_clock.save_alarm(self.hour_count, self.min_count)
command = Alarm_clock.save_alarm(self)
I think I'm missing something obvious because I have managed to 'command' methods within the same class.
Here is a snippet of my code: It's for an alarm clock:
import tkinter as tk
import time
import vlc
import pygame
from mutagen.mp3 import MP3
class Alarm_clock():
def __init__(self):
alarm_list = []
def save_alarm(self):
print(init_alarm_gui.hour_count.get())
print(init_alarm_gui.min_count.get())
#get the hour and minute and append it to the alarm_list to be checked on real time
class init_alarm_gui():
def __init__(self, root):
self.root = root
self.hour_count = 0
self.min_count = 0
def make_widgets(self, root):
self.hour_count = tk.IntVar()
self.min_count = tk.IntVar()
self.time_label = tk.Label(text="")
self.time_label.pack()
self.Save_but = tk.Button(root, text = "Save Alarm", command=Alarm_clock.save_alarm)
self.Save_but.pack()
def main():
root = tk.Tk()
gui = init_alarm_gui(root)
gui.make_widgets(root)
root.mainloop()
if __name__ == "__main__":
main()
TyperError: save_alarm() missing 1 required positional argument: 'self'
You must create an instance of the class, and then call the method of the instance. This will automatically fill in the self parameter. This isn't unique to tkinter, it's just how python works.
alarm = Alarm_clock()
alarm.save_alarm()
Related
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 have a simple ui written by python tkinter, it only contains one button.
I found a problem here, if the button command is directed to a function, which includes creating an instance to perform its method. However, when i run this program, my pycharm told me I am passing one positional argument to the method, which i never did:
TypeError: tell_time() takes 0 positional arguments but 1 was given
For some reasons, I have to keep the method stay within the class. Could anyone tell me how to let the method run? Thanks a million!
def build_ui():
root = Tk()
root.title("Auto Hedger")
root.geometry("640x480")
btn1 = Button(root, text="get data", command=testing1)
btn1.pack()
root.mainloop()
class test_object():
def tell_time():
print(datetime.datetime.now())
def testing1():
aaa = test_object()
t1000 = Thread(target=aaa.tell_time, args=[])
t1000.start()
if __name__ == '__main__':
t_root = Thread(target=build_ui)
t_root.start()
Your tell_time method needs self as a parameter, since it is a class method and not a function. Adding that should make it work fine.
Try this:
from threading import Thread
from tkinter import *
import datetime
def build_ui():
root = Tk()
root.title("Auto Hedger")
root.geometry("640x480")
btn1 = Button(root, text="get data", command=testing1)
btn1.pack()
root.mainloop()
class test_object():
def tell_time(self):
print(datetime.datetime.now())
def testing1():
aaa = test_object()
t1000 = Thread(target=aaa.tell_time, args=[])
t1000.start()
if __name__ == '__main__':
t_root = Thread(target=build_ui)
t_root.start()
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.
From what I read, it is best practice to wrap code for a GUI in a class. How do I do this? I have come up with the following code based on some examples I have looked at, but this does not work as apparently DropdownMenu isn't defined? I have used this function successfully without the class wrapping.
import Tkinter as tk
import tkFileDialog
import os
class Window(tk.Frame):
def DropdownMenu(options,status,name):
optionFrame = tk.Frame(root)
optionLabel = tk.Label(optionFrame)
optionLabel["text"] = name
optionLabel.pack(side=LEFT)
var = StringVar(root)
var.set(status)
w = tk.OptionMenu(optionFrame, var, *options)
w.pack(side=LEFT)
optionFrame.pack()
return w
def __init__(self,parent):
tk.Frame.__init__(self,parent)
ndim_options = DropdownMenu(("1","2","3"),'-',"Number of dimensions")
if __name__ == "__main__":
root = tk.Tk()
Window(root).pack()
root.mainloop()
When you call DropdownMenu (inside the class), use self ...
ndim_options = self.DropdownMenu((...