What I am trying to do is:
In attempt to keep things tidy, possibly in error, I have an Application class, as well as functions: init, app_size, create_frames, create_widgets, and an updater function. There is quite a bit more, but if I can get this core part to work right I should be able to keep making progress.
This begins with the relatively standard tkinter UI initialization which includes the starting size of the application window.
# Initialize Application Window
pc_gui = tk.Tk()
pc_gui.geometry("1280x720")
# Initialize Updater Variables
UPDATE_RATE = 1000
# Run application
app = Application(pc_gui)
pc_gui.mainloop()
Then I create the class, main frame (pc_gui), and I call the various sub frame, application size, and images functions.
class Application(tk.Frame):
""" GUI """
def __init__(self, pc_gui):
""" Initialize the frame """
tk.Frame.__init__(self, pc_gui)
self.app_size()
self.grid()
self.create_frames()
self.create_images()
self.updater()
This is the app_size function. It's purpose is to parse the current size of the application window and the display monitor assuming the user will change these to suit while the program is running.
def app_size(self):
pc_gui.update_idletasks() # Get Current Application Values
app_width = pc_gui.winfo_width() # Current Application Width
app_height = pc_gui.winfo_height() # Current Application Height
disp_width = pc_gui.winfo_screenwidth() # Monitor (Screen) Width
disp_height = pc_gui.winfo_screenheight() # Monitor (Screen) Height
return app_width, app_height, disp_width, disp_height
Then I create the sub frames using the app_size values. The colors are simply there to help me keep track of things during development.
def create_frames(self):
""" Create Frames """
app_size = self.app_size() # Get size wxh of application window and display
app_width = app_size[0]
app_height = app_size[1]
disp_width = app_size[2]
disp_height = app_size[3]
geometry = "%dx%d" % (app_width, app_height) # Create text value for geometry
pc_gui.geometry(geometry) # Set Application Window Size
# Section of Application window dedicated to source video
video_frame = tk.Frame(
master = pc_gui,
width = app_width*.75,
height = app_height,
bg = "blue"
)
video_frame.place(
x = 0,
y = 0
)
# Section of Application window dedicated to calculations
side_frame = tk.Frame(
master = pc_gui,
width = app_width*.25,
height = app_height,
bg = "red"
)
side_frame.place(
x = app_width*.75,
y = 0
)
pc_gui.update_idletasks()
sf_x = side_frame.winfo_x()
sf_y = side_frame.winfo_y()
sf_w = side_frame.winfo_width()
sf_h = side_frame.winfo_height()
return sf_x, sf_y, sf_w, sf_h
def updater(self):
#self.create_frames() # Update Application Window
self.create_images() # Update Images Widget
self.after(UPDATE_RATE, self.updater) # Call "updater" function after 1 second
Then I start creating widgets. For the image (Label) widgets I would like to use grid, but I am having the hardest time figuring out how to reference the sub frame (side_frame) in the create_images function. I have truncated the important bits to make this question more to the point.
def create_images(self):
sf_dims = self.create_frames()
sf_x = sf_dims[0]
sf_y = sf_dims[1]
sf_w = sf_dims[2]
sf_h = sf_dims[3]
...
fp1_lbl = tk.Label(
master = pc_gui,
image = fp1
)
fp1_lbl.image = fp1
fp1_lbl.place(
x=sf_x + img_padding,
y=sf_y + 20,
anchor='nw'
)
Everything up to this point "works" admittedly rather inefficiently. What I would like to do is not have those sf_x, _y, _w, and _h hanging out there in the wind and it follows that I would also not have to call the frame function from widget function in order to get them.
The reason is that I feel it's not in the right spirit of python frames as I would like to use Grid on the side frame only but create (or use) Grid from the widget function (the point of creating the side_frame in the first place) and I would prefer to only refresh the sub parts of the application window that need to be refreshed, and not the whole smash every 1 second.
What ends up happening is the images flicker every 1 second, even when nothing needs to be updated and while the images do resize according to the application window I am doing this with the Place functionality and effectively ignoring the existence of side_frame.
My current attempts have been around the following
fp1_lbl = tk.Label(
master = self.side_frame,
image = fp1
)
fp1_lbl.image = fp1
fp1_lbl.place(
x=sf_x + img_padding,
y=sf_y + 20,
anchor='nw'
)
I get versions of the following error:
AttributeError: 'Application' object has no attribute 'side_frame'
You need to name things with the "self" prefix in order to use them in other methods. So when you create the subframes:
# Section of Application window dedicated to calculations
self.side_frame = tk.Frame(
master = pc_gui,
width = app_width*.25,
height = app_height,
bg = "red"
)
self.side_frame.place(
x = app_width*.75,
y = 0
)
Now you can use them anywhere in your class as you have tried:
fp1_lbl = tk.Label(
master = self.side_frame,
image = fp1
)
Remember there is no implied relationship between a variable named foo
and self.foo
. They are 2 completely unrelated names.
As for your resizing, tkinter will do that for you. You can use the relwidth and relheight arguments to set the width and height to a fraction between 0 and 1. Here's a demo:
import tkinter as tk
class Application(tk.Frame):
""" GUI """
def __init__(self, pc_gui):
""" Initialize the frame """
tk.Frame.__init__(self, pc_gui)
self.create_frames()
def create_frames(self):
# insert the subframes in *this* Frame (self), not in the master Frame
self.video_frame = tk.Frame(self, bg = "blue")
self.video_frame.place(relheight=1.0, relwidth=.25)
self.side_frame = tk.Frame(self, bg = "red")
self.side_frame.place(relheight=1.0, relwidth=.75, relx=1.0, anchor='ne')
pc_gui = tk.Tk()
pc_gui.geometry("1280x720")
win = Application(pc_gui)
win.pack(fill=tk.BOTH, expand=True)
tk.Button(pc_gui, text = "Don't click me!").pack()
pc_gui.mainloop()