1

I have made OptionMenu which is at centre of window but the second OptionMenu should come side to it and not below it. How can I place it side by side.

from Tkinter import*

class MyOptionMenu(OptionMenu):
    def __init__(self, master, status, *options):
        self.var = StringVar(master)
        self.var.set(status)
        OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('calibri',(10)),bg='white',width=12)
        self['menu'].config(font=('calibri',(10)),bg='white')

root = Tk()
mymenu1 = MyOptionMenu(root, 'Select status', 'a','b','c')
mymenu2 = MyOptionMenu(root, 'Select another status', 'd','e','f')
mymenu1.pack()
mymenu2.pack()
root.mainloop()

I tried mymenu2.pack(side=LEFT) but it gives weird output see screenshot.

screenshot

j_4321
  • 15,431
  • 3
  • 34
  • 61
Ice fire
  • 143
  • 3
  • 10

4 Answers4

2

You need to modify options for both menus as in:

mymenu1.pack(side="left")
mymenu2.pack(side="right")

For more complex layouts try using grid instead of pack.

Nae
  • 14,209
  • 7
  • 52
  • 79
  • I think you did not get my point I want both menu to be in centre whenever you resize the window if I use this `mymenu1.pack(side="left")` `mymenu2.pack(side="right")` One menu goes to extreme left and other to extreme right. – Ice fire Dec 11 '17 at 15:05
  • @Icefire If you want to pack them together, having a frame widget as their parent to contain them would be a much better solution. – Nae Dec 11 '17 at 15:18
2

Use place instead of pack geometry which will give you flexibility

from Tkinter import *

class MyOptionMenu(OptionMenu):
    def __init__(self, master, status, *options):
        self.var = StringVar(master)
        self.var.set(status)
        OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('calibri',(10)),bg='white',width=12)
        self['menu'].config(font=('calibri',(10)),bg='white')

root = Tk()


mymenu1 = MyOptionMenu(root, 'Select status', 'a','b','c')
mymenu2 = MyOptionMenu(root, 'Select another status', 'd','e','f')
mymenu1.place(x=30, y=0) # this an example of place
mymenu2.place(x=160, y=0)
root.mainloop()
AD WAN
  • 1,414
  • 2
  • 15
  • 28
  • 1
    I agree that the `place` method is flexible in the sense that the widget can be put in an arbitrary location. But the main drawback is that to put two widgets side by side, you need to know the size of one of the widget to avoid overlapping. On the contrary, there is no overlapping issue with `pack` or `grid`. – j_4321 Dec 11 '17 at 15:15
  • By the way, with this method, the widgets do not stay at the center of the window when you resize it, but it can be done with `place` if you do: `mymenu1.place(relx=0.5, y=0, anchor='ne')` and `mymenu2.place(relx=0.5, y=0, anchor='nw')` – j_4321 Dec 11 '17 at 15:30
  • sure but he can increase the 'y' to position it where he want it in the center – AD WAN Dec 11 '17 at 15:37
  • I was talking about the horizontal centering, not the vertical one. – j_4321 Dec 11 '17 at 15:38
  • The `pack()` methodology is really the best way to lay out two widgets side-by-side or one on top of the other. `grid()` is fine, too. But `place()` is really not appropriate for such a simple scenario. – GaryMBloom Dec 12 '17 at 16:19
  • @Gary02127 I think `grid()` is better. With Grid you can add other widgets later without having to worry about the order of placement like you do with `pack()`. – Mike - SMT Sep 13 '18 at 13:55
2

For the sake of completeness, here is how to have the two option-menus side by side in the upper center of the window , even after resizing it, with the three possible layout:

1) With grid:

This layout puts widgets in cells defined by the row and column numbers.

import Tkinter as tk  # python 2
# from tkinter import tk  # python 3

class MyOptionMenu(tk.OptionMenu):
    def __init__(self, master, status, *options):
        self.var = tk.StringVar(master)
        self.var.set(status)
        tk.OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('calibri',(10)),bg='white',width=12)
        self['menu'].config(font=('calibri',(10)),bg='white')

root = tk.Tk()
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
mymenu1 = MyOptionMenu(root, 'Select status', 'a','b','c')
mymenu2 = MyOptionMenu(root, 'Select another status', 'd','e','f')
mymenu1.grid(row=0, column=0, sticky='e')
mymenu2.grid(row=0, column=1, sticky='w')
root.mainloop()

The two key elements in the code are:

  • root.columnconfigure(<column number>, weight=1): by default, the column weight is 0, so it does not expand to fill the window. You can set this to any integer. For instance if you have two columns, one with weight 1 and the other with weight 2, then the second one will take twice as much space as the first.

  • the sticky option in .grid(): it can be any combination of the cardinal points n, s, e, w. For instance, sticky='e' means that the widget will be on the east side of the grid cell and sticky='ew' means that it will fill the column horizontally.

2) With pack: pack stacks widgets starting from the side given in option, so there is no direct way to get both widgets side by side at the upper center of the window. However this can be done using a frame container:

import Tkinter as tk

class MyOptionMenu(tk.OptionMenu):
    def __init__(self, master, status, *options):
        self.var = tk.StringVar(master)
        self.var.set(status)
        tk.OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('calibri',(10)),bg='white',width=12)
        self['menu'].config(font=('calibri',(10)),bg='white')

root = tk.Tk()
frame = tk.Frame(root)  # container
mymenu1 = MyOptionMenu(frame, 'Select status', 'a','b','c')
mymenu2 = MyOptionMenu(frame, 'Select another status', 'd','e','f')
mymenu1.pack(side='left') 
mymenu2.pack(side='left')
frame.pack()
root.mainloop()

By default, the side option o .pack() is 'top', so frame is put at the upper center of the window. But to have the option-menus side by side in the frame, you need to use 'left' or 'right'.

pack is easy to use if your layout is simple. But as soon as you want something more complicated, you need to use many frame containers to get what you want, so it is easier to use grid instead.

3) With place:

import Tkinter as tk

class MyOptionMenu(tk.OptionMenu):
    def __init__(self, master, status, *options):
        self.var = tk.StringVar(master)
        self.var.set(status)
        tk.OptionMenu.__init__(self, master, self.var, *options)
        self.config(font=('calibri',(10)),bg='white',width=12)
        self['menu'].config(font=('calibri',(10)),bg='white')

root = tk.Tk()

mymenu1 = MyOptionMenu(root, 'Select status', 'a','b','c')
mymenu2 = MyOptionMenu(root, 'Select another status', 'd','e','f')
mymenu1.place(relx=0.5, y=0, anchor='ne')
mymenu2.place(relx=0.5, y=0, anchor='nw')
root.mainloop()
  • .place() can take the position of the widget either as an absolute position (in pixels, with respect to the top left corner of the container) with the options x and y or as a relative position with the options relx and rely. So, relx=0.5 means half the width of the container (in the code sample, the container is root).
  • The option anchor determine which part of the widget is put at the coordinates given in .place. So widget.place(x=x, y=y, anchor='nw') will place the top left corner of the widget at position (x, y).

With place, the widget can be put in an arbitrary position, but you have to handle manually the overlapping issues while pack and grid handle overlapping automatically.

j_4321
  • 15,431
  • 3
  • 34
  • 61
  • Your code using `grid()` gives `AttributeError` how to fix it. – Ice fire Dec 13 '17 at 12:47
  • @Icefire When I run only my code sample on my computer, there is no error. So it certainly comes from something you have added and I will need more details to be able to help you. – j_4321 Dec 13 '17 at 12:52
  • @Icefire The tkinter module is different in python 2 and python 3. The way you imported tkinter in the question is valid for python 2 (`Tkinter` with a capital T) but in python 3, which you are using in the screenshot, you need to import tkinter with `import tkinter as tk` (small t). – j_4321 Dec 13 '17 at 12:59
  • Can you tell me why have you used `tk.OptionMenu.__init__()` – Ice fire Dec 13 '17 at 13:04
  • 1
    @Icefire When I do `tk.OptionMenu.__init__(self, master, self.var, *options)` I call the `__init__` method of the `tk.OptionMenu` class and I need it to create the OptionMenu. What I mean is that `MyOptionMenu` is a customized `tk.OptionMenu` so when I create a new `MyOptionMenu` (which is done by calling `__init__`), I need to create a `tk.OptionMenu` (by calling `tk.OptionMenu.__init__`) and customize it (all the rest in `__init__`). – j_4321 Dec 13 '17 at 13:10
  • why have to passed `tk.OptionMenu` in class ? – Ice fire Dec 13 '17 at 13:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/161112/discussion-between-j-4321-and-ice-fire). – j_4321 Dec 13 '17 at 13:20
  • Need help here https://stackoverflow.com/questions/47807902/how-to-use-class-methods-from-another-py-file – Ice fire Dec 14 '17 at 07:56
1

Your issue is related to the .pack() method. By default, it packs to the TOP, so that widgets are stacked vertically. If you want them side-by-side, you need to change the default using the side kw option. Your options for pack(side=xxx) are TOP, LEFT, BOTTOM, RIGHT, with TOP being the default. So, for the two widgets to be side by side, you would say:

widget1.pack(side=Tkinter.LEFT)
widget2.pack(side=Tkinter.LEFT)

or, in your case, since you did from Tkinter import *, you would say:

mymenu1.pack(side=LEFT)
mymenu2.pack(side=LEFT)
GaryMBloom
  • 5,350
  • 1
  • 24
  • 32