Python: How to use a tkinter Checkbutton in OOP functions - python

I have got stuck with this particular bit of code. I have condensed down the problem to this section.
I am running a sort of menu in Python, where the first menu sends you to the second menu, and in the second menu, there is a checkbutton the user can toggle on/off. In the third menu, I want it to read if the checkbutton is on/off and convert it to a Boolean. Code:
import tkinter as tk
class MainMenu(object):
def __init__(self):
self.launch_MainMenu()
def launch_MainMenu(self):
self.master = tk.Tk()
tk.Button(self.master,text="MY BUTTON",command= lambda:self.launch_SideMenu()).grid()
tk.mainloop()
def launch_SideMenu(self):
self.master2 = tk.Tk()
self.var1 = tk.IntVar()
tk.Checkbutton(self.master2,variable=self.var1).grid()
tk.Button(self.master2,text="Test",command= lambda:self.launch_FinalMenu()).grid()
def launch_FinalMenu(self):
d = bool(int(self.var1.get()))
print(d,self.var1.get())
mainMenu = MainMenu()
Output: Whether the checkbox is on or off, it outputs "False 0".
Any help would be very much appreciated!

As per the hint from Lafexlos, the error is in calling tk.Tk() twice. For a new window, you must use tk.Toplevel().
Simply changing the keyline to:
self.master2 = tk.Toplevel()
fixes everything. This took me a long time to work out. Thanks for the help, and best of luck to you coders reading this in the future.

Related

Tkinter winfo_ismapped() method not working

I have a program in python which in which I use Listboxes, buttons and labels. So today I came conflicting with a problem. I wanted to make my listbox appear when a button is clicked and disappear when the same button is clicked again. How can I achieve this? I tried using the winfo_ismapped() method but didnt seem to work. I think I might have done something crazy. If so, please point it out and give me a corrected answer. Else please tell me a better way to do it.
My Code:
import tkinter as tk
from tkinter import *
root = tk.Tk()
root.geometry('500x500')
def showMenu():
overlay = Listbox(root, bg="green", height=22, width=58)
if overlay.winfo_ismapped() == 0:
overlay.place(x=0,y=35)
else:
overlay.placeforget()
button = tk.Button(root,text="place/remove", command=showMenu)
button.place(x=0,y=0)
root.mainloop()
Actually it comes when I press the button but hide after I press it again.
In the same way I have another issue with these labels too.
CODE:
import tkinter as tk
root = tk.Tk()
def placeFun():
successtext = tk.Label(root, text="Success", anchor='nw', bg="#212121", fg="#ff3300",font=("Consolas", 15, "bold"))
if successtext.winfo_ismapped() == 0:
successtext.place(x=0,y=50)
else:
succestext.forget()
button = tk.Button(root, text='place/rem', width=25, command=placeFun)
button.place(x=0,y=0)
root.mainloop()
Please Note: I want a professional way to handle this, I said it because, I know a way in which we use variables like:
globalvartimes = 0
def somefunc():
if times % 2 == 0:
show the listbox
global times
times += 2
else:
remove the listbox
times += 1
*This shows the listbox when times is even and remove it when it's odd.
These makes the code look non-professional and long.
The problem is every time showMenu() is called another Listbox is created. To fix that, create the Listbox outside of the function (so it's a global).
(I also noticed you misspelled the name of place_forget() method.)
import tkinter as tk
from tkinter import *
root = tk.Tk()
root.geometry('500x500')
def showMenu():
if overlay.winfo_ismapped(): # Placed?
overlay.place_forget()
else:
overlay.place(x=0,y=35)
overlay = Listbox(root, bg="green", height=22, width=58)
button = tk.Button(root,text="place/remove", command=showMenu)
button.place(x=0,y=0)
root.mainloop()
This looks like it is what is wrong with your Label example, too.
Note: If you want to write "professional" code, I suggest you read (and start following) the
PEP 8 - Style Guide for Python Code.

How do I bind the ListBox of a ComboBox in Python Tkinter

I am looking to make a bind that allows me to place a certain value depending on the key pressed by the user.
Here is an example code in which I do it:
from tkinter import *
from tkinter import ttk
v=Tk()
combo = ttk.Combobox(values=['item1','item2','item3','item4'], state='readonly')
combo.current(0)
combo.pack()
def function(Event):
if(Event.char in '1234'):
combo.set(f'item{Event.char}')
combo.bind('<Key>', function)
v.mainloop()
They will tell me "If that works, what are you doing asking this?"
Well it turns out that if the Combobox is deployed, the bind stops working.
How could the problem be solved?
I know that this part of the question should not be asked, since it has nothing to do with the problem in question. But I would like to ask that if something is wrong in this question, or misspelled, or whatever, be informed. The page of "how to ask a good question" does not serve me, since from my point of view, I do everything as they say there.
I did my best to make what is written here as detailed and understandable as possible.
Hope your understanding, thank you.
Based on what Henry Yik proposed and studying what was returned by Event.widget, I found the solution to the problem.
from tkinter import *
from tkinter import ttk
v=Tk()
# I create two test combobox
for _ in range(2):
combo = ttk.Combobox(values=['item1','item2','item3','item4'], state='readonly')
combo.current(0)
combo.pack()
# I create a test entry to test if the function correctly recognizes when it should be executed
entrada = Entry()
entrada.pack()
def function(Event):
"""
If Event.widget is a str and ends with ".popdown.f.l" I consider it to be the Listbox,
I get the path of the Combobox it belongs to and convert it to a widget.
Afterwards, I set the value of Event.widget to that of the supposed combobox.
"""
if(isinstance(Event.widget, str) and Event.widget.endswith(".popdown.f.l")):
Event.widget = v._nametowidget(Event.widget[:-len(".popdown.f.l")])
# If Event.widget is not a Combobox, it stops the execution of the function.
if(not isinstance(Event.widget, ttk.Combobox)):
return
if(Event.char in '1234'):
Event.widget.set(f'item{Event.char}')
v.bind_all('<Key>', function)
v.mainloop()

Python Tkinter how to hide a widget without removing it

I know similar things have been asked a lot, but I've tried to figure this out for two hours now and I'm not getting anywhere. I want to have a button in a Tkinter window that is only visible on mouseover. So far I failed at making the button invisible in the first place (I'm familiar with events and stuff, that's not what this question is about) pack_forget() won't work, because I want the widget to stay in place. I'd like some way to do it like I indicated in the code below:
import tkinter as tki
class MyApp(object):
def __init__(self, root_win):
self.root_win = root_win
self.create_widgets()
def create_widgets(self):
self.frame1 = tki.Frame(self.root_win)
self.frame1.pack()
self.btn1 = tki.Button(self.frame1, text='I\'m a button')
self.btn1.pack()
self.btn1.visible=False #This doesnt't work
def main():
root_win = tki.Tk()
my_app = MyApp(root_win)
root_win.mainloop()
if __name__ == '__main__':
main()
Is there any way to set the visibility of widgets directly? If not, what other options are there?
Use grid as geometry manager and use:
self.btn1.grid_remove()
which will remember its place.
You can try using event to call function.
If "Enter" occurs for button then call a function that calls pack()
and if "Leave" occurs for button then call a function that calls pack_forget().
Check this link for event description:List of All Tkinter Events
If you wish your button to stay at a defined place then you can use place(x,y) instead of pack()

How can I perform an action after mainloop() has been called in Tkinter?

I want to make a GUI command line using the Text widget. For debugging purposes, I am trying to print whatever the user types into the separate GUI window to the system terminal. I know that it is frowned upon to mix GUI and Text Based commands into the same script, but I am just debugging, so forgive me 😉
Here is my code:
from Tkinter import *
main = Tk()
console = Text(main)
console.pack()
main.mainloop()
while True:
text = console.get("1.0", "end-1c")
print(text)
My current issue is that when the mainloop starts, (of course) the while loop doesn't. If I were to move the while loop in front of the mainloop call, it would never call mainloop. I really want it to continuously check for new text.
Is there a way to like "pause" the mainloop, or just carry out the command, maybe on a new thread or something?
I want to avoid using main.after(), but if that is the only way, then so be it. ¯\(°_o)/¯
I recommend using main.after(), as it's the canonical way to do things like this in Tkinter. The following will also ensure that it only tries to print every second, instead of as fast as the console can handle it (as the while loop in your code would do if it worked).
def print_console():
print(console.get("1.0", "end-1c"))
main.after(1000, print_console)
print_console()
main.mainloop()
You can also bind widgets to "Modified"
from Tkinter import *
class TextModified():
def __init__(self):
root = Tk()
self.txt = Text(root)
self.txt.pack()
self.txt.focus_set()
self.txt.bind('<<Modified>>', self.changed)
Button(text='Exit', command=root.quit).pack()
root.mainloop()
def changed(self, value=None):
flag = self.txt.edit_modified()
if flag: # prevent from getting called twice
print "changed called", self.txt.get("1.0", "end-1c")
## reset so this will be called on the next change
self.txt.edit_modified(False)
TM=TextModified()

How to test to see if a top-level window is open?

I'm feeling somewhat like Python programming may not be my thing....
I have created a tkinter GUI that uses a button callback to open another window (other searches say this window should be a top-level window) and it works pretty good, how-ever each time the button is pressed it opens another identical (as far as I can tell) window.
Question: how can I test to see if a window (opened with the button) already exists and thus prevent duplicates from being generated???
NOTE: I am not (yet) a OOP programmer so please avoid that paradigm if possible...
regards,
Bill W.
I am not (yet) a OOP programmer so please avoid that paradigm if possible...
Sooner or later, you'll have to understand object-oriented programming if you want to program in Python successfully. The alternative (global variables and functions everywhere) is not definitely a good approach. Even Tkinter has lots of classes with its respective methods, so it looks like it is necessary for your purpose.
Back to your question, a solution could be setting the Toplevel window as an attribute of a class where you wrap all your application, and only open a new window if this attribute is None. To set this attribute to None when you close the window, you can use the protocol method to set a callback.
This is a small working example where you can see how it would work:
import Tkinter as tk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.button = tk.Button(self, text="Open a new window", command=self.openwindow)
self.button.pack()
self.toplevel = None
def openwindow(self):
if self.toplevel is None:
self.toplevel = tk.Toplevel(self)
self.toplevel.protocol('WM_DELETE_WINDOW', self.removewindow)
def removewindow(self):
self.toplevel.destroy()
self.toplevel = None
app = App()
app.mainloop()
You can use this method winfo_exists() to check if the window exists.
if the return value is 0, it means the window doesn't exist.
if the return value is 1, it means the window exists.
Sample Code...
from tkinter import *
mainWindow = Tk()
mainWindow.title("This is main Window")
# create a top-level window
newWindow = Toplevel(mainWindow)
newWindow.title("This is Toplevel window")
print("Before destroying window = " + str(newWindow.winfo_exists()))
newWindow.destroy()
print("After destroying window = " + str(newWindow.winfo_exists()))
mainloop()
Output is...
# Before destroying window = 1
# After destroying window = 0

Categories