0

Ive rewritten this for more context, in a logical order this is what i want the program to do

1 by pressing open file it needs to open a specified file and put it into the text widget (done) 2 by pressing the extract file it needs to extract something from a specified subtree(in an xml file) 3 export the extracted data to a text file

but lets go back to point nr 2 as i have not yet written the extractor(easy part) first i need to refference a file i want to edit, and that is where i run into my problem. inside extract it cant acess vars in openfile and i dont want to reopen the file again.

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


interface = tk.Tk()
interface.geometry("500x500")
interface.title("Text display")

def openfile():
    filename = filedialog.askopenfilename()
    print(filename)
    file = open(filename)
    txt = file.read()
    print(txt)
    T = tk.Text(interface, height=10, width=50)

    T.insert(tk.END, txt)
    T.grid(column=1, row=2)
    return txt

def extract():

    print(txt)

button = ttk.Button(interface, text="Open text File", command=openfile)  # <------
button.grid(column=1, row=1)

buttonex = ttk.Button(interface, text="Extract subtitles", command=extract)  # <------
buttonex.grid(column=2, row=1)

interface.mainloop()

NameError: name 'txt' is not defined (when i press extract)

  • 5
    You need to read up on the concept of *local variables*. The normal behavior is that nothing inside of a function has any existence outside of the function, unless explicitly `return`ed, or stored in some external location. – jasonharper Dec 12 '19 at 17:21
  • Return the open file handle from `openfile`, and pass it as an argument to `extract`. For anything more complicated, you'll have to show how exactly `openfile` and `extract` are used. – chepner Dec 12 '19 at 17:21
  • @chepner when it comes to files or similar resources that need to be freed (database connections etc), the good practice is that whoever acquired the resource (here "opened the file") is also responsible for freeing it. Given your reputation, I'm very suprised you advise such a solution... – bruno desthuilliers Dec 12 '19 at 17:32
  • Please edit your question to provide more context - how are those functions called, what's the real problem you're trying to solve etc. – bruno desthuilliers Dec 12 '19 at 17:38
  • 1
    @brunodesthuilliers It’s definitely *not* against “good practice” and that’s trivially disproved by *reductio ad absurdum*: if only the local scope can open the file, then how do you implement `open`, since you’re not allowed to return an opened file from it? There’s nothing wrong with having `with openfile() as f: …` in your code. – Konrad Rudolph Dec 12 '19 at 17:43
  • @brunodesthuilliers Notice I didn't post an answer, precisely because there's not enough context to know exactly how the code should be structured. If these are methods of a class, for example, then the class itself may be a context manager that would save the return value of `openfile` as an attribute from `__enter__` and ensue that `__exit__` closes it. – chepner Dec 12 '19 at 17:48
  • If anything, I erred in assuming that both `openfile` and `extract` are supposed to read from the same open handle (something that maybe false, given the description of *reopening* the file in another function). – chepner Dec 12 '19 at 17:50
  • If you never let a file be used across multiple scopes, you wouldn't need a `with` statement; you would simply provide a function `withFile(fname, callback)` that opens the named file, passes the file object as the only argument to the given callback function, then closes the file once `callback` returns or before reraising an uncaught exception from `callback`. – chepner Dec 12 '19 at 17:53
  • @chepner I didn't say a file shouldn't be passed to other functions, just that it's the function or object that opens the file that should be responsible for closing it. That's actually, as you mention, the whole point of context managers. – bruno desthuilliers Dec 12 '19 at 17:56
  • 1
    @brunodesthuilliers Context managers are *one* (very good) way to manage the issue; it's not the only or required solution. – chepner Dec 12 '19 at 18:00
  • @chepner yes I've been using other ways to manage those issues too (in other languages, and in Python before context managers were added). – bruno desthuilliers Dec 12 '19 at 18:05
  • I agree with some of the other commenters that there are multiple issues at play here. It would be good to have some more context for this question/program. – AMC Dec 12 '19 at 18:08
  • @jasonharper thank you for pointing out local and global var, i ended up making a global var in a function makes it so i can use it inside of another, very obvious actually – Johann Davel De Beer Dec 12 '19 at 21:21

2 Answers2

1

As the edit of the initial questions shows that a GUI is planned I would suggest to move your TK widgets into an interface class as in the tkinter documentation. Further, if you plan to do more complex manipulations you should make an own class to hold your data resp. manipulate it.

    import tkinter as tk

    class App(tk.Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.pack()
           self.create_widgets()

        def create_widgets(self):
           # Create your buttons and connect them to the methods
           self.button_load = tk.Button(self)
           self.button_load["command"] = self.loadTextFromFile()
           self.button_load["text"] = "Load Data"
           self.button_load.gird(column=1, row=1)

           self.buttonex = tk.Button(self)
           self.buttonex["text"] = "Extract subtitles"
           self.buttonex["command"] = self.extract()
           self.buttonex.grid(column=2, row=1)

           # Create the text widget
           self.text_widget = tk.Text(self, height=10, width=50)
           self.text_widget.grid(column=1, row=2)
       def loadTextFromFile(self):
           filename = filedialog.askopenfilename()
           print(filename)
           try:
               file = open(filename)
               txt = file.read()
               file.close()
               self.text_widget.insert(tk.END, txt)
           except Exception:
               # If anything went wrong, close the file before reraising
               file.close()
               raise
        def extract(self):
           # Now you have access to self.text_widget and it holds your read in text
           do_something(self.text_widget)

    # Maybe at the following functions to make your file importable without directly executing it. Could come in handy later on.
    def run():
        # create the application
        myapp = App()
        #
        # here are method calls to the window manager class
        #
        myapp.master.title("My Do-Nothing Application")
        myapp.master.maxsize(1000, 400)

        # start the program
        myapp.mainloop()

    if __name__ == '__main__':
        run()

Have a look at the tkinter documentation for further examples: https://docs.python.org/3/library/tkinter.html

The additional if clause checks if your module is the main module and executes the run function. This defines an entry point if you directly run your module and prevents functions from execution at import time if you intend to import the module into another module. A more detailed explanation can be found here: What does if __name__ == "__main__": do?

---------- Old Answer --------------

As pointed out in the comment you need to somehow return your local variables back to the calling function in order to be able to use them somewhere else. This could be achieved by a simple return statement:

def openfile():
  """ It is the responsibility of the caller to close the return value """
  filename = filedialog.askopenfilename()
  print(filename)
  try:
      file = open(filename)
      txt = file.read()
      T = tk.Text(interface, height=10, width=50)

      T.insert(tk.END, txt)
      T.grid(column=1, row=2)
      return file
  except Exception:
       # If anything went wrong, close the file before reraising
       file.close()
       raise

would for example return the open fileid for further manipulation. E.g.:

def extract():
  with openfile() as file:
      txtex = file.read()
      print (txtex)

If your goal is howeer, to manipulate the file content and read it later you would need to save your manipulations back into the file otherwise your second read would not see the changes. Dont forget to close your file again.

oliverm
  • 113
  • 8
  • As I already commented above, chepner's advice to return the opened file goes against all good practices. Not to mention the fact that this (ill named) `openfile` function does much more than "opening a file". It's impossible to tell what the right solution here is without much more context (how those two functions are used etc) but _this_ is surely not a good solution. My 2 cents etc.... – bruno desthuilliers Dec 12 '19 at 17:37
  • I've edit the answer with one approach to handling a "dangling" resource returned by `openfile`. – chepner Dec 12 '19 at 17:57
  • Seems that we both edited it right now sorry for that – oliverm Dec 12 '19 at 17:58
  • @JohannDavelDeBeer I've update my answer with regards to your updated question including more context. – oliverm Dec 13 '19 at 13:53
-1

I changed three lines where I believe to have come across typos:

  1. From: self.button_load["command"] = self.loadTextFromFile() to: self.button_load["command"] = self.loadTextFromFile
  2. From: self.button_load.gird(column=1, row=1) to: self.button_load.grid(column=1, row=1)
  3. From: self.buttonex["command"] = self.extract() to: self.buttonex["command"] = self.extract

Thank you for this example!

DonP
  • 24
  • 3