Tkinter - Passing variables between frames after askopenfilename?

Related searches

New to Tkinter. I've been at this for a few days now. I'm looking to pass the file path of an Mp4 video (retrieved using askopenfilename and a button) to another frame (where I grab the first frame and display it, so the user can select a ROI).

UPDATED! MINIMAL, VIABLE EXAMPLE: RUNNING THIS CODE, THE FILENAME CHOSEN DOES NOT DISPLAY ON THE SECOND FRAME (PROBLEM):

LARGE_FONT=("Verdana",12)

import tkinter as tk
from tkinter import filedialog 
from tkinter import *


filename = ''


class HRDetectorApp(tk.Tk): #in brackets, what the class inherits

    def __init__(self,*args,**kwargs): #this will always load when we run the program. self is implied args = unlimited vars. kwargs are keywords arguments (dictionaries)

        tk.Tk.__init__(self,*args,**kwargs)
        container = tk.Frame(self)

        container.pack(side="top", fill="both", expand=True) #pack into top, fill into entire top space, and expand will expand into the whole window. fill into the area you packed.

        container.grid_rowconfigure(0, weight=1) #min size zero, weight is priority
        container.grid_columnconfigure(0,weight=1)

        self.frames = {}
        self.shared_data = {"filename": tk.StringVar()}

        for F in (StartPage, SelectROIPage):

            frame = F(container,self)

            self.frames[F] = frame

            frame.grid(row=0,column=0, sticky="nsew") #sticky = alignment + stretch, north south east west

        self.show_frame(StartPage)
        self.title("Heart Rate Detection")


    def show_frame(self,cont):
        frame=self.frames[cont]
        frame.tkraise()

    def get_page(self, page_class):
        return self.frames[page_class]



def openFile():
    root = tk.Tk()
    global filename 
    filename = filedialog.askopenfilename(title="Select an Mp4 Video", filetypes =(("Mp4 Files", "*.mp4"),))
    root.update_idletasks()
    print(filename)


class StartPage(tk.Frame):


    def __init__(self,parent,controller):
        self.controller = controller
        #global filename
        #filename = ""
        tk.Frame.__init__(self,parent)
        label = tk.Label(self,text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)
        self.filename = tk.StringVar()

        chooseFileButton = tk.Button(self,text="Upload a Video",command=openFile)
        chooseFileButton.pack()

        goButton = tk.Button(self,text ="Click me after you've uploaded a video!", command = lambda: controller.show_frame(SelectROIPage))
        goButton.pack()



class SelectROIPage(tk.Frame):

    def __init__(self,parent,controller):

        tk.Frame.__init__(self,parent)
        label = tk.Label(self,text="Select a Region of Interest (R.O.I)", font=LARGE_FONT)
        label.pack(pady=10,padx=10)
        label = tk.Label(self, text = "selected file : " + filename)
        label.pack()



app = HRDetectorApp()
app.mainloop()

How to reproduce?

  1. Click "Upload a video" and select an MP4 file.
  2. Click "Click me after you've uploaded a video"

For some reason, the variable doesn't update after calling askopenfilename. I've tried to use global variables, using root.update, nothing has worked (see my attempts commented out)

Any help is greatly appreciated, thank you!

ORIGINAL CODE : thanks for your suggestions to simplify it :)

LARGE_FONT=("Verdana",12)

import tkinter as tk
from tkinter import filedialog 
from tkinter import *
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.widgets import RectangleSelector
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import cv2
import numpy as np
import sys
import time
from scipy import signal
import scipy.signal as signal
import selectinwindow


class HRDetectorApp(tk.Tk): #in brackets, what the class inherits

    def __init__(self,*args,**kwargs): #this will always load when we run the program. self is implied args = unlimited vars. kwargs are keywords arguments (dictionaries)

        tk.Tk.__init__(self,*args,**kwargs)
        container = tk.Frame(self)

        container.pack(side="top", fill="both", expand=True) #pack into top, fill into entire top space, and expand will expand into the whole window. fill into the area you packed.

        container.grid_rowconfigure(0, weight=1) #min size zero, weight is priority
        container.grid_columnconfigure(0,weight=1)

        self.frames = {}
        self.shared_data = {"filename": tk.StringVar()}

        for F in (StartPage, SelectROIPage):

            frame = F(container,self)

            self.frames[F] = frame

            frame.grid(row=0,column=0, sticky="nsew") #sticky = alignment + stretch, north south east west

        self.show_frame(StartPage)
        self.title("Heart Rate Detection")


    def show_frame(self,cont):
        frame=self.frames[cont]
        frame.tkraise()

    def get_page(self, page_class):
        return self.frames[page_class]


#
#def openFile():
#    root = tk.Tk()
#    global filename 
#    root.filename = filedialog.askopenfilename(title="Select an Mp4 Video", filetypes =(("Mp4 Files", "*.mp4"),))
#    filename = root.filename 
#    root.update_idletasks()
#    #name = root.filename
#    
#    print(filename)


class StartPage(tk.Frame):


    def __init__(self,parent,controller):
        self.controller = controller
#        global filename
#        filename = ""
        tk.Frame.__init__(self,parent)
        label = tk.Label(self,text="Start Page", font=LARGE_FONT)
        label.pack(pady=10,padx=10)
        self.filename = tk.StringVar()

        chooseFileButton = tk.Button(self,text="Upload a Video",command=self.openFile)
        chooseFileButton.pack()

        goButton = tk.Button(self,text ="Click me after you've uploaded a video!", command = lambda: controller.show_frame(SelectROIPage))
        goButton.pack()

    def openFile(self):
        #root = tk.Tk()
        #global filename 
        #root.filename = filedialog.askopenfilename(title="Select an Mp4 Video", filetypes =(("Mp4 Files", "*.mp4"),))
       # filename = root.filename 
        self.filename.set(filedialog.askopenfilename(title="Select an Mp4 Video", filetypes =(("Mp4 Files", "*.mp4"),)))
        #root.update()
        #if filename == "":
            #root.after(1000,openFile(self))
        #name = root.filename

        print(self.filename.get())

**code to use rectangle selector for selecting ROI**

class SelectROIPage(tk.Frame):

    def __init__(self,parent,controller):


        tk.Frame.__init__(self,parent)
        self.controller = controller
        startpg = self.controller.get_page(StartPage)
        file = startpg.filename.get() **THIS IS NOT UPDATING**
        label = tk.Label(self,text="Select a Region of Interest (R.O.I)", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        #file=filename

        **code to read image and display it (confirmed no errors here!)**



app = HRDetectorApp()
app.mainloop()

You are never updating the value in SelectROIPage. The page is instantiated once, and in that instantiation you initialize file to '' with file = startpg.filename.get(), because this occurs before the button has been clicked when you create HRDetectorApp at frame = F(container, self)

When you click the button, it doesn't create a new frame, thus does not call so it is not updating, and the value is an empty string. You must somehow update the value when the first button is clicked.

I see a few options:

  1. Use the same variable in both SelectROIPage and StartPage.
  2. Update the variable when the button is clicked by extending readyForROI (a bit kludgy in my opinion)
  3. Update the variable when the ROI frame is shown using a binding (overkill, but see: How would I make a method which is run every time a frame is shown in tkinter)

If it were me, I would choose the first.

UPDATE It's a bit trimmed but that's still not what I'd call a "minimum reproducible example".

I've edited your code a bit to accomplish what you're trying to do. I wanted to be able to point at a few other issues as well, so it's not quite minimal.

import tkinter as tk
from tkinter import filedialog 
from tkinter import *


class HRDetectorApp(tk.Tk): #in brackets, what the class inherits
    def __init__(self,*args,**kwargs): #this will always load when we run the program. self is implied args = unlimited vars. kwargs are keywords arguments (dictionaries)

        tk.Tk.__init__(self,*args,**kwargs)
        container = tk.Frame(self)

        container.pack(side="top", fill="both", expand=True) #pack into top, fill into entire top space, and expand will expand into the whole window. fill into the area you packed.

        container.grid_rowconfigure(0, weight=1) #min size zero, weight is priority
        container.grid_columnconfigure(0,weight=1)

        self.frames = {}
        self.shared_data = {"filename": tk.StringVar()}

        for F in (StartPage, SelectROIPage):

            frame = F(container,self)

            self.frames[F] = frame

            frame.grid(row=0,column=0, sticky="nsew") #sticky = alignment + stretch, north south east west

        self.show_frame(StartPage)

    def show_frame(self,cont):
        frame=self.frames[cont]
        frame.tkraise()

    def get_page(self, page_class):
        return self.frames[page_class]


class StartPage(tk.Frame):
    def __init__(self,  parent: tk.Widget, controller: HRDetectorApp):
        tk.Frame.__init__(self,parent)
        self.filename = controller.shared_data['filename'] # use the same object for filename here as in SelectROIPage
        tk.Button(self,text="Click me to pick a file", command=self.openFile).pack()
        tk.Button(self,text ="Click me after you've picked a file", command = lambda: controller.show_frame(SelectROIPage)).pack()

    def openFile(self):
        self.filename.set(filedialog.askopenfilename(title="Select a file"))
        print('filename in StartPage: {}'.format(self.filename.get()))


class SelectROIPage(tk.Frame):
    def __init__(self,  parent: tk.Widget, controller: HRDetectorApp):
        tk.Frame.__init__(self,parent)
        self.filename = controller.shared_data['filename']
        tk.Label(self, text = "selected file : ").pack() # text assigns a permanent value
        tk.Label(self, textvariable=self.filename).pack()


app = HRDetectorApp()
app.mainloop()

So let's discuss this a little ...

  1. First, you don't need to declare a variable global for this to work; that's more relevant to threading, i.e. a global variable can be accessed by all threads in the program.
  2. You also instantiated a second root with root=tk.Tk(). It's important that you only ever have one root (instance of tk.Tk()) in your program.
  3. You can call .grid() and .pack() directly after declaring a label if you don't need access to the widget in the future.
  4. The actual issue you are having. What I meant was to provide a reference to the same variable in both classes. You were close with shared_data['filename'], but you never called or assigned it! Both classes have access to the variable (through controller), and thus they can access the value of the variable.
  5. The benefit of using tkinter variables rather than pythong variables is callbacks and traces. If you use the text property of the label, it will assign a static string to the label. The second label iv'e created uses the textvariable property. When you assign a tkinter variable to this property, the text of the label will automatically update whenever the variable changes. You could get more complicated in what happens by using the .trace() method of tkinter variables to call a function whenever it is written.

Regarding minimum reproducible examples ... what I would have expected is a program which creates two frames, where clicking a button in one frame (StartPage) updates a string variable in the other frame (SelectROIPage), and , each with the same parent frame (HRDetectorApp). I wouldn't expect it to be more than 20-30 lines.

Pass data frame through Tkinter classes, as @t.m.adam suggested and it succeeds passing information between windows as long as I create For the 2 variables you have just below the line class gui(tk. import Tkinter as tk import pandas as pd import tkFileDialog class gui(tk. self.filename = tkFileDialog.askopenfilename() self.first_frame_entry. delete(0, tk. While working with GUI one may need to open files and read data from it or may require to write data in that particular file. One can achieve this with the help of open() function (python built-in) but one may not be able to select any required file unless provides a path to that particular file in code.

You can bind <Expose> event to the SelectROIPage class and update a label in the event callback (which is called when SelectROIPage is shown):

class SelectROIPage(tk.Frame):
    def __init__(self,parent,controller):
        tk.Frame.__init__(self,parent)
        self.controller = controller
        self.startpg = self.controller.get_page(StartPage)
        tk.Label(self,text="Select a Region of Interest (R.O.I)", font=LARGE_FONT).pack(pady=10,padx=10)
        self.label = tk.Label(self) # label used to show the selected filename
        self.label.pack()
        self.bind('<Expose>', self.on_expose)

    def on_expose(self, event):
        self.label.config(text='Selected file: '+self.startpg.filename.get())

Python GUI Development with Tkinter: Part 3, Tk() def print_path(): f = tkinter.filedialog.askopenfilename( parent=root, Then, after creating our root window in line 4, we define a new function in line 6 (which The object is then stored in the image variable, which we can pass as an There, we have a simple for loop, that will iterate through values between 1 and 10. However, I am having a difficult time with passing a variable from one function to another in my code. The problem seems to occur after I run choose() and create self.newLists based on the desired indices.

In your methods you reference the global filename. But in the global scope you don't create filename.

If you were to do that (say just before app = HRDetectorApp()), it should work.

Update

Based on your MVE, this is what happens.

You are creating the SelectROIPage object in the HRDetectorApp.__init__ method. At that time, the global variable filename points to an empty string.

What I would suggest it to create a synthetic event named e.g. <<Update>>. In the SelectROIPage class, you define an on_update method that sets the label text. In SelectROIPage.__init__, you bind the on_update method to the <<Update>> event.

In the handler for clicking the goButton in StartPage, you call the event_generate method on the SelectROIPage object to send it the message. (This requires that the StartPage has an attribute for the SelectROIPage object)

tkinter - global variable not working, I am using Tkinter and doing GUI so that there is a 'save', 'save as', and I Have tried to set the name from filedialog.askopenfilename(), to a global variable to use in my 'save' Frame( self .parent, bd = 2 , padx = 2 , pady = 2 , relief = tk. pass. def quit( self ):. self .parent.destroy(). sys.exit( 0 ). def main():. .askopenfilename: Directory, Title, Extension: To open file: Dialog that requests selection of an existing file..asksaveasfilename: Directory, Title, Extension) To save file: Dialog that requests creation or replacement of a file..askdirectory: None: To open directory: Tkinter Open File. The askopenfilename function to creates an file dialog

Passing variables between classes in tkinter : learnpython, Passing variables between classes in tkinter I want to ge the text entered in the entry widget to get into the second frame. import tkinter as tk # python 3 NOTE: You may see an error response in the cmd box after completing this step. command option in Tkinter Button widget is triggered when the user presses the button. In some scenarios, you need to pass arguments to the attached command function, but you couldn’t simply pass the arguments like below, button = tk.Button(app, text="Press Me", command=action(args)) We will introduce two ways to pass the arguments to the

Use a Dialog box to load an Image into a Tkinter Label, Loading a new image into a Tkinter label with the help of a file dialog box. In between the round brackets of basename, we have our path_to_image variable. This will get you the name of the file from the long path that Python returns from askopenfilename. But we're just storing the path in a handy variable for use later. [Tkinter] Unable to Access global Variable when switching between frames: tziyong: 1: 559: Nov-03-2019, 01:08 AM Last Post: balenaucigasa [Tkinter] Extrakt a Variable from a closed tkinter window: hWp: 5: 832: Aug-23-2019, 09:01 PM Last Post: woooee [Tkinter] sleep(n) not working inside tkinter mainloop: roger31415: 2: 1,602: Jul-14-2019, 06:57

After we store the tuple returned by askcolor in variable color, we then use that variable in line 9 to configure the b1 button. As you already know, the bg argument is responsible for controlling the button's background color. We pass the first element of the color tuple to it (the color

Comments
  • I know it's extra work, but please try to create a minimal reproducible example (stackoverflow.com/help/minimal-reproducible-example) rather than posting all of your code. It's easier to provide help, and you may solve your own problem in the process.
  • Done. Thank you!
  • Hey, I updated my example - I used the same , global variable in both (as you suggested) and still no luck.. any idea what I'm still doing wrong?
  • @mimss I've updated my answer, hopefully that clarifies things. You don't need a global variable in the C/C++ sense. You need something more akin to a reference or pointer, so that both classes see the same data.
  • Great! One small issue - you said that if I use textvariable, the label will automatically update (as it does). However, I need to now pass this varible to OpenCv for a series of other tasks, BUT if I call the filename in SelectROIPage by calling: filename = self.file.get() & it doesn't update. Is there a way I can force the variable to update the same way that it does when used in a label? THANK YOU! :)
  • I don't understand. In the example code I posted, self.filename will update, and self.filename.get() WILL return the updated filename. It is doing what you describe. In your code, you have multiple StringVariables, it's possible you're referencing the wrong one; without seeing updated code I can't say what's wrong.
  • Would that trigger when raising above another frame? "This event occurs whenever at least some part of your application or widget becomes visible after having been covered up by another window." It's not another window covering the widget, but another widget...
  • Yes, it will be triggered when it is raising above another frame as long as it is covered by another frame before. If it is already on the top, then no.
  • I tried this solution and when running my code, it just prompts the askopenfile dialog to popup BEFORE I click the button - any idea why?
  • @mimss I used your updated code with my solution and did not have the mentioned issue.
  • @mimss Also you should not create Tk() instance inside openFile() function. Remove root = tk.Tk() and root.update_idletasks() from openFile().
  • Hey! Thanks so much for your suggestion. Gave that a shot and it didn't work :/ To clarify... I had SOME success using the global variable approach, however the variable would only update AFTER I close the app.
  • Judging by the imports, your code seems to be part of a larger program, most of which we don't see. Try to create a minimum viable example that exhibits the problematic behavior.
  • Hi just updated it with an example that shows the issue. Please have a look. thank you :)