Cannot change widget config from a method (Tkinter) - python

I have a class where I create a Label on the init method:
import tkinter as tk
from tkinter.filedialog import askopenfilename
class app(tk.Tk):
def __init__(self, master):
label1 = tk.Label(master, text="Select file...")
label1.pack()
Then with a button I call a method to choose a file and change the label text to filename path.
def files(self):
filename = askopenfilename()
self.label1.config(text=filename)
The problem is that when I choose the file, the app gets closed without errors, so I don't know what's going on.
Outside the class I have:
root = tk.Tk()
app_gui = app(root)
root.mainloop()

In your specific case, there are two problems. The first is that you're creating two instances of tk.Tk. You should never do that.
The second is that you aren't creating self.label1 so any attempt to modify it will fail.
The solution is to first remove tk.Tk as a superclass for app. The second is to properly define self.label1
class app():
def __init__(self, master):
self.label1 = tk.Label(...)
...
On a side note, you should seriously consider following the naming conventions of PEP8 and name your main class App. PEP8 is nearly universal in the python world, and deviating from it makes your code harder to read.

Related

tkinter. How can I change the screen without having to open a new window?

How am I able to do this? I am new to this so if it is possible to not use OOP, that would be great :) I’m trying to do that part myself.
Since Python is an OOP language, and the Tkinter interface is inherently object based you will not be getting away from any OOP.
That being said. To my thinking the best method, would be to keep track of all created elements place them and then remove them were necessary. This method is a lot of effort but perhaps a good learning project.
As example pack an object in the Tkinter window and pack_forget the object when you need to remove it. (Label and Button in the example below).
from tkinter import Tk, Label, Button
class QBox:
""" Class that is an object"""
def __init__(self, master):
""" Class init Method"""
self.master = master
master.title("Question Box")
self.label = Label(master, text="Welcome to the Question Box")
self.button = Button(master, command=self.change, text="Clickme", fg='Black')
self.label.pack()
self.button.pack()
def change(self):
self.label.pack_forget()
def main():
root = Tk()
QBox(root)
root.mainloop()
if __name__ == '__main__':
main()
This is the basic concept of the pack and pack_forget functions Tkinter has to offer. Hope this helps.

How do I create a save function for an application that has multiple classes?

I'm developing a program using Tkinter for my GUI. Right now I'm conceptualizing the entire project, so the code I will provide is very simplistic.
Essentially, I wanted to create a main window that has several widgets each written in their own classes. One of the widgets would be the navbar where "File" -> "Save As" would exist.
My issue with this is if I have navbar as a separate class that is instantiated in the master class, the save function written in the navbar would be unable to view the variables in the other classes.
I have thought of potentially two solutions for this, but I am not sure if either one is necessarily the right thing to do for best Software Engineering practices.
Potential solution 1: Create a separate thread that constantly waits for the user to click save. Once it clicks save it changes an event flag which causes the main class to call some save function to save all variables. My issue with this is it is a constant waste of resources. The thread will be wasting resources constantly waiting for the save button to be clicked.
Potential solution 2: Create the navbar in a separate class, but define the navbar functions in the main class that instantiates it. My issue with this is that it makes the main class colluded with extraneous functions that I would like to have defined elsewhere for better practices. Additionally, I am not entirely sure how I would do this but I am sure there is some way that it could be done if I spent time looking into it.
class Main:
def __init__(self, rt):
self.rt = rt
self.navbar = navbar.NavBar(rt)
self.rt.mainloop()
class NavBar(tkinter.Frame):
def __init__(self, master):
tkinter.Frame.__init__(self, master)
self.master = master
self.bg='red'
self.text = tkinter.Text(self, height=1, width=30)
self.text.insert(tkinter.END, "File")
self.text.pack()
self.grid(row=0, column=0)
if __name__ == "__main__":
root = tkinter.Tk()
root.title('Automation')
main = Main(root)
Here, Navbar would have a button named File which if you scrolled over would generate a list of other buttons, one of which being "Save As". If this Save As button is clicked, I would want to be able to save all variables belonging to Main.
TLDR: I want to figure out the best way for me to create a save file functionality, being able to save variables from different classes that are all instantiated under a main umbrella class.
Normally I will setup the Main class as the controller of all the Frames, and have the Main inherits from Tk directly. Below is a sample on how to access attributes from other classes using this approach:
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.navbar = NavBar(self)
self.another_frame = AnotherFrame(self)
self.navbar.grid(row=0, column=0)
self.another_frame.grid(row=1, column=0)
class NavBar(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
tk.Button(self,text="Button of first frame",command=self.print_other_frame).pack()
def print_other_frame(self):
print (self.master.another_frame.entry.get()) #access master attributes
class AnotherFrame(tk.Frame):
def __init__(self,master):
tk.Frame.__init__(self, master)
self.entry = tk.Entry(self)
self.entry.pack()
self.entry.insert(0,"Test from 2nd frame")
if __name__ == "__main__":
root = Main()
root.title('Automation')
root.mainloop()

Inheritance from Tkinter Frame in varying implementations

I'm fairly new to python and try to build a simple GUI following an object oriented approach. Therefor I let my widget classes inherit from tk.Frame and create an application controller to build the GUI.
My application contains the following two files:
mainModule.py
# coding: utf8
from testPackage import myGUI
import Tkinter as tk
root = tk.Tk() # Main window
my_gui = myGUI.MainApplication(root)
root.mainloop() # Hold window open until we close it
myGUI.py
# coding: utf8
import Tkinter as tk
# Application initializer
class MainApplication(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, master)
self.configure_gui()
self.pack() # <--- CODE IN QUESTION
self.create_widgets()
def configure_gui(self):
self.master.title("Any title")
self.master.geometry('800x600')
self.master.minsize(600, 100)
def create_widgets(self):
self.main_window = MainWindow(self)
self.main_window.pack()
# Main Data Window
class MainWindow(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, master)
self.main_window = tk.Label(self, text="This is a test")
self.main_window.pack(side="top", fill="x")
Initially running my code without self.pack() (marked as #Code in question) in the MainApplication class definition gave me only the root basic window without the Label created from MainWindow class.
As simple as the answer may be but why?
I used a few sources to get into the topic inbefore and some of them didn't use any geometry manager on the root window (or i'm to inexpierienced to see it. Source below for examples).
Source:
https://www.begueradj.com/tkinter-best-practices.html
http://python-textbok.readthedocs.io/en/latest/Introduction_to_GUI_Programming.html#putting-it-all-together
Tkinter example code for multiple windows, why won't buttons load correctly?
Only after reading following answers regarding widget creation I came aware of the missing part:
Best way to structure a tkinter application
creating a custom widget in tkinter
Figuring that pack() on initialization would be the same as
MainApplication(root).pack(side="top", fill="both", expand=True)
or
Example(root).place(x=0, y=0, relwidth=1, relheight=1)
for a grid based layout.
I guess it's a very simple or obvious element i don't see relating to inheritance but i'm not entirely sure why have to pack() the MainApplication Frame and furthermore why it seems to work for example without this step.
Because MainWindow inherits from Frame, it is itself a frame. If you never call pack, place, or grid on it, it will be invisible. This is no different than if you created a button or scrollbar or any other widget and then don't call one of those methods on it.
Since all of the other widgets are a children of this frame, they will be invisible since their parent is invisible.
Somewhat unrelated to the question being asked, self.pack() is a code smell. Generally speaking, a class should never call pack, place or grid on itself. This tightly couples itself to the caller (meaning, this widget has to know that the caller is using one of those methods).
In other words, if you decide that MainApplication wants to switch from pack to grid for all of its children, you can't just update MainApplication, you also have to update MainWindow. In this case, the root window has only one child so the problem is fairly small, but by doing it this way you are starting a bad practice that will eventually cause you problems.
The rule of thumb is that the function that creates a widget should be responsible for adding it to the screen. That means that it would be better to do it like this:
root = tk.Tk()
my_gui = myGUI.MainApplication(root)
my_gui.pack(fill="both", expand=True)
You would then need to remove self.pack() from MainApplication.__init__.
You also have some very misleading code that might be contributing to the confusion. Take a look at this code:
# Main Data Window
class MainWindow(tk.Frame):
def __init__(self, master):
...
self.main_window = tk.Label(self, text="This is a test")
self.main_window.pack(side="top", fill="x")
Notice how you have a class named MainWindow. Within that you have a label that is named self.main_window, but self.main_window is not a MainWindow. This is especially confusing since the function that creates MainWindow also creates an instance variable named self.main_window.
You might want to consider renaming this label to be something else (eg: self.label, self.greeting, etc).

using the Frame() class to create frames

I am quite new to tkinter and I wonder how I should create frames.
If one is prefered over the other one, why is it so?
Is it better like this:
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent):
main_frame = tk.Frame(parent)
root = tk.Tk()
main_app = MainApplication(root)
root.mainloop()
or like this:
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
root = tk.Tk()
main_app = MainApplication(root)
root.mainloop()
The first one creates two frames -- the instance itself (main_app) is a frame, and it contains a child frame (main_frame). Though, because you don't call the __init__ of Frame, the first frame is not properly constructed.
If you are immediately going to create an internal frame and put everything inside it, it's pointless to inherit from Frame.
By the way, your code doesn't quite work. If you want to actually see the application widget (and its children) then you're going to need to call pack, place or grid on the widget.
For example:
root = tk.Tk()
main_app = MainApplication(root)
main_app.pack(fill="both", expand=True)
root.mainloop()

Returning a value after calling a function with a button in Tkinter

from Tkinter import *
from tkFileDialog import askopenfilename
from PIL import Image
def main():
filename = askopenfilename(filetypes=[("Jpeg","*.jpg")])
return filename
root = Tk()
button = Button(root,text="Open",command=main)
button.pack()
root.title("Image Manipulation Program")
root.mainloop()
I am kind of a newbie at programming in general, but I am trying to make an imaging program through the Tkinter GUI library. What I need to be able to do in the code above is return the string that is stored in filename so it is in the global scope of the program and I am able to use it. The problem is I don't know how to do this when calling the function with a button. I cannot find the answer to this problem on any website so I would appreciate anybody's help with this problem.
If you use the class based approach to Tk applications, instead of returning values from event handlers, you can assign them to instance variables. This the best approach, as function-based GUI applications don't scale well precisely because the need to place stuff at module scope.
from Tkinter import *
class Application(Frame):
def main(self):
self.filename = askopenfilename(filetypes=[("Jpeg","*.jpg")])
def createWidgets(self):
self.button = Button(root,text="Open",command=self.main)
self.button.pack()
def __init__(self, master=None):
Frame.__init__(self, master)
self.filename = None
self.pack()
self.createWidgets()
root = Tk()
root.title("Image Manipulation Program")
app = Application(master=root)
app.mainloop()
Generally, it is bad practice to use global variables to pass information around your program. However, if you really must do this, use a mutable data type (such as a list or a dict) as your global variable and change its contents from your callback function, main.
returned_values = {} # Create an empty dict.
def main():
returned_values['filename'] = askopenfilename(filetypes=[("Jpeg","*.jpg")])
# returned_values['filename'] may now be accessed in the global scope.
If you intend to do this frequently, consider implementing your own class to pass information around.

Categories