Dropdown menu command execution - python-3.x

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.

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

Python Tkinter menubar command displays out of window

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)

AttributeError: 'NoneType' object has no attribute '_root' (My case is different)

from tkinter import*
decision=0
fs = IntVar()
def coverscreen():
slide=Tk() #Init window
slide.title('The Castle of Redemption BETA') #Give window a title
frame=Frame(slide)
btn1=Button(slide,text='Start Game',command=slide.destroy) #Init 1st button
fsbox=Checkbutton(frame,text='Fullscreen',\
variable=fs, onvalue=1,offvalue=0)
img=PhotoImage(file='cover.gif') # Init picture
label = Label(image=img) # Init label that contains picture
label.image = img # keep a reference!
label.pack() # Places the label on the window
btn1.pack(side=BOTTOM,pady=5) # Places the 1st button on the window
fsbox.pack()
frame.pack(padx=50,pady=50)
slide.mainloop() # Starts the window
def page(name,b1,b2,write,f,fscreen):
slide=Tk() #Init window
if fscreen == 1:
slide.overrideredirect(True)
slide.geometry("{0}x{1}+0+0".format(slide.winfo_screenwidth(), slide.winfo_screenheight()))
slide.title(name) #Give window a title
btn1=Button(slide,text=b1,command=slide.destroy) #Init 1st button
btn2=Button(slide,text=b2,command=slide.destroy) #Init 2nd button
txt=Label(slide,text=write)# Init story text
img=PhotoImage(file=f) # Init picture
label = Label(image=img) # Init label that contains picture
label.image = img # keep a reference!
label.pack() # Places the label on the window
btn1.pack(side=BOTTOM,pady=5) # Places the 1st button on the window
btn2.pack(side=BOTTOM,pady=5) # Places the 2nd button on the window
txt.pack(side=TOP,pady=5) # Places the text on the window
slide.mainloop() # Starts the window
coverscreen()
page('Start','Continue','Go Back','Example Story Text.','cover.gif',fs.get()) #Example of the created function 'page'
I am making a game with a menu that appears on startup that has a checkbox, that when checked will open the game window in fullscreen. When I try to run the program I get this error:
AttributeError: 'NoneType' object has no attribute '_root'
The problem is that you are defining fs = IntVar() before you have created the root window Tk().
Note:
It is not recommended to create multiple root windows Tk(), if you want to display a second or more windows use a Toplevel widget.
If you only want a single window at all times then use frames to contain the widow contents then create and destroy those while using the same root.

binding menu item to notebook tab in python

I would like to bind a menu item to a notebook frame using a function(like gotofirst tab).
for instance a menu: file with 3 item such that if I click on the 3rd item in the menu file, the 3rd tab in the notebook should be selected or it should go to the 3rd tab and at the same time not see other tabs(hide them or disable them).
from tkinter import *
from tkinter import ttk
### defining functions ###
def GotoFirst():
pass
def Gotosecond():
pass
def Gotothird():
pass
### Frame and notebook ###
root = Tk()
root.geometry('1000x700+1000+40')
root.resizable(FALSE,FALSE)
root.rowconfigure(0,weight=1)
root.columnconfigure(0,weight=1)
root.title("Test Fieldbook")
fieldbook = ttk.Notebook(root)
f1 = ttk.Frame(fieldbook);
f2 = ttk.Frame(fieldbook);
f3 = ttk.Frame(fieldbook);
fieldbook.add(f1, text="1st")
fieldbook.add(f2, text="2nd")
fieldbook.add(f3, text="3rd")
fieldbook.grid(row=0, column=0, sticky=(N,W,S,E))
### Creating menu bar ###
menubar=Menu(root)
filemenu=Menu(menubar)
filemenu.add_command(label="Help Docs",command=GotoFirst)
filemenu.add_command(label="About ESB",command=Gotosecond)
filemenu.add_command(label="About ESB",command=Gotothird)
menubar.add_cascade(label="Help",menu=filemenu)
root.config(menu=menubar)
root.mainloop()
You can use the select method:
fieldbook.select(0)
and
fieldbook.select(f1)
do the same thing.
Thank you for the hint, I manage to make it work.
fieldbook.tab(f1, state='normal')
fieldbook.select(f1)
fieldbook.hide(f3)
with .tab() I can change the state as I wish and with .hide() I can make a specific tab disappear momentarily and call it back anytime with .add()

Resources