user3692825
user3692825

Reputation: 126

Tkinter Python: How can I use the Frame Widget to put my labels in line with one another?

I am a newbie at programming. I'm using Tkinter in Python. I am trying to make it so the labels in my program will be side by side with one another (with a little space in between, maybe like 50 pixels). The two labels I would like to have side by side are: "ingredients" and "steps". I have tried creating a frame so that they could be next to each other, but the ingredients is always above the steps, not side by side. I looked at other questions on StackOverflow but I couldn't quiet apply them to my program. So can I make it so my two labels, steps and ingredients, are side by side with some space in between. Would I continue using the frame or would I use .grid or something else?

Any help is greatly appreciated!! And if you could add comments in your code so I know what you are doing, that would be great! Thank you!

import Tkinter
class Cookbook(Tkinter.Tk):
    def __init__(self):
        Tkinter.Tk.__init__(self)
        self.title("Cookbook")
        self.geometry("500x500+0+0")

        self.button = []
        for r in range(1):
            for c in range(1):
                b = Button(self).grid(row=r,column=c)
                self.button.append(b)

class Button(Tkinter.Button):
    def __init__(self,parent):
        b = Tkinter.Button.__init__(self, parent, text="Add A New Recipie", height=8, width=15, command=self.make_window)

    def make_window(self):
        popwindow = IngredientAdder()
        popwindow.title_box()
        popwindow.ingredients_box()
        popwindow.steps_box()
        popwindow.init_gui()

class IngredientAdder(Tkinter.Tk):
    def __init__(self):
        Tkinter.Tk.__init__(self)
        self.title("Recipie")
        self.geometry("550x500")

    def title_box(self):
        #Frame for the Title Label and the Title Entry Box
        test = Tkinter.Frame(self, height=100, relief=Tkinter.SUNKEN)
        test.pack(anchor=Tkinter.NW,side=Tkinter.TOP)

        #putting in a Title LABEL and ENTRY BOX
        Tkinter.Label(test,text="Title:").pack(anchor=Tkinter.NW,side=Tkinter.LEFT)
        self.entry1 = Tkinter.Entry(test,width=450)
        self.entry1.pack(anchor=Tkinter.NW,side=Tkinter.TOP)

    def ingredients_box(self):
        #Frame for the Ingredients Label and the Ingredients Entry Boxes
        self.test2 = Tkinter.Frame(self, height=100,width=20, relief=Tkinter.SUNKEN)
        self.test2.pack(anchor=Tkinter.NW,side=Tkinter.TOP)

        #Put an Ingredients label at the top of the window and anchor it there
        Tkinter.Label(self.test2,text="Ingredients:").pack(anchor=Tkinter.NW,side=Tkinter.TOP)

    def steps_box(self):
        #Frame for the Steps Label and the Steps Textbox
        test3 = Tkinter.Frame(self, height=100,width=50, relief=Tkinter.SUNKEN)
        test3.pack(anchor=Tkinter.NE,side=Tkinter.RIGHT)

        #putting in an entry box and Steps label for the steps of the recepie
        Tkinter.Label(test3,text="Steps:").pack(anchor=Tkinter.N,side=Tkinter.TOP)
        self.entry2 = Tkinter.Text(test3,width=40,height=33,font="helvetica 12",padx=5,pady=5).pack(anchor=Tkinter.NE,side=Tkinter.TOP)

    def title_save(self):
         self.title_entries.append(self.entry1)

    def text_box(self):
         self.text_entries.append(self.entry2)

    # function to add new ingredients
def add_ingredient_entry(self):
        entry = Tkinter.Entry(self.test2)
        entry.pack(anchor=Tkinter.NW,side=Tkinter.TOP)
        self.ingredient_entries.append(entry)

    # get contents of all entry boxes
    def save_recipe(self):
        for words in self.title_entries:
            print words.get()
        for ingredient in self.ingredient_entries:
            print ingredient.get()
        for text in self.text_entries:
            print text.get('1.0','end')
       print "[Recipie saved]"

    # build initial widgets 
    def init_gui(self):
        # title saved in this array
        self.title_entries = []     

        # this is a list of ingredients entry boxes
        self.ingredient_entries = []

        #this saves the list in this array, hopefully
        self.text_entries = []

        #Making a frame at the bottom to put both buttons in line
        self.test4 = Tkinter.Frame(self,height=10, relief=Tkinter.SUNKEN)
        self.test4.pack(side=Tkinter.BOTTOM)

        #Put these two buttons at the bottom of the window and anchor them there
        Tkinter.Button(self.test4,text="Save recipe",command=self.save_recipe, width=15).pack(anchor=Tkinter.SE,side=Tkinter.RIGHT)
        Tkinter.Button(self.test4,text="Add ingredient",command=self.add_ingredient_entry, width=15).pack(anchor=Tkinter.NW,side=Tkinter.LEFT)

        #New ingredients will be added between the label and the buttons 
        self.add_ingredient_entry()

top = Cookbook()
top.mainloop()

Upvotes: 1

Views: 9037

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 386315

The first thing you need to do is change your style of programming slightly. You are putting the requirement to pack a widget within the function that creates the widget, which makes managing your code really difficult. Instead, the caller of a function that creates a widget should be responsible for packing (placing, gridding) it.

For example, instead of:

def make_window(self):
    ...
    popwindow.ingredients_box()
    popwindow.steps_box()
    ...

def steps_box(self):
    #Frame for the Steps Label and the Steps Textbox
    test3 = Tkinter.Frame(self, height=100,width=50, relief=Tkinter.SUNKEN)
    test3.pack(anchor=Tkinter.NE,side=Tkinter.RIGHT)

You should do something like this:

def make_window(self):
    ...
    ingredients_box = popwindow.ingredients_box(self)
    steps_box = popwindow.steps_box(self)
    ...
    ingredients_box.pack(side="left")
    steps_box.pack(side="right")

def ingredients_box(self, parent):
    #Frame for the Steps Label and the Steps Textbox
    test3 = Tkinter.Frame(parent, height=100,width=50, relief=Tkinter.SUNKEN)
    test3.pack(anchor=Tkinter.NE,side=Tkinter.RIGHT)
    return test3

That by itself won't solve your problem, but it will make it easier to solve. The next step is to take a look at your overall layout, and realize that using a grid is a more natural layout than using pack.

When you do that, it becomes pretty simple to change just the one function that is responsible for laying out the GUI rather than the myriad of functions that try to both create widgets and lay them out.

With that, you can then do:

def make_window(self):
    ingredients_box = ...
    steps_box = ...
    ...

    ingredients_box.grid(row=1, column=0, sticky="nsew")
    steps_box.grid(row=1, column=1, sticky="nsew")
    ...

The overall layout becomes much easier to visualize in the code, and much easier to reconfigure (which will inevitiably happen)

Notes:

You have a bug in that you are creating more than one instance of Tk. If you need to create a popup window, create an instance of Toplevel. Tk should be reserved for the root window, and only the root window.

You also have a bug in this statement:

self.entry2 = Tkinter.Text(test3,...).pack(...)

In python, when you do x=y().z(), x gets the value of .z(). In Tkinter, pack always returns None, so self.entry2 will be set to None.

Upvotes: 0

Bob
Bob

Reputation: 704

If you wish for two labels to be side by side you can simply use the pack manager to pack them to the same side like so:

testlabel.pack(side=Tkinter.LEFT)
testlabel2.pack(side=Tkinter.LEFT)

However the preferred way would be to use the grid manager for things like this. To align two labels you would do something like this:

testlabel.grid(row=0, column=0)
testlabel2.grid(row=0, column=1)

If you are talking about how to use them with frames, it would be something like this:

labelframe = Tkinter.Frame()
testlabel = Tkinter.Label(labelframe)
testlabel2 = Tkinter.Label(labelframe)
testlabel.grid(row=0, column=0)
testlabel2.grid(row=0, column=1)
labelframe.pack()

This would group the two labels together inside of that frame which you can then align in your main window. However if you have a bunch of labels and you all want them side by side like a table you could probably just get away with using grid for the main window. You tend to use frames when you want to group widgets together and move them to specific places in relation to other widgets.

Upvotes: 0

Related Questions