This is a section from my code. Basically, I want access to the value given in the entry box, which is a part of the def init ..Is there any way around this without using global variables? Also, it is set up this way so that this variable can be used throughout my program. Thanks.
import tkinter as tk
class MainApp(tk.Tk):
def test():
a = __init__.test_entry.get()
print(a)
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
test_entry = tk.Entry(self)
test_entry.pack()
submit_button = tk.Button(self, text="Submit", command=MyApp.test)
submit_button.pack()
if __name__ == "__main__":
app = MainApp()
app.mainloop()
No, there's no way for an external function to access the local variables defined in a function.
There are several ways to solve the problem you have without that though. You could make the variables defined in __init__ instance variables (attributes of self) and make sure your external function can access the right instance (perhaps in a global variable).
Or you could define a function within the __init__ function. Such a closure can access the variables defined in an outer namespace without issue. In your specific instance, you could even make the closure a really small one, using lambda to call the "real" outer function with an argument:
class MainApp(tk.Tk):
def test(test_entry):
a = test_entry.get()
print(a)
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
test_entry = tk.Entry(self)
test_entry.pack()
submit_button = tk.Button(self, text="Submit", command=lambda: MyApp.test(test_entry))
submit_button.pack()
The expression lambda: MyApp.test(test_entry) defines an unnamed function that keeps hold of the test_entry defined in the namespace of the __init__ method. When the lambda function is called, it passes that value on to the test function. You could also move the whole test definition into the __init__ method, and so call it directly, without a lambda, but that is often ugly as it involves lots of deep indentation.
I'm not sure if i understand you correctly tbh
Have you considered using this instead?
self.__test_entry = tk.Entry(self)
Related
Thankful for any assistance in my question.
I am using Python 3 with tkinter where I have setup multiple pages, however I am trying to call a specific textvariable which is in a class, within a definition and wondering if it's possible to do with the way I built my script.
It's quite a big script so I don't want to paste all the code here, I hope this will be enough.
Basically the class is called POIIN(tk.frame) and I am trying to call
class POPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.entryboxes()
def entryboxes(self):
# Entry boxes + variables
global voucher
voucher = IntVar()
entry_box3 = tk.Entry(self, textvariable=voucher, width=32, bg="lightgreen")
entry_box3.place(x=435, y=151)
I am trying to call the textvariable by doing the following outside of the class:
def do_mssql():
try:
if int(POPage.entryboxes(voucher).get()) > int(maxN):
voucherchktext()
elif int(voucher.get()) < int(minN):
voucherchktext()
else:
sql()
except:
vouchermissing()
However it seems to be calling the IntVar() and I don't get any error message, (quite new with Python)
Basically I can pull the voucher from another class (page) with just doing the int(voucher.get()).
I could of course rename the textvariable and call it that way (which I don't mind), but it would be great if there is any way around this.
Greatful for any help!
BR,
Thanks for posting your entire code on pastebin. By looking at your code I can see that none of your classes actually take ownership of the tkinter widgets they create. In otherwords, they don't have any instance variables. I would suggest a radical redesign and getting rid of all global variables and objects - in this case it's a major code smell and suggests that there is a flaw in your design - and that's fine, you did say you were a beginner after all. I see a lot of good things in your code, too! Not to be one-sided...
Basically, the change I'm recommending is this: Each frame class should take ownership of the widgets they create. That includes things like tk.Labels, tk.Buttons, tk.Entrys and tk.IntVars among others.
The major benefit this provides, is that any instance of your frame class will have its own tk.IntVar (or whatever entry widget variable, like tk.StringVar) which it does not need to share with any of the other classes (you called it a textvariable). This makes sense if you think about it: Every tk.Entry widget is coupled with one variable object that keeps track of the user's entered data for that entry widget - if you create just one global entry variable and share it with all entry widgets in all frames, you could easily lose a user's entered data - there's really no reason to do that.
Try running/playing around with the following code snippet:
import tkinter as tk
class MyFrame(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.label = tk.Label(self, text="Some Text")
self.label.pack()
self.entry_var = tk.IntVar()
self.entry = tk.Entry(self, textvariable=self.entry_var, width=32)
self.entry.pack()
self.button = tk.Button(self, text="Print this frame's 'entry_var'", command=lambda: print(self.entry_var.get()))
self.button.pack()
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Title")
self.geometry("256x256")
self.resizable(width=False, height=False)
self.frames = []
for frame in MyFrame(), MyFrame():
self.frames.append(frame)
frame.pack()
def main():
application = Application()
application.mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
You have a main Application (you call it Ghost in your example), which has multiple frames (my example only has one frame class, but the Application has two instances of that frame class). Each frame instance has an entry widget and a unique, completely separate and distinct corresponding entry variable object. There is no reason for frames to share the same entry variable object. Making the widgets and entry variable objects instance variables of their classes should eliminate your use of global variables entirely.
In my example, if I needed to get access to the values in the entry widgets from outside the classes - like in the main function for example, you would say application.frames[0].entry_var.get() to get the value in the first frame's entry widget.
Inspired by this 300+ vote closed Q&A: Best way to structure a tkinter application?, I'm looking to avoid explicitly using root in a function within a class. I think it should be implicitly declared through self or parent or something like that. Here is the code in question:
I have this code...
self.label_this = tk.StringVar()
self.label_last = tk.StringVar()
self.label_total = tk.StringVar()
tk.Label(count_frame, textvariable=self.label_this, \
font=(None, MON_FONTSIZE)).pack(anchor=tk.W)
tk.Label(count_frame, textvariable=self.label_last, \
font=(None, MON_FONTSIZE)).pack(anchor=tk.W)
tk.Label(count_frame, textvariable=self.label_total, \
font=(None, MON_FONTSIZE)).pack(anchor=tk.W)
self.update_cnt_labels()
Then later on...
''' Get list of Window ID's on monitor now '''
new_windows = self.windows_on_monitor(new_windows)
new_windows_cnt = len(new_windows) / WIN_CNT
if self.old_windows_cnt == new_windows_cnt :
FlashMessage (self.label_this, "No new windows to remove...", \
3, 750, 250)
self.update_cnt_labels()
return
Then later on...
class FlashMessage:
def __init__(self, widget, message, count=5, on=500, off=300):
self.delay_show (1, widget, message)
for i in range(count):
self.delay_show (on, widget, "")
self.delay_show (off, widget, message)
def delay_show(self, ms, widget, message):
root.after(ms, widget.set(message))
root.update_idletasks()
I want to avoid using root in the last two lines and use self or something similar.
My program call chain is something like:
the traditional: root = tk.Tk()
bunch of mainline initialization stuff.
the class: ResizingCanvas(mycanvas)
mainline function: popup(event) which is bound to <ButtonPress-1>
Dynamically formatted menu.tk_popup(event.x_root, event.y_root)
the class: RemoveNewWindows()
the function: remove()
the class: FlashMessage() (show above)
the function: self.delay_show() (shown above)
Each class and function has haphazard self, positional parameters, *args and **kwargs which mostly serve no purpose. Indeed even the __init__ above might be unnecessary. This is a result of copying code all over stack overflow.
Every second word in the program seems to be self but the word parent is only used in the class ResizingCanvas(). Do I have to propagate parent down the call list and use it somehow?
You can call after and update_idletasks on any widget. There are many such functions that can be called on any widget but which have a global effect.
In your case, you'll need to pass some widget into the FlashMessage constructor and save the reference. You can then use the reference to call the functions.
You're passing something called widget that doesn't actually contain a widget. You need to rename it to something more appropriate (eg: var), and then pass in an actual widget.
(Note: you also are calling after incorrectly, which I've fixed in the following example)
For example:
class FlashMessage:
def __init__(self, widget, var, message, count=5, on=500, off=300):
self.widget = widget
...
def delay_show(self, ...):
self.widget.after(ms, var.set, message)
self.widget.update_idletasks()
Then, whenever you create an instance of FlashMessage you need to add a widget as the first parameter.
For example, assuming that count_frame is defined in the context where you create an instance of FlashMessage and it is an actual widget, it might look something like this:
if self.old_windows_cnt == new_windows_cnt :
FlashMessage (count_frame, self.label_this, "No new windows to remove...", \
3, 750, 250)
self.update_cnt_labels()
return
This code is part of the main calculator application mentioned here: https://pastebin.com/ECA2AQzY
I am new to python and from my understanding, "self" is the first argument being passed automatically for each instance of a class. Here, I am confused whether "app" would be self? and Frame is some superclass?
I do not understand why init is called on Frame within a main init method?
Also, why is lambda needed? Can it just not be command = self.appendToDisplay("7"))
What was the need of calling grid method on "app" object when the grid is called in the class itself?
calculator = Tk()
calculator.title("Calculator")
calculator.resizable(0, 0)
#================================================
class Application(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.createWidgets()
#================================================
def createWidgets(self):
self.sevenButton = Button(self, font=("Helvetica", 11), text="7",
borderwidth=0, command=lambda: self.appendToDisplay("7"))
self.sevenButton.grid(row=1, column=0, sticky="NWNESWSE")
#================================================
app = Application(calculator).grid()
calculator.mainloop()
Let's try to answer your questions in order:
Application intherrits from Frame, that is all methods implemented in Frame are also avaliable in Application. In addition Application can implement its own methods as well as override implementations in Frame. In the latter case, the implementations in Application replace the implementations in Frame. This is the case for the __init__() method. The method in Application replaces the one in Frame. Thus, the Frame() __init__() method is never called and that is typically not good, since there may be a lot of the functionallity in Frame() that needs explicit initialization. Therefore, Application.__init__() must explicitly call Frame.__init__() to get it to initialize.
The difference between self.appendToDisplay("7") and lambda: self.appendToDisplay("7") is that in the first case the method appendToDisplay() is directly called and the result is returned. When using lambda instead of calling the function, a new function is defined. Thus, when using the lambda construct the result will not be directly available. Instead the result of the calculation will be delayed to a later time, when the command is to be executed. Thus, we want to display 7 on the display when the button is pressed and not now.
The grid call in Application.createWidgets() refers to the button, to place that in the calculator Application. The Application.grid() call on the other hand, is placing the whole calculator itself. Thus, it is a hierachical thing, where the parts of the application are first placed and then the application itself.
I hope the above answers help you understand the concepts a bit better. It may be quite a bit to digest and you should try to find a few tutorials that explains the concepts in a bit more detail.
I am trying change a Label in a Class with a function.
class Fenster2(tk.Frame):
def __init__(self, parent,controller):
tk.Frame.__init__(self,parent)
label_fluss1 = tk.Label(self, width=9)
label_fluss1.grid(row=3,column=2)
label_fluss2 = tk.Label(self, width=9)
label_fluss2.grid(row=4,column=2)
GPIO.add_event_detect(27,GPIO.RISING,callback=hochzaehlen1)
GPIO.add_event_detect(22,GPIO.RISING,callback=hochzaehlen2)
GPIO.add_event_detect(23,GPIO.FALLING,callback=interrupt)
def hochzaehlen1(callback):
global ticks1
ticks1 +=1
return ticks1
def hochzaehlen2(callback):
global ticks2
ticks2 +=1
return ticks2
def interrupt(callback):
global ticks1
global ticks2
global fluss1
fluss1=ticks1/582.0 # liter/min
fluss2=ticks2/354.0 # liter/min
ticks1=0
ticks2=0
Fenster2.label_fluss1.config(text=str(fluss1))
pb_fluss1.config(value=fluss1)
label_fluss2.config(text=str(fluss2))
pb_fluss2.config(value=fluss2)
Now, when interrupt tries to configure label_fluss1 it says Fenster2 has no instance label_fluss1. Anyone knows how to make this work?
I know that I will get the same problem with label_fluss2 and both progressbars.
Any advice is much appreciated.
Greetings Sebastian
Believe you need to use self on those variables when creating a class. That could be the issue causing this. so in your init() you would have:
self.label_fluss1 = tk.Label()
You would do this for all the variables in init being defined and your methods.
As for the class itself goes, why are you making all your methods children of callback that doesnt exist?
I would read up on creating classes more on https://docs.python.org/3/tutorial/classes.html to get a better understanding of instance variables, global, and local. The scope in which these reside are very important for being used. If the class is created correctly, there is no reason that when you create the object that it should not have the fluss1 attribute. As it stands now though, you need to define self before these variables. Then go from there structuring your class properly to call these attributes as needed.
You are missing a basic understanding of how classes work. You need to make the variable you want to update an instance variable, and then call it the same way:
class Fenster2(tk.Frame):
def __init__(self, parent,controller):
tk.Frame.__init__(self,parent)
self.label_fluss1 = tk.Label(self, width=9)
self.label_fluss1.grid(row=3,column=2)
# etc...
def interrupt(self):
#etc ...
self.label_fluss1.config(text=str(fluss1))
I recommend you find a basic tutorial on classes before you continue, since GUIs rely heavily on them.
I split up my class constructor by letting it call multiple functions, like this:
class Wizard:
def __init__(self, argv):
self.parse_arguments(argv)
self.wave_wand() # declaration omitted
def parse_arguments(self, argv):
if self.has_correct_argument_count(argv):
self.name = argv[0]
self.magic_ability = argv[1]
else:
raise InvalidArgumentsException() # declaration omitted
# ... irrelevant functions omitted
While my interpreter happily runs my code, Pylint has a complaint:
Instance attribute attribute_name defined outside __init__
A cursory Google search is currently fruitless. Keeping all constructor logic in __init__ seems unorganized, and turning off the Pylint warning also seems hack-ish.
What is a/the Pythonic way to resolve this problem?
The idea behind this message is for the sake of readability. We expect to find all the attributes an instance may have by reading its __init__ method.
You may still want to split initialization into other methods though. In such case, you can simply assign attributes to None (with a bit of documentation) in the __init__ then call the sub-initialization methods.
Just return a tuple from parse_arguments() and unpack into attributes inside __init__ as needed.
Also, I would recommend that you use Exceptions in lieu of using exit(1). You get tracebacks, your code is reusable, etc.
class Wizard:
def __init__(self, argv):
self.name,self.magic_ability = self.parse_arguments(argv)
def parse_arguments(self, argv):
assert len(argv) == 2
return argv[0],argv[1]
The best practice to solve this question is you need to build the parameter in Init part first,
Then adjust it in the Def
class MainApplication(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, self.master)
self.settingsFrame = None
self.create_widgets(master)
def create_widgets(self, master):
# frame Container
self.settingsFrame = tk.Frame(self.master, width=500, height=30, bg='white')
Although the definition of instance variables outside init isn't recommended in general, there are rare cases in which it is natural. For example, when you have a parent class that defines several variables that its child classes won't use, and whose definition will make its child waste time or resources, or will be simply unaesthetic.
One possible solution to this is using an init-extention function, that each child class may override, and in this function use function setattr in order to define the class-unique instance variables. May be this is not too aesthetic as well, but it eliminates the here-discussed linting warning.
For each attribute you want to set via function, call the function from the init. For example, the following works for me to set the attribute ascii_txt...
def __init__(self, raw_file=None, fingerprint=None):
self.raw_file = raw_file
self.ascii_txt = self.convert_resume_to_ascii()
def convert_resume_to_ascii(self):
ret_val = self.raw_file.upper()
return ret_val
If you are using Python 3, you can try
class Wizard:
def __init__(self, argv):
self.name: str = str()
self.magic_ability: str = str()
self.parse_arguments(argv)
self.wave_wand() # declaration omitted
def parse_arguments(self, argv):
if self.has_correct_argument_count(argv):
self.name = argv[0]
self.magic_ability = argv[1]
else:
raise InvalidArgumentsException() # declaration omitted
# ... irrelevant functions omitted
Although not as pythonic as the accepted answer, but it should get away the Pylint alert.
And if you don't concern about type and don't want to create a new object with object() use:
class Wizard:
def __init__(self, argv):
self.name = type(None)()
# ...
As None will cause type not match error.