Python Tkinter OOP Inheritance - python

I am trying to inherit some values from one class to a another one. I am using the function super to inherit. Below is a simplfied version of my problem. Thank you for help.
from tkinter import *
import random
class First(object):
def __init__(self,master):
super(First, self).__init__(master)
def random(self):
self._y = random.randint(11,20)
self._x = random.randint(1,10)
def random2(self):
s = First(root)
s.random()
class Second(Frame,First):
def __init__(self,master):
super(Second, self).__init__(master)
self.grid()
self.menuFrame = Frame(self)
self.create_menu_widgets()
self.menuFrame.grid()
def create_menu_widgets(self):
btnMainMenu = Button(self.menuFrame,font=("consolas",18,"bold"),text="Main Menu")
btnMainMenu.pack()
def print(self):
print(self._y,self._x)
root = Tk()
x = Second(root)
x.random()
x.random2()
x.print()
root.configure(background = 'green')
root.mainloop()
I keep on getting the error:
super(First, self).__init__(master)
TypeError: object.__init__() takes no parameters
Please help me, I think the problem is where I have s=First(root). Thanks for help.

When you call super on a class that is the highest in your hierarchy it will go object. object is the super class of all objects in Python. So super(First, self).__init__(master) will try to initialize the object not any of your classes. You can see this inheritance using the Class.__mro__. To figure out what I'm talking about.
And inheriting from object? That happens by default even if you don't specify anything. So I guess you wanted to inherit from Frame as object doesn't make any sense.
So change your code to this and it should be fixed.
from tkinter import *
import random
class First(Frame): # changed here
def random(self):
self._y = random.randint(11,20)
self._x = random.randint(1,10)
def random2(self):
s = First(root)
s.random()
class Second(First): # changed here
def __init__(self,master):
super(Second, self).__init__(master)
self.grid()
self.menuFrame = Frame(self)
self.create_menu_widgets()
self.menuFrame.grid()
def create_menu_widgets(self):
btnMainMenu = Button(self.menuFrame,font=("consolas",18,"bold"),text="Main Menu")
btnMainMenu.pack()
def print(self):
print(self._y,self._x)
root = Tk()
x = Second(root)
x.random()
x.random2()
x.print()
root.configure(background = 'green') # you cannot see this as your button fills everything
root.mainloop()

I see several issues in your example.
1:
you are assigning Second() to x but then calling x.random() and x.random2(). This will not work as your random methods only exist in the First() class.
2:
Don't name a function, method, variable or attribute the same thing as a built in method. This will cause problems.
Change your def print(self) to something like def my_print(self) or anything that is not exactly print. While we are talking about this print statement you only define self._x and self._y in your First() class but try to print them in your Second() class. This will never work. self is always a reference to the class object and never a reference to a class controller that was passed to the class.
Now I get what you are trying to do here and I will rebuild your code to show how to share information between classes.
You should not use a geometry manager fro inside the Frame class. Instead use it on the class variable name. This will allow you chose between any geometry manager for the class instead of sticking to just one kind.
As Vineeth pointed out you do not use supper for an object class.
The below code will run the Second() class and then when you want to reference the random methods on the First() class you can do so with the new methods I added to Second(). Let me know if you have any questions.
One last change is to import tkinter as tk this will help prevent accidentally overwriting an imported method from tkinter.
Here is a working example of your code:
import tkinter as tk
import random
class First(object):
def random(self):
return "From First.Random!", random.randint(11,20), random.randint(1,10)
def random2(self):
return "From First.Random2!", self.random()
class Second(tk.Frame):
def __init__(self, master):
super(Second, self).__init__(master)
self.menuFrame = tk.Frame(self)
self.menuFrame.grid()
tk.Button(self.menuFrame, font=("consolas", 18, "bold"), text="Main Menu").pack()
def random(self):
print(First().random())
def random2(self):
print(First().random2())
root = tk.Tk()
root.configure(background='green')
x = Second(root)
x.pack()
x.random()
x.random2()
root.mainloop()

Related

How to destroy label of one class on another class in tkinter?

from tkinter import *
master = Tk()
master.title("Add Questions")
master.geometry("800x600")
master.geometry("+275+70")
master.configure(bg="#D3D3D3")
class timer:
def __init__(self,root):
self.root=root
self.text = ""
self.second=10
self.l = Label(root,font=("Calibri (Body)",30),bg="#D3D3D3",fg="#333300")
self.l.place(relx=0.9,rely=0.05,anchor=CENTER)
def update(self):
if self.second>0:
m,s=divmod(self.second,60)
self.text=str(m).zfill(2)+":"+str(s).zfill(2)
self.l.configure(text=self.text)
self.second=self.second-1
self.l.after(1000,self.update)
else:
self.l.configure(text="Time off!",fg="red",font=("Calibri (Body)",70))
self.l.place_configure(relx=0.5,rely=0.5,anchor=CENTER)
self.l.after(1000,lambda: questions(master))
class questions:
def __init__(self,root):
a=timer(master)
a.update()
master.mainloop()
This is my code... class timer: is for countdown. In class questions: I want to destroy self.l label of class timer:. But I don't know how to use self.l attribute in class questions. What should I do?
You can use the .destroy() method.
This makes any widget disappear, and you can't use it again.
So, in your example, this would be self.l.destroy().
As for your class, you can have the second class inherit the first class.
To do that, update your class like this:
class questions(timer):.
And this would be your timer class:
class timer(Object):.
Read up on class inheritance in python.
Hopefully this helps!

How do i pass a class variable to another class?

I'm making a barcode generator. I need the input in class GUI to be read by class Barcode so it can print the lines on the canvas.
from tkinter import *
class GUI(Frame):
def __init__(self, master=None):
...
self.code_input = Entry(master)
self.code_input.pack()
self.code = self.code_input.get()
...
self.barcode = Barcode(master, height=250, width=200)
self.barcode.pack()
self.barcode.draw()
....
class Barcode(Canvas):
def draw(self):
self.ean = GUI.code
If I reference directly like the above it says AttributeError: type object 'GUI' has no attribute 'code'
If I do inheritance method (based on https://stackoverflow.com/a/19993844/10618936),class Barcode(Canvas, GUI) it says the same as the previous
If I use setter and getter methods:
class GUI(Frame)
def __init__(self, master=None):
...
#property
def code(self):
return self.__code
#code.setter
def code(self, code):
self.__code = code
then self.ean = GUI.code, it won't run the program and say
TypeError: 'property' object is not subscriptable instead
how do I fix this problem? Is the structure of my program really bad? I'm really not good at programming at all... I just want the variable in GUI to be transferred to class Barcode so it can compute and return the result back to GUI to display the barcode
You need to create an instance of the GUI, otherwise you are just referencing to the static class for which code is not defined. You could access it like in this example
class A():
def __init__(self):
self.code = "A"
class B():
def __init__(self):
self.code = "B"
def foo(self):
print(self.code + A().code)
b = B()
b.foo() # Outputs "BA"
Otherwise, to access it like an static variable within the class you need to define it inside the class root level
class A():
code = "C"
def __init__(self):
self.code = "A"
class B():
def __init__(self):
self.code = "B"
def foo(self):
print(self.code + A.code)
b = B()
b.foo() # Outputs "BC"
You should pass the GUI object to the Barcode class, which at the point you create the Barcode instance is self. If you want the Barcode to be inside the GUI frame, you can also directly use it as the Canvas master.
Another thing to notice is that with the way you have it now, self.code will be and remain an empty string, since you only define it right after you've created the Entry widget, at which point it is empty. You should use get on the Entry at the time you want to do something with the contents at that point.
from tkinter import *
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.code_input = Entry(self)
self.code_input.pack()
self.barcode = Barcode(self, height=250, width=200)
self.barcode.pack()
Button(self, text="Update Barcode", command=self.barcode.draw).pack()
class Barcode(Canvas):
def __init__(self, master, height, width):
Canvas.__init__(self, master, height=height, width=width)
self.master = master
self.text = self.create_text((100,100))
def draw(self):
self.itemconfig(self.text, text=self.master.code_input.get())
root = Tk()
gui = GUI(root)
gui.pack()
root.mainloop()
For illustration purposes I create a text object on the canvas and update that with the current value of the Entry on a Button click.

Tkinter - Give name of variable option of a Scale through a class

I am starting to learn OOP and I've been struggling with some basic stuff.
In the code below, I have created a class Scales() that I want to use to create 2 very similar scales, with only their variable option being different.
How can I pass the name of these variables as a parameter when I call Scales() and make both of them a DoubleVar type?
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk
import numpy as np
class Scales(Frame):
def __init__(self, parent, variable_name, label_text, initial_value,
final_value):
self.parent = parent
self.bar_length = 200
self.variable_name = variable_name
self.label_text = label_text
self.initial_value = initial_value
self.final_value = final_value
# self.variable_name = DoubleVar()
self.scale_name = Scale(self.parent, variable=self.variable_name,
orient=HORIZONTAL,
from_=self.initial_value,
to=self.final_value,
length=self.bar_length, cursor="hand",
label=self.label_text)
class MainApplication(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.slice_number_scale = Scales(self.parent, slice_number,
"Slice Number", 1, 24)
if __name__ == '__main__':
root = Tk()
root.geometry("800x600")
MainApplication(root)
root.mainloop()
If the variables are going to live as instance variables of your Scales class, then there's absolutely no reason to give them separate names; every reference to them is going to be in the context of some particular instance. You'd probably want to define a get() method that does something like return self.variable.get(), for the convenience of the class's user.
If the variables live somewhere outside the class, then Scales should not care what their names are; pass the variable itself as a parameter to the class constructor, and pass it on as the variable= option to Scale().
Just create the variables in each Scale class instance that is created, then access them through the instance's name. Here's what I mean:
from tkinter import *
#from tkinter import ttk
#from PIL import Image, ImageTk
#import numpy as np
class Scale(Frame):
""" Dummy version of class for testing and illustration. """
def __init__(self, parent, orient=None, from_=None, to=None, length=None,
cursor=None, label=None):
Frame.__init__(self, parent) # initialize base class
self.variable = DoubleVar() # create variable and make attribute
class Scales(Frame):
def __init__(self, parent, label_text, initial_value,
final_value):
self.parent = parent
self.bar_length = 200
# self.variable_name = variable_name
self.label_text = label_text
self.initial_value = initial_value
self.final_value = final_value
# self.variable_name = DoubleVar()
self.scale1 = Scale(self.parent,
# variable=self.variable_name,
orient=HORIZONTAL,
from_=self.initial_value,
to=self.final_value,
length=self.bar_length,
cursor="hand",
label=self.label_text)
self.scale1.pack()
class MainApplication(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
slice_number = 42
self.slice_number_scale = Scales(self.parent, slice_number, 1, 24)
root = Tk()
app = MainApplication(root)
app.mainloop()
After doing this you can access the variable for each Scale instance within a Scales instance as self.scale1.variable (and self.scale2.variable after you add it). Within the MainApplication instance they can be referred to as self.slice_number_scale.scale1.variable (and self.slice_number_scale2.variable).
For the latter you might want to add methods to class MainApplication to make such references more succinct, such as:
class MainApplication(Frame):
....
def get_scale_var1(self):
return self.slice_number_scale.scale1.variable.get()

How to communicate between two classes

I have a confusion accessing the methods from one class to the other. I want to write on tkinter text widget on class A from inside the method of class B. How am I supposed to do it?
from abc import xyz
from Tkinter import *
class A(Frame):
def write(self,text):
self.display.insert(END,text+'\n')
def __init__(self,parent):
Frame.__init__(self,parent)
self.parent=parent
self.initUI()
def initUI(self):
self.grid(row=0,sticky=N+E+S+W)
self.columnconfigure(0,weight=1)
self.rowconfigure(0,weight=1)
self.display = Text(self)
self.display.grid(row=0,sticky=N+E+S+W)
self.yscroll = Scrollbar(self,command=self.display.yview)
self.yscroll.grid(row=0,column=1,sticky=N+S)
self.display.config(yscrollcommand=self.yscroll.set)
class B(xyz):
def dataReceived(self):
data = 'hello world'
get = A()
get.write(data + '\n')
def main():
root = Tk()
ex = App(root)
root.mainloop()
This gives me error as
get = A()
exceptions.TypeError: __init__() takes exactly 2 arguments (1 given)
Please suggest me the way to inherit the properties of class A in Class B. Thank You for taking time to read this.
If you do not wish to pass a parent, in class A change __init__() to :
def __init__(self,parent=None):
Your problem isn't communicating between two classes, your problem is that you forgot to give the A class an argument. Note the parent argument:
def __init__(self,parent):
Frame.__init__(self,parent)
self.parent=parent
self.initUI()
You have to set the units (text and parent) when you first make the class. So it would be get = A(data + "/n", "some str"
The you could run the get.function without giving any parameters.
Also I'm not sure of you are aware but you are adding two new lines to data not just one

Labels being acessed from other classes?

I have two classes here and I want the label from the parent class to be available in the child class. I've tried making the label a global variable but that doesn't seem to work either unless I'm doing something wrong (I've only been using Python for two weeks). Here's the code.
from tkinter import *
class Parent_Class(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.grid()
self.create_labels()
def create_labels(self):
self.label_1 = Label(self)
self.label_1["text"] = "I'm in the Parent Class."
self.label_1.grid(row = 0, column = 0, sticky = W)
class Child_Class():
def __init__(self):
self.change_label()
def change_label(self):
label_1["text"] = "I'm in the Child Class."
window = Tk()
window.title("Label Test")
window.geometry("250x250")
app = Parent_Class(window)
window.mainloop()
You should never directly change a widget outside of a class that creates it. If you choose to modify that original class, you end up having to change two or more classes. Instead, you give the class with the label an interface that other classes can use. You can directly access the label, but it results in what is called "tight coupling"
Regardless, the key to making it work is that you have to pass to the child a reference to the parent.
It works something like this, using an interface. Again, you could directly access parent.label instead of calling an interface, but that's generally a bad idea.
class Parent_Class(...):
def __init__(self, ...):
...
self.label = Label(...)
...
def set_label(self, string):
self.label.configure(text=string)
class Child_Class(...):
def __init__(self, parent):
...
self.parent = parent
...
def change_label(self):
self.parent.set_label("I'm in the Child Class")
# of, if tight coupling is acceptable to you:
# self.parent.label.configure(text="I'm in the Child Class")
...
p = Parent_Class(...)
c = Child_Class(parent=p)

Categories

Resources