I feel like I've scoured the web for an eternity, rephrased my question a thousand times for something I feel like should be very simple.
I wonder if there is a way to check if a Tkinter Widget is active (not greyed out / disabled). I have a set of OptionMenus that start out disabled, and are configured to state=ACTIVE when they click a checkbox, so that the user can select which OptionMenus they want to use.
When I try to "submit" the fields in the OptionMenus, I only want the ones that are ACTIVE. I already tried if OptionMenu.state == ACTIVE but then I get an error that OptionMenu has no attribute state, even though I configure that earlier.
Here is a sample of my code:
from tkinter import *
class Application(Frame):
# Initializing the window and the required variables
def __init__(self, master=None):
Frame.__init__(self, master)
self.checkbox_in_use = BooleanVar(self, False)
self.checkbox = Checkbutton(self, text="check",
var=self.checkbox_in_use,
command=self.check_change
self.checkbox.grid(row=0, column=1, sticky='W')
self.menu = OptionMenu(title_setting,
"Menu",
"Menu",
["Menu1", "Menu2"])
self.menu.grid(row=1, column=1)
self.menu.config(state=DISABLED)
submit = Button(self, text="submit",
command=self.submit_function)
submit.grid(row=2, column=0)
self.master = master
self.init_window()
# Initialize the window
def init_window(self):
self.master.title("Example")
self.pack(fill=BOTH, expand=1)
def check_change(self):
if self.checkbox_in_use.get():
self.menu.config(state=ACTIVE)
else:
self.menu.config(state=DISABLED)
def submit_function(self):
# This is the part I want to do something with.
if self.menu.state == ACTIVE:
print("You are good to go! Do the stuff.")
root = Tk()
root.geometry("400x300")
app = Application(root)
root.mainloop()
Thank you for all responses.
All you need is cget() for this. self.menu.cget('state') will do the trick.
That said I want to point out some other things in your code.
You Application class already has an __init__ at the start so why use:
# Initialize the window
def init_window(self):
self.master.title("Example")
self.pack(fill=BOTH, expand=1)
You really should not pack the frame from inside the frame class but rather when calling the class. Also pack wont work here it will throw an error. Do this instead: app = Application(root).grid().
Take a look at the reformatted example below (with cget()).
from tkinter import *
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master.title("Example")
self.checkbox_in_use = BooleanVar(self, False)
self.checkbox = Checkbutton(self, text="check", var=self.checkbox_in_use, command=self.check_change)
self.checkbox.grid(row=0, column=1, sticky='W')
self.menu = OptionMenu(master,"Menu","Menu",["Menu1", "Menu2"])
self.menu.grid(row=1, column=1)
self.menu.config(state=DISABLED)
Button(self, text="submit", command=self.submit_function).grid(row=2, column=0)
def check_change(self):
if self.checkbox_in_use.get():
self.menu.config(state=ACTIVE)
else:
self.menu.config(state=DISABLED)
def submit_function(self):
print(self.menu.cget('state'))
root = Tk()
root.geometry("400x300")
app = Application(root).grid()
root.mainloop()
Related
My aim is to generate a window with a button "NewWindow" on it. If I press the button, the program should open a new window. This window I stored in a class "NewWindow" to quickly reproduce it.
In this "NewWindow" I have another button. If I press it the label of basic window should be updated and the window "NewWindow" should be closed automatically.
Here is my code:
from tkinter import *
class NewWindow(Toplevel):
def __init__(self, master = None):
super().__init__(master = master)
self.title('NewWindow')
self.lb = Label(self, text='Hello')
self.lb.grid(column=0, row=0, columnspan=1)
self.bt1 = Button(self, text="apply Hello", command= self.bt_press)
self.bt1.grid(column=0, row=1)
def bt_press(self):
window.basic_lb.text = "Hello"
window = Tk()
def new_Editor():
a = NewWindow(window)
window.title("BasicWindow")
window.basic_lb = Label(window, text='None')
window.basic_lb.grid(column=0, row=0, columnspan=1)
window.basic_bt = Button(window, text="NewWindow", command=new_Editor)
window.basic_bt.grid(column=0, row=1)
window.mainloop()
Problems:
At start both windows NewWindow and BasicWindow are displayd. I only want to open BasicWindow and NewWindow should be opened after button basic_bt is clicked. How can I solve it? (already solved by commed below)
Why the label text in basic_lb did not get some update after pressing self.bt1?
How is it possible to close NewWindow with use of bt_press method?
You have a few typos/errors in your code that are casuing some of your problems. As #Tim said, when you pass a function to a command like command=function(), it will be called on runtime, not when the button is pressed. You need to pass the function handle to the command, command=function. You got around this by using a lambda function in your button command, but it is easier to just have command=self.bt_press
Answering your second question, window.basic_lb.text = "Hello" is not how you change the text in a tkinter Label, use <Label>.config(text="Hello"). You also should use self.master and define self.master = master in __init__ instead of just using window, because while you can access window due to it not being defined in local scope, it's better to explicitly define it.
You can close a window using window.destroy().
Your working code is now:
from tkinter import *
class NewWindow(Toplevel):
def __init__(self, master = None):
super().__init__(master = master)
self.title('NewWindow')
self.master = master
self.lb = Label(self, text='Hello')
self.lb.grid(column=0, row=0, columnspan=1)
self.bt1 = Button(self, text="apply Hello", command=self.bt_press)
self.bt1.grid(column=0, row=1)
def bt_press(self):
self.master.basic_lb.config(text="Hello")
self.destroy()
window = Tk()
def new_Editor():
a = NewWindow(window)
window.title("BasicWindow")
window.basic_lb = Label(window, text='None')
window.basic_lb.grid(column=0, row=0, columnspan=1)
window.basic_bt = Button(window, text="NewWindow", command=new_Editor)
window.basic_bt.grid(column=0, row=1)
window.mainloop()
I'm just getting started coding in Python/Tkinter for a small Pymol plugin. Here I'm trying to have a toggle button and report its status when it is clicked. The button goes up and down, but toggleAVA never gets called. Any ideas why?
from Tkinter import *
import tkMessageBox
class AVAGnome:
def __init__(self, master):
# create frames
self.F1 = Frame(rootGnome, padx=5, pady=5, bg='red')
# checkbuttons
self.AVAselected = IntVar()
self.AVAselected.trace("w", self.toggleAVA)
self.AVAbutton = Checkbutton(self.F1, text='AVA', indicatoron=0, variable=self.AVAselected)
# start layout procedure
self.layout()
def layout(self):
self.F1.pack(side=TOP, fill=BOTH, anchor=NW)
#entry and buttons
self.AVAbutton.pack(side=LEFT)
def toggleAVA(self, *args):
if (self.AVAselected.get()):
avastatus = "selected"
else:
avastatus = "unselected"
tkMessageBox.showinfo("AVA status", avastatus)
def __init__(self):
open_GnomeUI()
def open_GnomeUI():
# initialize window
global rootGnome
rootGnome = Tk()
rootGnome.title('AVAGnome')
global gnomeUI
gnomeUI = AVAGnome(rootGnome)
I tested your code with Pymol.
Problem is because you use Tk() to create your window. You have to use Toplevel() and then it will work correctly with trace() or with command=.
Pymol is created with tkinter which can have only one window created with Tk() - it is main window in program. Every other window has to be created with Toplevel().
I have attached a working version of your code below. You can refer to it to learn where you went wrong. Generally, you have to mind how you structure your code if you are using a class format.This will help you visualize your code and debug better. You can read this discussion to help you.
from Tkinter import *
import tkMessageBox
class AVAGnome(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
# create frames
self.F1 = Frame(self, padx=5, pady=5, bg='red')
# checkbutton
self.AVAselected = IntVar()
self.AVAselected.trace("w", self.toggleAVA)
self.AVAbutton = Checkbutton(
self.F1, text='AVA', indicatoron=0, width=10,
variable=self.AVAselected)
# start layout procedure
self.F1.pack(side=TOP, fill=BOTH, anchor=NW)
self.AVAbutton.pack(side=LEFT) #entry and buttons
def toggleAVA(self, *args):
if (self.AVAselected.get()):
avastatus = "selected"
else:
avastatus = "unselected"
tkMessageBox.showinfo("AVA status", avastatus)
if __name__ == '__main__':
rootGnome = Tk()
rootGnome.title('AVAGnome')
gnomeUI = AVAGnome(rootGnome)
gnomeUI.pack(fill="both", expand=True)
gnomeUI.mainloop()
Update: The above code structure is for standalone tkinter programme. I am attempting to convert this working code to follow Pymol plugin example. Revised code is posted below and is susceptible to further revision.
# https://pymolwiki.org/index.php/Plugins_Tutorial
# I adapted from the example in the above link and converted my previous code to
#
from Tkinter import *
import tkMessageBox
def __init__(self): # The example had a self term here.
self.open_GnomeUI()
class AVAGnome(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
# create frames
self.F1 = Frame(self, padx=5, pady=5, bg='red')
# checkbutton
self.AVAselected = IntVar()
self.AVAselected.trace("w", self.toggleAVA)
self.AVAbutton = Checkbutton(
self.F1, text='AVA', indicatoron=0, width=10,
variable=self.AVAselected)
# start layout procedure
self.F1.pack(side=TOP, fill=BOTH, anchor=NW)
self.AVAbutton.pack(side=LEFT) #entry and buttons
def toggleAVA(self, *args):
if (self.AVAselected.get()):
avastatus = "selected"
else:
avastatus = "unselected"
tkMessageBox.showinfo("AVA status", avastatus)
# Note, I added a "self" term throughout function.
# Try w/ & w/o "self" to see which works.
def open_GnomeUI(self):
self.rootGnome = Tk()
self.rootGnome.title('AVAGnome')
self.gnomeUI = AVAGnome(self.rootGnome)
self.gnomeUI.pack(fill="both", expand=True)
self.gnomeUI.mainloop()
I am trying to make second menu button next to the one i already made but no matter what code i use, i cannot make a second one. I want to place it next the first button by using .grid() not .place()
heres the code
from tkinter import *
import tkinter.messagebox
import ctypes
class Application(Frame):
def __init__(self, master):
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
mb = Menubutton (self, text="Sniper Rifle", relief=RAISED)
mb.menu = Menu (mb, tearoff = 0)
mb["menu"] = mb.menu
self.m40a5Var = IntVar()
self.srrVar = IntVar()
mb.menu.add_checkbutton (label="M40A5", variable=self.m40a5Var)
mb.menu.add_checkbutton (label="SRR", variable=self.srrVar)
mb.grid()
root = Tk()
root.title("heeey")
root.geometry("180x100")
app = Application(root)
root.mainloop()
I don't understand the question. You say that no matter what you try you can't create a second button. All you have to do to create another button is... create another button:
mb = Menubutton(...)
another_mb = Menubutton(...)
...
mb.grid(row=0, column=0)
another_mb.grid(row=0, column=1)
So I am currently trying to create a button on a GUI that will let the user generate a new entry field.
I have no idea how to do this. I'm guessing that it will require a lambda function, but apart from that, I have no idea.
Here's the basic code I have so far:
from tkinter import *
class prac:
def autoAddWidget(self,frame,x,y):
self.entryField = Entry(frame,text="Entry Field")
self.entryField.grid(row=x, column=y)
#lambda function?
def __init__(self, master):
frame = Frame(master, width=60, height=50)
frame.pack()
x=1
self.addWidgetButton = Button(frame, text="Add new widget", command=self.autoAddWidget(frame, x,0))
self.addWidgetButton.grid(row=0, column=0)
x+=1
root = Tk()
app = prac(root)
root.mainloop()
Would appreciate the help.
Thanks
You're passing to the command argument result from the method self.autoAddWidget(frame, x,0) not method itself. You have to pass there a reference to a callable object, a function that will be called when the event occurs. Please check a documentation next time before you ask the question.
Ok, I fixed the code, now it works:
from tkinter import *
class Prac:
def autoAddWidget(self):
self.entryField = Entry(self.frame,text="Entry Field")
self.entryField.grid(row=self.x, column=0)
self.x+=1
def __init__(self, master):
self.frame = Frame(master, width=60, height=50)
self.frame.pack()
self.x=1
self.addWidgetButton = Button(self.frame, text="Add new widget", command=self.autoAddWidget)
self.addWidgetButton.grid(row=0, column=0)
root = Tk()
app = Prac(root)
root.mainloop()
I'm attempting to create a class and inherit from Toplevel() so that the GUI elements of the class can be contained within a separate window. Usually I would just inherit from Frame() but for my purpose I need everything to be in a window. I'm writing the code in my GUI template script just so I can figure out how to get it working before I stick it in the actual script I want to use it in. Here is the code:
from Tkinter import *
import socket
myplayername = ''
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
class InfoLabel(Frame):
def __init__(self, name, value, bgc, nfgc, vfgc, master=None):
Frame.__init__(self, master)
self.pack()
Label(master=self, text=name, bg=bgc, fg=nfgc).pack({"side": "left"})
Label(master=self, text=value, bg=bgc, fg=vfgc).pack({"side": "left"})
class LabelEntry(Frame):
def __init__(self, name, variable, bgc, fgc, entrysize, master=None):
Frame.__init__(self, master)
self.pack()
Label(master=self, text=name, bg=bgc, fg=fgc).pack({"side": "left"})
Entry(master=self, textvariable=variable, bg=bgc).pack({"side": "left"})
class HostGameWindow(Toplevel):
def __init__(self):
global myplayername
Toplevel.__init__(self)
self.title('Host a Game')
hostname = socket.gethostname()
hostipaddr = socket.gethostbyname(hostname)
hostport = 11489
players = 0
portsv = StringVar(value=str(hostport))
numofplayers = StringVar(value=str(players))
myname = StringVar(value=myplayername)
hostgameframe = Frame(master=self, bg='#999', bd=3, relief=RIDGE, padx=5, pady=5).pack({"side": "left"})
hoststatusframe = Frame(master=self, bg='white', bd=3, relief=RIDGE).pack({"side": "left"})
hostbuttonframe = Frame(master=hostgameframe, bd=2, relief=RAISED, padx=5, pady=5).pack({"side": "bottom"})
InfoLabel(master=hostgameframe, name='Hostname:', value=hostname, bgc='#999', nfgc='blue', vfgc='red').pack({"side": "top"})
InfoLabel(master=hostgameframe, name='IP Address:', value=hostipaddr, bgc='#999', nfgc='blue', vfgc='red').pack({"side": "top"})
LabelEntry(master=hostgameframe, name='Host on port:', variable=portsv, bgc='#999', fgc='blue', entrysize=len(str(hostport))).pack({"side": "top"})
LabelEntry(master=hostgameframe, name='Players Joining:', variable=numofplayers, bgc='#999', fgc='blue', entrysize=2).pack({"side": "top"})
LabelEntry(master=hostgameframe, name='Player Name:', variable=myname, bgc='#999', fgc='blue', entrysize=16).pack({"side": "top"})
Button(master=hostbuttonframe, text='Host Game', width=10).pack({"side": "left"})
Button(master=hostbuttonframe, text='Start Game', width=10).pack({"side": "left"})
def close():
self.destroy()
def HostGameDialog():
HostGameWindow()
Button(master=self, text='Host a Game', command=HostGameDialog).pack()
root = Tk()
app = Application(master=root)
#root.wm_iconbitmap(default='INSERT ICON HERE')
#root.wm_title("TITLE OF PROGRAM")
#app.master.maxsize(640, 480)
app.master.minsize(640, 480)
app.master.resizable(0, 0)
app.mainloop()
app.quit()
Now for some reason when I click the 'Host a Game' Button, it calls the HostGameDialog() function and it creates the HostGameWindow(), BUT the window that gets created is sized down as small as it can be and all the GUI elements that should be contained in the HostGameWindow() class instead appear in the main Application() frame. And the thing which is really wierd to me is that it doesn't give any errors, it just puts all the widgets inside the main application frame instead of the Toplevel() that gets created.
What the heck am I doing wrong? Why aren't any the widgets placed inside the Toplevel()?
I've been at it for hours now and nothing is making sense. Please if you know anything that could be of some help to me let me know.
Wow, I've never had to wait so long for reply here before, this must be a pretty nifty problem, I still don't know what to do. Any ideas are greatly appreciated!
I guess no one know what to make of this... I'll keep checking here though!
SOLVED! turns out its not a good idea to create AND pack a widget on the same line if you create a reference to it. Creating and packing a widget on the same line only works if you just call the Widget(args*).pack(args*) without creating a reference to it.
When you do x=a().b(), what is stored in x is the result of b().
Consider the following line of code:
hostgameframe = Frame(self, bg='#999', bd=3,
relief=RIDGE, padx=5, pady=5).pack({"side": "left"})
If we collapse all of the options (for clarity) we're left with this:
hostgameframe = Frame(...).pack(...)
Can you see what's happening? hostgameframe is being set to the result of pack(...). pack always returns None, so hostgameframe is None. When you later create another widget and set this as it's master, that widget ends up going in the main window.
So, to solve your problem you need to separate the creation of the widget from the layout. Personally I think this is a best practice that you should always adhere to. For cases where you don't need to keep a reference to the widget, combining them into one statement is harmless. Even so, I think your code will be easier to manage if you get in the habit of always separating widget creation from widget layout.
hostgameframe = Frame(self, bg='#999', bd=3, relief=RIDGE, padx=5, pady=5)
hostgameframe.pack({"side": "left"})