18166
18166

Reputation: 111

How to add options to a OptionMenu from a text file

I'm writing a program for my class that allows you to make a recipe, save it and edit it even after closing the program. You obviously need a text file to do this.

I am using an OptionMenu (Tkinter, Python 3.3.3), but I cannot figure out how to keep updating it to have the first option in the list I have made in my text file. So how do I do that?

My code is thus:

###########################################
###########################################
### RECIPE BOOK TASK ##### By 18166 #######
###########################################
###########################################

from tkinter import *


def script ():

    #### MAIN ####

    fake_window = Tk()
    new_recipe_window = fake_window
    start_window = fake_window
    start_window.title("Recipe Book Task")

    #### MAIN ####



    ## DATA FILE ##

    global datafile
    datafile = open("StoredRecipes.txt", "a+")

    ## DATA FILE ##



    ### Functions ###

    def close (x):                                                             ## Close Original Window ##
        global start_window
        global new_recipe_window
        (x).withdraw()

    def new_recipe ():
        new_recipe_window = Tk()                                                ## Making new window ##
        new_recipe_window.title("New Recipe")
        close(start_window)

        recipe_name_label = Label(new_recipe_window, text="Recipe Name: ")      ## Making new recipe label ##
        recipe_name_label.grid(row=0, column=0)
        recipe_name_box = Entry(new_recipe_window)                              ## Making new recipe entry ##
        recipe_name_box.grid(row=0, column=1)

        num_people_label = Label(new_recipe_window, text="Number of people: ")  ## Making number of people label ##
        num_people_label.grid(row=1, column=0)
        num_people_box = Entry(new_recipe_window)                               ## Making number of people entry ##
        num_people_box.grid(row=1, column=1)

        item_label = Label(new_recipe_window, text="Items: ")                   ## Making item label ##
        item_label.grid(row=2, column=0)
        item_box = Entry(new_recipe_window)                                     ## Making item entry ##
        item_box.grid(row=2, column=1)

        quantity_label = Label(new_recipe_window, text="Quantity: ")            ## Making quantity label ##
        quantity_label.grid(row=3, column=0)
        quantity_box = Entry(new_recipe_window)                                 ## Making quantity entry ##
        quantity_box.grid(row=3, column=1)

        unit_label = Label(new_recipe_window, text="Unit: ")                    ## Making unit label ##
        unit_label.grid(row=4, column=0)
        unit_box = Entry(new_recipe_window)                                     ## Making unit entry ##
        unit_box.grid(row=4, column=1)

        def write ():
            a = recipe_name_box.get()
            b = num_people_box.get()
            c = item_box.get()
            d = quantity_box.get()
            e = unit_box.get()

            line = (a, b, c, d, e)
            datafile.write(str(line) + "\n")
            datafile.close()

            saved_recipes.config(a)

            close(new_recipe_window)

            script()



        finish_button = Button(new_recipe_window, text="Save and Finish", command=write)       ## Making finish button ##
        finish_button.grid(row=5, column=0, sticky=S)




    # Dropdown Box #

    default = StringVar(start_window, 'Recipe 1')
    default.set("Select Your Recipe")

    saved_recipes = OptionMenu(start_window, default,  "Hi")
    saved_recipes.grid(row=0, column=1)

    # Dropdown Box #



    # New Recipe Button #

    new_recipe = Button(start_window, text="New Recipe", command=new_recipe)
    new_recipe.grid(row=0, column=0)

    # New Recipe Button #

script()

(Sorry for the block, I think all is useful to answering possibly?)

Upvotes: 2

Views: 1731

Answers (2)

Jasper
Jasper

Reputation: 3947

To add elements to an OptionList, you can use the following method (from http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html)

datafile = open("StoredRecipes.txt", "r")
  for line in datafile.readlines():
    saved_recipes['menu'].add_command(label=line,
      command=lambda temp = line: saved_recipes.setvar(saved_recipes.cget("textvariable"), value = temp))

Which uses (has to use) a closure and an anonymous function -- definitely nothing you should deal with on your level of experience (guessing from the structure of your code).

This snippet adds a command for each line in your file. Because an OptionMenu is something that executes things when elements are selected, you have to provide a command for each line. Right now this is just setting the displayed text to the selected line.

To accomplish this, it uses an anonymous function (lambda) that sets the textvariable of the OptionMenu to the current line.

Upvotes: 0

Michael0x2a
Michael0x2a

Reputation: 64248

I believe you have two different options.

One option you could do is set up a timer to check the text file every couple of seconds, see if it's changed at all, and update your OptionMenu accordingly. You can find more info on how to do this here, but in a nutshell, you'd want your code to look something like:

def recheck(root, option_menu, file_name):
    with open(file_name) as my_file:
        lines = my_file.readlines():
        # `lines` is a list where each item is a single line
        # do any checks and updates you need here.

    root.after(1000, recheck, root, option_menu, file_name)  
    # schedule the function to run again after 1000 milliseconds.

def script():
    # set up your gui

    start_window.after(1000, recheck, start_window, option_menu, "StoredRecipies.txt") 

Note: you can find more info on the with statement here: http://effbot.org/zone/python-with-statement.htm

The downside of this is that the update will be a little laggy -- you'll end up rechecking the file only once a second, so the update won't be instantaneous.

Alternatively, you could use something like Watchdog. It's a 3rd party library that you can set up to "watch" a particular file and run a function whenever the file changes. It's much more responsive in that you'll call the function only if the file actually changes, but it might end up being more complicated since you need to figure out how to make it work with Tkinter. I'm going to guess that your code will look roughly like this:

import os.path
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer

def setup_observer(option_menu, filename):
    normalized_filename = os.path.normpath(input_filename)

    class MyEvent(FileSystemEventHandler):
        def on_modified(self, event):
            if os.path.normpath(event.src_path) == normalized_filename:
                # update your option menu

    observer = Observer()
    observer.schedule(MyEvent(), '.', recursive=False)
    return observer

def script():
    # setup gui

    observer = setup_observer(option_menu, "myfile.txt")

    start_window.mainloop()

Upvotes: 1

Related Questions