Python Tkinter menubar command displays out of window - python-3.x

I've been looking for a way to solve this. The menu command displays out of the main window, like it's anchored to the right of the 'Fichier' dropdown instead of to the left. There doesn't seem to be an option of this in the Menu args. Anyone knows how to make the menu stick to the inside of the window?
Caption
class FenetrePrincipale(Tk):
def __init__(self):
super().__init__()
# ...
self.menubar = Menu()
self.menu_fichier = Menu(self.menubar, tearoff=0)
self.menu_fichier.add_command(label="Ouvrir connexion...", command=self.ouvrir_connexion, accelerator="Ctrl+O")
self.menubar.add_cascade(label="Fichier", menu=self.menu_fichier)
self.config(menu=self.menubar)

Related

How can I change the state of a menu with a button inside tkinter?

I want to make a unit converter in tkinter. I made two drop-down menus; the first one allows the user to select the unit they want to convert from, and the second one allows them to choose the unit they want to convert to. I want to disable all the options that do not make sense in the second menu after they have selected an option in the first one (if they want to convert kilograms it would not make sense to choose centimeters in the second menu)
I have tried to use a StringVar() to change the state of the menu, but it is not working. I have no idea of what to do next. I have been using the documentation of Tutorialspoint, but I cannot find anything that works (first time using tkinter).
import tkinter as tk
root = tk.Tk()
root.geometry('600x600')
my_var = tk.StringVar()
my_var.set('active')
unit_1 = tk.Menubutton(root,text='This is the first menu button',bg='white',activebackground='#2E64FE',activeforeground='#FFFFFF')
menu_1 = tk.Menu(unit_1)
unit_1.config(menu=menu_1)
menu_1.add_command(label='Inches',command= lambda: my_var.set('disabled') )
menu_1.add_command(label='Kilograms')
unit_2 = tk.Menubutton(root,text='This is the second menu button',bg='white',activebackground='#2E64FE',activeforeground='#FFFFFF')
menu_2 = tk.Menu(unit_2)
unit_2.config(menu=menu_2)
menu_2.add_command(label='Centimeters')
menu_2.add_command(label='Pounds',state= my_var.get())
unit_1.place(relx=0.03,rely=0.08,relheight=0.04,relwidth=0.45)
unit_2.place(relx=0.52,rely=0.08,relheight=0.04,relwidth=0.45)
root.mainloop()
Here I am trying to make the button 'Inches' in the first menu to disable the button 'Pounds' in the second menu, but when I click on 'Inches' nothing happens to 'Pounds'.
tk.StringVar() is used to change text on something, for example if you want to have a button with dynamic text, you may want to use a tk.StringVar() for that.
What you want to do is something different; you want to change the configuration of a label. So you need to find the element and adjust its state:
import tkinter as tk
root = tk.Tk()
root.geometry('600x600')
my_var = tk.StringVar()
my_var.set('active')
unit_1 = tk.Menubutton(root,text='This is the first menu button',bg='white',activebackground='#2E64FE',activeforeground='#FFFFFF')
menu_1 = tk.Menu(unit_1)
unit_1.config(menu=menu_1)
menu_1.add_command(label='Inches', command=lambda: disable_pounds())
menu_1.add_command(label='Kilograms', command=lambda: disable_centimeters())
unit_2 = tk.Menubutton(root,text='This is the second menu button',bg='white',activebackground='#2E64FE',activeforeground='#FFFFFF')
menu_2 = tk.Menu(unit_2)
unit_2.config(menu=menu_2)
menu_2.add_command(label='Centimeters')
menu_2.add_command(label='Pounds',state= my_var.get())
unit_1.place(relx=0.03,rely=0.08,relheight=0.04,relwidth=0.45)
unit_2.place(relx=0.52,rely=0.08,relheight=0.04,relwidth=0.45)
def disable_pounds():
menu_2.entryconfig("Pounds", state="disabled")
menu_2.entryconfig("Centimeters", state="active")
def disable_centimeters():
menu_2.entryconfig("Pounds", state="active")
menu_2.entryconfig("Centimeters", state="disabled")
root.mainloop()

Is it possible to make Tkinter scrollbars move independently of each other in different Toplevel windows?

Imagine there are two Tkinter.Toplevel() windows, called Window_1 and Window_2, which can be opened by clicking the same button (lets called Button_0).
Button_0 is pressed and Window_1 pops up. In Window_1, I can scroll up and down using a mouse pad (MAC OS). After that, I left Window_1 open.
Button_0 is pressed again and Window_2 pops up, while Window_1 stays open. In Window_2, I can again scroll up and down.
Now, I go back to Window_1 and try to scroll using mouse pad, contents in Window_1 DO NOT MOVE, but contents in Window_2 DO MOVE.
Then I close Window_2, and try to scroll on Window_1, this time I got error messages asking for a canvas on Window_2.
I did bind function,
def on_vertical(canvas,event):
canvas.yview_scroll(-3 * event.delta, 'units')
to a canvas inside each windows. As far as I know about the error, it seems that this function could not be used twice at the same time (both windows are opened).
I would like the way that when both Windows stay open. While on each window, I can scroll up-down while the another one do not move. Is it possible to code that?
This is the code example (please do noted that the Window name is not corrected label.)
from tkinter import *
######################## FUNCTIONS (DEF) ########################
def on_vertical(canvas,event):
canvas.yview_scroll(-3 * event.delta, 'units')
######################## FUNCTIONS (CLASS) ########################
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
#INITIAL WINDOW
def init_window(self):
self.master.title("Main Window")
self.pack(fill=BOTH, expand=1)
Button(self, text="Button_0",command = self.load_and_print).place(x = 7, y = 95)
# creating a button instance
Button(self, text="EXIT PROGRAM", command=self.client_exit).place(x=500, y=250)
#OPEN A NEW WINDOW CONTAINING STOCK LISTS
def load_and_print(self):
new_window = Toplevel(self)
new_window.title("Window")
canvas = Canvas(new_window, width = 800, height = 500, scrollregion = (0, 0, 0, 2500))
frame = Frame(canvas)
vbar = Scrollbar(new_window, orient = VERTICAL, command = canvas.yview)
vbar.pack(side = RIGHT,fill = Y)
canvas.create_window(0,0, window = frame, anchor = NW)
canvas.config(yscrollcommand = vbar.set)
canvas.pack(side = TOP,expand = True,fill = BOTH)
canvas.bind_all('<MouseWheel>', lambda event, canvas=canvas: on_vertical(canvas,event))
#MAKE PROGRAM EXIT
def client_exit(self):
exit()
######################## MAIN PROGRAMME ########################
#call window
root = Tk()
#size of the window
root.geometry("700x300")
app = Window(root)
root.mainloop()
root.update()
The problem is that you are using bind_all instead of bind for the mousewheel event.
Because you're using bind_all, each time you create a new window it replaces the old binding with a new binding. No matter which window gets the event, your function will always only work for the last window to be created. And, of course, when that window is destroyed then the mouse binding will throw an error since the canvas no longer exists.
Using bind
One solution is simple: use bind instead of bind_all.
canvas.bind_all('<MouseWheel>', lambda event, canvas=canvas: on_vertical(canvas,event))
Using bind_all
If you want the benefits of bind_all -- namely, that the scrolling works even if the mouse is over some other widget, you need to modify on_vertical to figure out which canvas to scroll at the time that it is running instead of having the canvas being passed in.
You can do that with a little bit of introspection. For example, the event object knows which widget received the event. From that you can figure out which window the mouse is in, and from that you can figure out which canvas to scroll.
For example, move the binding up to the __init__ and change it like this:
self.bind_all('<MouseWheel>', on_vertical)
Next, change on_vertical to figure out which canvas to scroll. In the following example I assume each toplevel has exactly one canvas and that you always want to scroll that canvas (ie: you lose the ability to scroll text widgets and listboxes)
If that's not the case, you can add whatever logic you want to figure out which widget to scroll.
def on_vertical(event):
top = event.widget.winfo_toplevel()
for child in top.winfo_children():
if child.winfo_class() == "Canvas":
child.yview_scroll(-3 * event.delta, 'units')
break

Tkinter display different frames

UPDATE : The problem was solved by removing the window.mainloop() in my second function.
I'm trying to make a game in Python 3.7 using tkinter.
The game begins with a menu (button-widgets in a frame). Clicking in the 'Play' button should open another menu using a different frame. This second menu should contain a 'back' button to return to the first menu.
Each menu is defined in a function. So to go from the main menu to the play menu I call the function playMenu(window) in the function used as command by the 'Play' button.
It looks like this :
def clickButtonPlay():
menuFrame.grid_remove()
playMenu(window)
menuFrame.grid()
In the play menu, the function used as 'back button' command put an end to the function by destroying its frame and using return.
So the program should get back to the clickButtonPlay() function and show the frame of the main menu back, but instead I get a tkinter error :
_tkinter.TclError: can't invoke "grid" command: application has been destroyed
But my frame menuFrame hasn't been destroyed, just un-grid!
Can anyone help me understand what's wrong with the code or find an easier way to do the same thing?
Thank you very much!
Here's a sample of how my program works:
mainMenu file :
import tkinter as tk
from PlayMenu import playMenu
window = tk.Tk()
window.grid()
def menu(window):
def clickButtonPlay():
menuFrame.grid_remove()
playMenu(window)
menuFrame.grid()
menuFrame = tk.Frame(window)
menuFrame.grid()
background = tk.Label(menuFrame, image= backgroundImage)
background.grid()
playButton = tk.Button(menuFrame, image= playButtonImage[0], command= clickButtonPlay)
playButton.place(relx= 0.5, rely= 0.15)
window.mainloop()
menu(window)
playMenu file :
class MyError(Exception):
pass
def _playMenu(window):
def clickButtonBack():
playMenuFrame.destroy()
raise MyError
playMenuFrame = tk.Frame(window)
playMenuFrame.grid()
background = tk.Label(playMenuFrame, image= backgroundImage)
background.grid()
backButton = tk.Button(playMenuFrame, image= backButtonImage[0], command= clickButtonBack)
backButton.place(relx=0.375, rely=0.8)
window.mainloop()
def playMenu(window):
try:
return _playMenu(window)
except MyError:
return
The problem (or at least a problem) is that you're calling mainloop more than once. Each time you call it, a new infinite loop is created. The new loop won't exit until the main window is destroyed. Once that happens, the previous loop will likely throw errors since the widgets it's managing no longer exist.

Dropdown menu command execution

Is it possible to get a drop-down menu using python's tkinter modules which execute commands when clicked on?
# Imports tkinter
from tkinter import *
#creates a root, then a popup and a frame inside to work with
root = Tk()
popup = Toplevel()
popup_frame = Frame(popup)
#creates a labelframe to hold the label and menu
labelframe_example = LabelFrame(root, text="Title For Widget", pady=3, padx=3)
#label providing description about menu
Label(labelframe_example, text="Descriptive label", width=25, height=0).pack()
#options displayed in dropdown menu
options = ["Option1","Option2", "Option3"]
#gains the variable of what is in the menu
variable = StringVar(labelframe_example)
#sets the inital text for the menu, before an option is selected
variable.set("Pre-option text placeholder")
#places the dropdown menu in the labelframe, with the displayed text being whatever the variable is and calls the options, expands the menu to size of labelframe
OptionMenu(labelframe_example, variable, *options).pack(fill=BOTH, expand=1)
#packs frame
labelframe_example.pack()
#creates a function to destroy old frame inside the popup window and recreate it using the information for option 1
def executed_command_1(popup_frame):
popup_frame.destroy()
popup_frame = Frame(popup)
Label(popup_frame, text="Hey Look it worked, a new frame was created for option 1").pack()
#creates a function to destroy old frame inside the popup window and recreate it using the information for option 2
def executed_command_2(popup_frame):
popup_frame.destroy()
popup_frame = Frame(popup)
Label(popup_frame, text="Hey Look it worked, a new frame was created for option 2").pack()
#creates a function to destroy old frame inside the popup window and recreate it using the information for option 3
def executed_command_3(popup_frame):
popup_frame.destroy()
popup_frame = Frame(popup)
Label(popup_frame, text="Hey Look it worked, a new frame was created for option 3").pack()
#unsure how to actually make a way for option specific function to open
option_execution_button = Button(root, text="Open Option", width=25, padx=5, pady=5,
command=lambda:executed_command_1/2/3(popup_frame)).pack()
In other words, there are 2 windows, 1 window has a widget with a drop-down menu, and the other should update based on whichever option was selected, the button is not actually necessary if there is a way for this to happen without the button that would be fine too.
I just can't figure out a way to do it, any help is welcome.
Sorry if its hard to understand, if you don't understand I will attempt to clarify further.

How to place buttons left and on top of each other python3.4 tkinter

import tkinter as tk
def pressed():
print("Button Pressed!")
def create_layout(frame):
"""
Add two buttons to the frame.
Both buttons should have the callback (command) pressed, and they should
have the labels "Button1" and "Button2".
Args:
frame (tk.Frame): The frame to create the two buttons in.
"""
button1 = tk.Button(frame, text="Button1", command=pressed)
button1.pack(side=tk.TOP, pady=20)
button2 = tk.Button(frame, text="Button2", command=pressed)
button2.pack(side=tk.TOP, ipadx=20)
There is my code above, It packs the button1 on top of button2 as intended but they are placed in the middle of the window, I need to place them to the left while keeping them on top of each other. How would I do this? This is just a exercise from my tutorial which I didn't get to and as you guys can see I have attempted. Thanks in advance.
The pack method has several options, one of which controls how the widget is aligned in its space.
In your case, you want the anchor option. You want to anchor your widgets to the west (left) side of the container:
button1.pack(..., anchor="w")
button2.pack(..., anchor="w")
For a tiny bit more information see https://docs.python.org/2/library/tkinter.html#packer-options

Resources