Calling Tkinter Objects from Another File - python

class SneakerGuide(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
global stockXURLInput
stockXURLInput = Entry(sneakerBasicDetails)
stockXURLInput.grid(row=5, column=1)
So I have this class, and within it, I have this function that is an initialization function and withing that I have a bunch of different things I want to .get() from. I just typed out one example. But I am trying to get the entry input outside of that class (actually I am trying to get it from another file but I know how to import them and stuff. How would I go about doing that in this case in particular

For objects you want to access outside of the class, you must make them instance attributes of the class.
For example, you wouldn't use global like in your example. The use of global largely defeats the purpose of using classes. Instead, you do it like this:
class SneakerGuide(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.stockXURLInput = Entry(sneakerBasicDetails)
self.stockXURLInput.grid(row=5, column=1)
To get the value out of the entry, all you need is a reference to the instance of the class.
root = tk.Tk()
...
guide = SneakerGuide(root)
...
print(f"The url is {guide.stockXURLInput.get9)}")
This isn't anything unique to tkinter, this is how all python objects work.

Related

tkinter widget calling a method from another widget

Whilst working on a tkinter application (Tcl/Tk 8.6 and Python 3.9.2) I recently encountered an error that I was able to resolve, but I think the existence of the error highlights some gaps in my knowledge and potential weaknesses in my approach.
A reproducible example of the error is below - this code will not work and returns the error AttributeError: 'parent_class' object has no attribute 'first'.
from tkinter import *
from tkinter import ttk
class child_one(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.x = 10
def print_x(self):
print(self.x)
class child_two(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.b = ttk.Button(self, text='Button 1',
command=parent.first.print_x).grid()
class parent_class(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.grid()
self.second = child_two(self)
self.first = child_one(self)
self.first.grid()
self.second.grid()
if __name__ == '__main__':
root = Tk()
w = parent_class(root)
root.mainloop()
However the code will work if I reverse the order in which the instances of child_one and child_two are created i.e. replacing
self.second = child_two(self)
self.first = child_one(self)
with
self.first = child_one(self)
self.second = child_two(self)
in the definition of parent_class.
I'd really appreciate any explanations or link to resources to help me understand the program flow which causes this to happen - it appears to me that when I create w and get to the line self.second = child_two(self) Python is just looking at the part of the instance of parent_class which has already been created, and not the whole definition of the class.
Would this happen if this was not the first instance of parent_class to be created? Is it specific to tkinter? (I was only able to create a simple reproducible example with tkinter widgets, not with classes more generally.)
I suppose another solution would be to make print_x a (static?) method of parent_class? I also assume there's not enough detail here to definitively state if that (or alternative structures) would be preferable to facilitate interface between the components of my application, but would be interested to hear any suggestions for good practices in this space.
There's really no mystery here. If you do self.first = child_one(self) first, it defines self.first. When you then call child_two(self), it is able to access parent.first since it was previously created.
However, if you call child_two(self) first, at that point in time parent.first doesn't exist. Thus, when you do command=parent.first.print_x, parent.first doesn't exist.
it appears to me that when I create w and get to the line self.second = child_two(self) Python is just looking at the part of the instance of parent_class which has already been created
That is correct. You can't reference things that haven't been created yet.
Would this happen if this was not the first instance of parent_class to be created? Is it specific to tkinter?
I'm not quite sure what you're asking in the first part of that question. It will always happen if you try to reference any object attribute before that attribute has been created. And no, this isn't specific to tkinter. It's a fundamental aspect of the way that python works.
This is a good example of why it's generally best to create proper functions rather than using lambda. lambda is good when you need to pass arguments, but you don't need to do that in this case. Even then, a proper function is better than directly referencing some other object at the time the button is defined. An arguably better way would be to use a function so that self.parent.first doesn't need to be resolved until you actually click the button.
For example:
class child_two(ttk.Frame):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.parent = parent
self.b = ttk.Button(self, text='Button 1', command=self.print_x)
self.b.grid()
def print_x(self):
self.parent.first.print_x()
When you say "Python is just looking at the part of the instance of parent_class which has already been created, and not the whole definition of the class", you seem to be expecting python to have built a static description of your class before the program starts running.
Python does not work that way, it's a dynamic language. As Bryan just said, the first variable is created only when you assign to it for the first.

tkinter: How to access a StringVar and objects from a different class?

I'm having a simple tkinter two frame application with a Label, Entry and Button widget and I want to access a StringVar() of FrameOne with a Entry and Button of FrameTwo.
If have seen a lots of examples of code, but do not get how this is been done in my example below. Many programmers are using a controller. If I would use a controller, I end up from an error to another.
For example:
FirstFrame = FrameOne(mainWindow)`
TypeError: __init__() missing 1 required positional argument: 'controller'
Which I completely understand, because I do not pass anything into the new 'controller' class argument when calling the Frame class. But I do not know what I should pass into this to solve it. Perhaps it is also caused by the lack of knowledge of using class variables (any literature tips are welcome).
The same counts for the solution to inherit FrameOne into FrameTwo. I bump into the same amount of errors applying to my code.
Another thing is that many programmers have examples of two frames that are not visible at the same time, while in my example I have two frames underneath each other at the same time.
An different related issue that I have is, what if the label widget of FrameOne was a Text widget? How do I access the widget from FrameTwo.
I could make it work with globals, but I do not want to use such writing and I will keep the access widget problem anyhow.
Please find my code below:
import tkinter as tk
class AppWindow():
def __init__(self, master):
self.master = master
master.title("Test Application")
master.geometry("1060x680")
master.grid_propagate(False)
class FrameOne(tk.Frame):
def __init__(self, parent):
super().__init__()
self["borderwidth"]=5
self["relief"]="ridge"
self.LabelText = tk.StringVar()
self.LabelText.set("It is not working yet")
self.testlabel = tk.Label(self, textvariable=self.LabelText)
self.testlabel.grid(row=1, column=1)
class FrameTwo(tk.Frame):
def __init__(self, parent):
super().__init__()
self["borderwidth"]=5
self["relief"]="ridge"
self.testentry = tk.Entry(self)
self.testentry.insert("end", "This should be working")
self.testentry.grid(row=1,column=1)
self.testbutton = tk.Button(self, text="Test the label", command=self.updatelabel)
self.testbutton.grid(row=1,column=2)
def updatelabel(self):
FrameOne.LabelText.set(self.testentry.get()) #HOW TO FIX THIS CODE THE RIGHT WAY?
#Create a window as defined in the AppWindow class
mainWindow = AppWindow(tk.Tk())
#Create a Frame as defined in class FrameOne
FirstFrame = FrameOne(mainWindow)
FirstFrame.grid(row=0, column=0) #Positioning Frame on Window
#Create a Frame as defined in class FrameOne
SecondFrame = FrameTwo(mainWindow)
SecondFrame.grid(row=1, column=0) #Positioning Frame on Window
Like with any python object, you access an attribute of an object using a reference to the object.
In your case, updatelabel should look like this:
def updatelabel(self):
FirstFrame.LabelText.set(self.testentry.get())
Note: your use of uppercase characters for instance variables makes your code much harder to comprehend. I recommend following the naming guidelines in PEP8.

How to get variable from another class without instantiation?

How would i pass a varible from another class without instanting the class? the reason i do not want to instantiate the class is because i would have to pass self.master which would mess up the classes window i am passing the variable to.
class MainPageGUI:
def __init__(self, master):
self.master = master
self.master.title("Jans Corp")
self.master.configure(background='lightgrey')
self.master.geometry("1200x800")
listbox = tk.Listbox(self.master,width=150, height=35) # varibable i would like to use in the other class
listbox.place(x=150, y = 130)
Class i would like to pass the variable in:
class NewEmployee:
def __init__(self, master): #Creating basic GUI to add employees
self.master = master
self.master.title("Jans Corp")
self.master.configure(background="lightgrey")
self.master.geometry("300x500")
aa = MainPageGUI(self.master) ## my attempt at it, its wrong as the class get
self.listbox = self.aa.listbox
In general terms, the answer to "How to get variable from another class without instantiation?" is "you can't".
Your code example doesn't provide enough information to give a more concrete example. We don't know, for example, how, when, or where you create the instance of MainPageGUI, or how, when, and where you create an instance of NewEmployee.
I'm going to assume you've already created an instance of MainPageGUI before creating a NewEmployee.
In your case, you're trying to access something in MainPageGUI from another class. You don't want to create another MainPageGUI. Instead, what you need is a reference to the original MainPageGUI. Since that class must be instantiated somewhere, you simply need to pass that instance down when creating a new NewEmployee.
That means that you need to define NewEmployee something like this:
class NewEmployee:
def __init__(self, master, main_gui):
self.main_gui = main_gui
...
Then, anywhere in NewEmployee where you need to reference the listbox, you would use self.main_gui.listbox.
Of course, this also requires that MainGUI actually defines self.listbox. Right now your code does listbox = tk.Listbox(...) when it needs to be self.listbox = tk.Listbox(...).

Obtaining widget values in tkinter

How do I refer to the widget values in the following code.Here I have added widgets by calling methods in the app class for different frames.Next,I want to access the values in all the Widgets(which the user enters) of all the frames at the same time.But I am not able to figure out how should I refer to them and access their values!
class myapp():
def __init__(self,parent):
self.parent=parent
self.container=Frame(self.parent)
self.container.pack()
self.tab1=Button(self.container,text='tab1',command=self.tab1Click)
self.tab2=Button(self.container,text='tab*emphasized text*2',command=self.tab2Click)
self.tab1.pack()
self.tab2.pack()
def tab1Click(self):
top=Toplevel()
self.container1=Frame(top)
self.add_widget1(self.container1)#self.add_widgeti(parent) is a method in myapp() class to add a widget to a frame
self.add_widget2(self.container1)
self.add_widget3(self.container1)
self.container1.pack()
def tab2Click(self):
top=Toplevel()
self.container2=Frame(top)
self.add_widget2(self.container2)
self.add_widget4(self.container2)
self.add_widget5(self.container2)
self.container2.pack()
def write(self):
#here I want to write the values contained in the widgets in both frames in a file,but I am not able to figure out how do I refer to them and access their values.
Any help will be highly appreciated.Thanks in advance.
The widgets in which the user can write have a get method that returns their content. But in order to do this, you need to store your widget in a class variable for example.
Edit: I had misunderstood the problem and I hadn't realized that the add_widget function would be called for different containers for the same instance. One way to keep track of all created widgets is to create a widget list:
add self.widgets = [] in __init__
define theadd_widget method like that:
def add_widget(self, container):
self.widgets.append(Entry(container, text="enter text here"))
self.widgets[-1].pack()
Then to get the text entered by the user in all widgets (inside the write function):
texts = []
for widget in self.widgets:
texts.append(widget.get())

Using variables declared in functions and use them in another function in a different class

#!/usr/bin/python
import MainPanel
import wx
########################################################################
class OtherFrame(wx.Frame):##open PDB frame
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, wx.ID_ANY, "Secondary Frame")
panel = wx.Panel(self)##create panel
chain = wx.Button(panel, label = "OK", pos=(100,80),size=(53,25))##button create
self.Bind(wx.EVT_BUTTON,self.getchain,chain)##bind event to button go to getchain method
def getchain(self,event):
global flag
flag = 1
import OtherFrame
#######
class MainPanel(wx.Panel):##main frame
""""""
#----------------------------------------------------------------------
def __init__(self, parent, size = (5000,5000)):
"""Constructor"""
wx.Panel.__init__(self, parent=parent, size = (5000,5000))
self.frame = parent
Publisher().subscribe(self.showFrame, ("show.mainframe"))
def showFrame(self, msg):
#------------------------------------------------------------------
def createfigure():
OtherF = OtherFrame.OtherFrame()
OtherF.getchain()
print flag
First of all I have shortened down my code massively due to it being over 1000 lines of code.
What I am wanting to do is get the variables declared in getchain and use them in the def createfigure. They are from different classes.
At the moment I am getting the error getchain() takes exactly 2 arguments (1 given)
Could someone tell me where I am going wrong, and how to get the variables I use in def getchain(self,event) and use them in def createfigure
You shouldn't do that. If you really, really want to, then it's better to make the variables class properties by pre-pending them with "self.". So if you want to access the "flag" variable in the other class, make it "self.flag".
Then you can access it using OtherF.flag. However, I think if you need to get something from another class, I recommend using pubsub to pass it around. Here's a tutorial:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
When a method is defined as using "self" as a parameter in python, the object it is applied on is implicitly passed as a parameter. Then, any other parameters you declare must be supplied explicitly in the parentheses. On the second to last line of the excerpt you provided, you don't supply the required parameter "event".

Categories

Resources